mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-30 00:12:50 -05:00
General code cleanups
This commit is contained in:
@@ -7,79 +7,22 @@ import Quickshell
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Durations match M3 token tiers: short4/medium4/long4
|
|
||||||
readonly property int durShort: 200
|
readonly property int durShort: 200
|
||||||
readonly property int durMed: 450
|
readonly property int durMed: 450
|
||||||
readonly property int durLong: 600
|
readonly property int durLong: 600
|
||||||
|
|
||||||
readonly property int slidePx: 80
|
readonly property int slidePx: 80
|
||||||
|
|
||||||
// Material Design 3 motion curves (for QML BezierSpline)
|
|
||||||
// Use groups of: [cx1, cy1, cx2, cy2, endX, endY, ...]
|
|
||||||
// Single-segment cubics end with 1,1.
|
|
||||||
|
|
||||||
// Emphasized (multi-segment) – for on-screen-to-on-screen moves
|
|
||||||
readonly property var emphasized: [
|
readonly property var emphasized: [
|
||||||
0.05, 0.00, 0.133333, 0.06, 0.166667, 0.40,
|
0.05, 0.00, 0.133333, 0.06, 0.166667, 0.40,
|
||||||
0.208333, 0.82, 0.25, 1.00, 1.00, 1.00
|
0.208333, 0.82, 0.25, 1.00, 1.00, 1.00
|
||||||
]
|
]
|
||||||
|
|
||||||
// Emphasized decelerate – entering
|
|
||||||
readonly property var emphasizedDecel: [ 0.05, 0.70, 0.10, 1.00, 1.00, 1.00 ]
|
readonly property var emphasizedDecel: [ 0.05, 0.70, 0.10, 1.00, 1.00, 1.00 ]
|
||||||
|
|
||||||
// Emphasized accelerate – exiting
|
|
||||||
readonly property var emphasizedAccel: [ 0.30, 0.00, 0.80, 0.15, 1.00, 1.00 ]
|
readonly property var emphasizedAccel: [ 0.30, 0.00, 0.80, 0.15, 1.00, 1.00 ]
|
||||||
|
|
||||||
// Standard set – for small/subtle transitions
|
|
||||||
readonly property var standard: [ 0.20, 0.00, 0.00, 1.00, 1.00, 1.00 ]
|
readonly property var standard: [ 0.20, 0.00, 0.00, 1.00, 1.00, 1.00 ]
|
||||||
readonly property var standardDecel: [ 0.00, 0.00, 0.00, 1.00, 1.00, 1.00 ]
|
readonly property var standardDecel: [ 0.00, 0.00, 0.00, 1.00, 1.00, 1.00 ]
|
||||||
readonly property var standardAccel: [ 0.30, 0.00, 1.00, 1.00, 1.00, 1.00 ]
|
readonly property var standardAccel: [ 0.30, 0.00, 1.00, 1.00, 1.00, 1.00 ]
|
||||||
|
|
||||||
// readonly property QtObject direction: QtObject {
|
|
||||||
// readonly property int fromLeft: 0
|
|
||||||
// readonly property int fromRight: 1
|
|
||||||
// readonly property int fadeOnly: 2
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Slide transitions (surface/large moves)
|
|
||||||
// // Enter = emphasizedDecel, Exit = emphasizedAccel
|
|
||||||
// readonly property Component slideInLeft: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "x"; from: -root.slidePx; to: 0; duration: root.durMed
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.emphasizedDecel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// readonly property Component slideOutLeft: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "x"; to: -root.slidePx; duration: root.durShort
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.emphasizedAccel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// readonly property Component slideInRight: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "x"; from: root.slidePx; to: 0; duration: root.durMed
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.emphasizedDecel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// readonly property Component slideOutRight: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "x"; to: root.slidePx; duration: root.durShort
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.emphasizedAccel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Fade transitions (small/subtle moves)
|
|
||||||
// // Enter = standardDecel, Exit = standardAccel
|
|
||||||
// readonly property Component fadeIn: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "opacity"; from: 0.0; to: 1.0; duration: root.durMed
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.standardDecel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// readonly property Component fadeOut: Transition {
|
|
||||||
// NumberAnimation {
|
|
||||||
// properties: "opacity"; to: 0.0; duration: root.durShort
|
|
||||||
// easing.type: Easing.BezierSpline; easing.bezierCurve: root.standardAccel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@@ -52,20 +52,16 @@ Singleton {
|
|||||||
|
|
||||||
function onLightModeChanged() {
|
function onLightModeChanged() {
|
||||||
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
||||||
console.log("Light mode changed - updating dynamic colors");
|
|
||||||
colorUpdateTrigger++;
|
colorUpdateTrigger++;
|
||||||
colorsUpdated();
|
colorsUpdated();
|
||||||
|
|
||||||
// If dynamic theme is active, regenerate system themes with new light/dark mode
|
|
||||||
if (typeof Theme !== "undefined" && Theme.isDynamicTheme) {
|
if (typeof Theme !== "undefined" && Theme.isDynamicTheme) {
|
||||||
console.log("Regenerating system themes for new light/dark mode");
|
|
||||||
generateSystemThemes();
|
generateSystemThemes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractColors() {
|
function extractColors() {
|
||||||
console.log("Colors.extractColors() called, matugenAvailable:", matugenAvailable);
|
|
||||||
extractionRequested = true;
|
extractionRequested = true;
|
||||||
if (matugenAvailable)
|
if (matugenAvailable)
|
||||||
fileChecker.running = true;
|
fileChecker.running = true;
|
||||||
@@ -91,7 +87,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Colors.qml → home =", homeDir);
|
|
||||||
matugenCheck.running = true;
|
matugenCheck.running = true;
|
||||||
checkGtkThemingAvailability();
|
checkGtkThemingAvailability();
|
||||||
checkQtThemingAvailability();
|
checkQtThemingAvailability();
|
||||||
@@ -105,15 +100,12 @@ Singleton {
|
|||||||
command: ["which", "matugen"]
|
command: ["which", "matugen"]
|
||||||
onExited: (code) => {
|
onExited: (code) => {
|
||||||
matugenAvailable = (code === 0);
|
matugenAvailable = (code === 0);
|
||||||
console.log("Matugen in PATH:", matugenAvailable);
|
|
||||||
if (!matugenAvailable) {
|
if (!matugenAvailable) {
|
||||||
console.warn("Matugen missing → dynamic theme disabled");
|
|
||||||
ToastService.wallpaperErrorStatus = "matugen_missing";
|
ToastService.wallpaperErrorStatus = "matugen_missing";
|
||||||
ToastService.showWarning("matugen not found - dynamic theming disabled");
|
ToastService.showWarning("matugen not found - dynamic theming disabled");
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
if (extractionRequested) {
|
if (extractionRequested) {
|
||||||
console.log("Continuing with color extraction");
|
|
||||||
fileChecker.running = true;
|
fileChecker.running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,8 +119,6 @@ Singleton {
|
|||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
matugenProcess.running = true;
|
matugenProcess.running = true;
|
||||||
} else {
|
} else {
|
||||||
console.error("code", code);
|
|
||||||
console.error("Wallpaper not found:", wallpaperPath);
|
|
||||||
ToastService.wallpaperErrorStatus = "error";
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
ToastService.showError("Wallpaper processing failed");
|
ToastService.showError("Wallpaper processing failed");
|
||||||
}
|
}
|
||||||
@@ -146,7 +136,6 @@ Singleton {
|
|||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
const out = matugenCollector.text;
|
const out = matugenCollector.text;
|
||||||
if (!out.length) {
|
if (!out.length) {
|
||||||
console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr);
|
|
||||||
ToastService.wallpaperErrorStatus = "error";
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
ToastService.showError("Wallpaper Processing Failed");
|
ToastService.showError("Wallpaper Processing Failed");
|
||||||
return ;
|
return ;
|
||||||
@@ -158,7 +147,6 @@ Singleton {
|
|||||||
generateAppConfigs();
|
generateAppConfigs();
|
||||||
ToastService.clearWallpaperError();
|
ToastService.clearWallpaperError();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("JSON parse failed:", e);
|
|
||||||
ToastService.wallpaperErrorStatus = "error";
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
ToastService.showError("Wallpaper Processing Failed");
|
ToastService.showError("Wallpaper Processing Failed");
|
||||||
}
|
}
|
||||||
@@ -173,7 +161,6 @@ Singleton {
|
|||||||
|
|
||||||
function generateAppConfigs() {
|
function generateAppConfigs() {
|
||||||
if (!matugenColors || !matugenColors.colors) {
|
if (!matugenColors || !matugenColors.colors) {
|
||||||
console.warn("No matugen colors available for app config generation");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,8 +184,7 @@ Singleton {
|
|||||||
var secondary = dark.secondary || "#8ab4f8";
|
var secondary = dark.secondary || "#8ab4f8";
|
||||||
var inverse = dark.inverse_primary || "#6200ea";
|
var inverse = dark.inverse_primary || "#6200ea";
|
||||||
|
|
||||||
var content = `// AUTO-GENERATED on ${new Date().toISOString()}
|
var content = `layout {
|
||||||
layout {
|
|
||||||
border {
|
border {
|
||||||
active-color "${primary}"
|
active-color "${primary}"
|
||||||
inactive-color "${secondary}"
|
inactive-color "${secondary}"
|
||||||
@@ -209,8 +195,7 @@ layout {
|
|||||||
background-color "${bg}"
|
background-color "${bg}"
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
niriConfigWriter.command = ["bash", "-c", `echo '${content}' > niri-colors.generated.kdl`];
|
Quickshell.execDetached(["bash", "-c", `echo '${content}' > niri-colors.generated.kdl`]);
|
||||||
niriConfigWriter.running = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateGhosttyConfig() {
|
function generateGhosttyConfig() {
|
||||||
@@ -236,8 +221,7 @@ layout {
|
|||||||
var error_b = light.error || "#b00020";
|
var error_b = light.error || "#b00020";
|
||||||
var inverse_b = light.inverse_primary || "#bb86fc";
|
var inverse_b = light.inverse_primary || "#bb86fc";
|
||||||
|
|
||||||
var content = `# AUTO-GENERATED on ${new Date().toISOString()}
|
var content = `background = ${bg}
|
||||||
background = ${bg}
|
|
||||||
foreground = ${fg}
|
foreground = ${fg}
|
||||||
cursor-color = ${inverse}
|
cursor-color = ${inverse}
|
||||||
selection-background = ${secondary}
|
selection-background = ${secondary}
|
||||||
@@ -259,8 +243,10 @@ palette = 13=${tertiary_ctr_b}
|
|||||||
palette = 14=${inverse_b}
|
palette = 14=${inverse_b}
|
||||||
palette = 15=${fg_b}`;
|
palette = 15=${fg_b}`;
|
||||||
|
|
||||||
ghosttyConfigWriter.command = ["bash", "-c", `echo '${content}' > ghostty-colors.generated.conf`];
|
var ghosttyConfigDir = configDir + "/ghostty";
|
||||||
ghosttyConfigWriter.running = true;
|
var ghosttyConfigPath = ghosttyConfigDir + "/config-colors";
|
||||||
|
|
||||||
|
Quickshell.execDetached(["bash", "-c", `mkdir -p '${ghosttyConfigDir}' && echo '${content}' > '${ghosttyConfigPath}'`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkGtkThemingAvailability() {
|
function checkGtkThemingAvailability() {
|
||||||
@@ -273,97 +259,49 @@ palette = 15=${fg_b}`;
|
|||||||
|
|
||||||
function generateSystemThemes() {
|
function generateSystemThemes() {
|
||||||
if (systemThemeGenerationInProgress) {
|
if (systemThemeGenerationInProgress) {
|
||||||
console.log("System theme generation already in progress, skipping");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matugenAvailable) {
|
if (!matugenAvailable) {
|
||||||
console.warn("Matugen not available, cannot generate system themes");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wallpaperPath || wallpaperPath === "") {
|
if (!wallpaperPath || wallpaperPath === "") {
|
||||||
console.warn("No wallpaper path set, cannot generate system themes");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Generating system themes using matugen templates");
|
|
||||||
console.log("Wallpaper:", wallpaperPath);
|
|
||||||
console.log("Shell directory:", shellDir);
|
|
||||||
|
|
||||||
// Get current theme preferences
|
|
||||||
const isLight = (typeof Theme !== "undefined" && Theme.isLightMode) ? "true" : "false";
|
const isLight = (typeof Theme !== "undefined" && Theme.isLightMode) ? "true" : "false";
|
||||||
const iconTheme = (typeof Prefs !== "undefined" && Prefs.iconTheme) ? Prefs.iconTheme : "System Default";
|
const iconTheme = (typeof Prefs !== "undefined" && Prefs.iconTheme) ? Prefs.iconTheme : "System Default";
|
||||||
const gtkTheming = (typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) ? "true" : "false";
|
const gtkTheming = (typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) ? "true" : "false";
|
||||||
const qtTheming = (typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) ? "true" : "false";
|
const qtTheming = (typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) ? "true" : "false";
|
||||||
|
|
||||||
console.log("Theme mode:", isLight === "true" ? "light" : "dark");
|
|
||||||
console.log("Icon theme:", iconTheme);
|
|
||||||
console.log("GTK theming enabled:", gtkTheming);
|
|
||||||
console.log("Qt theming enabled:", qtTheming);
|
|
||||||
|
|
||||||
systemThemeGenerationInProgress = true;
|
systemThemeGenerationInProgress = true;
|
||||||
systemThemeGenerator.command = [shellDir + "/generate-themes.sh", wallpaperPath, shellDir, configDir, "generate", isLight, iconTheme, gtkTheming, qtTheming];
|
systemThemeGenerator.command = [shellDir + "/generate-themes.sh", wallpaperPath, shellDir, configDir, "generate", isLight, iconTheme, gtkTheming, qtTheming];
|
||||||
systemThemeGenerator.running = true;
|
systemThemeGenerator.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateGtkThemes() {
|
function generateGtkThemes() {
|
||||||
console.log("Generating GTK themes using matugen templates");
|
|
||||||
generateSystemThemes();
|
generateSystemThemes();
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateQtThemes() {
|
function generateQtThemes() {
|
||||||
console.log("Generating Qt themes using matugen templates");
|
|
||||||
generateSystemThemes();
|
generateSystemThemes();
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreSystemThemes() {
|
function restoreSystemThemes() {
|
||||||
console.log("Restoring original system themes");
|
|
||||||
|
|
||||||
const shellDir = root.shellDir;
|
const shellDir = root.shellDir;
|
||||||
if (!shellDir) {
|
if (!shellDir) {
|
||||||
console.warn("Shell directory not available, cannot restore system themes");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current theme preferences
|
|
||||||
const isLight = (typeof Theme !== "undefined" && Theme.isLightMode) ? "true" : "false";
|
const isLight = (typeof Theme !== "undefined" && Theme.isLightMode) ? "true" : "false";
|
||||||
const iconTheme = (typeof Prefs !== "undefined" && Prefs.iconTheme) ? Prefs.iconTheme : "System Default";
|
const iconTheme = (typeof Prefs !== "undefined" && Prefs.iconTheme) ? Prefs.iconTheme : "System Default";
|
||||||
const gtkTheming = (typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) ? "true" : "false";
|
const gtkTheming = (typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) ? "true" : "false";
|
||||||
const qtTheming = (typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) ? "true" : "false";
|
const qtTheming = (typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) ? "true" : "false";
|
||||||
|
|
||||||
console.log("Restoring to theme mode:", isLight === "true" ? "light" : "dark");
|
|
||||||
console.log("Icon theme:", iconTheme);
|
|
||||||
console.log("GTK theming enabled:", gtkTheming);
|
|
||||||
console.log("Qt theming enabled:", qtTheming);
|
|
||||||
|
|
||||||
systemThemeRestoreProcess.command = [shellDir + "/generate-themes.sh", "", shellDir, configDir, "restore", isLight, iconTheme, gtkTheming, qtTheming];
|
systemThemeRestoreProcess.command = [shellDir + "/generate-themes.sh", "", shellDir, configDir, "restore", isLight, iconTheme, gtkTheming, qtTheming];
|
||||||
systemThemeRestoreProcess.running = true;
|
systemThemeRestoreProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: niriConfigWriter
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("Generated niri-colors.generated.kdl");
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to generate niri config, exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: ghosttyConfigWriter
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("Generated ghostty-colors.generated.conf");
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to generate ghostty config, exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: gtkAvailabilityChecker
|
id: gtkAvailabilityChecker
|
||||||
@@ -371,7 +309,6 @@ palette = 15=${fg_b}`;
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
gtkThemingEnabled = (exitCode === 0);
|
gtkThemingEnabled = (exitCode === 0);
|
||||||
console.log("GTK theming available:", gtkThemingEnabled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +318,6 @@ palette = 15=${fg_b}`;
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
qtThemingEnabled = (exitCode === 0);
|
qtThemingEnabled = (exitCode === 0);
|
||||||
console.log("Qt theming available:", qtThemingEnabled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,21 +333,10 @@ palette = 15=${fg_b}`;
|
|||||||
id: systemThemeStderr
|
id: systemThemeStderr
|
||||||
}
|
}
|
||||||
|
|
||||||
onStarted: {
|
|
||||||
console.log("System theme generation process started with command:", command);
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
systemThemeGenerationInProgress = false;
|
systemThemeGenerationInProgress = false;
|
||||||
console.log("System theme generation process exited with code:", exitCode);
|
|
||||||
|
|
||||||
if (exitCode === 0) {
|
if (exitCode !== 0) {
|
||||||
console.log("System themes generated successfully");
|
|
||||||
console.log("stdout:", systemThemeStdout.text);
|
|
||||||
} else {
|
|
||||||
console.error("System theme generation failed, exit code:", exitCode);
|
|
||||||
console.error("stdout:", systemThemeStdout.text);
|
|
||||||
console.error("stderr:", systemThemeStderr.text);
|
|
||||||
ToastService.showError("Failed to generate system themes: " + systemThemeStderr.text);
|
ToastService.showError("Failed to generate system themes: " + systemThemeStderr.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,21 +354,10 @@ palette = 15=${fg_b}`;
|
|||||||
id: restoreThemeStderr
|
id: restoreThemeStderr
|
||||||
}
|
}
|
||||||
|
|
||||||
onStarted: {
|
|
||||||
console.log("System theme restoration process started with command:", command);
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
console.log("System theme restoration process exited with code:", exitCode);
|
|
||||||
|
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
console.log("System themes restored successfully");
|
|
||||||
console.log("stdout:", restoreThemeStdout.text);
|
|
||||||
ToastService.showInfo("System themes restored to default");
|
ToastService.showInfo("System themes restored to default");
|
||||||
} else {
|
} else {
|
||||||
console.error("System theme restoration failed, exit code:", exitCode);
|
|
||||||
console.error("stdout:", restoreThemeStdout.text);
|
|
||||||
console.error("stderr:", restoreThemeStderr.text);
|
|
||||||
ToastService.showWarning("Failed to restore system themes: " + restoreThemeStderr.text);
|
ToastService.showWarning("Failed to restore system themes: " + restoreThemeStderr.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,10 @@ Singleton {
|
|||||||
property var topBarCenterWidgets: ["clock", "music", "weather"]
|
property var topBarCenterWidgets: ["clock", "music", "weather"]
|
||||||
property var topBarRightWidgets: ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"]
|
property var topBarRightWidgets: ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"]
|
||||||
|
|
||||||
// Reactive ListModel properties for TopBar
|
|
||||||
property alias topBarLeftWidgetsModel: leftWidgetsModel
|
property alias topBarLeftWidgetsModel: leftWidgetsModel
|
||||||
property alias topBarCenterWidgetsModel: centerWidgetsModel
|
property alias topBarCenterWidgetsModel: centerWidgetsModel
|
||||||
property alias topBarRightWidgetsModel: rightWidgetsModel
|
property alias topBarRightWidgetsModel: rightWidgetsModel
|
||||||
|
|
||||||
// Signal to force immediate TopBar layout refresh
|
|
||||||
signal forceTopBarLayoutRefresh()
|
signal forceTopBarLayoutRefresh()
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
@@ -108,7 +106,6 @@ Singleton {
|
|||||||
|
|
||||||
if (missingFonts.length > 0) {
|
if (missingFonts.length > 0) {
|
||||||
var message = "Missing fonts: " + missingFonts.join(", ") + ". Using system defaults."
|
var message = "Missing fonts: " + missingFonts.join(", ") + ". Using system defaults."
|
||||||
console.warn("Prefs: " + message)
|
|
||||||
ToastService.showWarning(message)
|
ToastService.showWarning(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +159,6 @@ Singleton {
|
|||||||
showWorkspaceIndex = settings.showWorkspaceIndex !== undefined ? settings.showWorkspaceIndex : false;
|
showWorkspaceIndex = settings.showWorkspaceIndex !== undefined ? settings.showWorkspaceIndex : false;
|
||||||
showWorkspacePadding = settings.showWorkspacePadding !== undefined ? settings.showWorkspacePadding : false;
|
showWorkspacePadding = settings.showWorkspacePadding !== undefined ? settings.showWorkspacePadding : false;
|
||||||
if (settings.topBarWidgetOrder) {
|
if (settings.topBarWidgetOrder) {
|
||||||
// Migrate from old single list to new three-list system
|
|
||||||
topBarLeftWidgets = settings.topBarWidgetOrder.filter(w => ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w));
|
topBarLeftWidgets = settings.topBarWidgetOrder.filter(w => ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w));
|
||||||
topBarCenterWidgets = settings.topBarWidgetOrder.filter(w => ["clock", "music", "weather"].includes(w));
|
topBarCenterWidgets = settings.topBarWidgetOrder.filter(w => ["clock", "music", "weather"].includes(w));
|
||||||
topBarRightWidgets = settings.topBarWidgetOrder.filter(w => ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w));
|
topBarRightWidgets = settings.topBarWidgetOrder.filter(w => ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w));
|
||||||
@@ -497,7 +493,6 @@ Singleton {
|
|||||||
function updateListModel(listModel, order) {
|
function updateListModel(listModel, order) {
|
||||||
listModel.clear();
|
listModel.clear();
|
||||||
for (var i = 0; i < order.length; i++) {
|
for (var i = 0; i < order.length; i++) {
|
||||||
// Handle both old string format and new object format
|
|
||||||
var widgetId = typeof order[i] === "string" ? order[i] : order[i].id;
|
var widgetId = typeof order[i] === "string" ? order[i] : order[i].id;
|
||||||
var enabled = typeof order[i] === "string" ? true : order[i].enabled;
|
var enabled = typeof order[i] === "string" ? true : order[i].enabled;
|
||||||
var size = typeof order[i] === "string" ? undefined : order[i].size;
|
var size = typeof order[i] === "string" ? undefined : order[i].size;
|
||||||
@@ -577,21 +572,16 @@ Singleton {
|
|||||||
updateQtIconTheme(themeName);
|
updateQtIconTheme(themeName);
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// If dynamic theme is active, regenerate system themes with new icon theme
|
|
||||||
if (typeof Theme !== "undefined" && Theme.isDynamicTheme && typeof Colors !== "undefined") {
|
if (typeof Theme !== "undefined" && Theme.isDynamicTheme && typeof Colors !== "undefined") {
|
||||||
console.log("Icon theme changed during dynamic theming - regenerating system themes");
|
|
||||||
Colors.generateSystemThemes();
|
Colors.generateSystemThemes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateGtkIconTheme(themeName) {
|
function updateGtkIconTheme(themeName) {
|
||||||
console.log("Updating GTK icon theme to:", themeName);
|
|
||||||
var gtkThemeName = (themeName === "System Default") ? systemDefaultIconTheme : themeName;
|
var gtkThemeName = (themeName === "System Default") ? systemDefaultIconTheme : themeName;
|
||||||
|
|
||||||
// Update icon theme via dconf/gsettings AND settings.ini files
|
|
||||||
if (gtkThemeName !== "System Default" && gtkThemeName !== "") {
|
if (gtkThemeName !== "System Default" && gtkThemeName !== "") {
|
||||||
var script =
|
var script =
|
||||||
"# Update dconf/gsettings with multiple fallbacks for Fedora\n" +
|
|
||||||
"if command -v gsettings >/dev/null 2>&1 && gsettings list-schemas | grep -q org.gnome.desktop.interface; then\n" +
|
"if command -v gsettings >/dev/null 2>&1 && gsettings list-schemas | grep -q org.gnome.desktop.interface; then\n" +
|
||||||
" gsettings set org.gnome.desktop.interface icon-theme '" + gtkThemeName + "'\n" +
|
" gsettings set org.gnome.desktop.interface icon-theme '" + gtkThemeName + "'\n" +
|
||||||
" echo 'Updated via gsettings'\n" +
|
" echo 'Updated via gsettings'\n" +
|
||||||
@@ -635,7 +625,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateQtIconTheme(themeName) {
|
function updateQtIconTheme(themeName) {
|
||||||
console.log("Updating Qt icon theme to:", themeName);
|
|
||||||
var qtThemeName = (themeName === "System Default") ? "" : themeName;
|
var qtThemeName = (themeName === "System Default") ? "" : themeName;
|
||||||
|
|
||||||
var home = _shq(root._homeUrl.replace("file://", ""));
|
var home = _shq(root._homeUrl.replace("file://", ""));
|
||||||
@@ -743,7 +732,6 @@ Singleton {
|
|||||||
wallpaperPath = path;
|
wallpaperPath = path;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// Trigger dynamic theming if enabled
|
|
||||||
if (wallpaperDynamicTheming && path && typeof Theme !== "undefined") {
|
if (wallpaperDynamicTheming && path && typeof Theme !== "undefined") {
|
||||||
Theme.switchTheme(themeIndex, true, true);
|
Theme.switchTheme(themeIndex, true, true);
|
||||||
}
|
}
|
||||||
@@ -753,7 +741,6 @@ Singleton {
|
|||||||
wallpaperDynamicTheming = enabled;
|
wallpaperDynamicTheming = enabled;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// If enabled and we have a wallpaper, trigger dynamic theming
|
|
||||||
if (enabled && wallpaperPath && typeof Theme !== "undefined") {
|
if (enabled && wallpaperPath && typeof Theme !== "undefined") {
|
||||||
Theme.switchTheme(themeIndex, true, true);
|
Theme.switchTheme(themeIndex, true, true);
|
||||||
}
|
}
|
||||||
@@ -763,7 +750,6 @@ Singleton {
|
|||||||
wallpaperPath = imagePath;
|
wallpaperPath = imagePath;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// Trigger color extraction if dynamic theming is enabled
|
|
||||||
if (wallpaperDynamicTheming && typeof Colors !== "undefined") {
|
if (wallpaperDynamicTheming && typeof Colors !== "undefined") {
|
||||||
Colors.extractColors();
|
Colors.extractColors();
|
||||||
}
|
}
|
||||||
@@ -799,7 +785,6 @@ Singleton {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to safely single-quote shell strings
|
|
||||||
function _shq(s) {
|
function _shq(s) {
|
||||||
return "'" + String(s).replace(/'/g, "'\\''") + "'";
|
return "'" + String(s).replace(/'/g, "'\\''") + "'";
|
||||||
}
|
}
|
||||||
@@ -819,64 +804,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gtk3Process
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to update GTK 3 settings, exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gtk4Process
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to update GTK 4 settings, exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: reloadThemeProcess
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
} else {
|
|
||||||
console.log("GTK theme reload failed (this is normal if gsettings is not available), exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: qtThemeProcess
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
// Qt theme reload signal sent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: envCheckProcess
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
console.warn("Environment check failed, exit code:", exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: envSetProcess
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: systemDefaultDetectionProcess
|
id: systemDefaultDetectionProcess
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import Quickshell.Services.UPower
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Theme definitions with complete Material 3 expressive color palettes
|
|
||||||
property var themes: [{
|
property var themes: [{
|
||||||
"name": "Blue",
|
"name": "Blue",
|
||||||
"primary": "#42a5f5",
|
"primary": "#42a5f5",
|
||||||
@@ -171,7 +170,6 @@ Singleton {
|
|||||||
"surfaceContainer": "#201a19",
|
"surfaceContainer": "#201a19",
|
||||||
"surfaceContainerHigh": "#2b2221"
|
"surfaceContainerHigh": "#2b2221"
|
||||||
}]
|
}]
|
||||||
// Light theme variants
|
|
||||||
property var lightThemes: [{
|
property var lightThemes: [{
|
||||||
"name": "Blue Light",
|
"name": "Blue Light",
|
||||||
"primary": "#1976d2",
|
"primary": "#1976d2",
|
||||||
@@ -333,11 +331,9 @@ Singleton {
|
|||||||
"surfaceContainer": "#f3f3f3",
|
"surfaceContainer": "#f3f3f3",
|
||||||
"surfaceContainerHigh": "#ececec"
|
"surfaceContainerHigh": "#ececec"
|
||||||
}]
|
}]
|
||||||
// Current theme index (10 = Auto/Dynamic)
|
|
||||||
property int currentThemeIndex: 0
|
property int currentThemeIndex: 0
|
||||||
property bool isDynamicTheme: false
|
property bool isDynamicTheme: false
|
||||||
property bool isLightMode: false
|
property bool isLightMode: false
|
||||||
// Dynamic color properties that change based on current theme
|
|
||||||
property color primary: isDynamicTheme ? Colors.accentHi : getCurrentTheme().primary
|
property color primary: isDynamicTheme ? Colors.accentHi : getCurrentTheme().primary
|
||||||
property color primaryText: isDynamicTheme ? Colors.primaryText : getCurrentTheme().primaryText
|
property color primaryText: isDynamicTheme ? Colors.primaryText : getCurrentTheme().primaryText
|
||||||
property color primaryContainer: isDynamicTheme ? Colors.primaryContainer : getCurrentTheme().primaryContainer
|
property color primaryContainer: isDynamicTheme ? Colors.primaryContainer : getCurrentTheme().primaryContainer
|
||||||
@@ -352,25 +348,20 @@ Singleton {
|
|||||||
property color outline: isDynamicTheme ? Colors.outline : getCurrentTheme().outline
|
property color outline: isDynamicTheme ? Colors.outline : getCurrentTheme().outline
|
||||||
property color surfaceContainer: isDynamicTheme ? Colors.surfaceContainer : getCurrentTheme().surfaceContainer
|
property color surfaceContainer: isDynamicTheme ? Colors.surfaceContainer : getCurrentTheme().surfaceContainer
|
||||||
property color surfaceContainerHigh: isDynamicTheme ? Colors.surfaceContainerHigh : getCurrentTheme().surfaceContainerHigh
|
property color surfaceContainerHigh: isDynamicTheme ? Colors.surfaceContainerHigh : getCurrentTheme().surfaceContainerHigh
|
||||||
// Static colors that don't change with themes
|
|
||||||
property color archBlue: "#1793D1"
|
property color archBlue: "#1793D1"
|
||||||
property color success: "#4CAF50"
|
property color success: "#4CAF50"
|
||||||
property color warning: "#FF9800"
|
property color warning: "#FF9800"
|
||||||
property color info: "#2196F3"
|
property color info: "#2196F3"
|
||||||
property color error: "#F2B8B5"
|
property color error: "#F2B8B5"
|
||||||
|
|
||||||
// Common alpha color variants for consistency
|
|
||||||
// Primary colors with alpha
|
|
||||||
property color primaryHover: Qt.rgba(primary.r, primary.g, primary.b, 0.12)
|
property color primaryHover: Qt.rgba(primary.r, primary.g, primary.b, 0.12)
|
||||||
property color primaryHoverLight: Qt.rgba(primary.r, primary.g, primary.b, 0.08)
|
property color primaryHoverLight: Qt.rgba(primary.r, primary.g, primary.b, 0.08)
|
||||||
property color primaryPressed: Qt.rgba(primary.r, primary.g, primary.b, 0.16)
|
property color primaryPressed: Qt.rgba(primary.r, primary.g, primary.b, 0.16)
|
||||||
property color primarySelected: Qt.rgba(primary.r, primary.g, primary.b, 0.3)
|
property color primarySelected: Qt.rgba(primary.r, primary.g, primary.b, 0.3)
|
||||||
property color primaryBackground: Qt.rgba(primary.r, primary.g, primary.b, 0.04)
|
property color primaryBackground: Qt.rgba(primary.r, primary.g, primary.b, 0.04)
|
||||||
|
|
||||||
// Secondary colors with alpha
|
|
||||||
property color secondaryHover: Qt.rgba(secondary.r, secondary.g, secondary.b, 0.08)
|
property color secondaryHover: Qt.rgba(secondary.r, secondary.g, secondary.b, 0.08)
|
||||||
|
|
||||||
// Surface colors with alpha
|
|
||||||
property color surfaceHover: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.08)
|
property color surfaceHover: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.08)
|
||||||
property color surfacePressed: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.12)
|
property color surfacePressed: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.12)
|
||||||
property color surfaceSelected: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.15)
|
property color surfaceSelected: Qt.rgba(surfaceVariant.r, surfaceVariant.g, surfaceVariant.b, 0.15)
|
||||||
@@ -382,7 +373,6 @@ Singleton {
|
|||||||
property color surfaceTextLight: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.06)
|
property color surfaceTextLight: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.06)
|
||||||
property color surfaceTextMedium: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.7)
|
property color surfaceTextMedium: Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.7)
|
||||||
|
|
||||||
// Outline colors with alpha
|
|
||||||
property color outlineLight: Qt.rgba(outline.r, outline.g, outline.b, 0.05)
|
property color outlineLight: Qt.rgba(outline.r, outline.g, outline.b, 0.05)
|
||||||
property color outlineMedium: Qt.rgba(outline.r, outline.g, outline.b, 0.08)
|
property color outlineMedium: Qt.rgba(outline.r, outline.g, outline.b, 0.08)
|
||||||
property color outlineStrong: Qt.rgba(outline.r, outline.g, outline.b, 0.12)
|
property color outlineStrong: Qt.rgba(outline.r, outline.g, outline.b, 0.12)
|
||||||
@@ -390,14 +380,11 @@ Singleton {
|
|||||||
property color outlineHeavy: Qt.rgba(outline.r, outline.g, outline.b, 0.3)
|
property color outlineHeavy: Qt.rgba(outline.r, outline.g, outline.b, 0.3)
|
||||||
property color outlineButton: Qt.rgba(outline.r, outline.g, outline.b, 0.5)
|
property color outlineButton: Qt.rgba(outline.r, outline.g, outline.b, 0.5)
|
||||||
|
|
||||||
// Error colors with alpha
|
|
||||||
property color errorHover: Qt.rgba(error.r, error.g, error.b, 0.12)
|
property color errorHover: Qt.rgba(error.r, error.g, error.b, 0.12)
|
||||||
property color errorPressed: Qt.rgba(error.r, error.g, error.b, 0.9)
|
property color errorPressed: Qt.rgba(error.r, error.g, error.b, 0.9)
|
||||||
|
|
||||||
// Warning colors with alpha
|
|
||||||
property color warningHover: Qt.rgba(warning.r, warning.g, warning.b, 0.12)
|
property color warningHover: Qt.rgba(warning.r, warning.g, warning.b, 0.12)
|
||||||
|
|
||||||
// Shadow colors
|
|
||||||
property color shadowLight: Qt.rgba(0, 0, 0, 0.05)
|
property color shadowLight: Qt.rgba(0, 0, 0, 0.05)
|
||||||
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
||||||
property color shadowDark: Qt.rgba(0, 0, 0, 0.1)
|
property color shadowDark: Qt.rgba(0, 0, 0, 0.1)
|
||||||
@@ -429,80 +416,51 @@ Singleton {
|
|||||||
property real opacityMedium: 0.6
|
property real opacityMedium: 0.6
|
||||||
property real opacityHigh: 0.87
|
property real opacityHigh: 0.87
|
||||||
property real opacityFull: 1
|
property real opacityFull: 1
|
||||||
// Transparency system - can be overridden by Prefs
|
|
||||||
property real panelTransparency: 0.85
|
property real panelTransparency: 0.85
|
||||||
property real widgetTransparency: 0.85
|
property real widgetTransparency: 0.85
|
||||||
property real popupTransparency: 0.92
|
property real popupTransparency: 0.92
|
||||||
|
|
||||||
// Handle successful color extraction
|
|
||||||
function onColorsUpdated() {
|
function onColorsUpdated() {
|
||||||
console.log("Colors updated successfully - switching to dynamic theme");
|
|
||||||
// Only switch to dynamic theme if we're already in dynamic mode
|
|
||||||
if (isDynamicTheme) {
|
if (isDynamicTheme) {
|
||||||
currentThemeIndex = 10;
|
currentThemeIndex = 10;
|
||||||
isDynamicTheme = true;
|
isDynamicTheme = true;
|
||||||
console.log("Dynamic theme activated. Theme.primary should now be:", primary);
|
|
||||||
// Save preference after successful switch
|
|
||||||
if (typeof Prefs !== "undefined")
|
if (typeof Prefs !== "undefined")
|
||||||
Prefs.setTheme(currentThemeIndex, isDynamicTheme);
|
Prefs.setTheme(currentThemeIndex, isDynamicTheme);
|
||||||
|
|
||||||
} else {
|
|
||||||
console.log("Color extraction completed, but staying with static theme");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to switch themes
|
|
||||||
function switchTheme(themeIndex, isDynamic = false, savePrefs = true) {
|
function switchTheme(themeIndex, isDynamic = false, savePrefs = true) {
|
||||||
console.log("Theme.switchTheme called:", themeIndex, isDynamic, "savePrefs:", savePrefs);
|
|
||||||
if (isDynamic && themeIndex === 10) {
|
if (isDynamic && themeIndex === 10) {
|
||||||
console.log("Attempting to switch to dynamic theme - checking colors first");
|
|
||||||
// Set dynamic theme flag immediately so onColorsUpdated works
|
|
||||||
isDynamicTheme = true;
|
isDynamicTheme = true;
|
||||||
// Don't change theme index yet - wait for color extraction to succeed
|
|
||||||
if (typeof Colors !== "undefined") {
|
if (typeof Colors !== "undefined") {
|
||||||
console.log("Calling Colors.extractColors()");
|
|
||||||
Colors.extractColors();
|
Colors.extractColors();
|
||||||
} else {
|
|
||||||
console.error("Colors singleton not available");
|
|
||||||
}
|
}
|
||||||
} else if (themeIndex >= 0 && themeIndex < themes.length) {
|
} else if (themeIndex >= 0 && themeIndex < themes.length) {
|
||||||
// If switching away from dynamic theme, restore system themes
|
|
||||||
if (isDynamicTheme && typeof Colors !== "undefined") {
|
if (isDynamicTheme && typeof Colors !== "undefined") {
|
||||||
console.log("Switching away from dynamic theme, restoring system themes");
|
|
||||||
Colors.restoreSystemThemes();
|
Colors.restoreSystemThemes();
|
||||||
}
|
}
|
||||||
currentThemeIndex = themeIndex;
|
currentThemeIndex = themeIndex;
|
||||||
isDynamicTheme = false;
|
isDynamicTheme = false;
|
||||||
}
|
}
|
||||||
// Save preference (unless this is a startup restoration)
|
|
||||||
if (savePrefs && typeof Prefs !== "undefined")
|
if (savePrefs && typeof Prefs !== "undefined")
|
||||||
Prefs.setTheme(currentThemeIndex, isDynamicTheme);
|
Prefs.setTheme(currentThemeIndex, isDynamicTheme);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to toggle light/dark mode
|
|
||||||
function toggleLightMode(savePrefs = true) {
|
function toggleLightMode(savePrefs = true) {
|
||||||
console.log("Theme.toggleLightMode called, current isLightMode:", isLightMode);
|
|
||||||
isLightMode = !isLightMode;
|
isLightMode = !isLightMode;
|
||||||
console.log("Light mode toggled to:", isLightMode);
|
|
||||||
// Save preference
|
|
||||||
if (savePrefs && typeof Prefs !== "undefined")
|
if (savePrefs && typeof Prefs !== "undefined")
|
||||||
Prefs.setLightMode(isLightMode);
|
Prefs.setLightMode(isLightMode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get current theme array
|
|
||||||
function getCurrentThemeArray() {
|
function getCurrentThemeArray() {
|
||||||
return isLightMode ? lightThemes : themes;
|
return isLightMode ? lightThemes : themes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get current theme
|
|
||||||
function getCurrentTheme() {
|
function getCurrentTheme() {
|
||||||
var themeArray = getCurrentThemeArray();
|
var themeArray = getCurrentThemeArray();
|
||||||
return currentThemeIndex < themeArray.length ? themeArray[currentThemeIndex] : themeArray[0];
|
return currentThemeIndex < themeArray.length ? themeArray[currentThemeIndex] : themeArray[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smart transparency functions for content-aware backgrounds
|
|
||||||
function getPopupBackgroundAlpha() {
|
function getPopupBackgroundAlpha() {
|
||||||
return popupTransparency;
|
return popupTransparency;
|
||||||
}
|
}
|
||||||
@@ -511,7 +469,6 @@ Singleton {
|
|||||||
return popupTransparency;
|
return popupTransparency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience functions for themed backgrounds with transparency
|
|
||||||
function popupBackground() {
|
function popupBackground() {
|
||||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, popupTransparency);
|
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, popupTransparency);
|
||||||
}
|
}
|
||||||
@@ -606,7 +563,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPowerProfileLabel(profile) {
|
function getPowerProfileLabel(profile) {
|
||||||
console.log("Theme.getPowerProfileLabel called with profile:", profile);
|
|
||||||
switch (profile) {
|
switch (profile) {
|
||||||
case PowerProfile.PowerSaver:
|
case PowerProfile.PowerSaver:
|
||||||
return "Power Saver";
|
return "Power Saver";
|
||||||
@@ -632,14 +588,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize theme system
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Theme Component.onCompleted");
|
|
||||||
// Connect to Colors signal
|
|
||||||
if (typeof Colors !== "undefined")
|
if (typeof Colors !== "undefined")
|
||||||
Colors.colorsUpdated.connect(root.onColorsUpdated);
|
Colors.colorsUpdated.connect(root.onColorsUpdated);
|
||||||
|
|
||||||
// Initialize transparency values from Prefs
|
|
||||||
if (typeof Prefs !== "undefined") {
|
if (typeof Prefs !== "undefined") {
|
||||||
if (Prefs.popupTransparency !== undefined)
|
if (Prefs.popupTransparency !== undefined)
|
||||||
root.popupTransparency = Prefs.popupTransparency;
|
root.popupTransparency = Prefs.popupTransparency;
|
||||||
@@ -647,23 +599,18 @@ Singleton {
|
|||||||
if (Prefs.topBarWidgetTransparency !== undefined)
|
if (Prefs.topBarWidgetTransparency !== undefined)
|
||||||
root.widgetTransparency = Prefs.topBarWidgetTransparency;
|
root.widgetTransparency = Prefs.topBarWidgetTransparency;
|
||||||
|
|
||||||
// Connect to transparency changes
|
|
||||||
if (Prefs.popupTransparencyChanged)
|
if (Prefs.popupTransparencyChanged)
|
||||||
Prefs.popupTransparencyChanged.connect(function() {
|
Prefs.popupTransparencyChanged.connect(function() {
|
||||||
if (typeof Prefs !== "undefined" && Prefs.popupTransparency !== undefined)
|
if (typeof Prefs !== "undefined" && Prefs.popupTransparency !== undefined)
|
||||||
root.popupTransparency = Prefs.popupTransparency;
|
root.popupTransparency = Prefs.popupTransparency;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Prefs.topBarWidgetTransparencyChanged)
|
if (Prefs.topBarWidgetTransparencyChanged)
|
||||||
Prefs.topBarWidgetTransparencyChanged.connect(function() {
|
Prefs.topBarWidgetTransparencyChanged.connect(function() {
|
||||||
if (typeof Prefs !== "undefined" && Prefs.topBarWidgetTransparency !== undefined)
|
if (typeof Prefs !== "undefined" && Prefs.topBarWidgetTransparency !== undefined)
|
||||||
root.widgetTransparency = Prefs.topBarWidgetTransparency;
|
root.widgetTransparency = Prefs.topBarWidgetTransparency;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log("Theme initialized, waiting for Prefs to load settings and apply theme");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import qs.Services
|
|||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
// Don't hide the interface, just show toast
|
|
||||||
|
|
||||||
id: clipboardHistoryModal
|
id: clipboardHistoryModal
|
||||||
|
|
||||||
@@ -18,8 +17,6 @@ DankModal {
|
|||||||
property bool showClearConfirmation: false
|
property bool showClearConfirmation: false
|
||||||
property var clipboardEntries: []
|
property var clipboardEntries: []
|
||||||
property string searchText: ""
|
property string searchText: ""
|
||||||
property bool imagemagickAvailable: false
|
|
||||||
property string thumbnailCacheDir: ""
|
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
filteredClipboardModel.clear();
|
filteredClipboardModel.clear();
|
||||||
@@ -52,7 +49,7 @@ DankModal {
|
|||||||
clipboardHistoryModal.isVisible = true;
|
clipboardHistoryModal.isVisible = true;
|
||||||
initializeThumbnailSystem();
|
initializeThumbnailSystem();
|
||||||
refreshClipboard();
|
refreshClipboard();
|
||||||
console.log("ClipboardHistoryModal: Opening and refreshing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
@@ -62,34 +59,17 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initializeThumbnailSystem() {
|
function initializeThumbnailSystem() {
|
||||||
getCacheDirProcess.running = true;
|
// No initialization needed - using direct image display
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupTempFiles() {
|
function cleanupTempFiles() {
|
||||||
cleanupProcess.command = ["sh", "-c", "rm -f /tmp/clipboard_preview_*.png"];
|
Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"]);
|
||||||
cleanupProcess.running = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateThumbnails() {
|
function generateThumbnails() {
|
||||||
if (!imagemagickAvailable)
|
// No thumbnail generation needed - using direct image display
|
||||||
return ;
|
|
||||||
|
|
||||||
for (let i = 0; i < clipboardModel.count; i++) {
|
|
||||||
const entry = clipboardModel.get(i).entry;
|
|
||||||
const entryType = getEntryType(entry);
|
|
||||||
if (entryType === "image") {
|
|
||||||
const entryId = entry.split('\t')[0];
|
|
||||||
const thumbnailPath = `${thumbnailCacheDir}/${entryId}.png`;
|
|
||||||
thumbnailGenProcess.command = ["sh", "-c", `mkdir -p "${thumbnailCacheDir}" && cliphist decode ${entryId} | magick - -resize '128x128>' "${thumbnailPath}"`];
|
|
||||||
thumbnailGenProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThumbnailPath(entry) {
|
|
||||||
const entryId = entry.split('\t')[0];
|
|
||||||
return `${thumbnailCacheDir}/${entryId}.png`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshClipboard() {
|
function refreshClipboard() {
|
||||||
clipboardProcess.running = true;
|
clipboardProcess.running = true;
|
||||||
@@ -97,15 +77,14 @@ DankModal {
|
|||||||
|
|
||||||
function copyEntry(entry) {
|
function copyEntry(entry) {
|
||||||
const entryId = entry.split('\t')[0];
|
const entryId = entry.split('\t')[0];
|
||||||
copyProcess.command = ["sh", "-c", `cliphist decode ${entryId} | wl-copy`];
|
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`]);
|
||||||
copyProcess.running = true;
|
|
||||||
console.log("ClipboardHistoryModal: Entry copied, showing toast");
|
|
||||||
ToastService.showInfo("Copied to clipboard");
|
ToastService.showInfo("Copied to clipboard");
|
||||||
clipboardHistoryModal.hide();
|
clipboardHistoryModal.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteEntry(entry) {
|
function deleteEntry(entry) {
|
||||||
console.log("Deleting entry:", entry);
|
|
||||||
deleteProcess.command = ["sh", "-c", `echo '${entry.replace(/'/g, "'\\''")}' | cliphist delete`];
|
deleteProcess.command = ["sh", "-c", `echo '${entry.replace(/'/g, "'\\''")}' | cliphist delete`];
|
||||||
deleteProcess.running = true;
|
deleteProcess.running = true;
|
||||||
}
|
}
|
||||||
@@ -143,7 +122,7 @@ DankModal {
|
|||||||
return "text";
|
return "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
// DankModal configuration
|
|
||||||
visible: isVisible
|
visible: isVisible
|
||||||
width: 650
|
width: 650
|
||||||
height: 550
|
height: 550
|
||||||
@@ -157,7 +136,6 @@ DankModal {
|
|||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear confirmation dialog
|
|
||||||
DankModal {
|
DankModal {
|
||||||
id: clearConfirmDialog
|
id: clearConfirmDialog
|
||||||
|
|
||||||
@@ -264,7 +242,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data models
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: clipboardModel
|
id: clipboardModel
|
||||||
}
|
}
|
||||||
@@ -273,7 +250,6 @@ DankModal {
|
|||||||
id: filteredClipboardModel
|
id: filteredClipboardModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes
|
|
||||||
Process {
|
Process {
|
||||||
id: clipboardProcess
|
id: clipboardProcess
|
||||||
|
|
||||||
@@ -292,23 +268,11 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
updateFilteredModel();
|
updateFilteredModel();
|
||||||
generateThumbnails();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: copyProcess
|
|
||||||
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.error("Copy failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: deleteProcess
|
id: deleteProcess
|
||||||
|
|
||||||
@@ -317,10 +281,11 @@ DankModal {
|
|||||||
if (exitCode === 0)
|
if (exitCode === 0)
|
||||||
refreshClipboard();
|
refreshClipboard();
|
||||||
else
|
else
|
||||||
console.error("Delete failed with exit code:", exitCode);
|
console.warn("Failed to delete clipboard entry");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: clearProcess
|
id: clearProcess
|
||||||
|
|
||||||
@@ -332,71 +297,31 @@ DankModal {
|
|||||||
filteredClipboardModel.clear();
|
filteredClipboardModel.clear();
|
||||||
totalCount = 0;
|
totalCount = 0;
|
||||||
} else {
|
} else {
|
||||||
console.error("Clear failed with exit code:", exitCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: cleanupProcess
|
|
||||||
|
|
||||||
running: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: getCacheDirProcess
|
|
||||||
|
|
||||||
command: ["sh", "-c", "echo ${XDG_CACHE_HOME:-$HOME/.cache}/cliphist/thumbs"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
thumbnailCacheDir = text.trim();
|
|
||||||
checkImageMagickProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: checkImageMagickProcess
|
|
||||||
|
|
||||||
command: ["which", "magick"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
imagemagickAvailable = (exitCode === 0);
|
|
||||||
if (!imagemagickAvailable)
|
|
||||||
console.warn("ClipboardHistoryModal: ImageMagick not available, thumbnails disabled");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: thumbnailGenProcess
|
|
||||||
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("ClipboardHistoryModal: Thumbnail generation failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
console.log("ClipboardHistoryModal: IPC open() called");
|
|
||||||
clipboardHistoryModal.show();
|
clipboardHistoryModal.show();
|
||||||
return "CLIPBOARD_OPEN_SUCCESS";
|
return "CLIPBOARD_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
console.log("ClipboardHistoryModal: IPC close() called");
|
|
||||||
clipboardHistoryModal.hide();
|
clipboardHistoryModal.hide();
|
||||||
return "CLIPBOARD_CLOSE_SUCCESS";
|
return "CLIPBOARD_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
console.log("ClipboardHistoryModal: IPC toggle() called");
|
|
||||||
clipboardHistoryModal.toggle();
|
clipboardHistoryModal.toggle();
|
||||||
return "CLIPBOARD_TOGGLE_SUCCESS";
|
return "CLIPBOARD_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
@@ -559,7 +484,6 @@ DankModal {
|
|||||||
anchors.rightMargin: Theme.spacingS // Reduced right margin
|
anchors.rightMargin: Theme.spacingS // Reduced right margin
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Index number
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
@@ -577,63 +501,49 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content thumbnail/icon and text
|
|
||||||
Row {
|
Row {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - 68 // Account for index (24) + spacing (16) + delete button (32) - small margin
|
width: parent.width - 68 // Account for index (24) + spacing (16) + delete button (32) - small margin
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Thumbnail or icon container
|
|
||||||
Item {
|
Item {
|
||||||
width: entryType === "image" ? 48 : Theme.iconSize
|
width: entryType === "image" ? 48 : Theme.iconSize
|
||||||
height: entryType === "image" ? 48 : Theme.iconSize
|
height: entryType === "image" ? 48 : Theme.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// Image thumbnail
|
|
||||||
CachingImage {
|
CachingImage {
|
||||||
id: thumbnailImageSource
|
id: thumbnailImageSource
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: entryType === "image" && imagemagickAvailable ? "file://" + getThumbnailPath(model.entry) : ""
|
property string entryId: model.entry.split('\t')[0]
|
||||||
|
source: entryType === "image" && imageLoader.imageData ? `data:image/png;base64,${imageLoader.imageData}` : ""
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
smooth: true
|
smooth: true
|
||||||
cache: true
|
cache: true
|
||||||
visible: false
|
visible: false // Hide the original image
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
// Handle loading errors gracefully and retry once
|
|
||||||
onStatusChanged: {
|
Process {
|
||||||
if (status === Image.Error && source !== "") {
|
id: imageLoader
|
||||||
// Clear source to prevent repeated error attempts
|
property string imageData: ""
|
||||||
const originalSource = source;
|
|
||||||
source = "";
|
command: ["sh", "-c", `cliphist decode ${thumbnailImageSource.entryId} | base64 -w 0`]
|
||||||
// Retry once after 2 seconds to allow thumbnail generation
|
running: entryType === "image"
|
||||||
retryTimer.originalSource = originalSource;
|
|
||||||
retryTimer.start();
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
imageLoader.imageData = text.trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: retryTimer
|
|
||||||
|
|
||||||
property string originalSource: ""
|
|
||||||
|
|
||||||
interval: 2000
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (originalSource !== "" && thumbnailImageSource.source === "")
|
|
||||||
thumbnailImageSource.source = originalSource;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiEffect {
|
MultiEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
source: thumbnailImageSource
|
source: thumbnailImageSource
|
||||||
maskEnabled: true
|
maskEnabled: true
|
||||||
maskSource: clipboardCircularMask
|
maskSource: clipboardCircularMask
|
||||||
visible: entryType === "image" && imagemagickAvailable && thumbnailImageSource.status === Image.Ready
|
visible: entryType === "image" && thumbnailImageSource.status === Image.Ready
|
||||||
maskThresholdMin: 0.5
|
maskThresholdMin: 0.5
|
||||||
maskSpreadAtMin: 1
|
maskSpreadAtMin: 1
|
||||||
}
|
}
|
||||||
@@ -641,8 +551,8 @@ DankModal {
|
|||||||
Item {
|
Item {
|
||||||
id: clipboardCircularMask
|
id: clipboardCircularMask
|
||||||
|
|
||||||
width: 48
|
width: 48 - 4
|
||||||
height: 48
|
height: 48 - 4
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.smooth: true
|
layer.smooth: true
|
||||||
visible: false
|
visible: false
|
||||||
@@ -656,9 +566,8 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
visible: !(entryType === "image" && imagemagickAvailable && thumbnailImageSource.status === Image.Ready)
|
visible: !(entryType === "image" && thumbnailImageSource.status === Image.Ready)
|
||||||
name: {
|
name: {
|
||||||
if (entryType === "image")
|
if (entryType === "image")
|
||||||
return "image";
|
return "image";
|
||||||
@@ -716,7 +625,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete button
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
@@ -726,12 +634,11 @@ DankModal {
|
|||||||
iconColor: Theme.error
|
iconColor: Theme.error
|
||||||
hoverColor: Theme.errorHover
|
hoverColor: Theme.errorHover
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Delete clicked for entry:", model.entry);
|
|
||||||
deleteEntry(model.entry);
|
deleteEntry(model.entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main click area - explicitly excludes delete button area
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
|
|||||||
@@ -7,44 +7,31 @@ import qs.Common
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Core properties
|
|
||||||
property alias content: contentLoader.sourceComponent
|
property alias content: contentLoader.sourceComponent
|
||||||
// Sizing - can use fixed or relative to screen
|
|
||||||
property real width: 400
|
property real width: 400
|
||||||
property real height: 300
|
property real height: 300
|
||||||
// Screen-relative sizing helpers
|
|
||||||
readonly property real screenWidth: screen ? screen.width : 1920
|
readonly property real screenWidth: screen ? screen.width : 1920
|
||||||
readonly property real screenHeight: screen ? screen.height : 1080
|
readonly property real screenHeight: screen ? screen.height : 1080
|
||||||
// Background behavior
|
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
property real backgroundOpacity: 0.5
|
property real backgroundOpacity: 0.5
|
||||||
// Positioning
|
|
||||||
property string positioning: "center"
|
property string positioning: "center"
|
||||||
// "center", "top-right", "custom"
|
|
||||||
property point customPosition: Qt.point(0, 0)
|
property point customPosition: Qt.point(0, 0)
|
||||||
// Focus management
|
|
||||||
property string keyboardFocus: "ondemand"
|
property string keyboardFocus: "ondemand"
|
||||||
// "ondemand", "exclusive", "none"
|
|
||||||
property bool closeOnEscapeKey: true
|
property bool closeOnEscapeKey: true
|
||||||
property bool closeOnBackgroundClick: true
|
property bool closeOnBackgroundClick: true
|
||||||
// Animation
|
|
||||||
property string animationType: "scale"
|
property string animationType: "scale"
|
||||||
// "scale", "slide", "fade"
|
|
||||||
property int animationDuration: Theme.mediumDuration
|
property int animationDuration: Theme.mediumDuration
|
||||||
property var animationEasing: Theme.emphasizedEasing
|
property var animationEasing: Theme.emphasizedEasing
|
||||||
// Styling
|
|
||||||
property color backgroundColor: Theme.surfaceContainer
|
property color backgroundColor: Theme.surfaceContainer
|
||||||
property color borderColor: Theme.outlineMedium
|
property color borderColor: Theme.outlineMedium
|
||||||
property real borderWidth: 1
|
property real borderWidth: 1
|
||||||
property real cornerRadius: Theme.cornerRadiusLarge
|
property real cornerRadius: Theme.cornerRadiusLarge
|
||||||
property bool enableShadow: false
|
property bool enableShadow: false
|
||||||
|
|
||||||
// Signals
|
|
||||||
signal opened()
|
signal opened()
|
||||||
signal dialogClosed()
|
signal dialogClosed()
|
||||||
signal backgroundClicked()
|
signal backgroundClicked()
|
||||||
|
|
||||||
// Convenience functions
|
|
||||||
function open() {
|
function open() {
|
||||||
visible = true;
|
visible = true;
|
||||||
}
|
}
|
||||||
@@ -57,8 +44,6 @@ PanelWindow {
|
|||||||
visible = !visible;
|
visible = !visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PanelWindow configuration
|
|
||||||
// visible property is inherited from PanelWindow
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
@@ -76,7 +61,6 @@ PanelWindow {
|
|||||||
if (root.visible) {
|
if (root.visible) {
|
||||||
opened();
|
opened();
|
||||||
} else {
|
} else {
|
||||||
// Properly cleanup text input surfaces
|
|
||||||
if (Qt.inputMethod) {
|
if (Qt.inputMethod) {
|
||||||
Qt.inputMethod.hide();
|
Qt.inputMethod.hide();
|
||||||
Qt.inputMethod.reset();
|
Qt.inputMethod.reset();
|
||||||
@@ -92,7 +76,6 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background overlay
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
id: background
|
||||||
|
|
||||||
@@ -122,13 +105,11 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main content container
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: contentContainer
|
id: contentContainer
|
||||||
|
|
||||||
width: root.width
|
width: root.width
|
||||||
height: root.height
|
height: root.height
|
||||||
// Positioning
|
|
||||||
anchors.centerIn: positioning === "center" ? parent : undefined
|
anchors.centerIn: positioning === "center" ? parent : undefined
|
||||||
x: {
|
x: {
|
||||||
if (positioning === "top-right")
|
if (positioning === "top-right")
|
||||||
@@ -149,7 +130,6 @@ PanelWindow {
|
|||||||
border.color: root.borderColor
|
border.color: root.borderColor
|
||||||
border.width: root.borderWidth
|
border.width: root.borderWidth
|
||||||
layer.enabled: root.enableShadow
|
layer.enabled: root.enableShadow
|
||||||
// Animation properties
|
|
||||||
opacity: root.visible ? 1 : 0
|
opacity: root.visible ? 1 : 0
|
||||||
scale: {
|
scale: {
|
||||||
if (root.animationType === "scale")
|
if (root.animationType === "scale")
|
||||||
@@ -157,7 +137,6 @@ PanelWindow {
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Transform for slide animation
|
|
||||||
transform: root.animationType === "slide" ? slideTransform : null
|
transform: root.animationType === "slide" ? slideTransform : null
|
||||||
|
|
||||||
Translate {
|
Translate {
|
||||||
@@ -167,7 +146,6 @@ PanelWindow {
|
|||||||
y: root.visible ? 0 : -30
|
y: root.visible ? 0 : -30
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content area
|
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
|
||||||
@@ -176,7 +154,6 @@ PanelWindow {
|
|||||||
asynchronous: false
|
asynchronous: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animations
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
@@ -195,7 +172,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shadow effect
|
|
||||||
layer.effect: MultiEffect {
|
layer.effect: MultiEffect {
|
||||||
shadowEnabled: true
|
shadowEnabled: true
|
||||||
shadowHorizontalOffset: 0
|
shadowHorizontalOffset: 0
|
||||||
@@ -207,7 +183,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard handling
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: focusScope
|
id: focusScope
|
||||||
|
|
||||||
|
|||||||
@@ -44,9 +44,7 @@ DankModal {
|
|||||||
lastPath = Prefs.profileLastPath;
|
lastPath = Prefs.profileLastPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if last path exists, otherwise use home
|
|
||||||
if (lastPath && lastPath !== "") {
|
if (lastPath && lastPath !== "") {
|
||||||
// TODO: Could add directory existence check here
|
|
||||||
return lastPath;
|
return lastPath;
|
||||||
}
|
}
|
||||||
return homeDir;
|
return homeDir;
|
||||||
@@ -81,13 +79,11 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCurrentPathChanged: {
|
onCurrentPathChanged: {
|
||||||
// Path changed, model will update automatically
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateUp() {
|
function navigateUp() {
|
||||||
var path = currentPath;
|
var path = currentPath;
|
||||||
|
|
||||||
// Don't go above home directory
|
|
||||||
if (path === homeDir) {
|
if (path === homeDir) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -95,7 +91,6 @@ DankModal {
|
|||||||
var lastSlash = path.lastIndexOf('/');
|
var lastSlash = path.lastIndexOf('/');
|
||||||
if (lastSlash > 0) {
|
if (lastSlash > 0) {
|
||||||
var newPath = path.substring(0, lastSlash);
|
var newPath = path.substring(0, lastSlash);
|
||||||
// Don't go above home directory
|
|
||||||
if (newPath.length < homeDir.length) {
|
if (newPath.length < homeDir.length) {
|
||||||
currentPath = homeDir;
|
currentPath = homeDir;
|
||||||
saveLastPath(homeDir);
|
saveLastPath(homeDir);
|
||||||
@@ -117,7 +112,6 @@ DankModal {
|
|||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Header
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 40
|
height: 40
|
||||||
@@ -142,7 +136,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close button positioned at right
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
circular: false
|
circular: false
|
||||||
iconName: "close"
|
iconName: "close"
|
||||||
@@ -155,7 +148,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current path display and navigation
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -197,7 +189,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// File grid
|
|
||||||
GridView {
|
GridView {
|
||||||
id: fileGrid
|
id: fileGrid
|
||||||
|
|
||||||
@@ -251,7 +242,6 @@ DankModal {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
// Image preview or folder icon
|
|
||||||
Item {
|
Item {
|
||||||
width: 80
|
width: 80
|
||||||
height: 60
|
height: 60
|
||||||
@@ -282,7 +272,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// File name
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: delegateRoot.fileName || ""
|
text: delegateRoot.fileName || ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ DankModal {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
@@ -90,7 +89,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network Details
|
|
||||||
Flickable {
|
Flickable {
|
||||||
property real wheelMultiplier: 1.8
|
property real wheelMultiplier: 1.8
|
||||||
property int wheelBaseStep: 160
|
property int wheelBaseStep: 160
|
||||||
@@ -148,7 +146,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close Button
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 40
|
height: 40
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ DankModal {
|
|||||||
property string powerConfirmMessage: ""
|
property string powerConfirmMessage: ""
|
||||||
|
|
||||||
function executePowerAction(action) {
|
function executePowerAction(action) {
|
||||||
console.log("Executing power action:", action);
|
|
||||||
let command = [];
|
let command = [];
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "logout":
|
case "logout":
|
||||||
@@ -30,12 +30,10 @@ DankModal {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (command.length > 0) {
|
if (command.length > 0) {
|
||||||
powerActionProcess.command = command;
|
Quickshell.execDetached(command);
|
||||||
powerActionProcess.running = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DankModal configuration
|
|
||||||
visible: powerConfirmVisible
|
visible: powerConfirmVisible
|
||||||
width: 350
|
width: 350
|
||||||
height: 160
|
height: 160
|
||||||
@@ -45,17 +43,6 @@ DankModal {
|
|||||||
powerConfirmVisible = false;
|
powerConfirmVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: powerActionProcess
|
|
||||||
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.error("Power action failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -65,7 +52,6 @@ DankModal {
|
|||||||
width: parent.width - Theme.spacingM * 2
|
width: parent.width - Theme.spacingM * 2
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Title
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: powerConfirmTitle
|
text: powerConfirmTitle
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
@@ -84,7 +70,6 @@ DankModal {
|
|||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: powerConfirmMessage
|
text: powerConfirmMessage
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -98,12 +83,10 @@ DankModal {
|
|||||||
height: Theme.spacingS
|
height: Theme.spacingS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
|
||||||
Row {
|
Row {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Cancel button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 120
|
width: 120
|
||||||
height: 40
|
height: 40
|
||||||
@@ -131,7 +114,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 120
|
width: 120
|
||||||
height: 40
|
height: 40
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ DankModal {
|
|||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
processListModal.visible = false;
|
processListModal.visible = false;
|
||||||
// Close any open context menus
|
|
||||||
if (processContextMenu.visible)
|
if (processContextMenu.visible)
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ DankModal {
|
|||||||
closingModal();
|
closingModal();
|
||||||
|
|
||||||
}
|
}
|
||||||
// DankModal configuration
|
|
||||||
visible: settingsVisible
|
visible: settingsVisible
|
||||||
width: 750
|
width: 750
|
||||||
height: 750
|
height: 750
|
||||||
@@ -28,7 +27,6 @@ DankModal {
|
|||||||
settingsVisible = false;
|
settingsVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard focus and shortcuts
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: settingsModal.settingsVisible
|
focus: settingsModal.settingsVisible
|
||||||
@@ -41,7 +39,6 @@ DankModal {
|
|||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -66,7 +63,6 @@ DankModal {
|
|||||||
height: 1
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close button
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
circular: false
|
circular: false
|
||||||
iconName: "close"
|
iconName: "close"
|
||||||
@@ -78,7 +74,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tabbed Settings
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 50
|
height: parent.height - 50
|
||||||
@@ -102,13 +97,11 @@ DankModal {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - settingsTabBar.height
|
height: parent.height - settingsTabBar.height
|
||||||
|
|
||||||
// Content container with proper padding
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
// Personalization Tab
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 0
|
active: settingsTabBar.currentIndex === 0
|
||||||
@@ -119,7 +112,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time & Weather Tab
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 1
|
active: settingsTabBar.currentIndex === 1
|
||||||
@@ -130,7 +122,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widgets Tab
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 2
|
active: settingsTabBar.currentIndex === 2
|
||||||
@@ -141,7 +132,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launcher Tab
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 3
|
active: settingsTabBar.currentIndex === 3
|
||||||
@@ -152,7 +142,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appearance Tab
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 4
|
active: settingsTabBar.currentIndex === 4
|
||||||
@@ -172,19 +161,19 @@ DankModal {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
console.log("SettingsModal: IPC open() called");
|
|
||||||
settingsModal.settingsVisible = true;
|
settingsModal.settingsVisible = true;
|
||||||
return "SETTINGS_OPEN_SUCCESS";
|
return "SETTINGS_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
console.log("SettingsModal: IPC close() called");
|
|
||||||
settingsModal.settingsVisible = false;
|
settingsModal.settingsVisible = false;
|
||||||
return "SETTINGS_CLOSE_SUCCESS";
|
return "SETTINGS_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
console.log("SettingsModal: IPC toggle() called");
|
|
||||||
settingsModal.settingsVisible = !settingsModal.settingsVisible;
|
settingsModal.settingsVisible = !settingsModal.settingsVisible;
|
||||||
return "SETTINGS_TOGGLE_SUCCESS";
|
return "SETTINGS_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ DankModal {
|
|||||||
property bool spotlightOpen: false
|
property bool spotlightOpen: false
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
console.log("SpotlightModal: show() called");
|
|
||||||
spotlightOpen = true;
|
spotlightOpen = true;
|
||||||
console.log("SpotlightModal: spotlightOpen set to", spotlightOpen);
|
|
||||||
appLauncher.searchQuery = "";
|
appLauncher.searchQuery = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,6 @@ DankModal {
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DankModal configuration
|
|
||||||
visible: spotlightOpen
|
visible: spotlightOpen
|
||||||
width: 550
|
width: 550
|
||||||
height: 600
|
height: 600
|
||||||
@@ -45,7 +44,7 @@ DankModal {
|
|||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
console.log("SpotlightModal visibility changed to:", visible);
|
|
||||||
if (visible && !spotlightOpen)
|
if (visible && !spotlightOpen)
|
||||||
show();
|
show();
|
||||||
|
|
||||||
@@ -54,10 +53,9 @@ DankModal {
|
|||||||
spotlightOpen = false;
|
spotlightOpen = false;
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("SpotlightModal: Component.onCompleted called - component loaded successfully!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// App launcher logic
|
|
||||||
AppLauncher {
|
AppLauncher {
|
||||||
id: appLauncher
|
id: appLauncher
|
||||||
|
|
||||||
@@ -71,19 +69,19 @@ DankModal {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
console.log("SpotlightModal: IPC open() called");
|
|
||||||
spotlightModal.show();
|
spotlightModal.show();
|
||||||
return "SPOTLIGHT_OPEN_SUCCESS";
|
return "SPOTLIGHT_OPEN_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
console.log("SpotlightModal: IPC close() called");
|
|
||||||
spotlightModal.hide();
|
spotlightModal.hide();
|
||||||
return "SPOTLIGHT_CLOSE_SUCCESS";
|
return "SPOTLIGHT_CLOSE_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
console.log("SpotlightModal: IPC toggle() called");
|
|
||||||
spotlightModal.toggle();
|
spotlightModal.toggle();
|
||||||
return "SPOTLIGHT_TOGGLE_SUCCESS";
|
return "SPOTLIGHT_TOGGLE_SUCCESS";
|
||||||
}
|
}
|
||||||
@@ -97,7 +95,6 @@ DankModal {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
// Handle keyboard shortcuts
|
|
||||||
Keys.onPressed: function(event) {
|
Keys.onPressed: function(event) {
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
hide();
|
hide();
|
||||||
@@ -153,7 +150,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search field with view toggle buttons
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -211,13 +207,11 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// View mode toggle buttons next to search bar
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
visible: appLauncher.model.count > 0
|
visible: appLauncher.model.count > 0
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// List view button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 36
|
width: 36
|
||||||
height: 36
|
height: 36
|
||||||
@@ -246,7 +240,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid view button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 36
|
width: 36
|
||||||
height: 36
|
height: 36
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ DankModal {
|
|||||||
wifiPasswordInput = "";
|
wifiPasswordInput = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-reopen dialog on invalid password
|
|
||||||
Connections {
|
Connections {
|
||||||
function onPasswordDialogShouldReopenChanged() {
|
function onPasswordDialogShouldReopenChanged() {
|
||||||
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
|
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
|
||||||
@@ -48,7 +47,6 @@ DankModal {
|
|||||||
width: parent.width - Theme.spacingM * 2
|
width: parent.width - Theme.spacingM * 2
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
@@ -86,7 +84,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Password input
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
@@ -133,7 +130,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show password checkbox
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
@@ -177,7 +173,6 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 40
|
height: 40
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ PanelWindow {
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proper layer shell configuration
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||||
@@ -39,7 +38,6 @@ PanelWindow {
|
|||||||
visible: isVisible
|
visible: isVisible
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
// Full screen overlay setup for proper focus
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
@@ -47,7 +45,6 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// App launcher logic
|
|
||||||
AppLauncher {
|
AppLauncher {
|
||||||
id: appLauncher
|
id: appLauncher
|
||||||
|
|
||||||
@@ -59,12 +56,10 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background click to close (no visual background)
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: appDrawerPopout.isVisible
|
enabled: appDrawerPopout.isVisible
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
// Only close if click is outside the launcher panel
|
|
||||||
var localPos = mapToItem(launcherLoader, mouse.x, mouse.y);
|
var localPos = mapToItem(launcherLoader, mouse.x, mouse.y);
|
||||||
if (localPos.x < 0 || localPos.x > launcherLoader.width || localPos.y < 0 || localPos.y > launcherLoader.height)
|
if (localPos.x < 0 || localPos.x > launcherLoader.width || localPos.y < 0 || localPos.y > launcherLoader.height)
|
||||||
appDrawerPopout.hide();
|
appDrawerPopout.hide();
|
||||||
@@ -72,7 +67,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main launcher panel with asynchronous loading
|
|
||||||
Loader {
|
Loader {
|
||||||
id: launcherLoader
|
id: launcherLoader
|
||||||
|
|
||||||
@@ -108,11 +102,9 @@ PanelWindow {
|
|||||||
|
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadiusXLarge
|
radius: Theme.cornerRadiusXLarge
|
||||||
// Remove layer rendering for better performance
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
|
|
||||||
// Material 3 elevation with multiple layers
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: -3
|
anchors.margins: -3
|
||||||
@@ -142,7 +134,6 @@ PanelWindow {
|
|||||||
z: -1
|
z: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content with focus management
|
|
||||||
Item {
|
Item {
|
||||||
id: keyHandler
|
id: keyHandler
|
||||||
|
|
||||||
@@ -153,7 +144,6 @@ PanelWindow {
|
|||||||
forceActiveFocus();
|
forceActiveFocus();
|
||||||
|
|
||||||
}
|
}
|
||||||
// Handle keyboard shortcuts
|
|
||||||
Keys.onPressed: function(event) {
|
Keys.onPressed: function(event) {
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
appDrawerPopout.hide();
|
appDrawerPopout.hide();
|
||||||
@@ -174,7 +164,6 @@ PanelWindow {
|
|||||||
appLauncher.launchSelected();
|
appLauncher.launchSelected();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (!searchField.activeFocus && event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
} else if (!searchField.activeFocus && event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
||||||
// User started typing, focus search field and pass the character
|
|
||||||
searchField.forceActiveFocus();
|
searchField.forceActiveFocus();
|
||||||
searchField.insertText(event.text);
|
searchField.insertText(event.text);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
@@ -214,7 +203,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced search field
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
@@ -333,7 +321,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// App grid/list container with enhanced styling
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: {
|
height: {
|
||||||
@@ -347,7 +334,6 @@ PanelWindow {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
// List view
|
|
||||||
DankListView {
|
DankListView {
|
||||||
id: appList
|
id: appList
|
||||||
|
|
||||||
@@ -372,7 +358,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid view
|
|
||||||
DankGridView {
|
DankGridView {
|
||||||
id: appGrid
|
id: appGrid
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import qs.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Public interface
|
|
||||||
property string searchQuery: ""
|
property string searchQuery: ""
|
||||||
property string selectedCategory: "All"
|
property string selectedCategory: "All"
|
||||||
property string viewMode: "list" // "list" or "grid"
|
property string viewMode: "list" // "list" or "grid"
|
||||||
@@ -18,7 +17,6 @@ Item {
|
|||||||
property bool debounceSearch: true
|
property bool debounceSearch: true
|
||||||
property int debounceInterval: 50
|
property int debounceInterval: 50
|
||||||
property bool keyboardNavigationActive: false
|
property bool keyboardNavigationActive: false
|
||||||
// Categories (computed from AppSearchService)
|
|
||||||
property var categories: {
|
property var categories: {
|
||||||
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
|
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
|
||||||
return cat !== "Education" && cat !== "Science";
|
return cat !== "Education" && cat !== "Science";
|
||||||
@@ -28,18 +26,13 @@ Item {
|
|||||||
return cat !== "All";
|
return cat !== "All";
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// Category icons (computed from AppSearchService)
|
|
||||||
property var categoryIcons: categories.map((category) => {
|
property var categoryIcons: categories.map((category) => {
|
||||||
return AppSearchService.getCategoryIcon(category);
|
return AppSearchService.getCategoryIcon(category);
|
||||||
})
|
})
|
||||||
// App usage ranking helper
|
|
||||||
property var appUsageRanking: Prefs.appUsageRanking
|
property var appUsageRanking: Prefs.appUsageRanking
|
||||||
// Internal model
|
|
||||||
property alias model: filteredModel
|
property alias model: filteredModel
|
||||||
// Watch AppSearchService.applications changes via property binding
|
|
||||||
property var _watchApplications: AppSearchService.applications
|
property var _watchApplications: AppSearchService.applications
|
||||||
|
|
||||||
// Signals
|
|
||||||
signal appLaunched(var app)
|
signal appLaunched(var app)
|
||||||
signal categorySelected(string category)
|
signal categorySelected(string category)
|
||||||
signal viewModeSelected(string mode)
|
signal viewModeSelected(string mode)
|
||||||
@@ -50,7 +43,6 @@ Item {
|
|||||||
keyboardNavigationActive = false;
|
keyboardNavigationActive = false;
|
||||||
var apps = [];
|
var apps = [];
|
||||||
if (searchQuery.length === 0) {
|
if (searchQuery.length === 0) {
|
||||||
// Show apps from category
|
|
||||||
if (selectedCategory === "All") {
|
if (selectedCategory === "All") {
|
||||||
apps = AppSearchService.applications || [];
|
apps = AppSearchService.applications || [];
|
||||||
} else {
|
} else {
|
||||||
@@ -58,7 +50,6 @@ Item {
|
|||||||
apps = categoryApps.slice(0, maxResults);
|
apps = categoryApps.slice(0, maxResults);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Search with category filter
|
|
||||||
if (selectedCategory === "All") {
|
if (selectedCategory === "All") {
|
||||||
apps = AppSearchService.searchApplications(searchQuery);
|
apps = AppSearchService.searchApplications(searchQuery);
|
||||||
} else {
|
} else {
|
||||||
@@ -88,7 +79,6 @@ Item {
|
|||||||
return (a.name || "").localeCompare(b.name || "");
|
return (a.name || "").localeCompare(b.name || "");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert to model format and populate
|
|
||||||
apps.forEach((app) => {
|
apps.forEach((app) => {
|
||||||
if (app)
|
if (app)
|
||||||
filteredModel.append({
|
filteredModel.append({
|
||||||
@@ -103,7 +93,6 @@ Item {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard navigation functions
|
|
||||||
function selectNext() {
|
function selectNext() {
|
||||||
if (filteredModel.count > 0) {
|
if (filteredModel.count > 0) {
|
||||||
keyboardNavigationActive = true;
|
keyboardNavigationActive = true;
|
||||||
@@ -142,7 +131,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// App launching
|
|
||||||
function launchSelected() {
|
function launchSelected() {
|
||||||
if (filteredModel.count > 0 && selectedIndex >= 0 && selectedIndex < filteredModel.count) {
|
if (filteredModel.count > 0 && selectedIndex >= 0 && selectedIndex < filteredModel.count) {
|
||||||
var selectedApp = filteredModel.get(selectedIndex);
|
var selectedApp = filteredModel.get(selectedIndex);
|
||||||
@@ -151,28 +139,24 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function launchApp(appData) {
|
function launchApp(appData) {
|
||||||
if (!appData) {
|
if (!appData)
|
||||||
console.warn("AppLauncher: No app data provided");
|
|
||||||
return ;
|
return ;
|
||||||
}
|
|
||||||
appData.desktopEntry.execute();
|
appData.desktopEntry.execute();
|
||||||
appLaunched(appData);
|
appLaunched(appData);
|
||||||
Prefs.addAppUsage(appData.desktopEntry);
|
Prefs.addAppUsage(appData.desktopEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category management
|
|
||||||
function setCategory(category) {
|
function setCategory(category) {
|
||||||
selectedCategory = category;
|
selectedCategory = category;
|
||||||
categorySelected(category);
|
categorySelected(category);
|
||||||
}
|
}
|
||||||
|
|
||||||
// View mode management
|
|
||||||
function setViewMode(mode) {
|
function setViewMode(mode) {
|
||||||
viewMode = mode;
|
viewMode = mode;
|
||||||
viewModeSelected(mode);
|
viewModeSelected(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for changes
|
|
||||||
onSearchQueryChanged: {
|
onSearchQueryChanged: {
|
||||||
if (debounceSearch)
|
if (debounceSearch)
|
||||||
searchDebounceTimer.restart();
|
searchDebounceTimer.restart();
|
||||||
@@ -182,7 +166,6 @@ Item {
|
|||||||
onSelectedCategoryChanged: updateFilteredModel()
|
onSelectedCategoryChanged: updateFilteredModel()
|
||||||
onAppUsageRankingChanged: updateFilteredModel()
|
onAppUsageRankingChanged: updateFilteredModel()
|
||||||
on_WatchApplicationsChanged: updateFilteredModel()
|
on_WatchApplicationsChanged: updateFilteredModel()
|
||||||
// Initialize
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateFilteredModel();
|
updateFilteredModel();
|
||||||
}
|
}
|
||||||
@@ -191,7 +174,6 @@ Item {
|
|||||||
id: filteredModel
|
id: filteredModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search debouncing
|
|
||||||
Timer {
|
Timer {
|
||||||
id: searchDebounceTimer
|
id: searchDebounceTimer
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ Item {
|
|||||||
|
|
||||||
height: compact ? 36 : (72 + Theme.spacingS) // Single row vs two rows
|
height: compact ? 36 : (72 + Theme.spacingS) // Single row vs two rows
|
||||||
|
|
||||||
// Compact single-row layout (for SpotlightModal style)
|
|
||||||
Row {
|
Row {
|
||||||
visible: compact
|
visible: compact
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -55,13 +54,11 @@ Item {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two-row layout (for SpotlightModal organized style)
|
|
||||||
Column {
|
Column {
|
||||||
visible: !compact
|
visible: !compact
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Top row: All, Development, Graphics, Games (4 items)
|
|
||||||
Row {
|
Row {
|
||||||
property var topRowCategories: ["All", "Development", "Graphics", "Games"]
|
property var topRowCategories: ["All", "Development", "Graphics", "Games"]
|
||||||
|
|
||||||
@@ -105,7 +102,6 @@ Item {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom row: Internet, Media, Office, Settings, System (5 items)
|
|
||||||
Row {
|
Row {
|
||||||
property var bottomRowCategories: ["Internet", "Media", "Office", "Settings", "System"]
|
property var bottomRowCategories: ["Internet", "Media", "Office", "Settings", "System"]
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ Column {
|
|||||||
if (!CalendarService || !CalendarService.khalAvailable)
|
if (!CalendarService || !CalendarService.khalAvailable)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
// Calculate date range with padding
|
|
||||||
let firstDay = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
let firstDay = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
||||||
let dayOfWeek = firstDay.getDay();
|
let dayOfWeek = firstDay.getDay();
|
||||||
let startDate = new Date(firstDay);
|
let startDate = new Date(firstDay);
|
||||||
@@ -27,7 +26,6 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
// Load events when display date changes
|
|
||||||
onDisplayDateChanged: {
|
onDisplayDateChanged: {
|
||||||
loadEventsForMonth();
|
loadEventsForMonth();
|
||||||
}
|
}
|
||||||
@@ -35,7 +33,6 @@ Column {
|
|||||||
loadEventsForMonth();
|
loadEventsForMonth();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load events when calendar service becomes available
|
|
||||||
Connections {
|
Connections {
|
||||||
function onKhalAvailableChanged() {
|
function onKhalAvailableChanged() {
|
||||||
if (CalendarService && CalendarService.khalAvailable)
|
if (CalendarService && CalendarService.khalAvailable)
|
||||||
@@ -47,7 +44,6 @@ Column {
|
|||||||
enabled: CalendarService !== null
|
enabled: CalendarService !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Month navigation header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 40
|
height: 40
|
||||||
@@ -121,7 +117,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Days of week header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -148,7 +143,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calendar grid
|
|
||||||
Grid {
|
Grid {
|
||||||
property date firstDay: {
|
property date firstDay: {
|
||||||
let date = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
let date = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
||||||
@@ -196,7 +190,6 @@ Column {
|
|||||||
font.weight: isToday || isSelected ? Font.Medium : Font.Normal
|
font.weight: isToday || isSelected ? Font.Medium : Font.Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event indicator - bottom fill effect
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: eventIndicator
|
id: eventIndicator
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ PanelWindow {
|
|||||||
if (calendarVisible) {
|
if (calendarVisible) {
|
||||||
internalVisible = true;
|
internalVisible = true;
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
// This ensures opacity changes after window is visible
|
|
||||||
internalVisible = true;
|
internalVisible = true;
|
||||||
// Force re-trigger if needed
|
|
||||||
// Ensure events are loaded for current display month
|
|
||||||
calendarGrid.loadEventsForMonth();
|
calendarGrid.loadEventsForMonth();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -51,8 +48,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// Animation finished, now we can safely resize
|
|
||||||
|
|
||||||
id: mainContainer
|
id: mainContainer
|
||||||
|
|
||||||
readonly property real targetWidth: Math.min(Screen.width * 0.9, 600)
|
readonly property real targetWidth: Math.min(Screen.width * 0.9, 600)
|
||||||
@@ -67,21 +62,16 @@ PanelWindow {
|
|||||||
|
|
||||||
function calculateHeight() {
|
function calculateHeight() {
|
||||||
let contentHeight = Theme.spacingM * 2; // margins
|
let contentHeight = Theme.spacingM * 2; // margins
|
||||||
// Main row with widgets and calendar
|
|
||||||
let widgetHeight = 160;
|
let widgetHeight = 160;
|
||||||
// Media widget always present
|
|
||||||
widgetHeight += 140 + Theme.spacingM;
|
widgetHeight += 140 + Theme.spacingM;
|
||||||
// Weather widget always present
|
|
||||||
let calendarHeight = 300;
|
let calendarHeight = 300;
|
||||||
let mainRowHeight = Math.max(widgetHeight, calendarHeight);
|
let mainRowHeight = Math.max(widgetHeight, calendarHeight);
|
||||||
contentHeight += mainRowHeight + Theme.spacingM;
|
contentHeight += mainRowHeight + Theme.spacingM;
|
||||||
// Add events widget height - use calculated height instead of actual
|
|
||||||
if (CalendarService && CalendarService.khalAvailable) {
|
if (CalendarService && CalendarService.khalAvailable) {
|
||||||
let hasEvents = events.selectedDateEvents && events.selectedDateEvents.length > 0;
|
let hasEvents = events.selectedDateEvents && events.selectedDateEvents.length > 0;
|
||||||
let eventsHeight = hasEvents ? Math.min(300, 80 + events.selectedDateEvents.length * 60) : 120;
|
let eventsHeight = hasEvents ? Math.min(300, 80 + events.selectedDateEvents.length * 60) : 120;
|
||||||
contentHeight += eventsHeight;
|
contentHeight += eventsHeight;
|
||||||
} else {
|
} else {
|
||||||
// When no khal, reduce bottom margin to match top
|
|
||||||
contentHeight -= Theme.spacingM;
|
contentHeight -= Theme.spacingM;
|
||||||
}
|
}
|
||||||
return Math.min(contentHeight, parent.height * 0.9);
|
return Math.min(contentHeight, parent.height * 0.9);
|
||||||
@@ -98,7 +88,6 @@ PanelWindow {
|
|||||||
scale: calendarVisible ? 1 : 0.9
|
scale: calendarVisible ? 1 : 0.9
|
||||||
x: (Screen.width - targetWidth) / 2
|
x: (Screen.width - targetWidth) / 2
|
||||||
y: Theme.barHeight + 4
|
y: Theme.barHeight + 4
|
||||||
// Only resize after animation is complete
|
|
||||||
onOpacityChanged: {
|
onOpacityChanged: {
|
||||||
if (opacity === 1)
|
if (opacity === 1)
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
@@ -165,7 +154,6 @@ PanelWindow {
|
|||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Main row with widgets and calendar - improved spacing and proportions
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: {
|
height: {
|
||||||
@@ -176,7 +164,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Left section for widgets - improved visual hierarchy
|
|
||||||
Column {
|
Column {
|
||||||
id: leftWidgets
|
id: leftWidgets
|
||||||
|
|
||||||
@@ -200,7 +187,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right section for calendar - enhanced container
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingM : parent.width
|
width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingM : parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
@@ -263,7 +249,6 @@ PanelWindow {
|
|||||||
z: -1
|
z: -1
|
||||||
enabled: calendarVisible
|
enabled: calendarVisible
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
// Only close if click is outside the main container
|
|
||||||
var localPos = mapToItem(mainContainer, mouse.x, mouse.y);
|
var localPos = mapToItem(mainContainer, mouse.x, mouse.y);
|
||||||
if (localPos.x < 0 || localPos.x > mainContainer.width || localPos.y < 0 || localPos.y > mainContainer.height)
|
if (localPos.x < 0 || localPos.x > mainContainer.width || localPos.y < 0 || localPos.y > mainContainer.height)
|
||||||
calendarVisible = false;
|
calendarVisible = false;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import qs.Common
|
|||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
// Events widget for selected date - Material Design 3 style
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: events
|
id: events
|
||||||
|
|
||||||
@@ -17,7 +16,6 @@ Rectangle {
|
|||||||
function updateSelectedDateEvents() {
|
function updateSelectedDateEvents() {
|
||||||
if (CalendarService && CalendarService.khalAvailable) {
|
if (CalendarService && CalendarService.khalAvailable) {
|
||||||
let events = CalendarService.getEventsForDate(selectedDate);
|
let events = CalendarService.getEventsForDate(selectedDate);
|
||||||
console.log("Events: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events");
|
|
||||||
selectedDateEvents = events;
|
selectedDateEvents = events;
|
||||||
} else {
|
} else {
|
||||||
selectedDateEvents = [];
|
selectedDateEvents = [];
|
||||||
@@ -25,7 +23,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSelectedDateEventsChanged: {
|
onSelectedDateEventsChanged: {
|
||||||
console.log("Events: selectedDateEvents changed, count:", selectedDateEvents.length);
|
|
||||||
eventsList.model = selectedDateEvents;
|
eventsList.model = selectedDateEvents;
|
||||||
}
|
}
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -35,7 +32,6 @@ Rectangle {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
visible: shouldShow
|
visible: shouldShow
|
||||||
// Material elevation shadow
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateSelectedDateEvents();
|
updateSelectedDateEvents();
|
||||||
@@ -44,7 +40,6 @@ Rectangle {
|
|||||||
updateSelectedDateEvents();
|
updateSelectedDateEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update events when selected date or events change
|
|
||||||
Connections {
|
Connections {
|
||||||
function onEventsByDateChanged() {
|
function onEventsByDateChanged() {
|
||||||
updateSelectedDateEvents();
|
updateSelectedDateEvents();
|
||||||
@@ -58,7 +53,6 @@ Rectangle {
|
|||||||
enabled: CalendarService !== null
|
enabled: CalendarService !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header - always visible when widget is shown
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
|
|
||||||
@@ -85,7 +79,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No events placeholder - centered in entire widget (not just content area)
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
@@ -108,7 +101,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events list - positioned below header when there are events
|
|
||||||
ListView {
|
ListView {
|
||||||
id: eventsList
|
id: eventsList
|
||||||
|
|
||||||
@@ -157,7 +149,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
// Event indicator strip
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 4
|
width: 4
|
||||||
height: parent.height - 8
|
height: parent.height - 8
|
||||||
@@ -270,7 +261,7 @@ Rectangle {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.url && modelData.url !== "") {
|
if (modelData.url && modelData.url !== "") {
|
||||||
if (Qt.openUrlExternally(modelData.url) === false)
|
if (Qt.openUrlExternally(modelData.url) === false)
|
||||||
console.warn("Couldn't open", modelData.url);
|
console.warn("Failed to open URL: " + modelData.url);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ Rectangle {
|
|||||||
property string lastValidArtUrl: ""
|
property string lastValidArtUrl: ""
|
||||||
property real currentPosition: 0
|
property real currentPosition: 0
|
||||||
|
|
||||||
// Simple progress ratio calculation
|
|
||||||
function ratio() {
|
function ratio() {
|
||||||
return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0;
|
return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0;
|
||||||
}
|
}
|
||||||
@@ -41,26 +40,22 @@ Rectangle {
|
|||||||
|
|
||||||
interval: 2000
|
interval: 2000
|
||||||
running: {
|
running: {
|
||||||
// Run when no active player (for cache clearing) OR when playing (for position updates)
|
|
||||||
return (!activePlayer) || (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking);
|
return (!activePlayer) || (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking);
|
||||||
}
|
}
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!activePlayer) {
|
if (!activePlayer) {
|
||||||
// Clear cache when no player
|
|
||||||
lastValidTitle = "";
|
lastValidTitle = "";
|
||||||
lastValidArtist = "";
|
lastValidArtist = "";
|
||||||
lastValidAlbum = "";
|
lastValidAlbum = "";
|
||||||
lastValidArtUrl = "";
|
lastValidArtUrl = "";
|
||||||
stop(); // Stop after clearing cache
|
stop(); // Stop after clearing cache
|
||||||
} else if (activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
|
} else if (activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
|
||||||
// Update position when playing
|
|
||||||
currentPosition = activePlayer.position;
|
currentPosition = activePlayer.position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend events
|
|
||||||
Connections {
|
Connections {
|
||||||
function onPositionChanged() {
|
function onPositionChanged() {
|
||||||
if (!progressMouseArea.isSeeking)
|
if (!progressMouseArea.isSeeking)
|
||||||
@@ -83,7 +78,6 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
|
|
||||||
// Placeholder when no media - centered in entire widget
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -105,19 +99,16 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active content in a column
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
|
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
|
||||||
|
|
||||||
// Normal media info when playing
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Album Art
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 60
|
width: 60
|
||||||
height: 60
|
height: 60
|
||||||
@@ -161,7 +152,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track Info
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width - 60 - Theme.spacingM
|
width: parent.width - 60 - Theme.spacingM
|
||||||
height: parent.height
|
height: parent.height
|
||||||
@@ -218,7 +208,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress bar
|
|
||||||
Item {
|
Item {
|
||||||
id: progressBarContainer
|
id: progressBarContainer
|
||||||
|
|
||||||
@@ -252,7 +241,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drag handle
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progressHandle
|
id: progressHandle
|
||||||
|
|
||||||
@@ -318,7 +306,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global mouse area for drag tracking
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: progressGlobalMouseArea
|
id: progressGlobalMouseArea
|
||||||
|
|
||||||
@@ -345,7 +332,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control buttons - always visible
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -356,7 +342,6 @@ Rectangle {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
// Previous button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 28
|
width: 28
|
||||||
height: 28
|
height: 28
|
||||||
@@ -380,7 +365,6 @@ Rectangle {
|
|||||||
if (!activePlayer)
|
if (!activePlayer)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
// >8 s → jump to start, otherwise previous track
|
|
||||||
if (currentPosition > 8 && activePlayer.canSeek) {
|
if (currentPosition > 8 && activePlayer.canSeek) {
|
||||||
activePlayer.position = 0;
|
activePlayer.position = 0;
|
||||||
currentPosition = 0;
|
currentPosition = 0;
|
||||||
@@ -392,7 +376,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play/Pause button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
@@ -415,7 +398,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 28
|
width: 28
|
||||||
height: 28
|
height: 28
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ Rectangle {
|
|||||||
service: WeatherService
|
service: WeatherService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder when no weather - centered in entire widget
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -42,14 +41,12 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather content when available - original Column structure
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
visible: WeatherService.weather.available && WeatherService.weather.temp !== 0
|
visible: WeatherService.weather.available && WeatherService.weather.temp !== 0
|
||||||
|
|
||||||
// Weather header info
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
@@ -58,7 +55,6 @@ Rectangle {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Weather icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||||
size: Theme.iconSize + 8
|
size: Theme.iconSize + 8
|
||||||
@@ -103,7 +99,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather details grid
|
|
||||||
Grid {
|
Grid {
|
||||||
columns: 2
|
columns: 2
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|||||||
@@ -23,13 +23,10 @@ PanelWindow {
|
|||||||
|
|
||||||
visible: controlCenterVisible
|
visible: controlCenterVisible
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
// Enable/disable WiFi auto-refresh based on control center visibility
|
|
||||||
NetworkService.autoRefreshEnabled = visible && NetworkService.wifiEnabled;
|
NetworkService.autoRefreshEnabled = visible && NetworkService.wifiEnabled;
|
||||||
// Stop bluetooth scanning when control center is closed
|
|
||||||
if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering)
|
if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering)
|
||||||
BluetoothService.adapter.discovering = false;
|
BluetoothService.adapter.discovering = false;
|
||||||
|
|
||||||
// Refresh uptime when opened
|
|
||||||
if (visible && UserInfoService)
|
if (visible && UserInfoService)
|
||||||
UserInfoService.getUptime();
|
UserInfoService.getUptime();
|
||||||
|
|
||||||
@@ -59,7 +56,6 @@ PanelWindow {
|
|||||||
height: root.powerOptionsExpanded ? 570 : 500
|
height: root.powerOptionsExpanded ? 570 : 500
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
||||||
// GPU-accelerated scale + opacity animation
|
|
||||||
opacity: controlCenterVisible ? 1 : 0
|
opacity: controlCenterVisible ? 1 : 0
|
||||||
scale: controlCenterVisible ? 1 : 0.9
|
scale: controlCenterVisible ? 1 : 0.9
|
||||||
|
|
||||||
@@ -86,7 +82,6 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
// Remove layer rendering for better performance
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
|
|
||||||
@@ -95,7 +90,6 @@ PanelWindow {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Elegant User Header
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
@@ -115,7 +109,6 @@ PanelWindow {
|
|||||||
anchors.rightMargin: Theme.spacingL
|
anchors.rightMargin: Theme.spacingL
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Profile Picture Container
|
|
||||||
Item {
|
Item {
|
||||||
id: avatarContainer
|
id: avatarContainer
|
||||||
|
|
||||||
@@ -124,7 +117,6 @@ PanelWindow {
|
|||||||
width: 64
|
width: 64
|
||||||
height: 64
|
height: 64
|
||||||
|
|
||||||
// This rectangle provides the themed ring via its border.
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
@@ -134,7 +126,6 @@ PanelWindow {
|
|||||||
visible: parent.hasImage
|
visible: parent.hasImage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hidden Image loader. Its only purpose is to load the texture.
|
|
||||||
Image {
|
Image {
|
||||||
id: profileImageLoader
|
id: profileImageLoader
|
||||||
|
|
||||||
@@ -183,7 +174,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for when there is no image.
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
@@ -199,7 +189,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error icon for when the image fails to load.
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: "warning"
|
name: "warning"
|
||||||
@@ -210,7 +199,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// User Info Text
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
@@ -233,14 +221,12 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action Buttons - Lock, Power and Settings
|
|
||||||
Row {
|
Row {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.rightMargin: Theme.spacingL
|
anchors.rightMargin: Theme.spacingL
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Lock Button
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
buttonSize: 40
|
buttonSize: 40
|
||||||
iconName: "lock"
|
iconName: "lock"
|
||||||
@@ -254,7 +240,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power Button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
@@ -278,7 +263,6 @@ PanelWindow {
|
|||||||
color: powerButton.containsMouse || root.powerOptionsExpanded ? Theme.error : Theme.surfaceText
|
color: powerButton.containsMouse || root.powerOptionsExpanded ? Theme.error : Theme.surfaceText
|
||||||
|
|
||||||
Behavior on name {
|
Behavior on name {
|
||||||
// Smooth icon transition
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: dankIcon
|
target: dankIcon
|
||||||
@@ -330,7 +314,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings Button
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
buttonSize: 40
|
buttonSize: 40
|
||||||
iconName: "settings"
|
iconName: "settings"
|
||||||
@@ -348,7 +331,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animated Collapsible Power Options (optimized)
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: root.powerOptionsExpanded ? 60 : 0
|
height: root.powerOptionsExpanded ? 60 : 0
|
||||||
@@ -364,7 +346,6 @@ PanelWindow {
|
|||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
visible: root.powerOptionsExpanded
|
visible: root.powerOptionsExpanded
|
||||||
|
|
||||||
// Logout
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
@@ -414,7 +395,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reboot
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
@@ -464,7 +444,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspend
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
@@ -514,7 +493,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
@@ -566,7 +544,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single coordinated animation for power options
|
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
@@ -689,7 +666,6 @@ PanelWindow {
|
|||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
visible: root.currentTab === "display"
|
visible: root.currentTab === "display"
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
// Brightness Control
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -765,7 +741,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power menu height animation
|
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration // Faster for height changes
|
duration: Theme.shortDuration // Faster for height changes
|
||||||
@@ -778,12 +753,10 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to close
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
// Only close if click is outside the content loader
|
|
||||||
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
||||||
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
||||||
controlCenterVisible = false;
|
controlCenterVisible = false;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ ScrollView {
|
|||||||
interval: BrightnessService.ddcAvailable ? 500 : 50 // 500ms for slow DDC (i2c), 50ms for fast laptop backlight
|
interval: BrightnessService.ddcAvailable ? 500 : 50 // 500ms for slow DDC (i2c), 50ms for fast laptop backlight
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
console.log("Debounce timer fired, setting brightness to:", pendingValue);
|
|
||||||
BrightnessService.setBrightness(pendingValue);
|
BrightnessService.setBrightness(pendingValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,6 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Brightness Control
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -49,12 +48,12 @@ ScrollView {
|
|||||||
rightIcon: "brightness_high"
|
rightIcon: "brightness_high"
|
||||||
enabled: BrightnessService.brightnessAvailable
|
enabled: BrightnessService.brightnessAvailable
|
||||||
onSliderValueChanged: function(newValue) {
|
onSliderValueChanged: function(newValue) {
|
||||||
console.log("Slider changed to:", newValue);
|
|
||||||
brightnessDebounceTimer.pendingValue = newValue;
|
brightnessDebounceTimer.pendingValue = newValue;
|
||||||
brightnessDebounceTimer.restart();
|
brightnessDebounceTimer.restart();
|
||||||
}
|
}
|
||||||
onSliderDragFinished: function(finalValue) {
|
onSliderDragFinished: function(finalValue) {
|
||||||
console.log("Drag finished, immediate set:", finalValue);
|
|
||||||
brightnessDebounceTimer.stop();
|
brightnessDebounceTimer.stop();
|
||||||
BrightnessService.setBrightness(finalValue);
|
BrightnessService.setBrightness(finalValue);
|
||||||
}
|
}
|
||||||
@@ -70,7 +69,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display settings
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -82,12 +80,10 @@ ScrollView {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mode toggles row (Night Mode + Light/Dark Mode)
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Night mode toggle
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 80
|
height: 80
|
||||||
@@ -125,11 +121,9 @@ ScrollView {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (Prefs.nightModeEnabled) {
|
if (Prefs.nightModeEnabled) {
|
||||||
// Disable night mode - kill any running color temperature processes
|
|
||||||
nightModeDisableProcess.running = true;
|
nightModeDisableProcess.running = true;
|
||||||
Prefs.setNightModeEnabled(false);
|
Prefs.setNightModeEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
// Enable night mode using wlsunset or redshift
|
|
||||||
nightModeEnableProcess.running = true;
|
nightModeEnableProcess.running = true;
|
||||||
Prefs.setNightModeEnabled(true);
|
Prefs.setNightModeEnabled(true);
|
||||||
}
|
}
|
||||||
@@ -138,7 +132,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Light/Dark mode toggle
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 80
|
height: 80
|
||||||
@@ -195,7 +188,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Night mode processes
|
|
||||||
Process {
|
Process {
|
||||||
id: nightModeEnableProcess
|
id: nightModeEnableProcess
|
||||||
|
|
||||||
@@ -203,7 +195,7 @@ ScrollView {
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to enable night mode");
|
|
||||||
Prefs.setNightModeEnabled(false);
|
Prefs.setNightModeEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +208,7 @@ ScrollView {
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0)
|
if (exitCode !== 0)
|
||||||
console.warn("Failed to disable night mode");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading spinner for preference changes
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
id: ethernetLoadingSpinner
|
id: ethernetLoadingSpinner
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethernet toggle switch (matching WiFi style)
|
|
||||||
DankToggle {
|
DankToggle {
|
||||||
id: ethernetToggle
|
id: ethernetToggle
|
||||||
|
|
||||||
@@ -100,7 +98,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MouseArea for network preference (excluding toggle area)
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: ethernetPreferenceArea
|
id: ethernetPreferenceArea
|
||||||
|
|
||||||
@@ -111,7 +108,7 @@ Rectangle {
|
|||||||
enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "ethernet" && !NetworkService.changingNetworkPreference
|
enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "ethernet" && !NetworkService.changingNetworkPreference
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) {
|
if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) {
|
||||||
console.log("Ethernet card clicked for preference");
|
|
||||||
if (NetworkService.networkStatus !== "ethernet")
|
if (NetworkService.networkStatus !== "ethernet")
|
||||||
NetworkService.setNetworkPreference("ethernet");
|
NetworkService.setNetworkPreference("ethernet");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading spinner for preference changes
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
id: wifiLoadingSpinner
|
id: wifiLoadingSpinner
|
||||||
|
|
||||||
@@ -125,7 +124,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi toggle switch
|
|
||||||
DankToggle {
|
DankToggle {
|
||||||
id: wifiToggle
|
id: wifiToggle
|
||||||
|
|
||||||
@@ -137,7 +135,6 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (NetworkService.wifiEnabled) {
|
if (NetworkService.wifiEnabled) {
|
||||||
// When turning WiFi off, clear all cached WiFi data
|
|
||||||
NetworkService.currentWifiSSID = "";
|
NetworkService.currentWifiSSID = "";
|
||||||
NetworkService.wifiSignalStrength = "excellent";
|
NetworkService.wifiSignalStrength = "excellent";
|
||||||
NetworkService.wifiNetworks = [];
|
NetworkService.wifiNetworks = [];
|
||||||
@@ -154,7 +151,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MouseArea for network preference (excluding toggle area)
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: wifiPreferenceArea
|
id: wifiPreferenceArea
|
||||||
|
|
||||||
@@ -165,7 +161,7 @@ Rectangle {
|
|||||||
enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "wifi" && !NetworkService.changingNetworkPreference
|
enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "wifi" && !NetworkService.changingNetworkPreference
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) {
|
if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) {
|
||||||
console.log("WiFi card clicked for preference");
|
|
||||||
if (NetworkService.networkStatus !== "wifi")
|
if (NetworkService.networkStatus !== "wifi")
|
||||||
NetworkService.setNetworkPreference("wifi");
|
NetworkService.setNetworkPreference("wifi");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ Rectangle {
|
|||||||
visible = false;
|
visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop shadow
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: 4
|
anchors.topMargin: 4
|
||||||
@@ -76,7 +75,6 @@ Rectangle {
|
|||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
// Connect/Disconnect option
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -145,7 +143,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 5
|
height: 5
|
||||||
@@ -161,7 +158,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forget Network option (only for saved networks)
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -217,7 +213,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network Info option
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ Column {
|
|||||||
visible: NetworkService.wifiEnabled
|
visible: NetworkService.wifiEnabled
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Available Networks Section with refresh button (spanning version)
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
@@ -55,7 +54,6 @@ Column {
|
|||||||
height: 1
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi refresh button (spanning version)
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 28
|
width: 28
|
||||||
height: 28
|
height: 28
|
||||||
@@ -99,7 +97,6 @@ Column {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!NetworkService.isScanning) {
|
if (!NetworkService.isScanning) {
|
||||||
// Immediate visual feedback
|
|
||||||
refreshIconSpan.rotation += 30;
|
refreshIconSpan.rotation += 30;
|
||||||
NetworkService.scanWifi();
|
NetworkService.scanWifi();
|
||||||
}
|
}
|
||||||
@@ -110,7 +107,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrollable networks container
|
|
||||||
Flickable {
|
Flickable {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 40
|
height: parent.height - 40
|
||||||
@@ -143,7 +139,6 @@ Column {
|
|||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
anchors.rightMargin: Theme.spacingM // Extra right margin for scrollbar
|
anchors.rightMargin: Theme.spacingM // Extra right margin for scrollbar
|
||||||
|
|
||||||
// Signal strength icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
id: signalIcon2
|
id: signalIcon2
|
||||||
|
|
||||||
@@ -154,7 +149,6 @@ Column {
|
|||||||
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network info
|
|
||||||
Column {
|
Column {
|
||||||
anchors.left: signalIcon2.right
|
anchors.left: signalIcon2.right
|
||||||
anchors.leftMargin: Theme.spacingXS
|
anchors.leftMargin: Theme.spacingXS
|
||||||
@@ -204,7 +198,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right side icons
|
|
||||||
Row {
|
Row {
|
||||||
id: rightIcons2
|
id: rightIcons2
|
||||||
|
|
||||||
@@ -212,7 +205,6 @@ Column {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
// Lock icon (if secured)
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "lock"
|
name: "lock"
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
@@ -221,7 +213,6 @@ Column {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context menu button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: wifiMenuButton
|
id: wifiMenuButton
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,11 @@ Item {
|
|||||||
property var wifiPasswordModalRef: wifiPasswordModal
|
property var wifiPasswordModalRef: wifiPasswordModal
|
||||||
property var networkInfoModalRef: networkInfoModal
|
property var networkInfoModalRef: networkInfoModal
|
||||||
|
|
||||||
// Properly sorted WiFi networks with connected networks first
|
|
||||||
property var sortedWifiNetworks: {
|
property var sortedWifiNetworks: {
|
||||||
if (!NetworkService.wifiAvailable || !NetworkService.wifiEnabled) {
|
if (!NetworkService.wifiAvailable || !NetworkService.wifiEnabled) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly reference both arrays to ensure reactivity
|
|
||||||
var allNetworks = NetworkService.wifiNetworks;
|
var allNetworks = NetworkService.wifiNetworks;
|
||||||
var savedNetworks = NetworkService.savedWifiNetworks;
|
var savedNetworks = NetworkService.savedWifiNetworks;
|
||||||
var currentSSID = NetworkService.currentWifiSSID;
|
var currentSSID = NetworkService.currentWifiSSID;
|
||||||
@@ -29,32 +27,25 @@ Item {
|
|||||||
|
|
||||||
var networks = [...allNetworks];
|
var networks = [...allNetworks];
|
||||||
|
|
||||||
// Update connected status, saved status and signal strength based on current state
|
|
||||||
networks.forEach(function(network) {
|
networks.forEach(function(network) {
|
||||||
network.connected = (network.ssid === currentSSID);
|
network.connected = (network.ssid === currentSSID);
|
||||||
// Update saved status based on savedWifiNetworks
|
|
||||||
network.saved = savedNetworks.some(function(saved) {
|
network.saved = savedNetworks.some(function(saved) {
|
||||||
return saved.ssid === network.ssid;
|
return saved.ssid === network.ssid;
|
||||||
});
|
});
|
||||||
// Use current connection's signal strength for connected network
|
|
||||||
if (network.connected && signalStrength) {
|
if (network.connected && signalStrength) {
|
||||||
network.signalStrength = signalStrength;
|
network.signalStrength = signalStrength;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort: connected networks first, then by signal strength
|
|
||||||
networks.sort(function(a, b) {
|
networks.sort(function(a, b) {
|
||||||
// Connected networks always come first
|
|
||||||
if (a.connected && !b.connected) return -1;
|
if (a.connected && !b.connected) return -1;
|
||||||
if (!a.connected && b.connected) return 1;
|
if (!a.connected && b.connected) return 1;
|
||||||
// If both connected or both not connected, sort by signal strength
|
|
||||||
return b.signal - a.signal;
|
return b.signal - a.signal;
|
||||||
});
|
});
|
||||||
|
|
||||||
return networks;
|
return networks;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force refresh of sortedWifiNetworks when networks are updated
|
|
||||||
property int forceRefresh: 0
|
property int forceRefresh: 0
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -64,13 +55,11 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-enable WiFi auto-refresh when network tab is visible
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
NetworkService.addRef();
|
NetworkService.addRef();
|
||||||
NetworkService.autoRefreshEnabled = true;
|
NetworkService.autoRefreshEnabled = true;
|
||||||
if (NetworkService.wifiEnabled)
|
if (NetworkService.wifiEnabled)
|
||||||
NetworkService.scanWifi();
|
NetworkService.scanWifi();
|
||||||
// Start smart monitoring
|
|
||||||
wifiMonitorTimer.start();
|
wifiMonitorTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,18 +68,15 @@ Item {
|
|||||||
NetworkService.autoRefreshEnabled = false;
|
NetworkService.autoRefreshEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two-column layout for WiFi and Ethernet (WiFi on left, Ethernet on right)
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// WiFi Column (left side)
|
|
||||||
Column {
|
Column {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// WiFi Content in Flickable
|
|
||||||
Flickable {
|
Flickable {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 30
|
height: parent.height - 30
|
||||||
@@ -106,7 +92,6 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Current WiFi connection status card
|
|
||||||
WiFiCard {
|
WiFiCard {
|
||||||
refreshTimer: refreshTimer
|
refreshTimer: refreshTimer
|
||||||
}
|
}
|
||||||
@@ -118,13 +103,11 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethernet Column (right side)
|
|
||||||
Column {
|
Column {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Ethernet Content in Flickable
|
|
||||||
Flickable {
|
Flickable {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 30
|
height: parent.height - 30
|
||||||
@@ -140,7 +123,6 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Ethernet connection status card (matching WiFi height)
|
|
||||||
EthernetCard {
|
EthernetCard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +134,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi disabled message spanning across both columns
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 100
|
anchors.topMargin: 100
|
||||||
@@ -190,14 +171,12 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi networks spanning across both columns when WiFi is enabled
|
|
||||||
WiFiNetworksList {
|
WiFiNetworksList {
|
||||||
wifiContextMenuWindow: wifiContextMenuWindow
|
wifiContextMenuWindow: wifiContextMenuWindow
|
||||||
sortedWifiNetworks: networkTab.sortedWifiNetworks
|
sortedWifiNetworks: networkTab.sortedWifiNetworks
|
||||||
wifiPasswordModalRef: networkTab.wifiPasswordModalRef
|
wifiPasswordModalRef: networkTab.wifiPasswordModalRef
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer for refreshing network status after WiFi toggle
|
|
||||||
Timer {
|
Timer {
|
||||||
id: refreshTimer
|
id: refreshTimer
|
||||||
interval: 2000
|
interval: 2000
|
||||||
@@ -212,18 +191,13 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-refresh when WiFi state changes
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NetworkService
|
target: NetworkService
|
||||||
function onWifiEnabledChanged() {
|
function onWifiEnabledChanged() {
|
||||||
if (NetworkService.wifiEnabled && visible) {
|
if (NetworkService.wifiEnabled && visible) {
|
||||||
// When WiFi is enabled, scan and update info (only if tab is visible)
|
|
||||||
// Add a small delay to ensure WiFi service is ready
|
|
||||||
wifiScanDelayTimer.start();
|
wifiScanDelayTimer.start();
|
||||||
// Start monitoring when WiFi comes back on
|
|
||||||
wifiMonitorTimer.start();
|
wifiMonitorTimer.start();
|
||||||
} else {
|
} else {
|
||||||
// When WiFi is disabled, clear all cached WiFi data
|
|
||||||
NetworkService.currentWifiSSID = "";
|
NetworkService.currentWifiSSID = "";
|
||||||
NetworkService.wifiSignalStrength = "excellent";
|
NetworkService.wifiSignalStrength = "excellent";
|
||||||
NetworkService.wifiNetworks = [];
|
NetworkService.wifiNetworks = [];
|
||||||
@@ -232,13 +206,11 @@ Item {
|
|||||||
NetworkService.connectingSSID = "";
|
NetworkService.connectingSSID = "";
|
||||||
NetworkService.isScanning = false;
|
NetworkService.isScanning = false;
|
||||||
NetworkService.refreshNetworkStatus();
|
NetworkService.refreshNetworkStatus();
|
||||||
// Stop monitoring when WiFi is off
|
|
||||||
wifiMonitorTimer.stop();
|
wifiMonitorTimer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delayed WiFi scan timer to ensure service is ready
|
|
||||||
Timer {
|
Timer {
|
||||||
id: wifiScanDelayTimer
|
id: wifiScanDelayTimer
|
||||||
interval: 1500
|
interval: 1500
|
||||||
@@ -249,14 +221,12 @@ Item {
|
|||||||
if (!NetworkService.isScanning) {
|
if (!NetworkService.isScanning) {
|
||||||
NetworkService.scanWifi();
|
NetworkService.scanWifi();
|
||||||
} else {
|
} else {
|
||||||
// If still scanning, try again in a bit
|
|
||||||
wifiRetryTimer.start();
|
wifiRetryTimer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retry timer for when WiFi is still scanning
|
|
||||||
Timer {
|
Timer {
|
||||||
id: wifiRetryTimer
|
id: wifiRetryTimer
|
||||||
interval: 2000
|
interval: 2000
|
||||||
@@ -271,7 +241,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smart WiFi monitoring - only runs when tab visible and conditions met
|
|
||||||
Timer {
|
Timer {
|
||||||
id: wifiMonitorTimer
|
id: wifiMonitorTimer
|
||||||
interval: 8000 // Check every 8 seconds
|
interval: 8000 // Check every 8 seconds
|
||||||
@@ -279,21 +248,17 @@ Item {
|
|||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!visible || !NetworkService.wifiEnabled) {
|
if (!visible || !NetworkService.wifiEnabled) {
|
||||||
// Stop monitoring when not needed
|
|
||||||
running = false;
|
running = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor connection changes and refresh networks when disconnected
|
|
||||||
var shouldScan = false;
|
var shouldScan = false;
|
||||||
var reason = "";
|
var reason = "";
|
||||||
|
|
||||||
// Always scan if not connected to WiFi
|
|
||||||
if (NetworkService.networkStatus !== "wifi") {
|
if (NetworkService.networkStatus !== "wifi") {
|
||||||
shouldScan = true;
|
shouldScan = true;
|
||||||
reason = "not connected to WiFi";
|
reason = "not connected to WiFi";
|
||||||
}
|
}
|
||||||
// Also scan occasionally even when connected to keep networks fresh
|
|
||||||
else if (NetworkService.wifiNetworks.length === 0) {
|
else if (NetworkService.wifiNetworks.length === 0) {
|
||||||
shouldScan = true;
|
shouldScan = true;
|
||||||
reason = "no networks cached";
|
reason = "no networks cached";
|
||||||
@@ -305,7 +270,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor tab visibility to start/stop smart monitoring
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible && NetworkService.wifiEnabled) {
|
if (visible && NetworkService.wifiEnabled) {
|
||||||
wifiMonitorTimer.start();
|
wifiMonitorTimer.start();
|
||||||
@@ -314,7 +278,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi Context Menu Window
|
|
||||||
WiFiContextMenu {
|
WiFiContextMenu {
|
||||||
id: wifiContextMenuWindow
|
id: wifiContextMenuWindow
|
||||||
parentItem: networkTab
|
parentItem: networkTab
|
||||||
@@ -322,7 +285,6 @@ Item {
|
|||||||
networkInfoModalRef: networkTab.networkInfoModalRef
|
networkInfoModalRef: networkTab.networkInfoModalRef
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background MouseArea to close the context menu
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: wifiContextMenuWindow.visible
|
visible: wifiContextMenuWindow.visible
|
||||||
@@ -336,7 +298,6 @@ Item {
|
|||||||
width: wifiContextMenuWindow.width
|
width: wifiContextMenuWindow.width
|
||||||
height: wifiContextMenuWindow.height
|
height: wifiContextMenuWindow.height
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// Prevent clicks on menu from closing it
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to dismiss overlay
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -51,9 +50,7 @@ PanelWindow {
|
|||||||
opacity: powerMenuVisible ? 1 : 0
|
opacity: powerMenuVisible ? 1 : 0
|
||||||
scale: powerMenuVisible ? 1 : 0.85
|
scale: powerMenuVisible ? 1 : 0.85
|
||||||
|
|
||||||
// Prevent click-through to background
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
// Consume the click to prevent it from reaching the background
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -65,7 +62,6 @@ PanelWindow {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
@@ -94,12 +90,10 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power options
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Log Out
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
@@ -146,7 +140,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspend
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
@@ -193,7 +186,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reboot
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
@@ -240,7 +232,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power Off
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ PanelWindow {
|
|||||||
onScreenYChanged: margins.top = Theme.barHeight + 4 + screenY
|
onScreenYChanged: margins.top = Theme.barHeight + 4 + screenY
|
||||||
onHasValidDataChanged: {
|
onHasValidDataChanged: {
|
||||||
if (!hasValidData && !exiting && !_isDestroying) {
|
if (!hasValidData && !exiting && !_isDestroying) {
|
||||||
console.warn("NotificationPopup: Data became invalid, forcing exit");
|
|
||||||
forceExit();
|
forceExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ PanelWindow {
|
|||||||
return enterX.restart();
|
return enterX.restart();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn("NotificationPopup created with invalid data");
|
|
||||||
forceExit();
|
forceExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ QtObject {
|
|||||||
property int baseNotificationHeight: 120
|
property int baseNotificationHeight: 120
|
||||||
property int maxTargetNotifications: 3
|
property int maxTargetNotifications: 3
|
||||||
property var popupWindows: [] // strong refs to windows (live until exitFinished)
|
property var popupWindows: [] // strong refs to windows (live until exitFinished)
|
||||||
// Track destroying windows to prevent duplicate cleanup
|
|
||||||
property var destroyingWindows: new Set()
|
property var destroyingWindows: new Set()
|
||||||
// Factory
|
|
||||||
property Component popupComponent
|
property Component popupComponent
|
||||||
|
|
||||||
popupComponent: Component {
|
popupComponent: Component {
|
||||||
@@ -34,7 +32,6 @@ QtObject {
|
|||||||
target: NotificationService
|
target: NotificationService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smart sweeper that only runs when needed
|
|
||||||
property Timer sweeper
|
property Timer sweeper
|
||||||
|
|
||||||
sweeper: Timer {
|
sweeper: Timer {
|
||||||
@@ -48,24 +45,21 @@ QtObject {
|
|||||||
toRemove.push(p);
|
toRemove.push(p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Check for various zombie conditions
|
|
||||||
const isZombie = p.status === Component.Null || (!p.visible && !p.exiting) || (!p.notificationData && !p._isDestroying) || (!p.hasValidData && !p._isDestroying);
|
const isZombie = p.status === Component.Null || (!p.visible && !p.exiting) || (!p.notificationData && !p._isDestroying) || (!p.hasValidData && !p._isDestroying);
|
||||||
if (isZombie) {
|
if (isZombie) {
|
||||||
console.warn("Sweeper found zombie window, cleaning up");
|
|
||||||
toRemove.push(p);
|
toRemove.push(p);
|
||||||
// Try to force cleanup
|
|
||||||
if (p.forceExit) {
|
if (p.forceExit) {
|
||||||
p.forceExit();
|
p.forceExit();
|
||||||
} else if (p.destroy) {
|
} else if (p.destroy) {
|
||||||
try {
|
try {
|
||||||
p.destroy();
|
p.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Error destroying zombie:", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove all zombies from array
|
|
||||||
if (toRemove.length > 0) {
|
if (toRemove.length > 0) {
|
||||||
for (let zombie of toRemove) {
|
for (let zombie of toRemove) {
|
||||||
const i = popupWindows.indexOf(zombie);
|
const i = popupWindows.indexOf(zombie);
|
||||||
@@ -74,7 +68,6 @@ QtObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
popupWindows = popupWindows.slice();
|
popupWindows = popupWindows.slice();
|
||||||
// Recompact after cleanup
|
|
||||||
const survivors = _active().sort((a, b) => {
|
const survivors = _active().sort((a, b) => {
|
||||||
return a.screenY - b.screenY;
|
return a.screenY - b.screenY;
|
||||||
});
|
});
|
||||||
@@ -82,7 +75,6 @@ QtObject {
|
|||||||
survivors[k].screenY = topMargin + k * baseNotificationHeight;
|
survivors[k].screenY = topMargin + k * baseNotificationHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stop the timer if no windows remain
|
|
||||||
if (popupWindows.length === 0)
|
if (popupWindows.length === 0)
|
||||||
sweeper.stop();
|
sweeper.stop();
|
||||||
|
|
||||||
@@ -91,7 +83,6 @@ QtObject {
|
|||||||
|
|
||||||
function _hasWindowFor(w) {
|
function _hasWindowFor(w) {
|
||||||
return popupWindows.some((p) => {
|
return popupWindows.some((p) => {
|
||||||
// More robust check for valid windows
|
|
||||||
return p && p.notificationData === w && !p._isDestroying && p.status !== Component.Null;
|
return p && p.notificationData === w && !p._isDestroying && p.status !== Component.Null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -101,13 +92,11 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _sync(newWrappers) {
|
function _sync(newWrappers) {
|
||||||
// Add new notifications
|
|
||||||
for (let w of newWrappers) {
|
for (let w of newWrappers) {
|
||||||
if (w && !_hasWindowFor(w))
|
if (w && !_hasWindowFor(w))
|
||||||
insertNewestAtTop(w);
|
insertNewestAtTop(w);
|
||||||
|
|
||||||
}
|
}
|
||||||
// Remove old notifications
|
|
||||||
for (let p of popupWindows.slice()) {
|
for (let p of popupWindows.slice()) {
|
||||||
if (!_isValidWindow(p))
|
if (!_isValidWindow(p))
|
||||||
continue;
|
continue;
|
||||||
@@ -119,13 +108,11 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert newest at top
|
|
||||||
function insertNewestAtTop(wrapper) {
|
function insertNewestAtTop(wrapper) {
|
||||||
if (!wrapper) {
|
if (!wrapper) {
|
||||||
console.warn("insertNewestAtTop: wrapper is null");
|
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
// Shift live, non-exiting windows down *now*
|
|
||||||
for (let p of popupWindows) {
|
for (let p of popupWindows) {
|
||||||
if (!_isValidWindow(p))
|
if (!_isValidWindow(p))
|
||||||
continue;
|
continue;
|
||||||
@@ -135,7 +122,6 @@ QtObject {
|
|||||||
|
|
||||||
p.screenY = p.screenY + baseNotificationHeight;
|
p.screenY = p.screenY + baseNotificationHeight;
|
||||||
}
|
}
|
||||||
// Create the new top window at fixed Y
|
|
||||||
const notificationId = wrapper && wrapper.notification ? wrapper.notification.id : "";
|
const notificationId = wrapper && wrapper.notification ? wrapper.notification.id : "";
|
||||||
const win = popupComponent.createObject(null, {
|
const win = popupComponent.createObject(null, {
|
||||||
"notificationData": wrapper,
|
"notificationData": wrapper,
|
||||||
@@ -144,24 +130,21 @@ QtObject {
|
|||||||
"screen": manager.modelData
|
"screen": manager.modelData
|
||||||
});
|
});
|
||||||
if (!win) {
|
if (!win) {
|
||||||
console.warn("Popup create failed");
|
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
// Validate the window was created properly
|
|
||||||
if (!win.hasValidData) {
|
if (!win.hasValidData) {
|
||||||
console.warn("Popup created with invalid data, destroying");
|
|
||||||
win.destroy();
|
win.destroy();
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
popupWindows.push(win);
|
popupWindows.push(win);
|
||||||
// Start sweeper if it's not running
|
|
||||||
if (!sweeper.running)
|
if (!sweeper.running)
|
||||||
sweeper.start();
|
sweeper.start();
|
||||||
|
|
||||||
_maybeStartOverflow();
|
_maybeStartOverflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overflow: keep one extra (slot #4), then ask bottom to exit gracefully
|
|
||||||
function _active() {
|
function _active() {
|
||||||
return popupWindows.filter((p) => {
|
return popupWindows.filter((p) => {
|
||||||
return _isValidWindow(p) && p.notificationData && p.notificationData.popup && !p.exiting;
|
return _isValidWindow(p) && p.notificationData && p.notificationData.popup && !p.exiting;
|
||||||
@@ -186,55 +169,46 @@ QtObject {
|
|||||||
|
|
||||||
const b = _bottom();
|
const b = _bottom();
|
||||||
if (b && !b.exiting) {
|
if (b && !b.exiting) {
|
||||||
// Tell the popup to animate out (don't destroy here)
|
|
||||||
b.notificationData.removedByLimit = true;
|
b.notificationData.removedByLimit = true;
|
||||||
b.notificationData.popup = false;
|
b.notificationData.popup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// After entrance, you may kick overflow (optional)
|
|
||||||
function _onPopupEntered(p) {
|
function _onPopupEntered(p) {
|
||||||
if (_isValidWindow(p))
|
if (_isValidWindow(p))
|
||||||
_maybeStartOverflow();
|
_maybeStartOverflow();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Primary cleanup path (after the popup finishes its exit)
|
|
||||||
function _onPopupExitFinished(p) {
|
function _onPopupExitFinished(p) {
|
||||||
if (!p)
|
if (!p)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
// Prevent duplicate cleanup
|
|
||||||
const windowId = p.toString();
|
const windowId = p.toString();
|
||||||
if (destroyingWindows.has(windowId))
|
if (destroyingWindows.has(windowId))
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
destroyingWindows.add(windowId);
|
destroyingWindows.add(windowId);
|
||||||
// Remove from popupWindows
|
|
||||||
const i = popupWindows.indexOf(p);
|
const i = popupWindows.indexOf(p);
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
popupWindows.splice(i, 1);
|
popupWindows.splice(i, 1);
|
||||||
popupWindows = popupWindows.slice();
|
popupWindows = popupWindows.slice();
|
||||||
}
|
}
|
||||||
// Release the wrapper
|
|
||||||
if (NotificationService.releaseWrapper && p.notificationData)
|
if (NotificationService.releaseWrapper && p.notificationData)
|
||||||
NotificationService.releaseWrapper(p.notificationData);
|
NotificationService.releaseWrapper(p.notificationData);
|
||||||
|
|
||||||
// Schedule destruction
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (p && p.destroy) {
|
if (p && p.destroy) {
|
||||||
try {
|
try {
|
||||||
p.destroy();
|
p.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Error destroying popup:", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clean up tracking after a delay
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
destroyingWindows.delete(windowId);
|
destroyingWindows.delete(windowId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Compact survivors (only live, non-exiting)
|
|
||||||
const survivors = _active().sort((a, b) => {
|
const survivors = _active().sort((a, b) => {
|
||||||
return a.screenY - b.screenY;
|
return a.screenY - b.screenY;
|
||||||
});
|
});
|
||||||
@@ -244,7 +218,6 @@ QtObject {
|
|||||||
_maybeStartOverflow();
|
_maybeStartOverflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emergency cleanup function
|
|
||||||
function cleanupAllWindows() {
|
function cleanupAllWindows() {
|
||||||
sweeper.stop();
|
sweeper.stop();
|
||||||
for (let p of popupWindows.slice()) {
|
for (let p of popupWindows.slice()) {
|
||||||
@@ -255,7 +228,7 @@ QtObject {
|
|||||||
else if (p.destroy)
|
else if (p.destroy)
|
||||||
p.destroy();
|
p.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Error during emergency cleanup:", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +236,6 @@ QtObject {
|
|||||||
destroyingWindows.clear();
|
destroyingWindows.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for changes to popup windows array
|
|
||||||
onPopupWindowsChanged: {
|
onPopupWindowsChanged: {
|
||||||
if (popupWindows.length > 0 && !sweeper.running)
|
if (popupWindows.length > 0 && !sweeper.running)
|
||||||
sweeper.start();
|
sweeper.start();
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ PanelWindow {
|
|||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
// Close any open context menus
|
|
||||||
if (processContextMenu.visible)
|
if (processContextMenu.visible)
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
|
|
||||||
@@ -58,7 +57,6 @@ PanelWindow {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
// Only close if click is outside the content loader
|
|
||||||
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
||||||
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
||||||
processListPopout.hide();
|
processListPopout.hide();
|
||||||
@@ -78,7 +76,6 @@ PanelWindow {
|
|||||||
height: targetHeight
|
height: targetHeight
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
||||||
// GPU-accelerated scale + opacity animation
|
|
||||||
opacity: processListPopout.isVisible ? 1 : 0
|
opacity: processListPopout.isVisible ? 1 : 0
|
||||||
scale: processListPopout.isVisible ? 1 : 0.9
|
scale: processListPopout.isVisible ? 1 : 0.9
|
||||||
|
|
||||||
@@ -108,7 +105,6 @@ PanelWindow {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
clip: true
|
clip: true
|
||||||
// Remove layer rendering for better performance
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
|
|
||||||
|
|||||||
@@ -294,7 +294,6 @@ Column {
|
|||||||
}
|
}
|
||||||
onModelChanged: {
|
onModelChanged: {
|
||||||
if (model && model.length > 0 && !isUserScrolling && stableY > 40)
|
if (model && model.length > 0 && !isUserScrolling && stableY > 40)
|
||||||
// Preserve scroll position when model updates
|
|
||||||
Qt.callLater(function() {
|
Qt.callLater(function() {
|
||||||
contentY = stableY;
|
contentY = stableY;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ ScrollView {
|
|||||||
topPadding: Theme.spacingL
|
topPadding: Theme.spacingL
|
||||||
bottomPadding: Theme.spacingXL
|
bottomPadding: Theme.spacingXL
|
||||||
|
|
||||||
// Display Settings Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: displaySection.implicitHeight + Theme.spacingL * 2
|
height: displaySection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -91,7 +90,6 @@ ScrollView {
|
|||||||
popupWidthOffset: 100
|
popupWidthOffset: 100
|
||||||
maxPopupHeight: 400
|
maxPopupHeight: 400
|
||||||
options: {
|
options: {
|
||||||
// Force refresh of icon themes to prevent stale data
|
|
||||||
Prefs.detectAvailableIconThemes();
|
Prefs.detectAvailableIconThemes();
|
||||||
return Prefs.availableIconThemes;
|
return Prefs.availableIconThemes;
|
||||||
}
|
}
|
||||||
@@ -108,7 +106,6 @@ ScrollView {
|
|||||||
text: "Font Family"
|
text: "Font Family"
|
||||||
description: "Select system font family"
|
description: "Select system font family"
|
||||||
currentValue: {
|
currentValue: {
|
||||||
// Always show the font name in parentheses for clarity
|
|
||||||
if (Prefs.fontFamily === Prefs.defaultFontFamily)
|
if (Prefs.fontFamily === Prefs.defaultFontFamily)
|
||||||
return "Default (" + Prefs.defaultFontFamily + ")";
|
return "Default (" + Prefs.defaultFontFamily + ")";
|
||||||
else
|
else
|
||||||
@@ -122,19 +119,15 @@ ScrollView {
|
|||||||
var availableFonts = Qt.fontFamilies();
|
var availableFonts = Qt.fontFamilies();
|
||||||
var rootFamilies = [];
|
var rootFamilies = [];
|
||||||
var seenFamilies = new Set();
|
var seenFamilies = new Set();
|
||||||
// Filter to root family names by removing common weight/style suffixes
|
|
||||||
for (var i = 0; i < availableFonts.length; i++) {
|
for (var i = 0; i < availableFonts.length; i++) {
|
||||||
var fontName = availableFonts[i];
|
var fontName = availableFonts[i];
|
||||||
// Skip fonts beginning with . (like .AppleSystem)
|
|
||||||
if (fontName.startsWith("."))
|
if (fontName.startsWith("."))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip the default font since we already added it as recommended
|
|
||||||
if (fontName === Prefs.defaultFontFamily)
|
if (fontName === Prefs.defaultFontFamily)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function(match, suffix) {
|
var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function(match, suffix) {
|
||||||
// Keep these suffixes as they're part of the family name
|
|
||||||
return match;
|
return match;
|
||||||
}).trim();
|
}).trim();
|
||||||
if (!seenFamilies.has(rootName) && rootName !== "") {
|
if (!seenFamilies.has(rootName) && rootName !== "") {
|
||||||
@@ -237,18 +230,14 @@ ScrollView {
|
|||||||
var availableFonts = Qt.fontFamilies();
|
var availableFonts = Qt.fontFamilies();
|
||||||
var monoFamilies = [];
|
var monoFamilies = [];
|
||||||
var seenFamilies = new Set();
|
var seenFamilies = new Set();
|
||||||
// Filter to likely monospace fonts
|
|
||||||
for (var i = 0; i < availableFonts.length; i++) {
|
for (var i = 0; i < availableFonts.length; i++) {
|
||||||
var fontName = availableFonts[i];
|
var fontName = availableFonts[i];
|
||||||
// Skip fonts beginning with .
|
|
||||||
if (fontName.startsWith("."))
|
if (fontName.startsWith("."))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip the default mono font since we already added it as recommended
|
|
||||||
if (fontName === Prefs.defaultMonoFontFamily)
|
if (fontName === Prefs.defaultMonoFontFamily)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Look for common monospace indicators
|
|
||||||
var lowerName = fontName.toLowerCase();
|
var lowerName = fontName.toLowerCase();
|
||||||
if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes("jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) {
|
if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes("jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) {
|
||||||
var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim();
|
var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim();
|
||||||
@@ -272,7 +261,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transparency Settings Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: transparencySection.implicitHeight + Theme.spacingL * 2
|
height: transparencySection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -391,7 +379,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme Picker Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: themeSection.implicitHeight + Theme.spacingL * 2
|
height: themeSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -458,12 +445,10 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme Grid
|
|
||||||
Column {
|
Column {
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
// First row - Blue, Deep Blue, Purple, Green, Orange
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -480,7 +465,6 @@ ScrollView {
|
|||||||
border.width: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 2 : 1
|
border.width: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 2 : 1
|
||||||
scale: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 1.1 : 1
|
scale: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 1.1 : 1
|
||||||
|
|
||||||
// Theme name tooltip
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: nameText.contentWidth + Theme.spacingS * 2
|
width: nameText.contentWidth + Theme.spacingS * 2
|
||||||
height: nameText.contentHeight + Theme.spacingXS * 2
|
height: nameText.contentHeight + Theme.spacingXS * 2
|
||||||
@@ -537,7 +521,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second row - Red, Cyan, Pink, Amber, Coral
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -557,7 +540,6 @@ ScrollView {
|
|||||||
visible: themeIndex < Theme.themes.length
|
visible: themeIndex < Theme.themes.length
|
||||||
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1
|
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1
|
||||||
|
|
||||||
// Theme name tooltip
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: nameText2.contentWidth + Theme.spacingS * 2
|
width: nameText2.contentWidth + Theme.spacingS * 2
|
||||||
height: nameText2.contentHeight + Theme.spacingXS * 2
|
height: nameText2.contentHeight + Theme.spacingXS * 2
|
||||||
@@ -616,13 +598,11 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacer
|
|
||||||
Item {
|
Item {
|
||||||
width: 1
|
width: 1
|
||||||
height: Theme.spacingM
|
height: Theme.spacingM
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto theme button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 120
|
width: 120
|
||||||
height: 40
|
height: 40
|
||||||
@@ -704,7 +684,6 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip for Auto button
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: autoTooltipText.contentWidth + Theme.spacingM * 2
|
width: autoTooltipText.contentWidth + Theme.spacingM * 2
|
||||||
height: autoTooltipText.contentHeight + Theme.spacingS * 2
|
height: autoTooltipText.contentHeight + Theme.spacingS * 2
|
||||||
@@ -768,7 +747,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// System App Theming Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: systemThemingSection.implicitHeight + Theme.spacingL * 2
|
height: systemThemingSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -840,17 +818,15 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Night mode processes
|
|
||||||
Process {
|
Process {
|
||||||
id: nightModeEnableProcess
|
id: nightModeEnableProcess
|
||||||
|
|
||||||
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0)
|
||||||
console.warn("Failed to enable night mode");
|
Prefs.setNightModeEnabled(true);
|
||||||
Prefs.setNightModeEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,7 +837,7 @@ ScrollView {
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0)
|
if (exitCode !== 0)
|
||||||
console.warn("Failed to disable night mode");
|
Prefs.setNightModeEnabled(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
// Profile Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: profileSection.implicitHeight + Theme.spacingL * 2
|
height: profileSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -63,7 +62,6 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Circular profile image preview
|
|
||||||
Item {
|
Item {
|
||||||
id: avatarContainer
|
id: avatarContainer
|
||||||
|
|
||||||
@@ -264,7 +262,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wallpaper Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: wallpaperSection.implicitHeight + Theme.spacingL * 2
|
height: wallpaperSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -305,7 +302,6 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Wallpaper Preview
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: 160
|
width: 160
|
||||||
height: 90
|
height: 90
|
||||||
@@ -463,7 +459,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Theming Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: dynamicThemeSection.implicitHeight + Theme.spacingL * 2
|
height: dynamicThemeSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section header
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: headerRow.height
|
height: headerRow.height
|
||||||
@@ -85,7 +84,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divider
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 1
|
height: 1
|
||||||
@@ -93,7 +91,6 @@ Column {
|
|||||||
visible: expanded || !collapsible
|
visible: expanded || !collapsible
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content
|
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
// Time Settings Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: timeSection.implicitHeight + Theme.spacingL * 2
|
height: timeSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -66,7 +65,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather Settings Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: weatherSection.implicitHeight + Theme.spacingL * 2
|
height: weatherSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ ScrollView {
|
|||||||
"icon": "remove",
|
"icon": "remove",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}]
|
}]
|
||||||
// Default widget configurations for each section (with enabled states)
|
|
||||||
property var defaultLeftWidgets: [{
|
property var defaultLeftWidgets: [{
|
||||||
"id": "launcherButton",
|
"id": "launcherButton",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@@ -295,7 +294,6 @@ ScrollView {
|
|||||||
if (!Prefs.topBarRightWidgets || Prefs.topBarRightWidgets.length === 0)
|
if (!Prefs.topBarRightWidgets || Prefs.topBarRightWidgets.length === 0)
|
||||||
Prefs.setTopBarRightWidgets(defaultRightWidgets);
|
Prefs.setTopBarRightWidgets(defaultRightWidgets);
|
||||||
|
|
||||||
// Ensure existing spacers have default sizes
|
|
||||||
["left", "center", "right"].forEach((sectionId) => {
|
["left", "center", "right"].forEach((sectionId) => {
|
||||||
var widgets = [];
|
var widgets = [];
|
||||||
if (sectionId === "left")
|
if (sectionId === "left")
|
||||||
@@ -334,7 +332,6 @@ ScrollView {
|
|||||||
topPadding: Theme.spacingL
|
topPadding: Theme.spacingL
|
||||||
bottomPadding: Theme.spacingXL
|
bottomPadding: Theme.spacingXL
|
||||||
|
|
||||||
// Header section
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -446,12 +443,10 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget sections
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
// Left Section
|
|
||||||
DankSections {
|
DankSections {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
title: "Left Section"
|
title: "Left Section"
|
||||||
@@ -478,7 +473,6 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center Section
|
|
||||||
DankSections {
|
DankSections {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
title: "Center Section"
|
title: "Center Section"
|
||||||
@@ -505,7 +499,6 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right Section
|
|
||||||
DankSections {
|
DankSections {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
title: "Right Section"
|
title: "Right Section"
|
||||||
@@ -534,7 +527,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workspace Section
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: workspaceSection.implicitHeight + Theme.spacingL * 2
|
height: workspaceSection.implicitHeight + Theme.spacingL * 2
|
||||||
@@ -597,7 +589,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip for reset button (positioned above the button)
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: tooltipText.contentWidth + Theme.spacingM * 2
|
width: tooltipText.contentWidth + Theme.spacingM * 2
|
||||||
height: tooltipText.contentHeight + Theme.spacingS * 2
|
height: tooltipText.contentHeight + Theme.spacingS * 2
|
||||||
@@ -630,7 +621,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget selection popup
|
|
||||||
DankWidgetSelectionPopup {
|
DankWidgetSelectionPopup {
|
||||||
id: widgetSelectionPopup
|
id: widgetSelectionPopup
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes the background transparent to mouse events
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: toast
|
item: toast
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ Item {
|
|||||||
interval: 256
|
interval: 256
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
// Generate fake audio levels when cava is unavailable
|
|
||||||
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25];
|
CavaService.values = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip on hover
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: batteryTooltip
|
id: batteryTooltip
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,9 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to dismiss overlay
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
// Only close if click is outside the content loader
|
|
||||||
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
|
||||||
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
|
||||||
batteryPopupVisible = false;
|
batteryPopupVisible = false;
|
||||||
@@ -70,7 +68,6 @@ PanelWindow {
|
|||||||
height: targetHeight
|
height: targetHeight
|
||||||
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
|
||||||
y: Theme.barHeight + Theme.spacingS
|
y: Theme.barHeight + Theme.spacingS
|
||||||
// GPU-accelerated scale + opacity animation
|
|
||||||
opacity: batteryPopupVisible ? 1 : 0
|
opacity: batteryPopupVisible ? 1 : 0
|
||||||
scale: batteryPopupVisible ? 1 : 0.9
|
scale: batteryPopupVisible ? 1 : 0.9
|
||||||
|
|
||||||
@@ -97,11 +94,9 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Theme.outlineMedium
|
border.color: Theme.outlineMedium
|
||||||
border.width: 1
|
border.width: 1
|
||||||
// Remove layer rendering for better performance
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
|
|
||||||
// Material 3 elevation with multiple layers
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: -3
|
anchors.margins: -3
|
||||||
@@ -273,7 +268,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No battery info card
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 80
|
height: 80
|
||||||
@@ -317,7 +311,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Battery details
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -334,7 +327,6 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
// Health
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
width: (parent.width - Theme.spacingXL) / 2
|
width: (parent.width - Theme.spacingXL) / 2
|
||||||
@@ -360,7 +352,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capacity
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
width: (parent.width - Theme.spacingXL) / 2
|
width: (parent.width - Theme.spacingXL) / 2
|
||||||
@@ -384,7 +375,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power profiles
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -465,7 +455,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degradation reason warning
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ Rectangle {
|
|||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
// Helper function for consistent WiFi signal icons
|
|
||||||
function getWiFiSignalIcon(signalStrength) {
|
function getWiFiSignalIcon(signalStrength) {
|
||||||
switch (signalStrength) {
|
switch (signalStrength) {
|
||||||
case "excellent":
|
case "excellent":
|
||||||
@@ -40,7 +39,6 @@ Rectangle {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
// Network Status Icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: {
|
||||||
if (NetworkService.networkStatus === "ethernet")
|
if (NetworkService.networkStatus === "ethernet")
|
||||||
@@ -56,7 +54,6 @@ Rectangle {
|
|||||||
visible: true
|
visible: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bluetooth Icon (when available and enabled) - moved next to network
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "bluetooth"
|
name: "bluetooth"
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
@@ -65,7 +62,6 @@ Rectangle {
|
|||||||
visible: BluetoothService.available && BluetoothService.enabled
|
visible: BluetoothService.available && BluetoothService.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio Icon with scroll wheel support
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: audioIcon.implicitWidth + 4
|
width: audioIcon.implicitWidth + 4
|
||||||
height: audioIcon.implicitHeight + 4
|
height: audioIcon.implicitHeight + 4
|
||||||
@@ -82,9 +78,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
// Scroll up - increase volume
|
|
||||||
// Scroll down - decrease volume
|
|
||||||
|
|
||||||
id: audioWheelArea
|
id: audioWheelArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -108,7 +101,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Microphone Icon (when active)
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "mic"
|
name: "mic"
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ Rectangle {
|
|||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
// Only show background when there's content to display
|
|
||||||
if (!FocusedWindowService.focusedAppName && !FocusedWindowService.focusedWindowTitle)
|
if (!FocusedWindowService.focusedAppName && !FocusedWindowService.focusedWindowTitle)
|
||||||
return "transparent";
|
return "transparent";
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ Rectangle {
|
|||||||
color: Prefs.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText)
|
color: Prefs.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification dot indicator
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 8
|
width: 8
|
||||||
height: 8
|
height: 8
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ Rectangle {
|
|||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
// Only show background when there are system tray items to display
|
|
||||||
if (SystemTray.items.values.length === 0)
|
if (SystemTray.items.values.length === 0)
|
||||||
return "transparent";
|
return "transparent";
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,9 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Theme.outlineMedium
|
border.color: Theme.outlineMedium
|
||||||
border.width: 1
|
border.width: 1
|
||||||
// Material 3 animations
|
|
||||||
opacity: showContextMenu ? 1 : 0
|
opacity: showContextMenu ? 1 : 0
|
||||||
scale: showContextMenu ? 1 : 0.85
|
scale: showContextMenu ? 1 : 0.85
|
||||||
|
|
||||||
// Material 3 drop shadow
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: 4
|
anchors.topMargin: 4
|
||||||
@@ -65,11 +63,9 @@ PanelWindow {
|
|||||||
menu: currentTrayItem && currentTrayItem.hasMenu ? currentTrayItem.menu : null
|
menu: currentTrayItem && currentTrayItem.hasMenu ? currentTrayItem.menu : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom menu styling using ListView
|
|
||||||
ListView {
|
ListView {
|
||||||
id: menuList
|
id: menuList
|
||||||
|
|
||||||
// Calculate maximum text width for dynamic menu sizing
|
|
||||||
property real maxTextWidth: {
|
property real maxTextWidth: {
|
||||||
let maxWidth = 0;
|
let maxWidth = 0;
|
||||||
if (model && model.values) {
|
if (model && model.values) {
|
||||||
@@ -101,7 +97,6 @@ PanelWindow {
|
|||||||
radius: modelData.isSeparator ? 0 : Theme.cornerRadiusSmall
|
radius: modelData.isSeparator ? 0 : Theme.cornerRadiusSmall
|
||||||
color: modelData.isSeparator ? "transparent" : (menuItemArea.containsMouse ? Theme.primaryHover : "transparent")
|
color: modelData.isSeparator ? "transparent" : (menuItemArea.containsMouse ? Theme.primaryHover : "transparent")
|
||||||
|
|
||||||
// Separator line
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: modelData.isSeparator
|
visible: modelData.isSeparator
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -110,7 +105,6 @@ PanelWindow {
|
|||||||
color: Theme.surfaceVariantAlpha
|
color: Theme.surfaceVariantAlpha
|
||||||
}
|
}
|
||||||
|
|
||||||
// Menu item content
|
|
||||||
Row {
|
Row {
|
||||||
visible: !modelData.isSeparator
|
visible: !modelData.isSeparator
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -176,7 +170,6 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to close
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
|
|||||||
@@ -125,7 +125,6 @@ PanelWindow {
|
|||||||
id: topBarContent
|
id: topBarContent
|
||||||
|
|
||||||
readonly property int availableWidth: width
|
readonly property int availableWidth: width
|
||||||
// Use estimated fixed widths to break circular dependencies
|
|
||||||
readonly property int launcherButtonWidth: 40
|
readonly property int launcherButtonWidth: 40
|
||||||
readonly property int workspaceSwitcherWidth: 120 // Approximate
|
readonly property int workspaceSwitcherWidth: 120 // Approximate
|
||||||
readonly property int focusedAppMaxWidth: 456 // Fixed width since we don't have focusedApp reference
|
readonly property int focusedAppMaxWidth: 456 // Fixed width since we don't have focusedApp reference
|
||||||
@@ -228,7 +227,6 @@ PanelWindow {
|
|||||||
anchors.bottomMargin: Theme.spacingXS
|
anchors.bottomMargin: Theme.spacingXS
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
// Dynamic left section
|
|
||||||
Row {
|
Row {
|
||||||
id: leftSection
|
id: leftSection
|
||||||
|
|
||||||
@@ -357,8 +355,8 @@ PanelWindow {
|
|||||||
item.onWidthChanged.connect(centerSection.updateLayout);
|
item.onWidthChanged.connect(centerSection.updateLayout);
|
||||||
if (model.widgetId === "spacer")
|
if (model.widgetId === "spacer")
|
||||||
item.spacerSize = Qt.binding(() => {
|
item.spacerSize = Qt.binding(() => {
|
||||||
return model.size || 20;
|
return model.size || 20;
|
||||||
});
|
});
|
||||||
|
|
||||||
Qt.callLater(centerSection.updateLayout);
|
Qt.callLater(centerSection.updateLayout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ Rectangle {
|
|||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
// Visibility is now controlled by TopBar.qml
|
|
||||||
width: visible ? Math.min(100, weatherRow.implicitWidth + Theme.spacingS * 2) : 0
|
width: visible ? Math.min(100, weatherRow.implicitWidth + Theme.spacingS * 2) : 0
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
|
|||||||
@@ -83,10 +83,8 @@ Rectangle {
|
|||||||
target: NiriService
|
target: NiriService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force update when padding preference changes
|
|
||||||
Connections {
|
Connections {
|
||||||
function onShowWorkspacePaddingChanged() {
|
function onShowWorkspacePaddingChanged() {
|
||||||
// Force re-evaluation by updating the property
|
|
||||||
var baseList = root.getDisplayWorkspaces();
|
var baseList = root.getDisplayWorkspaces();
|
||||||
root.workspaceList = Prefs.showWorkspacePadding ? root.padWorkspaces(baseList) : baseList;
|
root.workspaceList = Prefs.showWorkspacePadding ? root.padWorkspaces(baseList) : baseList;
|
||||||
}
|
}
|
||||||
@@ -128,7 +126,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show index for placeholders if Prefs.showWorkspaceIndex is true, otherwise show a subtle dot
|
|
||||||
StyledText {
|
StyledText {
|
||||||
visible: Prefs.showWorkspaceIndex
|
visible: Prefs.showWorkspaceIndex
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|||||||
@@ -12,28 +12,6 @@ Singleton {
|
|||||||
|
|
||||||
property var applications: DesktopEntries.applications.values
|
property var applications: DesktopEntries.applications.values
|
||||||
|
|
||||||
property var applicationsByName: {
|
|
||||||
var byName = {}
|
|
||||||
for (var i = 0; i < applications.length; i++) {
|
|
||||||
var app = applications[i]
|
|
||||||
byName[app.name.toLowerCase()] = app
|
|
||||||
}
|
|
||||||
return byName
|
|
||||||
}
|
|
||||||
|
|
||||||
property var applicationsByExec: {
|
|
||||||
var byExec = {}
|
|
||||||
for (var i = 0; i < applications.length; i++) {
|
|
||||||
var app = applications[i]
|
|
||||||
var execProp = app.execString || ""
|
|
||||||
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
|
|
||||||
if (cleanExec) {
|
|
||||||
byExec[cleanExec] = app
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return byExec
|
|
||||||
}
|
|
||||||
|
|
||||||
property var preppedApps: applications.map(app => ({
|
property var preppedApps: applications.map(app => ({
|
||||||
name: Fuzzy.prepare(app.name || ""),
|
name: Fuzzy.prepare(app.name || ""),
|
||||||
comment: Fuzzy.prepare(app.comment || ""),
|
comment: Fuzzy.prepare(app.comment || ""),
|
||||||
@@ -89,14 +67,7 @@ Singleton {
|
|||||||
return results.map(r => r.obj.entry)
|
return results.map(r => r.obj.entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppByName(name) {
|
|
||||||
return applicationsByName[name.toLowerCase()] || null
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAppByExec(exec) {
|
|
||||||
var cleanExec = exec.replace(/%[fFuU]/g, "").trim()
|
|
||||||
return applicationsByExec[cleanExec] || null
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCategoriesForApp(app) {
|
function getCategoriesForApp(app) {
|
||||||
if (!app || !app.categories) return []
|
if (!app || !app.categories) return []
|
||||||
|
|||||||
@@ -77,35 +77,7 @@ Singleton {
|
|||||||
return "bluetooth";
|
return "bluetooth";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceType(device) {
|
|
||||||
if (!device)
|
|
||||||
return "bluetooth";
|
|
||||||
|
|
||||||
var name = (device.name || device.deviceName || "").toLowerCase();
|
|
||||||
var icon = (device.icon || "").toLowerCase();
|
|
||||||
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis"))
|
|
||||||
return "headset";
|
|
||||||
|
|
||||||
if (icon.includes("mouse") || name.includes("mouse"))
|
|
||||||
return "mouse";
|
|
||||||
|
|
||||||
if (icon.includes("keyboard") || name.includes("keyboard"))
|
|
||||||
return "keyboard";
|
|
||||||
|
|
||||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung"))
|
|
||||||
return "phone";
|
|
||||||
|
|
||||||
if (icon.includes("watch") || name.includes("watch"))
|
|
||||||
return "watch";
|
|
||||||
|
|
||||||
if (icon.includes("speaker") || name.includes("speaker"))
|
|
||||||
return "speaker";
|
|
||||||
|
|
||||||
if (icon.includes("display") || name.includes("tv"))
|
|
||||||
return "tv";
|
|
||||||
|
|
||||||
return "bluetooth";
|
|
||||||
}
|
|
||||||
|
|
||||||
function canConnect(device) {
|
function canConnect(device) {
|
||||||
if (!device)
|
if (!device)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Singleton {
|
|||||||
laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
|
laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
|
||||||
laptopBrightnessProcess.running = true;
|
laptopBrightnessProcess.running = true;
|
||||||
} else if (ddcAvailable) {
|
} else if (ddcAvailable) {
|
||||||
console.log("Setting DDC brightness to:", brightnessLevel);
|
|
||||||
Quickshell.execDetached(["ddcutil", "setvcp", "10", brightnessLevel.toString()]);
|
Quickshell.execDetached(["ddcutil", "setvcp", "10", brightnessLevel.toString()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ Singleton {
|
|||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to set laptop brightness, exit code:", exitCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ Singleton {
|
|||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to get laptop brightness, exit code:", exitCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ Singleton {
|
|||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to get max laptop brightness, exit code:", exitCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ Singleton {
|
|||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to get DDC brightness, exit code:", exitCode);
|
|
||||||
brightnessLevel = 75;
|
brightnessLevel = 75;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,27 +45,18 @@ Singleton {
|
|||||||
|
|
||||||
function addRef() {
|
function addRef() {
|
||||||
refCount++;
|
refCount++;
|
||||||
console.log("NetworkService: addRef, refCount now:", refCount);
|
|
||||||
// Reference counting affects WiFi scanning operations only
|
|
||||||
// Basic network status monitoring always runs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeRef() {
|
function removeRef() {
|
||||||
refCount = Math.max(0, refCount - 1);
|
refCount = Math.max(0, refCount - 1);
|
||||||
console.log("NetworkService: removeRef, refCount now:", refCount);
|
|
||||||
// Stop intensive WiFi operations when no consumers
|
|
||||||
if (refCount === 0) {
|
if (refCount === 0) {
|
||||||
autoRefreshTimer.running = false;
|
autoRefreshTimer.running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load saved preference on startup
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Load preference from Prefs system
|
|
||||||
root.userPreference = Prefs.networkPreference
|
root.userPreference = Prefs.networkPreference
|
||||||
console.log("NetworkService: Loaded network preference from Prefs:", root.userPreference)
|
|
||||||
|
|
||||||
// Trigger immediate WiFi info update if WiFi is connected and enabled
|
|
||||||
if (root.networkStatus === "wifi" && root.wifiEnabled) {
|
if (root.networkStatus === "wifi" && root.wifiEnabled) {
|
||||||
updateCurrentWifiInfo()
|
updateCurrentWifiInfo()
|
||||||
}
|
}
|
||||||
@@ -582,7 +573,7 @@ Singleton {
|
|||||||
return b.signal - a.signal;
|
return b.signal - a.signal;
|
||||||
});
|
});
|
||||||
root.wifiNetworks = networks;
|
root.wifiNetworks = networks;
|
||||||
console.log("Found", networks.length, "WiFi networks");
|
|
||||||
// Stop scanning once we have results
|
// Stop scanning once we have results
|
||||||
if (networks.length > 0) {
|
if (networks.length > 0) {
|
||||||
root.isScanning = false;
|
root.isScanning = false;
|
||||||
@@ -615,7 +606,7 @@ Singleton {
|
|||||||
|
|
||||||
}
|
}
|
||||||
root.savedWifiNetworks = saved;
|
root.savedWifiNetworks = saved;
|
||||||
console.log("Found", saved.length, "saved WiFi networks");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -902,14 +893,14 @@ Singleton {
|
|||||||
|
|
||||||
root.networkInfoDetails = details;
|
root.networkInfoDetails = details;
|
||||||
root.networkInfoLoading = false;
|
root.networkInfoLoading = false;
|
||||||
console.log("Network info fetched for:", root.networkInfoSSID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
root.networkInfoLoading = false;
|
root.networkInfoLoading = false;
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.log("Failed to fetch network info, exit code:", exitCode);
|
|
||||||
root.networkInfoDetails = "Failed to fetch network information";
|
root.networkInfoDetails = "Failed to fetch network information";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -917,7 +908,7 @@ Singleton {
|
|||||||
stderr: SplitParser {
|
stderr: SplitParser {
|
||||||
splitMarker: "\\n"
|
splitMarker: "\\n"
|
||||||
onRead: (data) => {
|
onRead: (data) => {
|
||||||
console.log("WiFi info stderr:", data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ Singleton {
|
|||||||
try {
|
try {
|
||||||
data = JSON.parse(text);
|
data = JSON.parse(text);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("SysMonitorService: Failed to parse JSON:", error, "Raw text:", text.slice(0, 300));
|
|
||||||
isUpdating = false;
|
isUpdating = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -535,7 +535,7 @@ printf "}\\n"`
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Unified stats process failed with exit code:", exitCode);
|
|
||||||
isUpdating = false;
|
isUpdating = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +545,7 @@ printf "}\\n"`
|
|||||||
const fullText = text.trim();
|
const fullText = text.trim();
|
||||||
const lastBraceIndex = fullText.lastIndexOf('}');
|
const lastBraceIndex = fullText.lastIndexOf('}');
|
||||||
if (lastBraceIndex === -1) {
|
if (lastBraceIndex === -1) {
|
||||||
console.error("SysMonitorService: No JSON object found in output.", fullText);
|
|
||||||
isUpdating = false;
|
isUpdating = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Singleton {
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("UserInfoService: Failed to get user info");
|
|
||||||
root.username = "User";
|
root.username = "User";
|
||||||
root.fullName = "User";
|
root.fullName = "User";
|
||||||
root.hostname = "System";
|
root.hostname = "System";
|
||||||
@@ -55,7 +55,7 @@ Singleton {
|
|||||||
root.username = parts[0] || "";
|
root.username = parts[0] || "";
|
||||||
root.fullName = parts[1] || parts[0] || "";
|
root.fullName = parts[1] || parts[0] || "";
|
||||||
root.hostname = parts[2] || "";
|
root.hostname = parts[2] || "";
|
||||||
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ Singleton {
|
|||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("UserInfoService: Failed to get uptime");
|
|
||||||
root.uptime = "Unknown";
|
root.uptime = "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Singleton {
|
|||||||
|
|
||||||
function addRef() {
|
function addRef() {
|
||||||
refCount++;
|
refCount++;
|
||||||
console.log("WeatherService: addRef, refCount now:", refCount);
|
|
||||||
if (refCount === 1 && !weather.available) {
|
if (refCount === 1 && !weather.available) {
|
||||||
// Start fetching when first consumer appears
|
// Start fetching when first consumer appears
|
||||||
fetchWeather();
|
fetchWeather();
|
||||||
@@ -109,13 +109,13 @@ Singleton {
|
|||||||
|
|
||||||
function removeRef() {
|
function removeRef() {
|
||||||
refCount = Math.max(0, refCount - 1);
|
refCount = Math.max(0, refCount - 1);
|
||||||
console.log("WeatherService: removeRef, refCount now:", refCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchWeather() {
|
function fetchWeather() {
|
||||||
// Only fetch if someone is consuming the data
|
// Only fetch if someone is consuming the data
|
||||||
if (root.refCount === 0) {
|
if (root.refCount === 0) {
|
||||||
console.log("WeatherService: Skipping fetch - no consumers");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ Image {
|
|||||||
}
|
}
|
||||||
onCachePathChanged: {
|
onCachePathChanged: {
|
||||||
if (imageHash && cachePath) {
|
if (imageHash && cachePath) {
|
||||||
// Ensure cache directory exists before trying to load from cache
|
|
||||||
Paths.mkdir(Paths.imagecache);
|
Paths.mkdir(Paths.imagecache);
|
||||||
source = cachePath;
|
source = cachePath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import qs.Common
|
|||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// Force recreate popup when component becomes visible
|
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string text: ""
|
property string text: ""
|
||||||
@@ -26,7 +24,6 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceHover
|
color: Theme.surfaceHover
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Force a small delay to ensure proper initialization
|
|
||||||
forceRecreateTimer.start();
|
forceRecreateTimer.start();
|
||||||
}
|
}
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
@@ -104,7 +101,6 @@ Rectangle {
|
|||||||
popup.close();
|
popup.close();
|
||||||
} else if (popup) {
|
} else if (popup) {
|
||||||
var pos = dropdown.mapToItem(Overlay.overlay, 0, dropdown.height + 4);
|
var pos = dropdown.mapToItem(Overlay.overlay, 0, dropdown.height + 4);
|
||||||
// Center the wider popup over the dropdown button
|
|
||||||
popup.x = pos.x - (root.popupWidthOffset / 2);
|
popup.x = pos.x - (root.popupWidthOffset / 2);
|
||||||
popup.y = pos.y;
|
popup.y = pos.y;
|
||||||
popup.open();
|
popup.open();
|
||||||
@@ -112,7 +108,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a Row for the left-aligned content (icon + text)
|
|
||||||
Row {
|
Row {
|
||||||
id: contentRow
|
id: contentRow
|
||||||
|
|
||||||
@@ -135,14 +130,12 @@ Rectangle {
|
|||||||
text: root.currentValue
|
text: root.currentValue
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
// Constrain width for proper eliding
|
|
||||||
width: dropdown.width - contentRow.x - expandIcon.width - Theme.spacingM - Theme.spacingS
|
width: dropdown.width - contentRow.x - expandIcon.width - Theme.spacingM - Theme.spacingS
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anchor the expand icon to the right, outside of the Row
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
id: expandIcon
|
id: expandIcon
|
||||||
|
|
||||||
@@ -163,7 +156,6 @@ Rectangle {
|
|||||||
|
|
||||||
active: true
|
active: true
|
||||||
onRecreateFlagChanged: {
|
onRecreateFlagChanged: {
|
||||||
// Force recreation by toggling active
|
|
||||||
active = false;
|
active = false;
|
||||||
active = true;
|
active = true;
|
||||||
}
|
}
|
||||||
@@ -241,7 +233,6 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
|
|
||||||
// Search field
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: searchContainer
|
id: searchContainer
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ GridView {
|
|||||||
signal itemClicked(int index, var modelData)
|
signal itemClicked(int index, var modelData)
|
||||||
signal itemHovered(int index)
|
signal itemHovered(int index)
|
||||||
|
|
||||||
// Ensure the current item is visible
|
|
||||||
function ensureVisible(index) {
|
function ensureVisible(index) {
|
||||||
if (index < 0 || index >= gridView.count)
|
if (index < 0 || index >= gridView.count)
|
||||||
return ;
|
return ;
|
||||||
@@ -157,7 +156,6 @@ GridView {
|
|||||||
itemHovered(index);
|
itemHovered(index);
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
// Signal parent to reset keyboard navigation flag when mouse moves
|
|
||||||
keyboardNavigationReset();
|
keyboardNavigationReset();
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ StyledText {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
// Material Icons variable font axes support
|
|
||||||
font.variableAxes: ({
|
font.variableAxes: ({
|
||||||
"FILL": fill.toFixed(1),
|
"FILL": fill.toFixed(1),
|
||||||
"GRAD": grade,
|
"GRAD": grade,
|
||||||
@@ -26,7 +25,6 @@ StyledText {
|
|||||||
"wght": weight
|
"wght": weight
|
||||||
})
|
})
|
||||||
|
|
||||||
// Smooth transitions for variable axes
|
|
||||||
Behavior on fill {
|
Behavior on fill {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Appearance.anim.durations.quick
|
duration: Appearance.anim.durations.quick
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ ListView {
|
|||||||
signal itemClicked(int index, var modelData)
|
signal itemClicked(int index, var modelData)
|
||||||
signal itemHovered(int index)
|
signal itemHovered(int index)
|
||||||
|
|
||||||
// Ensure the current item is visible
|
|
||||||
function ensureVisible(index) {
|
function ensureVisible(index) {
|
||||||
if (index < 0 || index >= count)
|
if (index < 0 || index >= count)
|
||||||
return ;
|
return ;
|
||||||
@@ -156,7 +155,6 @@ ListView {
|
|||||||
itemHovered(index);
|
itemHovered(index);
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
// Signal parent to reset keyboard navigation flag when mouse moves
|
|
||||||
keyboardNavigationReset();
|
keyboardNavigationReset();
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ Column {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -48,7 +47,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget Items
|
|
||||||
Column {
|
Column {
|
||||||
id: itemsList
|
id: itemsList
|
||||||
|
|
||||||
@@ -252,7 +250,6 @@ Column {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Widget Control
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 200
|
width: 200
|
||||||
height: 40
|
height: 40
|
||||||
|
|||||||
@@ -1,277 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Common
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var items: []
|
|
||||||
property var allWidgets: []
|
|
||||||
property string title: ""
|
|
||||||
property string titleIcon: "widgets"
|
|
||||||
property string sectionId: ""
|
|
||||||
|
|
||||||
signal itemEnabledChanged(string itemId, bool enabled)
|
|
||||||
signal itemOrderChanged(var newOrder)
|
|
||||||
signal addWidget(string sectionId)
|
|
||||||
signal removeLastWidget(string sectionId)
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: root.titleIcon
|
|
||||||
size: Theme.iconSize
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: root.title
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width - 60
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget Items
|
|
||||||
Column {
|
|
||||||
id: itemsList
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.items
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
id: delegateItem
|
|
||||||
|
|
||||||
property int visualIndex: index
|
|
||||||
property bool held: dragArea.pressed
|
|
||||||
property string itemId: modelData.id
|
|
||||||
|
|
||||||
width: itemsList.width
|
|
||||||
height: 70
|
|
||||||
z: held ? 2 : 1
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: itemBackground
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 2
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.8)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingM
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
// Drag handle
|
|
||||||
Rectangle {
|
|
||||||
width: 40
|
|
||||||
height: parent.height
|
|
||||||
color: "transparent"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "drag_indicator"
|
|
||||||
size: Theme.iconSize - 4
|
|
||||||
color: Theme.outline
|
|
||||||
anchors.centerIn: parent
|
|
||||||
opacity: 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget icon
|
|
||||||
DankIcon {
|
|
||||||
name: modelData.icon
|
|
||||||
size: Theme.iconSize
|
|
||||||
color: modelData.enabled ? Theme.primary : Theme.outline
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget info
|
|
||||||
Column {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 2
|
|
||||||
width: parent.width - 200 // Leave space for toggle
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.text
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: modelData.enabled ? Theme.surfaceText : Theme.outline
|
|
||||||
elide: Text.ElideRight
|
|
||||||
width: parent.width
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.description
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: modelData.enabled ? Theme.outline : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
|
|
||||||
elide: Text.ElideRight
|
|
||||||
width: parent.width
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer to push toggle to right
|
|
||||||
Item {
|
|
||||||
width: parent.width - 280 // Dynamic width
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle - positioned at right edge
|
|
||||||
DankToggle {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: 48
|
|
||||||
height: 24
|
|
||||||
hideText: true
|
|
||||||
checked: modelData.enabled
|
|
||||||
onToggled: (checked) => {
|
|
||||||
root.itemEnabledChanged(modelData.id, checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag functionality
|
|
||||||
MouseArea {
|
|
||||||
id: dragArea
|
|
||||||
|
|
||||||
property bool validDragStart: false
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
drag.target: held && validDragStart ? delegateItem : undefined
|
|
||||||
drag.axis: Drag.YAxis
|
|
||||||
drag.minimumY: -delegateItem.height
|
|
||||||
drag.maximumY: itemsList.height
|
|
||||||
onPressed: (mouse) => {
|
|
||||||
// Only allow dragging from the drag handle area (first 60px)
|
|
||||||
if (mouse.x <= 60) {
|
|
||||||
validDragStart = true;
|
|
||||||
delegateItem.z = 2;
|
|
||||||
} else {
|
|
||||||
validDragStart = false;
|
|
||||||
mouse.accepted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onReleased: {
|
|
||||||
delegateItem.z = 1;
|
|
||||||
if (drag.active && validDragStart) {
|
|
||||||
// Calculate new index based on position
|
|
||||||
var newIndex = Math.round(delegateItem.y / (delegateItem.height + itemsList.spacing));
|
|
||||||
newIndex = Math.max(0, Math.min(newIndex, root.items.length - 1));
|
|
||||||
if (newIndex !== index) {
|
|
||||||
var newItems = root.items.slice();
|
|
||||||
var draggedItem = newItems.splice(index, 1)[0];
|
|
||||||
newItems.splice(newIndex, 0, draggedItem);
|
|
||||||
root.itemOrderChanged(newItems.map((item) => {
|
|
||||||
return item.id;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reset position
|
|
||||||
delegateItem.x = 0;
|
|
||||||
delegateItem.y = 0;
|
|
||||||
validDragStart = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animations for drag
|
|
||||||
Behavior on y {
|
|
||||||
enabled: !dragArea.held && !dragArea.drag.active
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add/Remove Controls
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width * 0.5
|
|
||||||
height: 40
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
border.width: 1
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingL
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Add or remove widgets"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.outline
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
// Add button
|
|
||||||
DankActionButton {
|
|
||||||
iconName: "add"
|
|
||||||
iconSize: Theme.iconSize - 4
|
|
||||||
iconColor: Theme.primary
|
|
||||||
hoverColor: Theme.primaryContainer
|
|
||||||
onClicked: {
|
|
||||||
root.addWidget(root.sectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove button
|
|
||||||
DankActionButton {
|
|
||||||
iconName: "remove"
|
|
||||||
iconSize: Theme.iconSize - 4
|
|
||||||
iconColor: Theme.error
|
|
||||||
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
|
|
||||||
enabled: root.items.length > 0
|
|
||||||
opacity: root.items.length > 0 ? 1 : 0.5
|
|
||||||
onClicked: {
|
|
||||||
if (root.items.length > 0)
|
|
||||||
root.removeLastWidget(root.sectionId);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,13 +19,11 @@ StyledRect {
|
|||||||
property alias validator: textInput.validator
|
property alias validator: textInput.validator
|
||||||
property alias inputMethodHints: textInput.inputMethodHints
|
property alias inputMethodHints: textInput.inputMethodHints
|
||||||
property alias maximumLength: textInput.maximumLength
|
property alias maximumLength: textInput.maximumLength
|
||||||
// Icon properties
|
|
||||||
property string leftIconName: ""
|
property string leftIconName: ""
|
||||||
property int leftIconSize: Theme.iconSize
|
property int leftIconSize: Theme.iconSize
|
||||||
property color leftIconColor: Theme.surfaceVariantText
|
property color leftIconColor: Theme.surfaceVariantText
|
||||||
property color leftIconFocusedColor: Theme.primary
|
property color leftIconFocusedColor: Theme.primary
|
||||||
property bool showClearButton: false
|
property bool showClearButton: false
|
||||||
// Custom properties
|
|
||||||
property color backgroundColor: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.9)
|
property color backgroundColor: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.9)
|
||||||
property color focusedBorderColor: Theme.primary
|
property color focusedBorderColor: Theme.primary
|
||||||
property color normalBorderColor: Theme.outlineStrong
|
property color normalBorderColor: Theme.outlineStrong
|
||||||
@@ -33,22 +31,18 @@ StyledRect {
|
|||||||
property int borderWidth: 1
|
property int borderWidth: 1
|
||||||
property int focusedBorderWidth: 2
|
property int focusedBorderWidth: 2
|
||||||
property real cornerRadius: Theme.cornerRadius
|
property real cornerRadius: Theme.cornerRadius
|
||||||
// Internal padding calculations
|
|
||||||
readonly property real leftPadding: Theme.spacingM + (leftIconName ? leftIconSize + Theme.spacingM : 0)
|
readonly property real leftPadding: Theme.spacingM + (leftIconName ? leftIconSize + Theme.spacingM : 0)
|
||||||
readonly property real rightPadding: Theme.spacingM + (showClearButton && text.length > 0 ? 24 + Theme.spacingM : 0)
|
readonly property real rightPadding: Theme.spacingM + (showClearButton && text.length > 0 ? 24 + Theme.spacingM : 0)
|
||||||
property real topPadding: Theme.spacingM
|
property real topPadding: Theme.spacingM
|
||||||
property real bottomPadding: Theme.spacingM
|
property real bottomPadding: Theme.spacingM
|
||||||
// Behavior control
|
|
||||||
property bool ignoreLeftRightKeys: false
|
property bool ignoreLeftRightKeys: false
|
||||||
property var keyForwardTargets: []
|
property var keyForwardTargets: []
|
||||||
|
|
||||||
// Signals
|
|
||||||
signal textEdited()
|
signal textEdited()
|
||||||
signal editingFinished()
|
signal editingFinished()
|
||||||
signal accepted()
|
signal accepted()
|
||||||
signal focusStateChanged(bool hasFocus)
|
signal focusStateChanged(bool hasFocus)
|
||||||
|
|
||||||
// Access to inner TextInput properties via functions
|
|
||||||
function getActiveFocus() {
|
function getActiveFocus() {
|
||||||
return textInput.activeFocus;
|
return textInput.activeFocus;
|
||||||
}
|
}
|
||||||
@@ -61,7 +55,6 @@ StyledRect {
|
|||||||
textInput.focus = value;
|
textInput.focus = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions
|
|
||||||
function forceActiveFocus() {
|
function forceActiveFocus() {
|
||||||
textInput.forceActiveFocus();
|
textInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
@@ -94,7 +87,6 @@ StyledRect {
|
|||||||
textInput.focus = false;
|
textInput.focus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default styling
|
|
||||||
width: 200
|
width: 200
|
||||||
height: 48
|
height: 48
|
||||||
radius: cornerRadius
|
radius: cornerRadius
|
||||||
@@ -102,7 +94,6 @@ StyledRect {
|
|||||||
border.color: textInput.activeFocus ? focusedBorderColor : normalBorderColor
|
border.color: textInput.activeFocus ? focusedBorderColor : normalBorderColor
|
||||||
border.width: textInput.activeFocus ? focusedBorderWidth : borderWidth
|
border.width: textInput.activeFocus ? focusedBorderWidth : borderWidth
|
||||||
|
|
||||||
// Left icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
id: leftIcon
|
id: leftIcon
|
||||||
|
|
||||||
@@ -153,7 +144,6 @@ StyledRect {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear button
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
id: clearButton
|
id: clearButton
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ Popup {
|
|||||||
|
|
||||||
signal widgetSelected(string widgetId, string targetSection)
|
signal widgetSelected(string widgetId, string targetSection)
|
||||||
|
|
||||||
// Prevent multiple openings
|
|
||||||
function safeOpen() {
|
function safeOpen() {
|
||||||
if (!isOpening && !visible) {
|
if (!isOpening && !visible) {
|
||||||
isOpening = true;
|
isOpening = true;
|
||||||
@@ -29,7 +28,6 @@ Popup {
|
|||||||
}
|
}
|
||||||
onClosed: {
|
onClosed: {
|
||||||
isOpening = false;
|
isOpening = false;
|
||||||
// Clear references to prevent memory leaks
|
|
||||||
allWidgets = [];
|
allWidgets = [];
|
||||||
targetSection = "";
|
targetSection = "";
|
||||||
}
|
}
|
||||||
@@ -44,7 +42,6 @@ Popup {
|
|||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
// Close button in top-right
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
iconName: "close"
|
iconName: "close"
|
||||||
iconSize: Theme.iconSize - 2
|
iconSize: Theme.iconSize - 2
|
||||||
@@ -65,7 +62,6 @@ Popup {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
anchors.topMargin: Theme.spacingL + 30 // Space for close button
|
anchors.topMargin: Theme.spacingL + 30 // Space for close button
|
||||||
|
|
||||||
// Header
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -95,7 +91,6 @@ Popup {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget List
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 120 // Leave space for header and description
|
height: parent.height - 120 // Leave space for header and description
|
||||||
@@ -120,7 +115,6 @@ Popup {
|
|||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Widget icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: modelData.icon
|
name: modelData.icon
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
@@ -128,7 +122,6 @@ Popup {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget info
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: 2
|
spacing: 2
|
||||||
@@ -154,7 +147,6 @@ Popup {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add icon
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "add"
|
name: "add"
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
|
|||||||
@@ -10,18 +10,14 @@ Text {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: Appearance.fontSize.normal
|
font.pixelSize: Appearance.fontSize.normal
|
||||||
font.family: {
|
font.family: {
|
||||||
// Use system default
|
|
||||||
|
|
||||||
var requestedFont = isMonospace ? Prefs.monoFontFamily : Prefs.fontFamily;
|
var requestedFont = isMonospace ? Prefs.monoFontFamily : Prefs.fontFamily;
|
||||||
var defaultFont = isMonospace ? Prefs.defaultMonoFontFamily : Prefs.defaultFontFamily;
|
var defaultFont = isMonospace ? Prefs.defaultMonoFontFamily : Prefs.defaultFontFamily;
|
||||||
// If user hasn't overridden the font and we're using the default
|
|
||||||
if (requestedFont === defaultFont) {
|
if (requestedFont === defaultFont) {
|
||||||
var availableFonts = Qt.fontFamilies();
|
var availableFonts = Qt.fontFamilies();
|
||||||
if (!availableFonts.includes(requestedFont))
|
if (!availableFonts.includes(requestedFont))
|
||||||
return isMonospace ? "Monospace" : "DejaVu Sans";
|
return isMonospace ? "Monospace" : "DejaVu Sans";
|
||||||
|
|
||||||
}
|
}
|
||||||
// Either user overrode it, or default font is available
|
|
||||||
return requestedFont;
|
return requestedFont;
|
||||||
}
|
}
|
||||||
font.weight: Prefs.fontWeight
|
font.weight: Prefs.fontWeight
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
//@ pragma UseQApplication
|
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -30,7 +29,6 @@ ShellRoot {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-monitor support using Variants
|
|
||||||
Variants {
|
Variants {
|
||||||
model: Quickshell.screens
|
model: Quickshell.screens
|
||||||
|
|
||||||
@@ -103,7 +101,6 @@ ShellRoot {
|
|||||||
id: settingsModal
|
id: settingsModal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application and clipboard components
|
|
||||||
AppDrawerPopout {
|
AppDrawerPopout {
|
||||||
id: appDrawerPopout
|
id: appDrawerPopout
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user