diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index fc089b07..d325d871 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Dock-Schwebeabstand", "description": "Schwebeabstand vom Bildschirmrand anpassen." + }, + "colorize-icons": { + "label": "Symbole einfärben", + "description": "Theme-Farben auf Dock-App-Symbole anwenden (nur nicht fokussierte Apps)." } }, "monitors": { @@ -935,6 +939,10 @@ "width": { "description": "Steuert die horizontale Größe des Widgets.", "label": "Widget-Breite" + }, + "colorize-icons": { + "label": "Symbole einfärben", + "description": "Theme-Farben auf das aktive Fenster-Symbol anwenden." } }, "system-monitor": { @@ -1128,6 +1136,16 @@ "only-same-output": { "label": "Nur vom gleichen Bildschirm", "description": "Zeige nur Apps vom dem Bildschirm an, wo sich die Taskbar befindet." + }, + "colorize-icons": { + "label": "Symbole einfärben", + "description": "Theme-Farben auf Taskbar-Symbole anwenden." + } + }, + "tray": { + "colorize-icons": { + "label": "Symbole einfärben", + "description": "Theme-Farben auf Tray-Symbole anwenden." } } } diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 1ce1a938..905ff182 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Dock floating distance", "description": "Adjust the floating distance from the screen edge." + }, + "colorize-icons": { + "label": "Colorize Icons", + "description": "Apply theme colors to dock app icons (non-focused apps only)." } }, "monitors": { @@ -918,6 +922,10 @@ "width": { "label": "Widget Width", "description": "Controls the horizontal size of the widget." + }, + "colorize-icons": { + "label": "Colorize Icons", + "description": "Apply theme colors to active window icon." } }, "system-monitor": { @@ -1111,6 +1119,16 @@ "only-same-output": { "label": "Only from same output", "description": "Show only apps from the output where the bar is located." + }, + "colorize-icons": { + "label": "Colorize Icons", + "description": "Apply theme colors to taskbar icons." + } + }, + "tray": { + "colorize-icons": { + "label": "Colorize Icons", + "description": "Apply theme colors to tray icons." } } } diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 16839a45..ff013f2d 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Distancia de flotación del dock", "description": "Ajusta la distancia de flotación desde el borde de la pantalla." + }, + "colorize-icons": { + "label": "Colorear iconos", + "description": "Aplicar colores del tema a los iconos de aplicaciones del dock (solo aplicaciones no enfocadas)." } }, "monitors": { @@ -918,6 +922,10 @@ "width": { "description": "Controla el tamaño horizontal del widget.", "label": "Ancho del widget" + }, + "colorize-icons": { + "label": "Colorear iconos", + "description": "Aplicar colores del tema al icono de la ventana activa." } }, "system-monitor": { @@ -1111,6 +1119,16 @@ "only-same-output": { "description": "Muestra solo las aplicaciones del resultado donde se encuentra la barra.", "label": "Solo de la misma salida" + }, + "colorize-icons": { + "label": "Colorear iconos", + "description": "Aplicar colores del tema a los iconos de la barra de tareas." + } + }, + "tray": { + "colorize-icons": { + "label": "Colorear iconos", + "description": "Aplicar colores del tema a los iconos de la bandeja del sistema." } } } diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 467edb51..2831d34f 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Distance de flottaison du dock", "description": "Ajustez la distance de flottaison par rapport au bord de l'écran." + }, + "colorize-icons": { + "label": "Coloriser les icônes", + "description": "Appliquer les couleurs du thème aux icônes d'applications du dock (applications non focalisées uniquement)." } }, "monitors": { @@ -918,6 +922,10 @@ "width": { "description": "Contrôle la taille horizontale du widget.", "label": "Largeur du widget" + }, + "colorize-icons": { + "label": "Coloriser les icônes", + "description": "Appliquer les couleurs du thème à l'icône de la fenêtre active." } }, "system-monitor": { @@ -1111,6 +1119,16 @@ "only-same-output": { "description": "Afficher uniquement les applications de la sortie où la barre est située.", "label": "Seulement à partir de la même sortie" + }, + "colorize-icons": { + "label": "Coloriser les icônes", + "description": "Appliquer les couleurs du thème aux icônes de la barre des tâches." + } + }, + "tray": { + "colorize-icons": { + "label": "Coloriser les icônes", + "description": "Appliquer les couleurs du thème aux icônes de la barre système." } } } diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 6f00f9f1..e1f03541 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Distância de flutuação da dock", "description": "Ajuste a distância de flutuação da borda da tela." + }, + "colorize-icons": { + "label": "Colorir ícones", + "description": "Aplicar cores do tema aos ícones de aplicativos da dock (apenas aplicativos não focados)." } }, "monitors": { @@ -918,6 +922,10 @@ "width": { "description": "Controla o tamanho horizontal do widget.", "label": "Largura do Widget" + }, + "colorize-icons": { + "label": "Colorir ícones", + "description": "Aplicar cores do tema ao ícone da janela ativa." } }, "system-monitor": { @@ -1111,6 +1119,16 @@ "only-same-output": { "description": "Mostrar apenas os aplicativos da saída onde a barra está localizada.", "label": "Apenas da mesma saída" + }, + "colorize-icons": { + "label": "Colorir ícones", + "description": "Aplicar cores do tema aos ícones da barra de tarefas." + } + }, + "tray": { + "colorize-icons": { + "label": "Colorir ícones", + "description": "Aplicar cores do tema aos ícones da bandeja do sistema." } } } diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 694f3d83..85db3559 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -324,6 +324,10 @@ "floating-distance": { "label": "Dock 浮动距离", "description": "调整距离屏幕边缘的浮动距离。" + }, + "colorize-icons": { + "label": "着色图标", + "description": "将主题颜色应用到 Dock 应用图标(仅限非聚焦应用)。" } }, "monitors": { @@ -918,6 +922,10 @@ "width": { "description": "控制小部件的水平尺寸。", "label": "小部件宽度" + }, + "colorize-icons": { + "label": "着色图标", + "description": "将主题颜色应用到活动窗口图标。" } }, "system-monitor": { @@ -1111,6 +1119,16 @@ "only-same-output": { "label": "仅显示同屏幕", "description": "仅显示任务栏所在屏幕上的应用程序" + }, + "colorize-icons": { + "label": "着色图标", + "description": "将主题颜色应用到任务栏图标。" + } + }, + "tray": { + "colorize-icons": { + "label": "着色图标", + "description": "将主题颜色应用到系统托盘图标。" } } } diff --git a/Assets/settings-default.json b/Assets/settings-default.json index e44e9bf9..fcaedad4 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -9,7 +9,6 @@ "floating": false, "marginVertical": 0.25, "marginHorizontal": 0.25, - "widgets": { "left": [ { @@ -37,6 +36,12 @@ { "id": "NotificationHistory" }, + { + "id": "WiFi" + }, + { + "id": "Bluetooth" + }, { "id": "Battery" }, @@ -63,7 +68,8 @@ "radiusRatio": 1, "screenRadiusRatio": 1, "animationSpeed": 1, - "animationDisabled": false + "animationDisabled": false, + "compactLockScreen": false }, "location": { "name": "Tokyo", @@ -138,7 +144,8 @@ "floatingRatio": 1, "onlySameOutput": true, "monitors": [], - "pinnedApps": [] + "pinnedApps": [], + "colorizeIcons": false }, "network": { "wifiEnabled": true @@ -158,7 +165,8 @@ "enabled": true, "location": "top_right", "monitors": [], - "autoHideMs": 2000 + "autoHideMs": 2000, + "alwaysOnTop": false }, "audio": { "volumeStep": 5, @@ -189,6 +197,7 @@ "templates": { "gtk": false, "qt": false, + "kcolorscheme": false, "kitty": false, "ghostty": false, "foot": false, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index a96a3539..e75ee3d0 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -273,6 +273,7 @@ Singleton { property list monitors: [] // Desktop entry IDs pinned to the dock (e.g., "org.kde.konsole", "firefox.desktop") property list pinnedApps: [] + property bool colorizeIcons: false } // network @@ -374,6 +375,23 @@ Singleton { } } + // ----------------------------------------------------- + // Function to preprocess paths by expanding "~" to user's home directory + function preprocessPath(path) { + if (typeof path !== "string" || path === "") { + return path + } + + // Expand "~" to user's home directory + if (path.startsWith("~/")) { + return Quickshell.env("HOME") + path.substring(1) + } else if (path === "~") { + return Quickshell.env("HOME") + } + + return path + } + // ----------------------------------------------------- // Public function to trigger immediate settings saving function saveImmediate() { diff --git a/Modules/Bar/Calendar/CalendarPanel.qml b/Modules/Bar/Calendar/CalendarPanel.qml index 0e097b3d..5ed2f161 100644 --- a/Modules/Bar/Calendar/CalendarPanel.qml +++ b/Modules/Bar/Calendar/CalendarPanel.qml @@ -10,6 +10,8 @@ import qs.Widgets NPanel { id: root + readonly property var now: Time.date + preferredWidth: (Settings.data.location.showWeekNumberInCalendar ? 400 : 380) * Style.uiScaleRatio preferredHeight: 520 * Style.uiScaleRatio @@ -193,12 +195,12 @@ NPanel { Canvas { id: secondsProgress anchors.fill: parent - property real progress: Time.date.getSeconds() / 60 + property real progress: now.getSeconds() / 60 onProgressChanged: requestPaint() Connections { target: Time function onDateChanged() { - const total = Time.date.getSeconds() * 1000 + Time.date.getMilliseconds() + const total = now.getSeconds() * 1000 + now.getMilliseconds() secondsProgress.progress = total / 60000 } } @@ -233,7 +235,7 @@ NPanel { NText { text: { - var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(Time.date, "hh AP") : Qt.locale().toString(Time.date, "HH") + var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(now, "hh AP") : Qt.locale().toString(now, "HH") return t.split(" ")[0] } @@ -245,7 +247,7 @@ NPanel { } NText { - text: Qt.formatTime(Time.date, "mm") + text: Qt.formatTime(now, "mm") pointSize: Style.fontSizeXXS font.weight: Style.fontWeightBold color: Color.mOnPrimary diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index a1b38912..80aa96e3 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -148,6 +148,15 @@ Item { asynchronous: true smooth: true visible: source !== "" + + // Apply dock shader to active window icon (always themed) + layer.enabled: widgetSettings.colorizeIcons !== false + layer.effect: ShaderEffect { + property color targetColor: Color.mOnSurface + property real colorizeMode: 0.0 // Dock mode (grayscale) + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb") + } } } @@ -317,6 +326,15 @@ Item { asynchronous: true smooth: true visible: source !== "" + + // Apply dock shader to active window icon (always themed) + layer.enabled: widgetSettings.colorizeIcons !== false + layer.effect: ShaderEffect { + property color targetColor: Color.mOnSurface + property real colorizeMode: 0.0 // Dock mode (grayscale) + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb") + } } } } diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 000120bf..3725a583 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -80,6 +80,15 @@ Rectangle { asynchronous: true opacity: modelData.isFocused ? Style.opacityFull : 0.6 + // Apply dock shader to all taskbar icons + layer.enabled: widgetSettings.colorizeIcons !== false + layer.effect: ShaderEffect { + property color targetColor: Color.mOnSurface + property real colorizeMode: 0.0 // Dock mode (grayscale) + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb") + } + Rectangle { anchors.bottomMargin: -2 anchors.bottom: parent.bottom diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 4ea42c6a..2a5665e7 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -192,6 +192,14 @@ Rectangle { } opacity: status === Image.Ready ? 1 : 0 + layer.enabled: widgetSettings.colorizeIcons !== false + layer.effect: ShaderEffect { + property color targetColor: Color.mOnSurface + property real colorizeMode: 1.0 // Tray mode (intensity-based) + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb") + } + MouseArea { anchors.fill: parent hoverEnabled: true diff --git a/Modules/ControlCenter/Cards/ProfileCard.qml b/Modules/ControlCenter/Cards/ProfileCard.qml index 230b22ca..0da32c37 100644 --- a/Modules/ControlCenter/Cards/ProfileCard.qml +++ b/Modules/ControlCenter/Cards/ProfileCard.qml @@ -27,7 +27,7 @@ NBox { NImageCircled { width: Math.round(Style.baseWidgetSize * 1.25 * Style.uiScaleRatio) height: width - imagePath: Settings.data.general.avatarImage + imagePath: Settings.preprocessPath(Settings.data.general.avatarImage) fallbackIcon: "person" borderColor: Color.mPrimary borderWidth: Math.max(1, Style.borderM) diff --git a/Modules/ControlCenter/Cards/TopCard.qml b/Modules/ControlCenter/Cards/TopCard.qml index 48ef1dcf..62eae416 100644 --- a/Modules/ControlCenter/Cards/TopCard.qml +++ b/Modules/ControlCenter/Cards/TopCard.qml @@ -32,7 +32,7 @@ NBox { NImageCircled { width: Style.baseWidgetSize * 1.25 height: width - imagePath: Settings.data.general.avatarImage + imagePath: Settings.preprocessPath(Settings.data.general.avatarImage) fallbackIcon: "person" borderColor: Color.mPrimary borderWidth: Math.max(1, Style.borderM) diff --git a/Modules/Dock/Dock.qml b/Modules/Dock/Dock.qml index cdc3dc0c..56a932e5 100644 --- a/Modules/Dock/Dock.qml +++ b/Modules/Dock/Dock.qml @@ -392,6 +392,15 @@ Variants { scale: appButton.hovered ? 1.15 : 1.0 + // Apply dock-specific colorization shader only to non-focused apps + layer.enabled: !appButton.isActive && Settings.data.dock.colorizeIcons + layer.effect: ShaderEffect { + property color targetColor: Color.mOnSurface + property real colorizeMode: 0.0 // Dock mode (grayscale) + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb") + } + Behavior on scale { NumberAnimation { duration: Style.animationNormal diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml index 90ebc2fc..e4da717a 100644 --- a/Modules/LockScreen/LockScreen.qml +++ b/Modules/LockScreen/LockScreen.qml @@ -49,6 +49,8 @@ Loader { locked: lockScreen.active WlSessionLockSurface { + readonly property var now: Time.date + Item { id: batteryIndicator property var battery: UPower.displayDevice @@ -101,8 +103,8 @@ Loader { visible: Settings.data.general.showScreenCorners property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Qt.rgba(0, 0, 0, 1) : Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity) - property real cornerRadius: Style.screenRadius - property real cornerSize: Style.screenRadius + property real cornerRadius: Style.screenRadius + property real cornerSize: Style.screenRadius // Top-left concave corner Canvas { @@ -246,12 +248,12 @@ Loader { // Time, Date, and User Profile Container Rectangle { - width: Math.max(500, contentRow.implicitWidth + 24) - height: 120 + width: Math.max(500 , contentRow.implicitWidth + 32) + height: Math.max(120 , contentRow.implicitHeight + 32) anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - anchors.topMargin: 100 - radius: Style.radiusL + anchors.topMargin: 100 + radius: Style.radiusL color: Color.mSurface border.color: Qt.alpha(Color.mOutline, 0.2) border.width: 1 @@ -259,13 +261,13 @@ Loader { RowLayout { id: contentRow anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 16 + spacing: 32 // Left side: Avatar Rectangle { - Layout.preferredWidth: 70 - Layout.preferredHeight: 70 + Layout.preferredWidth: 70 + Layout.preferredHeight: 70 Layout.alignment: Qt.AlignVCenter radius: width * 0.5 color: Color.transparent @@ -294,9 +296,9 @@ Loader { NImageCircled { anchors.centerIn: parent - width: 66 - height: 66 - imagePath: Settings.data.general.avatarImage + width: 66 + height: 66 + imagePath: Settings.preprocessPath(Settings.data.general.avatarImage) fallbackIcon: "person" SequentialAnimation on scale { @@ -318,12 +320,12 @@ Loader { // Center: User Info Column (left-aligned text) ColumnLayout { Layout.alignment: Qt.AlignVCenter - spacing: 2 + spacing: 2 // Welcome back + Username on one line NText { text: I18n.tr("lock-screen.welcome-back") + " " + Quickshell.env("USER") + "!" - pointSize: Style.fontSizeXXXL + pointSize: Style.fontSizeXXL font.weight: Font.Medium color: Color.mOnSurface horizontalAlignment: Text.AlignLeft @@ -332,7 +334,7 @@ Loader { // Date below NText { text: Qt.locale().toString(Time.date, "dddd, MMMM d") - pointSize: Style.fontSizeXXL + pointSize: Style.fontSizeXL font.weight: Font.Medium color: Color.mOnSurfaceVariant horizontalAlignment: Text.AlignLeft @@ -345,8 +347,8 @@ Loader { } Item { - Layout.preferredWidth: 70 - Layout.preferredHeight: 70 + Layout.preferredWidth: 70 + Layout.preferredHeight: 70 Layout.alignment: Qt.AlignVCenter // Seconds circular progress @@ -369,21 +371,21 @@ Loader { var ctx = getContext("2d") var centerX = width / 2 var centerY = height / 2 - var radius = Math.min(width, height) / 2 - 3 + var radius = Math.min(width, height) / 2 - 3 ctx.reset() // Background circle ctx.beginPath() ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI) - ctx.lineWidth = 2.5 + ctx.lineWidth = 2.5 ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.15) ctx.stroke() // Progress arc ctx.beginPath() ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI) - ctx.lineWidth = 2.5 + ctx.lineWidth = 2.5 ctx.strokeStyle = Color.mPrimary ctx.lineCap = "round" ctx.stroke() @@ -400,7 +402,7 @@ Loader { var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(Time.date, "hh AP") : Qt.locale().toString(Time.date, "HH") return t } - pointSize: Style.fontSizeL + pointSize: Style.fontSizeL font.weight: Style.fontWeightBold color: Color.mOnSurface horizontalAlignment: Text.AlignHCenter @@ -409,7 +411,7 @@ Loader { NText { text: Qt.formatTime(Time.date, "mm") - pointSize: Style.fontSizeL + pointSize: Style.fontSizeL font.weight: Style.fontWeightBold color: Color.mOnSurfaceVariant horizontalAlignment: Text.AlignHCenter @@ -422,12 +424,12 @@ Loader { // Error notification Rectangle { - width: 450 - height: 60 + width: 450 + height: 60 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - anchors.bottomMargin: 300 - radius: 30 + anchors.bottomMargin: 300 + radius: 30 color: Color.mError border.color: Color.mError border.width: 1 @@ -436,18 +438,18 @@ Loader { RowLayout { anchors.centerIn: parent - spacing: 10 + spacing: 10 NIcon { icon: "alert-circle" - pointSize: Style.fontSizeL + pointSize: Style.fontSizeL color: Color.mOnError } NText { text: lockContext.errorMessage || "Authentication failed" color: Color.mOnError - pointSize: Style.fontSizeL + pointSize: Style.fontSizeL font.weight: Font.Medium horizontalAlignment: Text.AlignHCenter } @@ -468,61 +470,62 @@ Loader { var hasKeyboard = keyboardLayout.currentLayout !== "Unknown" if (hasBattery && hasKeyboard) { - return 200 + return 200 } else if (hasBattery || hasKeyboard) { - return 120 + return 120 } else { return 0 } } - height: 40 + height: 40 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - anchors.bottomMargin: 96 + (Settings.data.general.compactLockScreen ? 116 : 220) - topLeftRadius: Style.radiusL - topRightRadius: Style.radiusL + anchors.bottomMargin: 96 + (Settings.data.general.compactLockScreen ? 116 : 220 ) + topLeftRadius: Style.radiusL + topRightRadius: Style.radiusL color: Color.mSurface visible: Settings.data.general.compactLockScreen && ((UPower.displayDevice && UPower.displayDevice.ready && UPower.displayDevice.isPresent) || keyboardLayout.currentLayout !== "Unknown") RowLayout { anchors.centerIn: parent - spacing: 16 + spacing: 16 // Battery indicator RowLayout { - spacing: 6 + spacing: 6 visible: UPower.displayDevice && UPower.displayDevice.ready && UPower.displayDevice.isPresent NIcon { icon: BatteryService.getIcon(Math.round(UPower.displayDevice.percentage * 100), UPower.displayDevice.state === UPowerDeviceState.Charging, true) - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: UPower.displayDevice.state === UPowerDeviceState.Charging ? Color.mPrimary : Color.mOnSurfaceVariant } NText { text: Math.round(UPower.displayDevice.percentage * 100) + "%" color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM font.weight: Font.Medium } } // Keyboard layout indicator RowLayout { - spacing: 6 + spacing: 6 visible: keyboardLayout.currentLayout !== "Unknown" NIcon { icon: "keyboard" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant } NText { text: keyboardLayout.currentLayout color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM font.weight: Font.Medium + elide: Text.ElideRight } } } @@ -530,38 +533,38 @@ Loader { // Bottom container with weather, password input and controls Rectangle { - width: 750 - height: Settings.data.general.compactLockScreen ? 120 : 220 + width: 750 + height: Settings.data.general.compactLockScreen ? 120 : 220 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - anchors.bottomMargin: 100 - radius: Style.radiusL + anchors.bottomMargin: 100 + radius: Style.radiusL color: Color.mSurface ColumnLayout { anchors.fill: parent - anchors.margins: 14 - spacing: 14 + anchors.margins: 14 + spacing: 14 // Weather section RowLayout { Layout.fillWidth: true - Layout.preferredHeight: 65 - spacing: 18 + Layout.preferredHeight: 65 + spacing: 18 visible: !Settings.data.general.compactLockScreen && LocationService.coordinatesReady && LocationService.data.weather !== null // Media widget with visualizer Rectangle { - Layout.preferredWidth: 220 - Layout.preferredHeight: 50 - radius: 25 + Layout.preferredWidth: 220 + Layout.preferredHeight: 50 + radius: 25 color: Color.transparent clip: true visible: MediaService.currentPlayer && MediaService.canPlay Loader { anchors.fill: parent - anchors.margins: 4 + anchors.margins: 4 active: Settings.data.audio.visualizerType === "linear" z: 0 sourceComponent: LinearSpectrum { @@ -574,7 +577,7 @@ Loader { Loader { anchors.fill: parent - anchors.margins: 4 + anchors.margins: 4 active: Settings.data.audio.visualizerType === "mirrored" z: 0 sourceComponent: MirroredSpectrum { @@ -587,7 +590,7 @@ Loader { Loader { anchors.fill: parent - anchors.margins: 4 + anchors.margins: 4 active: Settings.data.audio.visualizerType === "wave" z: 0 sourceComponent: WaveSpectrum { @@ -600,35 +603,35 @@ Loader { RowLayout { anchors.fill: parent - anchors.margins: 8 - spacing: 8 + anchors.margins: 8 + spacing: 8 z: 1 Rectangle { - Layout.preferredWidth: 34 - Layout.preferredHeight: 34 + Layout.preferredWidth: 34 + Layout.preferredHeight: 34 radius: width * 0.5 color: Color.transparent clip: true NImageCircled { anchors.fill: parent - anchors.margins: 2 + anchors.margins: 2 imagePath: MediaService.trackArtUrl fallbackIcon: "disc" - fallbackIconSize: Style.fontSizeM + fallbackIconSize: Style.fontSizeM borderColor: Color.mOutline - borderWidth: Math.max(1, Style.borderS) + borderWidth: Math.max(1, Style.borderS ) } } ColumnLayout { Layout.fillWidth: true - spacing: 2 + spacing: 2 NText { text: MediaService.trackTitle || "No media" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM font.weight: Style.fontWeightMedium color: Color.mOnSurface Layout.fillWidth: true @@ -637,7 +640,7 @@ Loader { NText { text: MediaService.trackArtist || "" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant Layout.fillWidth: true elide: Text.ElideRight @@ -649,46 +652,64 @@ Loader { Rectangle { Layout.preferredWidth: 1 Layout.fillHeight: true - Layout.rightMargin: 4 + Layout.rightMargin: 4 color: Qt.alpha(Color.mOutline, 0.3) visible: MediaService.currentPlayer && MediaService.canPlay } Item { - Layout.preferredWidth: Style.marginM + Layout.preferredWidth: Style.marginM visible: !(MediaService.currentPlayer && MediaService.canPlay) } // Current weather RowLayout { - Layout.preferredWidth: 180 - spacing: 8 + Layout.preferredWidth: 180 + spacing: 8 NIcon { Layout.alignment: Qt.AlignVCenter icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.current_weather.weathercode) - pointSize: Style.fontSizeXXXL + pointSize: Style.fontSizeXXXL color: Color.mPrimary } ColumnLayout { Layout.fillWidth: true - spacing: 2 + spacing: 2 RowLayout { Layout.fillWidth: true - spacing: 12 + spacing: 12 NText { - text: Math.round(LocationService.data.weather.current_weather.temperature) + "°" - pointSize: Style.fontSizeXL + text: { + var temp = LocationService.data.weather.current_weather.temperature + var suffix = "C" + if (Settings.data.location.useFahrenheit) { + temp = LocationService.celsiusToFahrenheit(temp) + suffix = "F" + } + temp = Math.round(temp) + return temp + "°" + suffix + } + pointSize: Style.fontSizeXL font.weight: Style.fontWeightBold color: Color.mOnSurface } NText { - text: LocationService.data.weather.current_weather.windspeed + " km/h" - pointSize: Style.fontSizeM + text: { + var wind = LocationService.data.weather.current_weather.windspeed + var unit = "km/h" + if (Settings.data.location.useFahrenheit) { + wind = wind * 0.621371 // Convert km/h to mph + unit = "mph" + } + wind = Math.round(wind) + return wind + " " + unit + } + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant font.weight: Font.Normal } @@ -696,17 +717,17 @@ Loader { RowLayout { Layout.fillWidth: true - spacing: 8 + spacing: 8 NText { text: Settings.data.location.name.split(",")[0] - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant } NText { text: (LocationService.data.weather.current && LocationService.data.weather.current.relativehumidity_2m) ? LocationService.data.weather.current.relativehumidity_2m + "% humidity" : "" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant } } @@ -715,19 +736,19 @@ Loader { // 3-day forecast RowLayout { - Layout.preferredWidth: 260 - Layout.rightMargin: 8 - spacing: 4 + Layout.preferredWidth: 260 + Layout.rightMargin: 8 + spacing: 4 Repeater { model: 3 delegate: ColumnLayout { Layout.fillWidth: true - spacing: 3 + spacing: 3 NText { - text: Qt.locale().toString(new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/")), "ddd") - pointSize: Style.fontSizeM + text: Qt.locale().toString(now, "ddd") + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true @@ -736,13 +757,23 @@ Loader { NIcon { Layout.alignment: Qt.AlignHCenter icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index]) - pointSize: Style.fontSizeXL + pointSize: Style.fontSizeXL color: Color.mOnSurfaceVariant } NText { - text: Math.round(LocationService.data.weather.daily.temperature_2m_max[index]) + "°/" + Math.round(LocationService.data.weather.daily.temperature_2m_min[index]) + "°" - pointSize: Style.fontSizeM + text: { + var max = LocationService.data.weather.daily.temperature_2m_max[index] + var min = LocationService.data.weather.daily.temperature_2m_min[index] + if (Settings.data.location.useFahrenheit) { + max = LocationService.celsiusToFahrenheit(max) + min = LocationService.celsiusToFahrenheit(min) + } + max = Math.round(max) + min = Math.round(min) + return max + "°/" + min + "°" + } + pointSize: Style.fontSizeM font.weight: Style.fontWeightMedium color: Color.mOnSurfaceVariant horizontalAlignment: Text.AlignHCenter @@ -753,45 +784,49 @@ Loader { } // Battery and Keyboard Layout (full mode only) - RowLayout { - Layout.preferredWidth: 60 - spacing: 4 + ColumnLayout { + Layout.preferredWidth: 60 + spacing: 8 // Battery RowLayout { - spacing: 4 + spacing: 4 visible: UPower.displayDevice && UPower.displayDevice.ready && UPower.displayDevice.isPresent NIcon { icon: BatteryService.getIcon(Math.round(UPower.displayDevice.percentage * 100), UPower.displayDevice.state === UPowerDeviceState.Charging, true) - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: UPower.displayDevice.state === UPowerDeviceState.Charging ? Color.mPrimary : Color.mOnSurfaceVariant } NText { text: Math.round(UPower.displayDevice.percentage * 100) + "%" color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM font.weight: Font.Medium + elide: Text.ElideRight + Layout.fillWidth: true } } // Keyboard Layout RowLayout { - spacing: 4 + spacing: 4 visible: keyboardLayout.currentLayout !== "Unknown" NIcon { icon: "keyboard" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnSurfaceVariant } NText { text: keyboardLayout.currentLayout color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM font.weight: Font.Medium + elide: Text.ElideRight + Layout.fillWidth: true } } } @@ -803,26 +838,26 @@ Loader { spacing: 0 Item { - Layout.preferredWidth: Style.marginM + Layout.preferredWidth: Style.marginM } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 24 + Layout.preferredHeight: 48 + radius: 24 color: Color.mSurface border.color: passwordInput.activeFocus ? Color.mPrimary : Qt.alpha(Color.mOutline, 0.3) border.width: passwordInput.activeFocus ? 2 : 1 Row { anchors.left: parent.left - anchors.leftMargin: 18 + anchors.leftMargin: 18 anchors.verticalCenter: parent.verticalCenter - spacing: 14 + spacing: 14 NIcon { icon: "lock" - pointSize: Style.fontSizeL + pointSize: Style.fontSizeL color: passwordInput.activeFocus ? Color.mPrimary : Color.mOnSurfaceVariant anchors.verticalCenter: parent.verticalCenter } @@ -834,7 +869,7 @@ Loader { height: 0 visible: false enabled: !lockContext.unlockInProgress - font.pointSize: Style.fontSizeM + font.pointSize: Style.fontSizeM color: Color.mPrimary echoMode: TextInput.Password passwordCharacter: "•" @@ -855,8 +890,8 @@ Loader { spacing: 0 Rectangle { - width: 2 - height: 20 + width: 2 + height: 20 color: Color.mPrimary visible: passwordInput.activeFocus && passwordInput.text.length === 0 anchors.verticalCenter: parent.verticalCenter @@ -876,7 +911,7 @@ Loader { } Row { - spacing: 6 + spacing: 6 visible: passwordInput.text.length > 0 anchors.verticalCenter: parent.verticalCenter @@ -885,7 +920,7 @@ Loader { NIcon { icon: "circle-filled" - pointSize: Style.fontSizeS + pointSize: Style.fontSizeS color: Color.mPrimary opacity: 1.0 } @@ -893,8 +928,8 @@ Loader { } Rectangle { - width: 2 - height: 20 + width: 2 + height: 20 color: Color.mPrimary visible: passwordInput.activeFocus && passwordInput.text.length > 0 anchors.verticalCenter: parent.verticalCenter @@ -917,10 +952,10 @@ Loader { Rectangle { anchors.right: parent.right - anchors.rightMargin: 8 + anchors.rightMargin: 8 anchors.verticalCenter: parent.verticalCenter - width: 36 - height: 36 + width: 36 + height: 36 radius: width * 0.5 color: submitButtonArea.containsMouse ? Color.mPrimary : Qt.alpha(Color.mPrimary, 0.8) border.color: Color.mPrimary @@ -930,7 +965,7 @@ Loader { NIcon { anchors.centerIn: parent icon: "arrow-forward" - pointSize: Style.fontSizeM + pointSize: Style.fontSizeM color: Color.mOnPrimary } @@ -951,40 +986,42 @@ Loader { } Item { - Layout.preferredWidth: Style.marginM + Layout.preferredWidth: Style.marginM } } // System control buttons RowLayout { Layout.fillWidth: true - Layout.preferredHeight: 48 - spacing: 10 + Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48 + spacing: 10 Item { - Layout.preferredWidth: Style.marginM + Layout.preferredWidth: Style.marginM } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 24 + Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48 + radius: Settings.data.general.compactLockScreen ? 18 : 24 color: logoutButtonArea.containsMouse ? Color.mTertiary : "transparent" + border.color: Color.mOutline + border.width: 1 RowLayout { anchors.centerIn: parent - spacing: 6 + spacing: 6 NIcon { icon: "logout" - pointSize: Style.fontSizeL + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL color: logoutButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant } NText { text: I18n.tr("session-menu.logout") color: logoutButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM font.weight: Font.Medium } } @@ -1002,28 +1039,85 @@ Loader { easing.type: Easing.OutCubic } } + + Behavior on border.color { + ColorAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 24 - color: rebootButtonArea.containsMouse ? Color.mTertiary : "transparent" + Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48 + radius: Settings.data.general.compactLockScreen ? 18 : 24 + color: suspendButtonArea.containsMouse ? Color.mTertiary : "transparent" + border.color: Color.mOutline + border.width: 1 RowLayout { anchors.centerIn: parent - spacing: 6 + spacing: 6 + + NIcon { + icon: "suspend" + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL + color: suspendButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant + } + + NText { + text: I18n.tr("session-menu.suspend") + color: suspendButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM + font.weight: Font.Medium + } + } + + MouseArea { + id: suspendButtonArea + anchors.fill: parent + hoverEnabled: true + onClicked: CompositorService.suspend() + } + + Behavior on color { + ColorAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + + Behavior on border.color { + ColorAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48 + radius: Settings.data.general.compactLockScreen ? 18 : 24 + color: rebootButtonArea.containsMouse ? Color.mTertiary : "transparent" + border.color: Color.mOutline + border.width: 1 + + RowLayout { + anchors.centerIn: parent + spacing: 6 NIcon { icon: "reboot" - pointSize: Style.fontSizeL + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL color: rebootButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant } NText { text: I18n.tr("session-menu.reboot") color: rebootButtonArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM font.weight: Font.Medium } } @@ -1041,30 +1135,37 @@ Loader { easing.type: Easing.OutCubic } } + + Behavior on border.color { + ColorAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 24 + Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48 + radius: Settings.data.general.compactLockScreen ? 18 : 24 color: shutdownButtonArea.containsMouse ? Color.mError : "transparent" - border.color: shutdownButtonArea.containsMouse ? Color.mError : Color.transparent + border.color: shutdownButtonArea.containsMouse ? Color.mError : Color.mOutline border.width: 1 RowLayout { anchors.centerIn: parent - spacing: 6 + spacing: 6 NIcon { icon: "shutdown" - pointSize: Style.fontSizeL + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL color: shutdownButtonArea.containsMouse ? Color.mOnError : Color.mOnSurfaceVariant } NText { text: I18n.tr("session-menu.shutdown") color: shutdownButtonArea.containsMouse ? Color.mOnError : Color.mOnSurfaceVariant - pointSize: Style.fontSizeM + pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM font.weight: Font.Medium } } @@ -1092,7 +1193,7 @@ Loader { } Item { - Layout.preferredWidth: Style.marginM + Layout.preferredWidth: Style.marginM } } } @@ -1102,4 +1203,4 @@ Loader { } } } -} +} \ No newline at end of file diff --git a/Modules/OSD/OSD.qml b/Modules/OSD/OSD.qml index bcb3bba9..ceca7d00 100644 --- a/Modules/OSD/OSD.qml +++ b/Modules/OSD/OSD.qml @@ -129,17 +129,9 @@ Variants { readonly property int hHeight: Math.round(64) readonly property int vHeight: Math.round(320) // Vertical OSD height (matches horizontal width) // Ensure an even width to keep the vertical bar perfectly centered - readonly property int barThickness: (function () { + readonly property int barThickness: { const base = Math.max(8, Math.round(8)) return (base % 2 === 0) ? base : base + 1 - })() - - Component.onCompleted: { - - } - - Component.onDestruction: { - } // Anchor selection based on location (window edges) @@ -218,8 +210,10 @@ Variants { opacity: 0 scale: 0.85 - anchors.horizontalCenter: verticalMode ? undefined : parent.horizontalCenter - anchors.verticalCenter: verticalMode ? parent.verticalCenter : undefined + // Only horizontally center when the window itself is centered (top/bottom positions) + // For left/right vertical mode, fill the parent width + anchors.horizontalCenter: (!panel.verticalMode && panel.isCentered) ? parent.horizontalCenter : undefined + anchors.verticalCenter: panel.verticalMode ? parent.verticalCenter : undefined Behavior on opacity { NumberAnimation { @@ -336,85 +330,90 @@ Variants { Component { id: verticalContent - ColumnLayout { - // Ensure inner padding respects the rounded corners; avoid clipping the icon/text - property int vMargin: (function () { - const styleMargin = Math.round(Style.marginL) - const cornerGuard = Math.round(osdItem.radius) - return Math.max(styleMargin, cornerGuard) - })() - property int vMarginTop: Math.max(Math.round(osdItem.radius), Math.round(Style.marginS)) - property int balanceDelta: Math.round(Style.marginS) + Item { anchors.fill: parent - anchors.topMargin: vMargin - anchors.leftMargin: vMargin - anchors.rightMargin: vMargin - anchors.bottomMargin: vMargin - spacing: Math.round(Style.marginS) - // Percentage text at top - Item { - Layout.fillWidth: true - Layout.preferredHeight: percentText.implicitHeight - NText { - id: percentText - text: root.getDisplayPercentage() - color: Color.mOnSurface - pointSize: Style.fontSizeS - family: Settings.data.ui.fontFixed - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + ColumnLayout { + // Ensure inner padding respects the rounded corners; avoid clipping the icon/text + property int vMargin: { + const styleMargin = Style.marginL + const cornerGuard = Math.round(osdItem.radius) + return Math.max(styleMargin, cornerGuard) } - } + property int vMarginTop: Math.max(Math.round(osdItem.radius), Style.marginS) + property int balanceDelta: Style.marginS + anchors.fill: parent + anchors.topMargin: vMargin + anchors.leftMargin: vMargin + anchors.rightMargin: vMargin + anchors.bottomMargin: vMargin + spacing: Style.marginS - // Progress bar - Item { - Layout.fillWidth: true - Layout.fillHeight: true // Fill remaining space between text and icon - Rectangle { - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.bottom: parent.bottom - width: panel.barThickness - radius: Math.round(panel.barThickness / 2) - color: Color.mSurfaceVariant + // Percentage text at top + Item { + Layout.fillWidth: true + Layout.preferredHeight: 20 + NText { + id: percentText + text: root.getDisplayPercentage() + color: Color.mOnSurface + pointSize: Style.fontSizeS + family: Settings.data.ui.fontFixed + width: 50 + height: parent.height + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + // Progress bar + Item { + Layout.fillWidth: true + Layout.fillHeight: true // Fill remaining space between text and icon Rectangle { - anchors.left: parent.left - anchors.right: parent.right + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top anchors.bottom: parent.bottom - height: parent.height * Math.min(1.0, root.getCurrentValue()) - radius: parent.radius - color: root.getProgressColor() + width: panel.barThickness + radius: Math.round(panel.barThickness / 2) + color: Color.mSurfaceVariant - Behavior on height { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: parent.height * Math.min(1.0, root.getCurrentValue()) + radius: parent.radius + color: root.getProgressColor() + + Behavior on height { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.InOutQuad + } } - } - Behavior on color { - ColorAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad + Behavior on color { + ColorAnimation { + duration: Style.animationNormal + easing.type: Easing.InOutQuad + } } } } } - } - // Icon at bottom - NIcon { - icon: root.getIcon() - color: root.getIconColor() - pointSize: Style.fontSizeXL - Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - Behavior on color { - ColorAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad + // Icon at bottom + NIcon { + icon: root.getIcon() + color: root.getIconColor() + pointSize: Style.fontSizeXL + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + Behavior on color { + ColorAnimation { + duration: Style.animationNormal + easing.type: Easing.InOutQuad + } } } } diff --git a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml b/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml index d5321f2c..acd6e785 100644 --- a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml @@ -18,6 +18,7 @@ ColumnLayout { property bool valueAutoHide: widgetData.autoHide !== undefined ? widgetData.autoHide : widgetMetadata.autoHide property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode property int valueWidth: widgetData.width !== undefined ? widgetData.width : widgetMetadata.width + property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : widgetMetadata.colorizeIcons function saveSettings() { var settings = Object.assign({}, widgetData || {}) @@ -25,6 +26,7 @@ ColumnLayout { settings.showIcon = valueShowIcon settings.scrollingMode = valueScrollingMode settings.width = parseInt(widthInput.text) || widgetMetadata.width + settings.colorizeIcons = valueColorizeIcons return settings } @@ -44,6 +46,14 @@ ColumnLayout { onToggled: checked => root.valueShowIcon = checked } + NToggle { + Layout.fillWidth: true + label: I18n.tr("bar.widget-settings.active-window.colorize-icons.label") + description: I18n.tr("bar.widget-settings.active-window.colorize-icons.description") + checked: root.valueColorizeIcons + onToggled: checked => root.valueColorizeIcons = checked + } + NTextInput { id: widthInput Layout.fillWidth: true diff --git a/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml b/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml index 8f1bdfc1..e98c0eaa 100644 --- a/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml @@ -16,11 +16,13 @@ ColumnLayout { // Local state property bool valueOnlyActiveWorkspaces: widgetData.onlyActiveWorkspaces !== undefined ? widgetData.onlyActiveWorkspaces : widgetMetadata.onlyActiveWorkspaces property bool valueOnlySameOutput: widgetData.onlySameOutput !== undefined ? widgetData.onlySameOutput : widgetMetadata.onlySameOutput + property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : widgetMetadata.colorizeIcons function saveSettings() { var settings = Object.assign({}, widgetData || {}) settings.onlySameOutput = valueOnlySameOutput settings.onlyActiveWorkspaces = valueOnlyActiveWorkspaces + settings.colorizeIcons = valueColorizeIcons return settings } @@ -39,4 +41,12 @@ ColumnLayout { checked: root.valueOnlyActiveWorkspaces onToggled: checked => root.valueOnlyActiveWorkspaces = checked } + + NToggle { + Layout.fillWidth: true + label: I18n.tr("bar.widget-settings.taskbar.colorize-icons.label") + description: I18n.tr("bar.widget-settings.taskbar.colorize-icons.description") + checked: root.valueColorizeIcons + onToggled: checked => root.valueColorizeIcons = checked + } } diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml index ca0c42d2..a9a41243 100644 --- a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml @@ -5,12 +5,14 @@ import qs.Commons import qs.Widgets ColumnLayout { + id: root // Properties to receive data from parent property var widgetData: ({}) // Expected by BarWidgetSettingsDialog property var widgetMetadata: ({}) // Expected by BarWidgetSettingsDialog - // Local state for the blacklist + // Local state property var localBlacklist: widgetData.blacklist || [] + property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : widgetMetadata.colorizeIcons ListModel { id: blacklistModel @@ -27,6 +29,14 @@ ColumnLayout { spacing: Style.marginM + NToggle { + Layout.fillWidth: true + label: I18n.tr("bar.widget-settings.tray.colorize-icons.label") + description: I18n.tr("bar.widget-settings.tray.colorize-icons.description") + checked: root.valueColorizeIcons + onToggled: checked => root.valueColorizeIcons = checked + } + ColumnLayout { Layout.fillWidth: true spacing: Style.marginS @@ -135,6 +145,7 @@ ColumnLayout { // Return the updated settings for this widget instance var settings = Object.assign({}, widgetData || {}) settings.blacklist = newBlacklist + settings.colorizeIcons = root.valueColorizeIcons return settings } } diff --git a/Modules/Settings/Tabs/DockTab.qml b/Modules/Settings/Tabs/DockTab.qml index 79c0c8be..ce40bcbb 100644 --- a/Modules/Settings/Tabs/DockTab.qml +++ b/Modules/Settings/Tabs/DockTab.qml @@ -93,6 +93,20 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL } + NToggle { + Layout.fillWidth: true + label: I18n.tr("settings.dock.appearance.colorize-icons.label") + description: I18n.tr("settings.dock.appearance.colorize-icons.description") + checked: Settings.data.dock.colorizeIcons + onToggled: checked => Settings.data.dock.colorizeIcons = checked + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL + Layout.bottomMargin: Style.marginXL + } + // Monitor Configuration ColumnLayout { spacing: Style.marginM diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index 0001f5ec..212829b0 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -21,9 +21,9 @@ ColumnLayout { // Avatar preview NImageCircled { - width: 108 - height: 108 - imagePath: Settings.data.general.avatarImage + width: 108 * Style.uiScaleRatio + height: 108 * Style.uiScaleRatio + imagePath: Settings.preprocessPath(Settings.data.general.avatarImage) fallbackIcon: "person" borderColor: Color.mPrimary borderWidth: Math.max(1, Style.borderM) @@ -50,7 +50,7 @@ ColumnLayout { id: avatarPicker title: I18n.tr("settings.general.profile.select-avatar") selectionMode: "files" - initialPath: Settings.data.general.avatarImage.substr(0, Settings.data.general.avatarImage.lastIndexOf("/")) || Quickshell.env("HOME") + initialPath: Settings.preprocessPath(Settings.data.general.avatarImage).substr(0, Settings.preprocessPath(Settings.data.general.avatarImage).lastIndexOf("/")) || Quickshell.env("HOME") nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] onAccepted: paths => { if (paths.length > 0) { diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index efb85537..f2809e14 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -43,7 +43,8 @@ Singleton { "showIcon": true, "autoHide": false, "scrollingMode": "hover", - "width": 145 + "width": 145, + "colorizeIcons": false }, "Battery": { "allowUserSettings": true, @@ -114,11 +115,13 @@ Singleton { "Taskbar": { "allowUserSettings": true, "onlySameOutput": true, - "onlyActiveWorkspaces": true + "onlyActiveWorkspaces": true, + "colorizeIcons": false }, "Tray": { "allowUserSettings": true, - "blacklist": [] + "blacklist": [], + "colorizeIcons": false }, "Workspace": { "allowUserSettings": true, diff --git a/Services/ScreenRecorderService.qml b/Services/ScreenRecorderService.qml index 7e57ce75..57fc578a 100644 --- a/Services/ScreenRecorderService.qml +++ b/Services/ScreenRecorderService.qml @@ -49,7 +49,7 @@ Singleton { function launchRecorder() { var filename = Time.getFormattedTimestamp() + ".mp4" - var videoDir = settings.directory + var videoDir = Settings.preprocessPath(settings.directory) if (videoDir && !videoDir.endsWith("/")) { videoDir += "/" } diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index c60f2eba..2046f6cc 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -10,6 +10,7 @@ Singleton { id: root readonly property ListModel fillModeModel: ListModel {} + readonly property string defaultDirectory: Settings.preprocessPath(Settings.data.wallpaper.directory) // All available wallpaper transitions readonly property ListModel transitionsModel: ListModel {} @@ -44,7 +45,7 @@ Singleton { if (!Settings.data.wallpaper.enableMultiMonitorDirectories) { // All monitors use the main directory for (var i = 0; i < Quickshell.screens.length; i++) { - root.wallpaperDirectoryChanged(Quickshell.screens[i].name, Settings.data.wallpaper.directory) + root.wallpaperDirectoryChanged(Quickshell.screens[i].name, root.defaultDirectory) } } else { // Only monitors without custom directories are affected @@ -52,7 +53,7 @@ Singleton { var screenName = Quickshell.screens[i].name var monitor = root.getMonitorConfig(screenName) if (!monitor || !monitor.directory) { - root.wallpaperDirectoryChanged(screenName, Settings.data.wallpaper.directory) + root.wallpaperDirectoryChanged(screenName, root.defaultDirectory) } } } @@ -177,16 +178,16 @@ Singleton { // Get specific monitor directory function getMonitorDirectory(screenName) { if (!Settings.data.wallpaper.enableMultiMonitorDirectories) { - return Settings.data.wallpaper.directory + return root.defaultDirectory } var monitor = getMonitorConfig(screenName) if (monitor !== undefined && monitor.directory !== undefined) { - return monitor.directory + return Settings.preprocessPath(monitor.directory) } // Fall back to the main/single directory - return Settings.data.wallpaper.directory + return root.defaultDirectory } // ------------------------------------------------------------------- @@ -218,7 +219,7 @@ Singleton { // Update Settings with new array to ensure proper persistence Settings.data.wallpaper.monitors = newMonitors.slice() - root.wallpaperDirectoryChanged(screenName, directory) + root.wallpaperDirectoryChanged(screenName, Settings.preprocessPath(directory)) } // ------------------------------------------------------------------- @@ -274,7 +275,7 @@ Singleton { found = true return { "name": screenName, - "directory": monitor.directory || getMonitorDirectory(screenName), + "directory": Settings.preprocessPath(monitor.directory) || getMonitorDirectory(screenName), "wallpaper": path } } diff --git a/Shaders/frag/appicon_colorize.frag b/Shaders/frag/appicon_colorize.frag new file mode 100644 index 00000000..ae108701 --- /dev/null +++ b/Shaders/frag/appicon_colorize.frag @@ -0,0 +1,29 @@ +#version 450 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(binding = 1) uniform sampler2D source; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec4 targetColor; + float colorizeMode; // 0.0 = dock mode (grayscale), 1.0 = tray mode (intensity) +} ubuf; + +void main() { + vec4 tex = texture(source, qt_TexCoord0); + + float intensity; + + if (ubuf.colorizeMode < 0.5) { + // Dock mode: Convert to grayscale using proper luminance weights + intensity = dot(tex.rgb, vec3(0.299, 0.587, 0.114)); + } else { + // Tray mode: Use the maximum RGB channel value as intensity + intensity = max(max(tex.r, tex.g), tex.b); + + // Normalize intensity to make all icons more uniform + intensity = smoothstep(0.1, 0.9, intensity); + } + + fragColor = vec4(ubuf.targetColor.rgb * intensity, tex.a) * ubuf.qt_Opacity; +} diff --git a/Shaders/qsb/appicon_colorize.frag.qsb b/Shaders/qsb/appicon_colorize.frag.qsb new file mode 100644 index 00000000..15618b86 Binary files /dev/null and b/Shaders/qsb/appicon_colorize.frag.qsb differ