From 492f2dd9cc7d00ba89f0d2d26fa395772e2eae13 Mon Sep 17 00:00:00 2001 From: ferreo Date: Sat, 12 Jul 2025 13:33:48 +0100 Subject: [PATCH] feat: add toggle for showing active window icon in title bar --- Bar/Modules/ActiveWindow.qml | 149 +++++++++++++-------- Settings/Settings.qml | 18 +-- Widgets/Sidebar/Config/ProfileSettings.qml | 98 +++++++++----- Widgets/Sidebar/Config/SettingsModal.qml | 71 ++++++---- 4 files changed, 214 insertions(+), 122 deletions(-) diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index e3fe43f..9216d07 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -2,67 +2,108 @@ import QtQuick import Quickshell import qs.Components import qs.Settings +import Quickshell.Wayland Item { - id: activeWindowWrapper - width: parent.width - property int fullHeight: activeWindowTitleContainer.height + id: activeWindowWrapper + width: parent.width + property int fullHeight: activeWindowTitleContainer.height - y: panel.activeWindowVisible ? barBackground.height : barBackground.height - fullHeight - height: panel.activeWindowVisible ? fullHeight : 1 - opacity: panel.activeWindowVisible ? 1 : 0 - clip: true + y: panel.activeWindowVisible ? barBackground.height : barBackground.height - fullHeight + height: panel.activeWindowVisible ? fullHeight : 1 + opacity: panel.activeWindowVisible ? 1 : 0 + clip: true - Behavior on height { NumberAnimation { duration: 300; easing.type: Easing.OutQuad } } - Behavior on y { NumberAnimation { duration: 300; easing.type: Easing.OutQuad } } - Behavior on opacity { NumberAnimation { duration: 250 } } + function getIcon() { + var icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId.toLowerCase(), true); + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId, true); + } + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title, true); + } + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title.toLowerCase(), "application-x-executable"); + } - Rectangle { - id: activeWindowTitleContainer - color: Theme.backgroundPrimary - bottomLeftRadius: Math.max(0, width / 2) - bottomRightRadius: Math.max(0, width / 2) + return icon; + } - width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + 24) - height: activeWindowTitle.implicitHeight + 12 + Behavior on height { + NumberAnimation { + duration: 300 + easing.type: Easing.OutQuad + } + } + Behavior on y { + NumberAnimation { + duration: 300 + easing.type: Easing.OutQuad + } + } + Behavior on opacity { + NumberAnimation { + duration: 250 + } + } - anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + id: activeWindowTitleContainer + color: Theme.backgroundPrimary + bottomLeftRadius: Math.max(0, width / 2) + bottomRightRadius: Math.max(0, width / 2) - Text { - id: activeWindowTitle - text: panel.displayedWindowTitle && panel.displayedWindowTitle.length > 60 - ? panel.displayedWindowTitle.substring(0, 60) + "..." - : panel.displayedWindowTitle - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption - color: Theme.textSecondary - elide: Text.ElideRight - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.fill: parent - anchors.margins: 6 - maximumLineCount: 1 - } - } + width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.showActiveWindowIcon ? 28 : 22)) + height: activeWindowTitle.implicitHeight + 12 - Corners { - id: activeCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - offsetX: activeWindowTitleContainer.x + activeWindowTitleContainer.width - 33 - offsetY: 0 - anchors.top: activeWindowTitleContainer.top - } + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: icon + width: 10 + height: 10 + anchors.left: parent.left + anchors.leftMargin: 6 + anchors.verticalCenter: parent.verticalCenter + source: ToplevelManager.activeToplevel ? getIcon() : "" + visible: Settings.showActiveWindowIcon + } + + Text { + id: activeWindowTitle + text: panel.displayedWindowTitle && panel.displayedWindowTitle.length > 60 ? panel.displayedWindowTitle.substring(0, 60) + "..." : panel.displayedWindowTitle + font.pixelSize: 12 + color: Theme.textSecondary + elide: Text.ElideRight + anchors.right: parent.right + anchors.rightMargin: 6 + horizontalAlignment: Settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + + maximumLineCount: 1 + } + } + + Corners { + id: activeCornerRight + position: "bottomleft" + size: 1.1 + fillColor: Theme.backgroundPrimary + offsetX: activeWindowTitleContainer.x + activeWindowTitleContainer.width - 33 + offsetY: 0 + anchors.top: activeWindowTitleContainer.top + } + + Corners { + id: activeCornerLeft + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: activeWindowTitleContainer.top + x: activeWindowTitleContainer.x + 33 - width + offsetY: 0 + } +} - Corners { - id: activeCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: activeWindowTitleContainer.top - x: activeWindowTitleContainer.x + 33 - width - offsetY: 0 - } - } \ No newline at end of file diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 20feec5..e9aa18c 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -9,7 +9,7 @@ QtObject { property string wallpaperFolder: "/home/lysec/nixos/assets/wallpapers" // Default path, make persistent property string currentWallpaper: "" property string videoPath: "~/Videos/" // Default path, make persistent - + property bool showActiveWindowIcon // Settings persistence property var settings: Qt.createQmlObject('import QtCore; Settings { category: "Quickshell" }', this, "settings") @@ -18,15 +18,14 @@ QtObject { } function loadSettings() { - let wc = settings.value("weatherCity", "Dinslaken"); - weatherCity = (wc !== undefined && wc !== null) ? wc : "Dinslaken"; - let pi = settings.value("profileImage", "https://cdn.discordapp.com/avatars/158005126638993408/de403f05fd7f74bb17e01a9b066a30fa?size=64"); - profileImage = (pi !== undefined && pi !== null) ? pi : "https://cdn.discordapp.com/avatars/158005126638993408/de403f05fd7f74bb17e01a9b066a30fa?size=64"; + weatherCity = settings.value("weatherCity", weatherCity) + profileImage = settings.value("profileImage", profileImage) let tempUnit = settings.value("weatherTempUnit", "celsius") useFahrenheit = (tempUnit === "fahrenheit") - wallpaperFolder = settings.value("wallpaperFolder", "/home/lysec/nixos/assets/wallpapers") - currentWallpaper = settings.value("currentWallpaper", "") - videoPath = settings.value("videoPath", "/home/lysec/Videos") + wallpaperFolder = settings.value("wallpaperFolder", wallpaperFolder) + currentWallpaper = settings.value("currentWallpaper", currentWallpaper) + videoPath = settings.value("videoPath", videoPath) + showActiveWindowIcon = settings.value("showActiveWindowIcon", showActiveWindowIcon) console.log("Loaded profileImage:", profileImage) } @@ -37,6 +36,7 @@ QtObject { settings.setValue("wallpaperFolder", wallpaperFolder) settings.setValue("currentWallpaper", currentWallpaper) settings.setValue("videoPath", videoPath) + settings.setValue("showActiveWindowIcon", showActiveWindowIcon) settings.sync() console.log("Saving profileImage:", profileImage) } @@ -45,4 +45,4 @@ QtObject { // onWeatherCityChanged: saveSettings() // onProfileImageChanged: saveSettings() // onUseFahrenheitChanged: saveSettings() -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 024545b..c47b226 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -7,12 +7,14 @@ import qs.Settings Rectangle { id: profileSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 140 + Layout.preferredHeight: 200 color: Theme.surface radius: 18 border.color: "transparent" border.width: 0 Layout.bottomMargin: 16 + property bool showActiveWindowIcon: false + signal showAWIconChanged(bool showActiveWindowIcon) ColumnLayout { anchors.fill: parent @@ -27,14 +29,13 @@ Rectangle { Text { text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: 20 color: Theme.accentPrimary } Text { text: "Profile Image" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody + font.pixelSize: 16 font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -83,7 +84,7 @@ Rectangle { anchors.centerIn: parent text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: 18 color: Theme.accentPrimary visible: Settings.profileImage === "" } @@ -92,7 +93,7 @@ Rectangle { // Text input styled exactly like weather city Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 40 + Layout.preferredHeight: 36 radius: 8 color: Theme.surfaceVariant border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline @@ -100,17 +101,10 @@ Rectangle { TextInput { id: profileImageInput - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 + anchors.fill: parent + anchors.margins: 12 text: Settings.profileImage - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -132,6 +126,58 @@ Rectangle { } } + + // Show Active Window Icon Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Show Active Window Icon" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: customSwitch + width: 52 + height: 32 + radius: 16 + color: Theme.accentPrimary + border.color: Theme.accentPrimary + border.width: 2 + + Rectangle { + id: thumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: showActiveWindowIcon ? customSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + showAWIconChanged(!showActiveWindowIcon) + } + } + } + } + // Video Path Input Row RowLayout { spacing: 8 @@ -139,15 +185,14 @@ Rectangle { Text { text: "Video Path" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: 14 color: Theme.textPrimary Layout.alignment: Qt.AlignVCenter } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 40 + Layout.preferredHeight: 36 radius: 8 color: Theme.surfaceVariant border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline @@ -155,17 +200,10 @@ Rectangle { TextInput { id: videoPathInput - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 + anchors.fill: parent + anchors.margins: 12 text: Settings.videoPath !== undefined ? Settings.videoPath : "" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -184,4 +222,4 @@ Rectangle { } } } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index ddc1523..085e175 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -20,12 +20,12 @@ PanelWindow { //border.width: 1 WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None - // Local properties for editing (not saved until apply) property string tempWeatherCity: (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : "" property bool tempUseFahrenheit: Settings.useFahrenheit property string tempProfileImage: (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : "" property string tempWallpaperFolder: (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : "" + property bool tempShowActiveWindowIcon: Settings.showActiveWindowIcon Rectangle { anchors.fill: parent @@ -55,7 +55,6 @@ PanelWindow { } Text { text: "Settings" - font.family: Theme.fontFamily font.pixelSize: 26 font.bold: true color: Theme.textPrimary @@ -117,21 +116,32 @@ PanelWindow { WeatherSettings { weatherCity: (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : "" useFahrenheit: tempUseFahrenheit - onCityChanged: function(city) { tempWeatherCity = city } - onTemperatureUnitChanged: function(useFahrenheit) { tempUseFahrenheit = useFahrenheit } + onCityChanged: function (city) { + tempWeatherCity = city; + } + onTemperatureUnitChanged: function (useFahrenheit) { + tempUseFahrenheit = useFahrenheit; + } } } CollapsibleCategory { title: "System" expanded: false - ProfileSettings { } + ProfileSettings { + showActiveWindowIcon: tempShowActiveWindowIcon + onShowAWIconChanged: function (showActiveWindowIcon) { + tempShowActiveWindowIcon = showActiveWindowIcon; + } + } } CollapsibleCategory { title: "Wallpaper" expanded: false WallpaperSettings { wallpaperFolder: (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : "" - onWallpaperFolderEdited: function(folder) { tempWallpaperFolder = folder } + onWallpaperFolderEdited: function (folder) { + tempWallpaperFolder = folder; + } } } } @@ -150,7 +160,6 @@ PanelWindow { Text { anchors.centerIn: parent text: "Apply Changes" - font.family: Theme.fontFamily font.pixelSize: 17 font.bold: true color: applyButtonArea.containsMouse ? Theme.onAccent : Theme.onAccent @@ -160,15 +169,16 @@ PanelWindow { anchors.fill: parent hoverEnabled: true onClicked: { - Settings.weatherCity = (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : "" - Settings.useFahrenheit = tempUseFahrenheit - Settings.profileImage = (typeof tempProfileImage !== 'undefined' && tempProfileImage !== null) ? tempProfileImage : "" - Settings.wallpaperFolder = (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : "" - Settings.saveSettings() + Settings.weatherCity = (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : ""; + Settings.useFahrenheit = tempUseFahrenheit; + Settings.profileImage = (typeof tempProfileImage !== 'undefined' && tempProfileImage !== null) ? tempProfileImage : ""; + Settings.wallpaperFolder = (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : ""; + Settings.showActiveWindowIcon = tempShowActiveWindowIcon; + Settings.saveSettings(); if (typeof weather !== 'undefined' && weather) { - weather.fetchCityWeather() + weather.fetchCityWeather(); } - settingsModal.closeSettings() + settingsModal.closeSettings(); } } } @@ -177,19 +187,21 @@ PanelWindow { // Function to open the modal and initialize temp values function openSettings() { - tempWeatherCity = (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : "" - tempUseFahrenheit = Settings.useFahrenheit - tempProfileImage = (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : "" - tempWallpaperFolder = (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : "" - if (tempWallpaperFolder === undefined || tempWallpaperFolder === null) tempWallpaperFolder = "" - visible = true + tempWeatherCity = (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : ""; + tempUseFahrenheit = Settings.useFahrenheit; + tempShowActiveWindowIcon = Settings.showActiveWindowIcon; + tempProfileImage = (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : ""; + tempWallpaperFolder = (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : ""; + if (tempWallpaperFolder === undefined || tempWallpaperFolder === null) + tempWallpaperFolder = ""; + visible = true; // Force focus on the text input after a short delay - focusTimer.start() + focusTimer.start(); } // Function to close the modal and release focus function closeSettings() { - visible = false + visible = false; } Timer { @@ -197,16 +209,17 @@ PanelWindow { interval: 100 repeat: false onTriggered: { - if (visible) { - // Focus will be handled by the individual components - } + if (visible) + // Focus will be handled by the individual components + {} } } // Release focus when modal becomes invisible onVisibleChanged: { - if (!visible) { - // Focus will be handled by the individual components - } + if (!visible) + // Focus will be handled by the individual components + {} } -} \ No newline at end of file +} +