From 9013464312f4e459e7b814e7b416e3de68f158be Mon Sep 17 00:00:00 2001 From: purian23 Date: Thu, 6 Aug 2026 20:52:14 -0400 Subject: [PATCH] feat(theme/settings): dedicated floating window styling with shared chrome - Refactor Widget Styling into Surface Styling & Floating Windows - New sync per-host opacity & foreground layer control - Shared DankFloatingWindow chrome - Settings fix to destroy Settings window on-hide so niri floating rules match subsequent runs Related: #2709 --- dank-qml-common | 2 +- quickshell/Common/SettingsData.qml | 6 + quickshell/Common/Theme.qml | 35 +- quickshell/Common/settings/SettingsSpec.js | 6 + quickshell/DMSShell.qml | 3 +- quickshell/Modals/AppPickerModal.qml | 4 - .../Modals/Changelog/ChangelogFeatureCard.qml | 2 +- .../Modals/Changelog/ChangelogModal.qml | 6 +- .../Modals/DankLauncherV2/LauncherContent.qml | 19 +- .../Modals/Greeter/GreeterCompletePage.qml | 4 +- .../Modals/Greeter/GreeterDoctorPage.qml | 2 +- .../Modals/Greeter/GreeterFeatureCard.qml | 2 +- quickshell/Modals/Greeter/GreeterModal.qml | 8 +- .../Modals/Greeter/GreeterQuickLink.qml | 2 +- .../Modals/Greeter/GreeterSettingsCard.qml | 2 +- .../Modals/Greeter/GreeterStatusCard.qml | 2 +- quickshell/Modals/KeybindsModalWindow.qml | 10 +- quickshell/Modals/MuxModal.qml | 4 - quickshell/Modals/PolkitAuthContent.qml | 9 +- quickshell/Modals/PolkitAuthModal.qml | 5 +- quickshell/Modals/ProcessListModal.qml | 6 +- quickshell/Modals/Settings/SettingsModal.qml | 13 +- .../Modals/Settings/SettingsSidebar.qml | 5 +- quickshell/Modals/WindowRuleModal.qml | 4 +- quickshell/Modals/WorkspaceRenameModal.qml | 4 +- .../Modules/Notepad/NotepadPopoutWindow.qml | 11 +- .../Modules/Notepad/NotepadSettings.qml | 4 +- .../Modules/Notepad/NotepadTextEditor.qml | 4 +- .../Modules/Settings/ColorDropdownRow.qml | 4 +- .../Modules/Settings/DesktopWidgetBrowser.qml | 8 +- .../Modules/Settings/NotificationsTab.qml | 12 +- quickshell/Modules/Settings/PluginBrowser.qml | 18 +- .../Modules/Settings/RunningAppsTab.qml | 2 - quickshell/Modules/Settings/ThemeBrowser.qml | 12 +- .../Modules/Settings/ThemeColorsTab.qml | 535 ++++++++++++------ .../Modules/Settings/WidgetSelectionPopup.qml | 34 +- .../Settings/Widgets/AppBrowserPopup.qml | 26 +- .../Settings/Widgets/DeviceAliasRow.qml | 2 - .../Modules/Settings/Widgets/SettingsCard.qml | 4 +- .../Settings/Widgets/SettingsDropdownRow.qml | 1 - .../Settings/Widgets/SettingsSliderCard.qml | 5 +- .../Settings/Widgets/SettingsSliderRow.qml | 1 - .../Settings/Widgets/SettingsToggleCard.qml | 4 +- .../Widgets/SystemMonitorVariantCard.qml | 2 +- quickshell/Services/CompositorService.qml | 43 ++ quickshell/Services/NiriService.qml | 8 +- quickshell/Services/PopoutService.qml | 9 +- quickshell/Widgets/DankFloatingWindow.qml | 49 ++ .../translations/extract_settings_index.py | 4 +- .../translations/settings_search_index.json | 263 +++++++-- 50 files changed, 810 insertions(+), 420 deletions(-) create mode 100644 quickshell/Widgets/DankFloatingWindow.qml diff --git a/dank-qml-common b/dank-qml-common index 3b06bd937..3513b348b 160000 --- a/dank-qml-common +++ b/dank-qml-common @@ -1 +1 @@ -Subproject commit 3b06bd9372e18bc8086cc3958d4f677d57fbfdd8 +Subproject commit 3513b348ba425eab27d35b27ce924a0e324dc359 diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 9960614b8..7635516ad 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -164,6 +164,11 @@ Singleton { property string matugenTargetMonitor: "" property real popupTransparency: 1.0 property real dockTransparency: 1 + property bool floatingWindowSyncGlobal: true + property real floatingWindowTransparency: 1.0 + property bool floatingWindowForegroundLayers: true + property real floatingWindowForegroundTransparency: 1.0 + property bool dmsWindowsFloating: true property string widgetBackgroundColor: "sch" property string widgetBackgroundCustomColor: "#6750A4" property real widgetBackgroundCustomStrength: 0.50 @@ -264,6 +269,7 @@ Singleton { onBlurEnabledChanged: saveSettings() property bool blurForegroundLayers: true onBlurForegroundLayersChanged: saveSettings() + property real foregroundLayerTransparency: 1.0 property real blurLayerOutlineOpacity: 0.12 onBlurLayerOutlineOpacityChanged: saveSettings() property bool blurBorderEnabled: true diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index 83dbfa57c..85565f8a9 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -345,11 +345,28 @@ Singleton { readonly property bool foregroundLayers: typeof SettingsData === "undefined" || (SettingsData.blurForegroundLayers ?? true) readonly property bool blurForegroundLayers: blurLayersActive && foregroundLayers readonly property bool transparentBlurLayers: blurLayersActive && !foregroundLayers + readonly property real foregroundLayerTransparency: typeof SettingsData === "undefined" ? 1.0 : (SettingsData.foregroundLayerTransparency ?? 1.0) readonly property bool notificationForegroundLayers: typeof SettingsData === "undefined" || (SettingsData.notificationForegroundLayers ?? true) readonly property color readableSurface: withAlpha(surfaceContainer, popupTransparency) readonly property color readableSurfaceHigh: withAlpha(surfaceContainerHigh, popupTransparency) readonly property color floatingSurface: foregroundLayers ? readableSurface : withAlpha(readableSurface, 0) - readonly property color floatingSurfaceHigh: foregroundLayers ? readableSurfaceHigh : withAlpha(readableSurfaceHigh, 0) + readonly property color floatingSurfaceHigh: foregroundLayers ? withAlpha(surfaceContainerHigh, foregroundLayerTransparency) : withAlpha(surfaceContainerHigh, 0) + readonly property bool floatingWindowSynced: typeof SettingsData === "undefined" || (SettingsData.floatingWindowSyncGlobal ?? true) + readonly property real floatingWindowTransparency: { + if (typeof SettingsData === "undefined" || floatingWindowSynced) + return popupTransparency; + return SettingsData.floatingWindowTransparency ?? 1.0; + } + readonly property bool floatingWindowForegroundLayers: floatingWindowSynced ? foregroundLayers : (SettingsData.floatingWindowForegroundLayers ?? true) + readonly property real floatingWindowForegroundTransparency: { + if (typeof SettingsData === "undefined" || floatingWindowSynced) + return foregroundLayerTransparency; + return SettingsData.floatingWindowForegroundTransparency ?? 1.0; + } + readonly property color floatingWindowSurface: withAlpha(surfaceContainer, floatingWindowTransparency) + readonly property color floatingWindowSurfaceHigh: floatingWindowForegroundLayers ? withAlpha(surfaceContainerHigh, floatingWindowForegroundTransparency) : withAlpha(surfaceContainerHigh, 0) + readonly property color floatingWindowNestedSurface: floatingWindowSurfaceHigh + readonly property color notepadWindowSurface: withAlpha(surfaceContainer, notepadTransparency) readonly property color nestedSurface: floatingSurfaceHigh readonly property color notificationFloatingSurface: notificationForegroundLayers ? readableSurface : withAlpha(readableSurface, 0) readonly property color notificationFloatingSurfaceHigh: notificationForegroundLayers ? readableSurfaceHigh : withAlpha(readableSurfaceHigh, 0) @@ -357,6 +374,20 @@ Singleton { readonly property real blurLayerOutlineOpacity: Math.max(0, Math.min(1, typeof SettingsData === "undefined" ? 0.12 : (SettingsData.blurLayerOutlineOpacity ?? 0.12))) readonly property real layerOutlineOpacity: blurLayerOutlineOpacity readonly property int layerOutlineWidth: layerOutlineOpacity > 0 ? 1 : 0 + readonly property real floatingWindowFieldAlpha: floatingWindowTransparency + readonly property color floatingWindowFieldColor: withAlpha(surfaceContainerHigh, floatingWindowFieldAlpha) + readonly property real popupFieldAlpha: { + if (transparentBlurLayers) + return 0.28; + if (blurForegroundLayers) + return Math.max(foregroundLayerTransparency, 0.62); + return popupTransparency; + } + readonly property color popupFieldColor: withAlpha(surfaceContainerHigh, popupFieldAlpha) + readonly property color popupFieldBorderColor: withAlpha(outline, blurLayersActive ? 0.16 : layerOutlineOpacity) + readonly property color popupFieldFocusedBorderColor: withAlpha(primary, blurLayersActive ? 0.72 : 1.0) + readonly property color floatingWindowFieldBorderColor: popupFieldBorderColor + readonly property color floatingWindowFieldFocusedBorderColor: popupFieldFocusedBorderColor property color surfaceTextHover: withAlpha(surfaceText, 0.08) property color surfaceTextAlpha: withAlpha(surfaceText, 0.3) @@ -1194,7 +1225,7 @@ Singleton { readonly property var _availableThemeNames: StockThemes.getAllThemeNames() property string currentThemeName: currentTheme - property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : popupTransparency + property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : floatingWindowTransparency property bool widgetBackgroundHasAlpha: { const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"; diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index 5003a2da0..b19a24e84 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -17,6 +17,11 @@ var SPEC = { popupTransparency: { def: 1.0, coerce: percentToUnit }, dockTransparency: { def: 1.0, coerce: percentToUnit }, + floatingWindowSyncGlobal: { def: true }, + floatingWindowTransparency: { def: 1.0, coerce: percentToUnit }, + floatingWindowForegroundLayers: { def: true }, + floatingWindowForegroundTransparency: { def: 1.0, coerce: percentToUnit }, + dmsWindowsFloating: { def: true }, widgetBackgroundColor: { def: "sch" }, widgetBackgroundCustomColor: { def: "#6750A4" }, @@ -97,6 +102,7 @@ var SPEC = { barElevationEnabled: { def: true }, blurEnabled: { def: false }, blurForegroundLayers: { def: true }, + foregroundLayerTransparency: { def: 1.0, coerce: percentToUnit }, blurLayerOutlineOpacity: { def: 0.12, coerce: percentToUnit }, blurBorderEnabled: { def: true }, blurBorderColor: { def: "outline" }, diff --git a/quickshell/DMSShell.qml b/quickshell/DMSShell.qml index 577dbf2f6..4bf0a0f21 100644 --- a/quickshell/DMSShell.qml +++ b/quickshell/DMSShell.qml @@ -635,7 +635,7 @@ Item { if (visible) { wasShown = true; } else if (wasShown) { - PopoutService.unloadSettings(); + Qt.callLater(() => PopoutService.unloadSettingsNow()); } } } @@ -940,6 +940,7 @@ Item { expandedWidthValue: 960 edgeGap: SettingsData.notepadEffectiveEdgeGap slideEdge: SettingsData.notepadSlideoutSide + customTransparency: Theme.notepadTransparency onIsVisibleChanged: { if (isVisible) diff --git a/quickshell/Modals/AppPickerModal.qml b/quickshell/Modals/AppPickerModal.qml index e5eb5f216..7a49b8f3a 100644 --- a/quickshell/Modals/AppPickerModal.qml +++ b/quickshell/Modals/AppPickerModal.qml @@ -318,10 +318,6 @@ DankModal { width: parent.width - Theme.spacingS * 2 anchors.horizontalCenter: parent.horizontalCenter height: 52 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modals/Changelog/ChangelogFeatureCard.qml b/quickshell/Modals/Changelog/ChangelogFeatureCard.qml index 4f13d01e9..941dc1e24 100644 --- a/quickshell/Modals/Changelog/ChangelogFeatureCard.qml +++ b/quickshell/Modals/Changelog/ChangelogFeatureCard.qml @@ -15,7 +15,7 @@ Rectangle { height: Math.round(Theme.fontSizeMedium * 4.2) radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.fill: parent diff --git a/quickshell/Modals/Changelog/ChangelogModal.qml b/quickshell/Modals/Changelog/ChangelogModal.qml index 8f89d04a9..fa9d9a7c3 100644 --- a/quickshell/Modals/Changelog/ChangelogModal.qml +++ b/quickshell/Modals/Changelog/ChangelogModal.qml @@ -4,10 +4,9 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true readonly property int modalWidth: 680 readonly property int modalHeight: screen ? Math.min(720, screen.height - 80) : 720 @@ -21,7 +20,6 @@ FloatingWindow { title: i18n("What's New") minimumSize: Qt.size(modalWidth, modalHeight) maximumSize: Qt.size(modalWidth, modalHeight) - color: Theme.surfaceContainer visible: false onClosed: visible = false @@ -112,7 +110,7 @@ FloatingWindow { anchors.right: parent.right anchors.bottom: parent.bottom height: Math.round(Theme.fontSizeMedium * 4.5) - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.top: parent.top diff --git a/quickshell/Modals/DankLauncherV2/LauncherContent.qml b/quickshell/Modals/DankLauncherV2/LauncherContent.qml index 02b0caff6..beed9e71c 100644 --- a/quickshell/Modals/DankLauncherV2/LauncherContent.qml +++ b/quickshell/Modals/DankLauncherV2/LauncherContent.qml @@ -23,17 +23,6 @@ FocusScope { property bool editMode: false property var editingApp: null property string editAppId: "" - readonly property bool _blurActive: Theme.blurForegroundLayers || Theme.transparentBlurLayers - readonly property real _launcherFieldAlpha: { - if (Theme.transparentBlurLayers) - return 0.28; - if (Theme.blurForegroundLayers) - return Math.max(Theme.popupTransparency, 0.62); - return Theme.popupTransparency; - } - readonly property color _launcherSearchFieldColor: Theme.withAlpha(Theme.surfaceContainerHigh, _launcherFieldAlpha) - readonly property color _launcherSearchBorderColor: Theme.withAlpha(Theme.outline, _blurActive ? 0.16 : Theme.layerOutlineOpacity) - readonly property color _launcherSearchFocusedBorderColor: Theme.withAlpha(Theme.primary, _blurActive ? 0.72 : 1.0) function resetScroll() { resultsList.resetScroll(); @@ -314,7 +303,7 @@ FocusScope { anchors.fill: parent anchors.topMargin: -Theme.cornerRadius // In connected mode the launcher provides the surface so update the toolbar for arcs - visible: !(root.parentModal?.frameOwnsConnectedChrome ?? false) && !root._blurActive + visible: !(root.parentModal?.frameOwnsConnectedChrome ?? false) && !Theme.blurLayersActive color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) radius: Theme.cornerRadius } @@ -472,12 +461,6 @@ FocusScope { DankTextField { id: searchField width: parent.width - (pluginBadge.visible ? pluginBadge.width + Theme.spacingS : 0) - cornerRadius: Theme.cornerRadius - backgroundColor: root._launcherSearchFieldColor - normalBorderColor: root._launcherSearchBorderColor - focusedBorderColor: root._launcherSearchFocusedBorderColor - borderWidth: 1 - focusedBorderWidth: 2 leftIconName: controller.activePluginId ? "extension" : controller.searchQuery.startsWith("/") ? "folder" : "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modals/Greeter/GreeterCompletePage.qml b/quickshell/Modals/Greeter/GreeterCompletePage.qml index 33877c8bb..a6e0e141c 100644 --- a/quickshell/Modals/Greeter/GreeterCompletePage.qml +++ b/quickshell/Modals/Greeter/GreeterCompletePage.qml @@ -146,7 +146,7 @@ Item { width: parent.width height: keybindsGrid.height + Theme.spacingM * 2 radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface readonly property bool useTwoColumns: width > 500 readonly property int columnCount: useTwoColumns ? 2 : 1 @@ -240,7 +240,7 @@ Item { width: parent.width height: noKeybindsColumn.height + Theme.spacingM * 2 radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface visible: !root.hasKeybinds Column { diff --git a/quickshell/Modals/Greeter/GreeterDoctorPage.qml b/quickshell/Modals/Greeter/GreeterDoctorPage.qml index b93821f95..c95eb7fc3 100644 --- a/quickshell/Modals/Greeter/GreeterDoctorPage.qml +++ b/quickshell/Modals/Greeter/GreeterDoctorPage.qml @@ -291,7 +291,7 @@ Item { anchors.leftMargin: Theme.spacingXL anchors.rightMargin: Theme.spacingXL radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface clip: true Column { diff --git a/quickshell/Modals/Greeter/GreeterFeatureCard.qml b/quickshell/Modals/Greeter/GreeterFeatureCard.qml index 5a1ba94b8..74516474b 100644 --- a/quickshell/Modals/Greeter/GreeterFeatureCard.qml +++ b/quickshell/Modals/Greeter/GreeterFeatureCard.qml @@ -15,7 +15,7 @@ Rectangle { height: Math.round(Theme.fontSizeMedium * 6.4) radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.fill: parent diff --git a/quickshell/Modals/Greeter/GreeterModal.qml b/quickshell/Modals/Greeter/GreeterModal.qml index b2469c5e8..c959457f0 100644 --- a/quickshell/Modals/Greeter/GreeterModal.qml +++ b/quickshell/Modals/Greeter/GreeterModal.qml @@ -5,11 +5,10 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root readonly property var log: Log.scoped("GreeterModal") - property bool disablePopupTransparency: true property int currentPage: 0 readonly property int totalPages: 3 readonly property var pageComponents: [welcomePage, doctorPage, completePage] @@ -90,7 +89,6 @@ FloatingWindow { title: I18n.tr("Welcome", "greeter modal window title") minimumSize: Qt.size(modalWidth, modalHeight) maximumSize: Qt.size(modalWidth, modalHeight) - color: Theme.surfaceContainer visible: false onClosed: visible = false @@ -173,7 +171,7 @@ FloatingWindow { width: pageIndicatorRow.width + Theme.spacingM * 2 height: indicatorHeight radius: indicatorHeight / 2 - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Row { id: pageIndicatorRow @@ -259,7 +257,7 @@ FloatingWindow { anchors.right: parent.right anchors.bottom: parent.bottom height: Math.round(Theme.fontSizeMedium * 4.5) - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.top: parent.top diff --git a/quickshell/Modals/Greeter/GreeterQuickLink.qml b/quickshell/Modals/Greeter/GreeterQuickLink.qml index d064275f6..96128d027 100644 --- a/quickshell/Modals/Greeter/GreeterQuickLink.qml +++ b/quickshell/Modals/Greeter/GreeterQuickLink.qml @@ -13,7 +13,7 @@ Rectangle { height: Math.round(Theme.fontSizeMedium * 3.1) radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.fill: parent diff --git a/quickshell/Modals/Greeter/GreeterSettingsCard.qml b/quickshell/Modals/Greeter/GreeterSettingsCard.qml index 804f4ca0f..d8c7ca3fb 100644 --- a/quickshell/Modals/Greeter/GreeterSettingsCard.qml +++ b/quickshell/Modals/Greeter/GreeterSettingsCard.qml @@ -15,7 +15,7 @@ Rectangle { height: Math.round(Theme.fontSizeMedium * 4.5) radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface Rectangle { anchors.fill: parent diff --git a/quickshell/Modals/Greeter/GreeterStatusCard.qml b/quickshell/Modals/Greeter/GreeterStatusCard.qml index 32d4df1b8..458f96e65 100644 --- a/quickshell/Modals/Greeter/GreeterStatusCard.qml +++ b/quickshell/Modals/Greeter/GreeterStatusCard.qml @@ -9,7 +9,7 @@ Rectangle { property string label: "" property string iconName: "" property color iconColor: Theme.surfaceText - property color bgColor: Theme.surfaceContainerHigh + property color bgColor: Theme.floatingWindowNestedSurface property bool selected: false signal clicked diff --git a/quickshell/Modals/KeybindsModalWindow.qml b/quickshell/Modals/KeybindsModalWindow.qml index b980e74af..57cd8c2ef 100644 --- a/quickshell/Modals/KeybindsModalWindow.qml +++ b/quickshell/Modals/KeybindsModalWindow.qml @@ -5,10 +5,9 @@ import qs.Modals import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: win - property bool disablePopupTransparency: true property alias shouldBeVisible: win.visible signal floatingToggleRequested @@ -30,7 +29,6 @@ FloatingWindow { minimumSize: Qt.size(Math.min(560, Screen.width), Math.min(400, Screen.height)) implicitWidth: 1000 implicitHeight: screen ? Math.min(820, screen.height - 100) : 820 - color: Theme.surfaceContainer visible: false onVisibleChanged: { @@ -61,12 +59,6 @@ FloatingWindow { onDoubleClicked: windowControls.tryToggleMaximize() } - Rectangle { - anchors.fill: parent - color: Theme.surfaceContainer - opacity: 0.5 - } - Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingL diff --git a/quickshell/Modals/MuxModal.qml b/quickshell/Modals/MuxModal.qml index 2059c82fa..f0a33c386 100644 --- a/quickshell/Modals/MuxModal.qml +++ b/quickshell/Modals/MuxModal.qml @@ -313,10 +313,6 @@ DankModal { width: parent.width height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.surfaceContainerHigh - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modals/PolkitAuthContent.qml b/quickshell/Modals/PolkitAuthContent.qml index f1aa87d4a..a2e2495f1 100644 --- a/quickshell/Modals/PolkitAuthContent.qml +++ b/quickshell/Modals/PolkitAuthContent.qml @@ -202,7 +202,7 @@ FocusScope { StyledText { text: root.currentFlow?.message ?? "" font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceTextMedium + color: Theme.surfaceText width: parent.width wrapMode: Text.Wrap maximumLineCount: 2 @@ -272,11 +272,6 @@ FocusScope { width: parent.width height: root.inputFieldHeight - backgroundColor: Theme.surfaceHover - normalBorderColor: Theme.outlineStrong - focusedBorderColor: Theme.primary - borderWidth: 1 - focusedBorderWidth: 2 leftIconName: root.polkitPamHasFprint ? "fingerprint" : "" leftIconSize: 20 leftIconColor: Theme.primary @@ -352,7 +347,7 @@ FocusScope { anchors.centerIn: parent text: I18n.tr("Authenticate") font.pixelSize: Theme.fontSizeMedium - color: Theme.background + color: Theme.primaryText font.weight: Font.Medium } diff --git a/quickshell/Modals/PolkitAuthModal.qml b/quickshell/Modals/PolkitAuthModal.qml index bd9ab52d9..3993bb4a0 100644 --- a/quickshell/Modals/PolkitAuthModal.qml +++ b/quickshell/Modals/PolkitAuthModal.qml @@ -4,11 +4,9 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true - function show() { if (contentLoader.item) contentLoader.item.reset(); @@ -29,7 +27,6 @@ FloatingWindow { title: I18n.tr("Authentication") minimumSize: Qt.size(460, 220) maximumSize: Qt.size(460, 220) - color: Theme.surfaceContainer visible: false onClosed: hide() diff --git a/quickshell/Modals/ProcessListModal.qml b/quickshell/Modals/ProcessListModal.qml index 08f75f677..1fc3b1041 100644 --- a/quickshell/Modals/ProcessListModal.qml +++ b/quickshell/Modals/ProcessListModal.qml @@ -8,11 +8,10 @@ import qs.Services import qs.Widgets import "../Common/Format.js" as Format -FloatingWindow { +DankFloatingWindow { id: processListModal readonly property var log: Log.scoped("ProcessListModal") - property bool disablePopupTransparency: true property int currentTab: 0 property string searchText: "" property string expandedPid: "" @@ -81,7 +80,6 @@ FloatingWindow { minimumSize: Qt.size(Math.min(Math.round(Theme.fontSizeMedium * 48), Screen.width), Math.min(Math.round(Theme.fontSizeMedium * 34), Screen.height)) implicitWidth: Math.round(Theme.fontSizeMedium * 71) implicitHeight: Math.round(Theme.fontSizeMedium * 51) - color: Theme.surfaceContainer visible: false onClosed: hide() @@ -420,7 +418,7 @@ FloatingWindow { Layout.margins: Theme.spacingL Layout.topMargin: Theme.spacingM radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) + color: Theme.floatingWindowNestedSurface border.color: Theme.outlineLight border.width: 1 clip: true diff --git a/quickshell/Modals/Settings/SettingsModal.qml b/quickshell/Modals/Settings/SettingsModal.qml index b5df81574..f1449e17d 100644 --- a/quickshell/Modals/Settings/SettingsModal.qml +++ b/quickshell/Modals/Settings/SettingsModal.qml @@ -5,7 +5,7 @@ import qs.Modals.FileBrowser import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: settingsModal property var profileBrowser: profileBrowserLoader.item @@ -95,7 +95,6 @@ FloatingWindow { minimumSize: Qt.size(500, 400) implicitWidth: 900 implicitHeight: screen ? Math.min(940, screen.height - 100) : 940 - color: Theme.surfaceContainer visible: false onClosed: hide() @@ -180,8 +179,6 @@ FloatingWindow { FocusScope { id: contentFocusScope - property bool disablePopupTransparency: true - LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true @@ -203,12 +200,6 @@ FloatingWindow { onDoubleClicked: windowControls.tryToggleMaximize() } - Rectangle { - anchors.fill: parent - color: Theme.surfaceContainer - opacity: 0.5 - } - Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingL @@ -276,7 +267,7 @@ FloatingWindow { width: parent.width height: showBanner ? bannerContent.implicitHeight + Theme.spacingM * 2 : 0 - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface visible: showBanner clip: true diff --git a/quickshell/Modals/Settings/SettingsSidebar.qml b/quickshell/Modals/Settings/SettingsSidebar.qml index 6d0feb1ca..02607520e 100644 --- a/quickshell/Modals/Settings/SettingsSidebar.qml +++ b/quickshell/Modals/Settings/SettingsSidebar.qml @@ -648,7 +648,7 @@ Rectangle { implicitWidth: __calculatedWidth width: __calculatedWidth height: parent.height - color: Theme.surfaceContainer + color: "transparent" radius: Theme.cornerRadius Component.onCompleted: { @@ -749,12 +749,9 @@ Rectangle { id: searchField width: parent.width - parent.leftPadding - parent.rightPadding placeholderText: I18n.tr("Search...") - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize - 4 showClearButton: text.length > 0 - usePopupTransparency: false onTextChanged: { SettingsSearchService.search(text); root.searchSelectedIndex = 0; diff --git a/quickshell/Modals/WindowRuleModal.qml b/quickshell/Modals/WindowRuleModal.qml index 75716d89c..54a5e6f0a 100644 --- a/quickshell/Modals/WindowRuleModal.qml +++ b/quickshell/Modals/WindowRuleModal.qml @@ -4,10 +4,9 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property var editingRule: null property bool isEditMode: editingRule !== null property bool isNiri: CompositorService.isNiri @@ -29,7 +28,6 @@ FloatingWindow { title: isEditMode ? I18n.tr("Edit Window Rule") : I18n.tr("Create Window Rule") minimumSize: Qt.size(500, 600) maximumSize: Qt.size(500, 600) - color: Theme.surfaceContainer visible: false onClosed: hide() diff --git a/quickshell/Modals/WorkspaceRenameModal.qml b/quickshell/Modals/WorkspaceRenameModal.qml index cb530aba2..1941558f3 100644 --- a/quickshell/Modals/WorkspaceRenameModal.qml +++ b/quickshell/Modals/WorkspaceRenameModal.qml @@ -4,18 +4,16 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root readonly property var log: Log.scoped("WorkspaceRenameModal") - property bool disablePopupTransparency: true readonly property int inputFieldHeight: Theme.fontSizeMedium + Theme.spacingL * 2 objectName: "workspaceRenameModal" title: I18n.tr("Rename Workspace") minimumSize: Qt.size(400, 160) maximumSize: Qt.size(400, 160) - color: Theme.surfaceContainer visible: false onClosed: hide() diff --git a/quickshell/Modules/Notepad/NotepadPopoutWindow.qml b/quickshell/Modules/Notepad/NotepadPopoutWindow.qml index e53b23f78..b565e221b 100644 --- a/quickshell/Modules/Notepad/NotepadPopoutWindow.qml +++ b/quickshell/Modules/Notepad/NotepadPopoutWindow.qml @@ -5,7 +5,7 @@ import qs.Services import qs.Widgets import qs.Modules.Notepad -FloatingWindow { +DankFloatingWindow { id: win property alias shouldBeVisible: win.visible @@ -27,7 +27,7 @@ FloatingWindow { minimumSize: Qt.size(360, 320) implicitWidth: 640 implicitHeight: 760 - color: Theme.surfaceContainer + surfaceColor: Theme.notepadWindowSurface visible: false onVisibleChanged: { @@ -38,7 +38,6 @@ FloatingWindow { } } - // A compositor close (e.g. niri close-window) onClosed: win.visible = false Item { @@ -58,12 +57,6 @@ FloatingWindow { onDoubleClicked: windowControls.tryToggleMaximize() } - Rectangle { - anchors.fill: parent - color: Theme.surfaceContainerHigh - opacity: 0.5 - } - Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingM diff --git a/quickshell/Modules/Notepad/NotepadSettings.qml b/quickshell/Modules/Notepad/NotepadSettings.qml index c9e5ef4b4..1dad62753 100644 --- a/quickshell/Modules/Notepad/NotepadSettings.qml +++ b/quickshell/Modules/Notepad/NotepadSettings.qml @@ -353,7 +353,7 @@ Item { anchors.leftMargin: -Theme.spacingM width: parent.width + Theme.spacingM text: I18n.tr("Surface Opacity") - description: I18n.tr("Override global transparency for Notepad") + description: I18n.tr("Override floating window transparency for Notepad") checked: SettingsData.notepadTransparencyOverride >= 0 onToggled: checked => { if (checked) { @@ -370,7 +370,7 @@ Item { width: parent.width + Theme.spacingM height: 24 visible: SettingsData.notepadTransparencyOverride >= 0 - value: Math.round((SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : SettingsData.popupTransparency) * 100) + value: Math.round(Theme.notepadTransparency * 100) minimum: 0 maximum: 100 unit: "" diff --git a/quickshell/Modules/Notepad/NotepadTextEditor.qml b/quickshell/Modules/Notepad/NotepadTextEditor.qml index 03baeff9a..c8121ee88 100644 --- a/quickshell/Modules/Notepad/NotepadTextEditor.qml +++ b/quickshell/Modules/Notepad/NotepadTextEditor.qml @@ -364,7 +364,7 @@ Column { height: 48 visible: searchVisible opacity: searchVisible ? 1 : 0 - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) + color: Theme.floatingWindowNestedSurface border.color: searchField.activeFocus ? Theme.primary : Theme.outlineMedium border.width: searchField.activeFocus ? 2 : 1 radius: Theme.cornerRadius @@ -941,7 +941,7 @@ Column { width: Math.min(root.width, 360) height: pathInfoRow.implicitHeight + Theme.spacingS * 2 radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) + color: Theme.floatingWindowNestedSurface border.color: Theme.outlineMedium border.width: 1 z: 10 diff --git a/quickshell/Modules/Settings/ColorDropdownRow.qml b/quickshell/Modules/Settings/ColorDropdownRow.qml index 78f8c85fe..6dfbf810b 100644 --- a/quickshell/Modules/Settings/ColorDropdownRow.qml +++ b/quickshell/Modules/Settings/ColorDropdownRow.qml @@ -118,7 +118,9 @@ Column { width: parent.width height: 56 radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowFieldColor + border.color: Theme.floatingWindowFieldBorderColor + border.width: 1 Row { anchors.fill: parent diff --git a/quickshell/Modules/Settings/DesktopWidgetBrowser.qml b/quickshell/Modules/Settings/DesktopWidgetBrowser.qml index 7f0be93a0..be8d1ffa6 100644 --- a/quickshell/Modules/Settings/DesktopWidgetBrowser.qml +++ b/quickshell/Modules/Settings/DesktopWidgetBrowser.qml @@ -6,10 +6,9 @@ import qs.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property string searchQuery: "" property var filteredWidgets: [] property int selectedIndex: -1 @@ -107,7 +106,6 @@ FloatingWindow { minimumSize: Qt.size(400, 350) implicitWidth: 500 implicitHeight: 550 - color: Theme.surfaceContainer visible: false onClosed: hide() @@ -276,10 +274,6 @@ FloatingWindow { id: searchField width: parent.width height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.surfaceContainerHigh - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modules/Settings/NotificationsTab.qml b/quickshell/Modules/Settings/NotificationsTab.qml index 46f544fdd..d0853460e 100644 --- a/quickshell/Modules/Settings/NotificationsTab.qml +++ b/quickshell/Modules/Settings/NotificationsTab.qml @@ -465,7 +465,6 @@ Item { iconName: "restart_alt" iconSize: 20 visible: JSON.stringify(SettingsData.notificationRules) !== JSON.stringify(SettingsData.getDefaultNotificationRules()) - backgroundColor: Theme.surfaceContainer iconColor: Theme.surfaceVariantText onClicked: SettingsData.resetNotificationRules() }, @@ -473,7 +472,6 @@ Item { buttonSize: 36 iconName: "add" iconSize: 20 - backgroundColor: Theme.surfaceContainer iconColor: Theme.primary onClicked: { SettingsData.addNotificationRule(); @@ -723,7 +721,7 @@ Item { } Item { - width: Math.max(0, parent.width - parent.spacing - mutedAppLabel.width - unmuteBtn.width - deleteBtn.width - Theme.spacingS * 5) + width: Math.max(0, parent.width - parent.spacing - mutedAppLabel.width - unmuteBtn.width - mutedDeleteBtn.width - Theme.spacingS * 5) height: 1 } @@ -736,7 +734,7 @@ Item { } Item { - id: deleteBtn + id: mutedDeleteBtn width: 28 height: 28 anchors.verticalCenter: parent.verticalCenter @@ -744,18 +742,18 @@ Item { Rectangle { anchors.fill: parent radius: Theme.cornerRadius - color: deleteArea.containsMouse ? Theme.withAlpha(Theme.error, 0.2) : Theme.withAlpha(Theme.error, 0) + color: mutedDeleteArea.containsMouse ? Theme.withAlpha(Theme.error, 0.2) : Theme.withAlpha(Theme.error, 0) } DankIcon { anchors.centerIn: parent name: "delete" size: 18 - color: deleteArea.containsMouse ? Theme.error : Theme.surfaceVariantText + color: mutedDeleteArea.containsMouse ? Theme.error : Theme.surfaceVariantText } MouseArea { - id: deleteArea + id: mutedDeleteArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor diff --git a/quickshell/Modules/Settings/PluginBrowser.qml b/quickshell/Modules/Settings/PluginBrowser.qml index eb4014abd..a5bbdbdb3 100644 --- a/quickshell/Modules/Settings/PluginBrowser.qml +++ b/quickshell/Modules/Settings/PluginBrowser.qml @@ -7,10 +7,9 @@ import qs.Modals.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property var allPlugins: [] property string searchQuery: "" property var filteredPlugins: [] @@ -608,7 +607,6 @@ FloatingWindow { return Math.round(Math.min(maxHeight, Math.max(540, parentModal.height * 0.8))); return Math.min(maxHeight, 760); } - color: Theme.surfaceContainer visible: false onClosed: hide() @@ -838,10 +836,6 @@ FloatingWindow { anchors.top: headerArea.bottom anchors.topMargin: Theme.spacingM height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.surfaceContainerHigh - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText @@ -1102,7 +1096,7 @@ FloatingWindow { ClippingRectangle { anchors.fill: parent radius: Theme.cornerRadius - 2 - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface CachingImage { id: cardPreview @@ -1362,7 +1356,7 @@ FloatingWindow { anchors.topMargin: Theme.spacingM anchors.bottom: parent.bottom z: 10 - color: Theme.surfaceContainer + color: Theme.floatingWindowSurface opacity: root.detailPluginId !== "" ? 1 : 0 visible: opacity > 0 @@ -1555,7 +1549,7 @@ FloatingWindow { width: parent.width height: Math.round(width * 0.52) radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface border.color: Theme.withAlpha(Theme.outline, 0.2) border.width: 1 @@ -1829,10 +1823,9 @@ FloatingWindow { id: thirdPartyConfirmLoader active: false - FloatingWindow { + DankFloatingWindow { id: thirdPartyConfirmModal - property bool disablePopupTransparency: true parentWindow: root function show() { @@ -1847,7 +1840,6 @@ FloatingWindow { title: I18n.tr("Third-Party Plugin Warning") implicitWidth: 500 implicitHeight: 350 - color: Theme.surfaceContainer visible: false FocusScope { diff --git a/quickshell/Modules/Settings/RunningAppsTab.qml b/quickshell/Modules/Settings/RunningAppsTab.qml index 08d6245bd..900aa8f22 100644 --- a/quickshell/Modules/Settings/RunningAppsTab.qml +++ b/quickshell/Modules/Settings/RunningAppsTab.qml @@ -32,7 +32,6 @@ Item { iconName: "restart_alt" iconSize: 20 visible: JSON.stringify(SettingsData.appIdSubstitutions) !== JSON.stringify(SettingsData.getDefaultAppIdSubstitutions()) - backgroundColor: Theme.surfaceContainer iconColor: Theme.surfaceVariantText onClicked: SettingsData.resetAppIdSubstitutions() }, @@ -40,7 +39,6 @@ Item { buttonSize: 36 iconName: "add" iconSize: 20 - backgroundColor: Theme.surfaceContainer iconColor: Theme.primary onClicked: SettingsData.addAppIdSubstitution("", "", "exact") } diff --git a/quickshell/Modules/Settings/ThemeBrowser.qml b/quickshell/Modules/Settings/ThemeBrowser.qml index 1f0387691..0f3a885a1 100644 --- a/quickshell/Modules/Settings/ThemeBrowser.qml +++ b/quickshell/Modules/Settings/ThemeBrowser.qml @@ -6,10 +6,9 @@ import qs.Modals.Common import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property var allThemes: [] property string searchQuery: "" property var filteredThemes: [] @@ -148,7 +147,6 @@ FloatingWindow { minimumSize: Qt.size(550, 450) implicitWidth: 700 implicitHeight: 700 - color: Theme.surfaceContainer visible: false onVisibleChanged: { @@ -301,10 +299,6 @@ FloatingWindow { anchors.top: descriptionText.bottom anchors.topMargin: Theme.spacingM height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.surfaceContainerHigh - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText @@ -401,7 +395,7 @@ FloatingWindow { width: hasPreview ? 180 : 0 height: parent.height radius: Theme.cornerRadius - 2 - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface visible: hasPreview Image { @@ -596,7 +590,7 @@ FloatingWindow { closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent background: Rectangle { - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface radius: Theme.cornerRadius border.width: 1 border.color: Theme.outlineMedium diff --git a/quickshell/Modules/Settings/ThemeColorsTab.qml b/quickshell/Modules/Settings/ThemeColorsTab.qml index 0ec93b99e..359be9fa0 100644 --- a/quickshell/Modules/Settings/ThemeColorsTab.qml +++ b/quickshell/Modules/Settings/ThemeColorsTab.qml @@ -65,112 +65,173 @@ Item { "label": I18n.tr("Custom", "widget background color option") })] - property var cursorIncludeStatus: ({ - "exists": false, - "included": false, - "configFormat": "", - "readOnly": false - }) + property var cursorIncludeStatus: defaultIncludeStatus() readonly property bool cursorReadOnly: CompositorService.isHyprland && cursorIncludeStatus.readOnly === true property bool checkingCursorInclude: false property bool fixingCursorInclude: false - function getCursorConfigPaths() { - const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)); - switch (CompositorService.compositor) { - case "niri": - return { - "configFile": configDir + "/niri/config.kdl", - "cursorFile": configDir + "/niri/dms/cursor.kdl", - "grepPattern": 'include.*"dms/cursor.kdl"', - "includeLine": 'include "dms/cursor.kdl"' - }; - case "hyprland": - return { - "configFile": configDir + "/hypr/hyprland.lua", - "cursorFile": configDir + "/hypr/dms/cursor.lua", - "grepPattern": "dms.cursor", - "includeLine": "require(\"dms.cursor\")" - }; - case "mango": - return { - "configFile": configDir + "/mango/config.conf", - "cursorFile": configDir + "/mango/dms/cursor.conf", - "grepPattern": 'source.*dms/cursor.conf', - "includeLine": "source=./dms/cursor.conf" - }; - default: - return null; - } + property var windowRulesIncludeStatus: defaultIncludeStatus() + readonly property bool windowRulesReadOnly: CompositorService.isHyprland && windowRulesIncludeStatus.readOnly === true + property bool checkingWindowRulesInclude: false + property bool fixingWindowRulesInclude: false + + readonly property var includeConfigSpecs: ({ + "cursor": ({ + "niri": { + "configName": "config.kdl", + "fragmentName": "cursor.kdl", + "grepPattern": 'include.*"dms/cursor.kdl"', + "includeLine": 'include "dms/cursor.kdl"' + }, + "hyprland": { + "configName": "hyprland.lua", + "fragmentName": "cursor.lua", + "grepPattern": "dms.cursor", + "includeLine": "require(\"dms.cursor\")" + }, + "mango": { + "configName": "config.conf", + "fragmentName": "cursor.conf", + "grepPattern": "source.*dms/cursor.conf", + "includeLine": "source=./dms/cursor.conf" + } + }), + "windowrules": ({ + "niri": { + "configName": "config.kdl", + "fragmentName": "windowrules.kdl", + "grepPattern": 'include.*"dms/windowrules.kdl"', + "includeLine": 'include "dms/windowrules.kdl"' + }, + "hyprland": { + "configName": "hyprland.lua", + "fragmentName": "windowrules.lua", + "grepPattern": "dms.windowrules", + "includeLine": "require(\"dms.windowrules\")" + }, + "mango": { + "configName": "config.conf", + "fragmentName": "windowrules.conf", + "grepPattern": "dms/windowrules.conf", + "includeLine": "source=./dms/windowrules.conf" + } + }) + }) + + function defaultIncludeStatus() { + return { + "exists": false, + "included": false, + "configFormat": "", + "readOnly": false + }; } - function checkCursorIncludeStatus() { - const compositor = CompositorService.compositor; - if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango") { - cursorIncludeStatus = { - "exists": false, - "included": false, - "configFormat": "", - "readOnly": false - }; + function getIncludeConfigPaths(includeKind) { + const spec = includeConfigSpecs[includeKind]?.[CompositorService.compositor]; + if (!spec) + return null; + const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/" + CompositorService.compositor; + return { + "configFile": configDir + "/" + spec.configName, + "fragmentFile": configDir + "/dms/" + spec.fragmentName, + "grepPattern": spec.grepPattern, + "includeLine": spec.includeLine + }; + } + + function checkIncludeStatus(includeKind, procTag, onFinished) { + const spec = includeConfigSpecs[includeKind]?.[CompositorService.compositor]; + if (!spec) { + onFinished(defaultIncludeStatus()); return; } - - const filename = (compositor === "niri") ? "cursor.kdl" : ((compositor === "hyprland") ? "cursor.lua" : "cursor.conf"); + const compositor = CompositorService.compositor; const compositorArg = (compositor === "mango") ? "mangowc" : compositor; - - checkingCursorInclude = true; - Proc.runCommand("check-cursor-include", [Proc.dmsBin, "config", "resolve-include", compositorArg, filename], (output, exitCode) => { - checkingCursorInclude = false; + Proc.runCommand(procTag, [Proc.dmsBin, "config", "resolve-include", compositorArg, spec.fragmentName], (output, exitCode) => { if (exitCode !== 0) { - cursorIncludeStatus = { - "exists": false, - "included": false, - "configFormat": "", - "readOnly": false - }; + onFinished(defaultIncludeStatus()); return; } try { - cursorIncludeStatus = JSON.parse(output.trim()); + onFinished(JSON.parse(output.trim())); } catch (e) { - cursorIncludeStatus = { - "exists": false, - "included": false, - "configFormat": "", - "readOnly": false - }; + onFinished(defaultIncludeStatus()); } }); } - function fixCursorInclude() { - if (cursorReadOnly) { + function checkCursorIncludeStatus() { + checkingCursorInclude = true; + checkIncludeStatus("cursor", "check-cursor-include", status => { + checkingCursorInclude = false; + cursorIncludeStatus = status; + }); + } + + function checkWindowRulesIncludeStatus() { + checkingWindowRulesInclude = true; + checkIncludeStatus("windowrules", "check-windowrules-include-theme", status => { + checkingWindowRulesInclude = false; + windowRulesIncludeStatus = status; + }); + } + + function fixInclude(includeKind, procTag, readOnly, onFinished) { + if (readOnly) { ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before changing these settings."), "dms setup", "hyprland-migration"); + onFinished(false); return; } - const paths = getCursorConfigPaths(); - if (!paths) + const paths = getIncludeConfigPaths(includeKind); + if (!paths) { + onFinished(false); return; - fixingCursorInclude = true; + } const unixTime = Math.floor(Date.now() / 1000); - const backupFile = paths.configFile + ".backup" + unixTime; const script = ConfigIncludeResolve.buildRepairScript({ configFile: paths.configFile, - backupFile: backupFile, - fragmentFile: paths.cursorFile, + backupFile: paths.configFile + ".backup" + unixTime, + fragmentFile: paths.fragmentFile, grepPattern: paths.grepPattern, includeLine: paths.includeLine }); - Proc.runCommand("fix-cursor-include", ["sh", "-c", script], (output, exitCode) => { + Proc.runCommand(procTag, ["sh", "-c", script], (output, exitCode) => onFinished(exitCode === 0)); + } + + function fixCursorInclude() { + fixingCursorInclude = true; + fixInclude("cursor", "fix-cursor-include", cursorReadOnly, success => { fixingCursorInclude = false; - if (exitCode !== 0) + if (!success) return; checkCursorIncludeStatus(); SettingsData.updateCompositorCursor(); }); } + function fixWindowRulesInclude() { + fixingWindowRulesInclude = true; + fixInclude("windowrules", "fix-windowrules-include-theme", windowRulesReadOnly, success => { + fixingWindowRulesInclude = false; + if (!success) + return; + if (CompositorService.isMango) + MangoService.reloadConfig(); + checkWindowRulesIncludeStatus(); + CompositorService.applyDmsWindowFloatingRule(); + }); + } + + function unsyncFloatingWindowSettings() { + if (!SettingsData.floatingWindowSyncGlobal) + return; + SettingsData.set("floatingWindowTransparency", SettingsData.popupTransparency); + SettingsData.set("floatingWindowForegroundLayers", SettingsData.blurForegroundLayers ?? true); + SettingsData.set("floatingWindowForegroundTransparency", SettingsData.foregroundLayerTransparency ?? 1.0); + SettingsData.set("floatingWindowSyncGlobal", false); + } + function isTemplateDetected(templateId) { if (!templateDetection || templateDetection.length === 0) return true; @@ -259,8 +320,10 @@ Item { themeColorsTab.templateDetection = JSON.parse(output.trim()); } catch (e) {} }); - if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) + if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) { checkCursorIncludeStatus(); + checkWindowRulesIncludeStatus(); + } refreshMatugenSchemePreviews(); } @@ -296,6 +359,78 @@ Item { } } + component IncludeWarningBox: StyledRect { + id: includeWarningBox + + property bool checking: false + property bool fixing: false + property bool includeReadOnly: false + property bool alreadyIncluded: false + property bool visibleCondition: true + property string fragmentPath: "" + property var onSetup: function () {} + + readonly property bool showLegacy: includeReadOnly + readonly property bool showSetup: !showLegacy && !alreadyIncluded + + width: parent.width + height: includeWarningContent.implicitHeight + Theme.spacingL * 2 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.primary, 0.15) + border.color: Theme.withAlpha(Theme.primary, 0.3) + border.width: 1 + visible: visibleCondition && (showLegacy || showSetup) && !checking + + Row { + id: includeWarningContent + anchors.fill: parent + anchors.margins: Theme.spacingL + spacing: Theme.spacingM + + DankIcon { + name: "warning" + size: Theme.iconSize + color: Theme.primary + anchors.verticalCenter: parent.verticalCenter + } + + Column { + width: parent.width - Theme.iconSize - (includeFixButton.visible ? includeFixButton.width + Theme.spacingM : 0) - Theme.spacingM + spacing: Theme.spacingXS + anchors.verticalCenter: parent.verticalCenter + + StyledText { + text: includeWarningBox.showLegacy ? I18n.tr("Hyprland conf mode") : I18n.tr("First Time Setup") + font.pixelSize: Theme.fontSizeMedium + font.weight: Font.Medium + color: Theme.primary + width: parent.width + horizontalAlignment: Text.AlignLeft + } + + StyledText { + text: includeWarningBox.showLegacy ? I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before changing these settings.") : I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg(includeWarningBox.fragmentPath) + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + wrapMode: Text.WordWrap + width: parent.width + horizontalAlignment: Text.AlignLeft + } + } + + DankButton { + id: includeFixButton + visible: !includeWarningBox.showLegacy && includeWarningBox.showSetup + text: includeWarningBox.fixing ? I18n.tr("Setting up...") : I18n.tr("Setup") + backgroundColor: Theme.primary + textColor: Theme.primaryText + enabled: !includeWarningBox.fixing + anchors.verticalCenter: parent.verticalCenter + onClicked: includeWarningBox.onSetup() + } + } + } + DankFlickable { anchors.fill: parent clip: true @@ -1137,15 +1272,28 @@ Item { SettingsCard { tab: "theme" - tags: ["automatic", "color", "mode", "schedule", "sunrise", "sunset"] - title: I18n.tr("Automatic Color Mode") - settingKey: "automaticColorMode" - iconName: "schedule" + tags: ["light", "dark", "mode", "appearance", "automatic", "color", "schedule", "sunrise", "sunset"] + title: I18n.tr("Color Mode") + settingKey: "colorMode" + iconName: "contrast" Column { width: parent.width spacing: Theme.spacingM + SettingsToggleRow { + tab: "theme" + tags: ["light", "dark", "mode"] + settingKey: "isLightMode" + text: I18n.tr("Light Mode") + description: I18n.tr("Use light theme instead of dark theme") + checked: SessionData.isLightMode + onToggled: checked => { + Theme.screenTransition(); + Theme.setLightMode(checked); + } + } + DankToggle { id: themeModeAutoToggle width: parent.width @@ -1568,27 +1716,6 @@ Item { } } - SettingsCard { - tab: "theme" - tags: ["light", "dark", "mode", "appearance"] - title: I18n.tr("Color Mode") - settingKey: "colorMode" - iconName: "contrast" - - SettingsToggleRow { - tab: "theme" - tags: ["light", "dark", "mode"] - settingKey: "isLightMode" - text: I18n.tr("Light Mode") - description: I18n.tr("Use light theme instead of dark theme") - checked: SessionData.isLightMode - onToggled: checked => { - Theme.screenTransition(); - Theme.setLightMode(checked); - } - } - } - SettingsCard { tab: "theme" tags: ["transparency", "opacity", "widget", "styling"] @@ -1723,12 +1850,21 @@ Item { } } } + } + + SettingsCard { + tab: "theme" + tags: ["surface", "popup", "transparency", "opacity", "modal", "border", "outline", "corner", "radius"] + title: I18n.tr("Surface Styling") + settingKey: "surfaceStyling" + iconName: "layers" + SettingsSliderRow { tab: "theme" tags: ["surface", "popup", "transparency", "opacity", "modal"] settingKey: "popupTransparency" text: I18n.tr("Surface Opacity") - description: I18n.tr("Controls opacity of shell surfaces, popouts, and modals") + description: I18n.tr("Controls opacity of shell surfaces, popouts, and modals", "Surface Opacity setting description") visible: !themeColorsTab.connectedFrameModeActive value: Math.round(SettingsData.popupTransparency * 100) minimum: 0 @@ -1748,6 +1884,21 @@ Item { onToggled: checked => SettingsData.set("blurForegroundLayers", checked) } + SettingsSliderRow { + tab: "theme" + tags: ["foreground", "layers", "opacity", "transparency", "contrast", "cards"] + settingKey: "foregroundLayerTransparency" + text: I18n.tr("Foreground Opacity") + description: I18n.tr("Opacity of foreground cards and nested surfaces on shell panels") + visible: SettingsData.blurForegroundLayers ?? true + value: Math.round((SettingsData.foregroundLayerTransparency ?? 1.0) * 100) + minimum: 0 + maximum: 100 + unit: "%" + defaultValue: 100 + onSliderValueChanged: newValue => SettingsData.set("foregroundLayerTransparency", newValue / 100) + } + SettingsSliderRow { tab: "theme" tags: ["foreground", "layers", "outline", "border", "cards", "widgets", "notifications", "control center"] @@ -1854,6 +2005,113 @@ Item { } } + SettingsCard { + tab: "theme" + tags: ["floating", "window", "settings", "notepad", "authentication", "polkit", "opacity", "transparency", "foreground", "tile", "tiling"] + title: I18n.tr("Floating Windows") + settingKey: "floatingWindows" + iconName: "open_in_new" + + SettingsToggleRow { + tab: "theme" + tags: ["floating", "window", "sync", "global", "surface", "opacity"] + settingKey: "floatingWindowSyncGlobal" + text: I18n.tr("Sync with Global Settings") + description: I18n.tr("Floating windows follow Surface Styling settings") + checked: SettingsData.floatingWindowSyncGlobal ?? true + onToggled: checked => SettingsData.set("floatingWindowSyncGlobal", checked) + } + + SettingsSliderRow { + id: floatingWindowOpacitySlider + tab: "theme" + tags: ["floating", "window", "opacity", "transparency"] + settingKey: "floatingWindowTransparency" + text: I18n.tr("Window Opacity") + description: I18n.tr("Opacity of floating DMS windows like Settings, Notepad, and authentication prompts") + value: Math.round(Theme.floatingWindowTransparency * 100) + minimum: 0 + maximum: 100 + unit: "%" + defaultValue: 100 + onSliderValueChanged: newValue => { + themeColorsTab.unsyncFloatingWindowSettings(); + SettingsData.set("floatingWindowTransparency", newValue / 100); + } + + Binding { + target: floatingWindowOpacitySlider + property: "value" + value: Math.round(Theme.floatingWindowTransparency * 100) + restoreMode: Binding.RestoreBinding + } + } + + SettingsToggleRow { + tab: "theme" + tags: ["floating", "window", "foreground", "layers", "contrast", "cards", "blur", "glass"] + settingKey: "floatingWindowForegroundLayers" + text: I18n.tr("Foreground Layers") + description: I18n.tr("Show foreground surfaces on cards inside floating windows") + checked: Theme.floatingWindowForegroundLayers + onToggled: checked => { + themeColorsTab.unsyncFloatingWindowSettings(); + SettingsData.set("floatingWindowForegroundLayers", checked); + } + } + + SettingsSliderRow { + id: floatingWindowForegroundOpacitySlider + tab: "theme" + tags: ["floating", "window", "foreground", "layers", "opacity", "transparency", "cards"] + settingKey: "floatingWindowForegroundTransparency" + text: I18n.tr("Foreground Opacity") + description: I18n.tr("Opacity of cards and nested surfaces inside floating windows") + visible: Theme.floatingWindowForegroundLayers + value: Math.round(Theme.floatingWindowForegroundTransparency * 100) + minimum: 0 + maximum: 100 + unit: "%" + defaultValue: 100 + onSliderValueChanged: newValue => { + themeColorsTab.unsyncFloatingWindowSettings(); + SettingsData.set("floatingWindowForegroundTransparency", newValue / 100); + } + + Binding { + target: floatingWindowForegroundOpacitySlider + property: "value" + value: Math.round(Theme.floatingWindowForegroundTransparency * 100) + restoreMode: Binding.RestoreBinding + } + } + + SettingsToggleRow { + tab: "theme" + tags: ["floating", "window", "tile", "tiling", "compositor", "rule", "niri", "hyprland", "mango"] + settingKey: "dmsWindowsFloating" + text: I18n.tr("Open Windows Floating") + description: I18n.tr("Open DMS windows floating instead of tiled") + visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango + checked: SettingsData.dmsWindowsFloating ?? true + onToggled: checked => { + SettingsData.set("dmsWindowsFloating", checked); + if (checked) + themeColorsTab.checkWindowRulesIncludeStatus(); + } + } + + IncludeWarningBox { + visibleCondition: (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) && (SettingsData.dmsWindowsFloating ?? true) + checking: themeColorsTab.checkingWindowRulesInclude + fixing: themeColorsTab.fixingWindowRulesInclude + includeReadOnly: themeColorsTab.windowRulesReadOnly + alreadyIncluded: themeColorsTab.windowRulesIncludeStatus.included + fragmentPath: "dms/windowrules" + onSetup: themeColorsTab.fixWindowRulesInclude + } + } + SettingsCard { tab: "theme" tags: ["blur", "background", "transparency", "glass", "frosted"] @@ -2144,80 +2402,13 @@ Item { width: parent.width spacing: Theme.spacingM - StyledRect { - id: cursorWarningBox - width: parent.width - height: cursorWarningContent.implicitHeight + Theme.spacingL * 2 - radius: Theme.cornerRadius - - readonly property bool showLegacy: themeColorsTab.cursorReadOnly - readonly property bool showSetup: !showLegacy && !themeColorsTab.cursorIncludeStatus.included - - color: (showLegacy || showSetup) ? Theme.withAlpha(Theme.primary, 0.15) : Theme.withAlpha(Theme.primary, 0) - border.color: (showLegacy || showSetup) ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.primary, 0) - border.width: 1 - visible: (showLegacy || showSetup) && !themeColorsTab.checkingCursorInclude - - Row { - id: cursorWarningContent - anchors.fill: parent - anchors.margins: Theme.spacingL - spacing: Theme.spacingM - - DankIcon { - name: "warning" - size: Theme.iconSize - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - Column { - width: parent.width - Theme.iconSize - (cursorFixButton.visible ? cursorFixButton.width + Theme.spacingM : 0) - Theme.spacingM - spacing: Theme.spacingXS - anchors.verticalCenter: parent.verticalCenter - - StyledText { - text: { - if (cursorWarningBox.showLegacy) - return I18n.tr("Hyprland conf mode"); - if (cursorWarningBox.showSetup) - return I18n.tr("First Time Setup"); - return ""; - } - font.pixelSize: Theme.fontSizeMedium - font.weight: Font.Medium - color: Theme.primary - width: parent.width - horizontalAlignment: Text.AlignLeft - } - - StyledText { - text: { - if (cursorWarningBox.showLegacy) - return I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before changing these settings."); - if (cursorWarningBox.showSetup) - return I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg("dms/cursor"); - return ""; - } - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceVariantText - wrapMode: Text.WordWrap - width: parent.width - horizontalAlignment: Text.AlignLeft - } - } - - DankButton { - id: cursorFixButton - visible: !cursorWarningBox.showLegacy && cursorWarningBox.showSetup - text: themeColorsTab.fixingCursorInclude ? I18n.tr("Setting up...") : I18n.tr("Setup") - backgroundColor: Theme.primary - textColor: Theme.primaryText - enabled: !themeColorsTab.fixingCursorInclude - anchors.verticalCenter: parent.verticalCenter - onClicked: themeColorsTab.fixCursorInclude() - } - } + IncludeWarningBox { + checking: themeColorsTab.checkingCursorInclude + fixing: themeColorsTab.fixingCursorInclude + includeReadOnly: themeColorsTab.cursorReadOnly + alreadyIncluded: themeColorsTab.cursorIncludeStatus.included + fragmentPath: "dms/cursor" + onSetup: themeColorsTab.fixCursorInclude } SettingsDropdownRow { diff --git a/quickshell/Modules/Settings/WidgetSelectionPopup.qml b/quickshell/Modules/Settings/WidgetSelectionPopup.qml index cf9b9cc06..a7b633eba 100644 --- a/quickshell/Modules/Settings/WidgetSelectionPopup.qml +++ b/quickshell/Modules/Settings/WidgetSelectionPopup.qml @@ -3,10 +3,9 @@ import Quickshell import qs.Common import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property var allWidgets: [] property string targetSection: "" property string searchQuery: "" @@ -15,10 +14,10 @@ FloatingWindow { property bool keyboardNavigationActive: false property var parentModal: null parentWindow: parentModal - readonly property bool blurActive: Theme.blurForegroundLayers || Theme.transparentBlurLayers - readonly property real surfaceAlpha: blurActive ? Math.min(Theme.popupTransparency, Theme.transparentBlurLayers ? 0.36 : 0.78) : 1.0 - readonly property real fieldAlpha: blurActive ? Math.min(Theme.popupTransparency, Theme.transparentBlurLayers ? 0.18 : 0.62) : 1.0 - readonly property real rowAlpha: blurActive ? Math.min(Theme.popupTransparency, Theme.transparentBlurLayers ? 0.12 : 0.52) : 0.30 + readonly property bool blurActive: Theme.blurLayersActive + readonly property bool floatingForegroundLayers: Theme.floatingWindowForegroundLayers + readonly property bool transparentBlurLayers: Theme.blurLayersActive && !floatingForegroundLayers + readonly property real rowAlpha: blurActive ? Math.min(Theme.floatingWindowTransparency, transparentBlurLayers ? 0.12 : 0.52) : 0.30 signal widgetSelected(string widgetId, string targetSection) @@ -112,7 +111,6 @@ FloatingWindow { minimumSize: Qt.size(400, 350) implicitWidth: 500 implicitHeight: 550 - color: blurActive ? Theme.withAlpha(Theme.surfaceContainer, 0) : Theme.surfaceContainer visible: false onClosed: hide() @@ -139,24 +137,6 @@ FloatingWindow { }); } - WindowBlur { - targetWindow: root - blurX: 0 - blurY: 0 - blurWidth: root.visible ? root.width : 0 - blurHeight: root.visible ? root.height : 0 - blurRadius: Theme.cornerRadius - } - - Rectangle { - anchors.fill: parent - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainer, root.surfaceAlpha) - border.color: root.blurActive ? Theme.outlineMedium : Theme.withAlpha(Theme.outlineMedium, 0) - border.width: root.blurActive ? Theme.layerOutlineWidth : 0 - antialiasing: true - } - FocusScope { id: widgetKeyHandler @@ -294,10 +274,6 @@ FloatingWindow { id: searchField width: parent.width height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, root.fieldAlpha) - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml b/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml index ebc5d95b9..42728f625 100644 --- a/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml +++ b/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml @@ -1,13 +1,11 @@ import QtQuick import Quickshell import qs.Common -import qs.Services import qs.Widgets -FloatingWindow { +DankFloatingWindow { id: root - property bool disablePopupTransparency: true property string searchQuery: "" property var filteredApps: [] property int selectedIndex: -1 @@ -23,28 +21,10 @@ FloatingWindow { minimumSize: Qt.size(400, 350) implicitWidth: 500 implicitHeight: 550 - color: "transparent" visible: false onClosed: hide() - WindowBlur { - targetWindow: root - blurX: 0 - blurY: 0 - blurWidth: root.visible ? root.width : 0 - blurHeight: root.visible ? root.height : 0 - blurRadius: Theme.cornerRadius - } - - Rectangle { - anchors.fill: parent - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainer, 0.95) - border.color: Theme.outlineMedium - border.width: Theme.layerOutlineWidth - } - FocusScope { anchors.fill: parent focus: true @@ -143,10 +123,6 @@ FloatingWindow { id: searchField width: parent.width height: 48 - cornerRadius: Theme.cornerRadius - backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, 0.8) - normalBorderColor: Theme.outlineMedium - focusedBorderColor: Theme.primary leftIconName: "search" leftIconSize: Theme.iconSize leftIconColor: Theme.surfaceVariantText diff --git a/quickshell/Modules/Settings/Widgets/DeviceAliasRow.qml b/quickshell/Modules/Settings/Widgets/DeviceAliasRow.qml index 37c5618bf..435abf807 100644 --- a/quickshell/Modules/Settings/Widgets/DeviceAliasRow.qml +++ b/quickshell/Modules/Settings/Widgets/DeviceAliasRow.qml @@ -122,7 +122,6 @@ Rectangle { buttonSize: 36 iconName: "restart_alt" iconSize: 20 - backgroundColor: Theme.surfaceContainerHigh iconColor: Theme.surfaceVariantText tooltipText: I18n.tr("Reset to default name") anchors.verticalCenter: parent.verticalCenter @@ -137,7 +136,6 @@ Rectangle { buttonSize: 36 iconName: root.isHidden ? "visibility" : "visibility_off" iconSize: 20 - backgroundColor: Theme.surfaceContainerHigh iconColor: root.isHidden ? Theme.primary : Theme.surfaceVariantText tooltipText: root.isHidden ? I18n.tr("Show device") : I18n.tr("Hide device") anchors.verticalCenter: parent.verticalCenter diff --git a/quickshell/Modules/Settings/Widgets/SettingsCard.qml b/quickshell/Modules/Settings/Widgets/SettingsCard.qml index 40fbf103e..add80f384 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsCard.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsCard.qml @@ -38,7 +38,9 @@ StyledRect { return h; } radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface + border.color: Theme.outlineMedium + border.width: Theme.layerOutlineWidth readonly property bool collapsed: collapsible && !expanded readonly property bool hasHeader: root.title !== "" || root.iconName !== "" diff --git a/quickshell/Modules/Settings/Widgets/SettingsDropdownRow.qml b/quickshell/Modules/Settings/Widgets/SettingsDropdownRow.qml index e57cd27ac..afa6572df 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsDropdownRow.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsDropdownRow.qml @@ -20,7 +20,6 @@ DankDropdown { width: parent?.width ?? 0 addHorizontalPadding: true - usePopupTransparency: false Rectangle { anchors.fill: parent diff --git a/quickshell/Modules/Settings/Widgets/SettingsSliderCard.qml b/quickshell/Modules/Settings/Widgets/SettingsSliderCard.qml index e1a08c4b8..8243f8e43 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsSliderCard.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsSliderCard.qml @@ -35,7 +35,9 @@ StyledRect { width: parent?.width ?? 0 height: Theme.spacingL * 2 + contentColumn.height radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface + border.color: Theme.outlineMedium + border.width: Theme.layerOutlineWidth Component.onCompleted: { if (!settingKey) @@ -110,7 +112,6 @@ StyledRect { iconName: "restart_alt" iconSize: 20 visible: root.defaultValue >= 0 && slider.value !== root.defaultValue - backgroundColor: Theme.surfaceContainerHigh iconColor: Theme.surfaceVariantText onClicked: { slider.value = root.defaultValue; diff --git a/quickshell/Modules/Settings/Widgets/SettingsSliderRow.qml b/quickshell/Modules/Settings/Widgets/SettingsSliderRow.qml index efac69ad6..16d2845e8 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsSliderRow.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsSliderRow.qml @@ -124,7 +124,6 @@ Item { iconName: "restart_alt" iconSize: 20 visible: root.defaultValue >= 0 && slider.value !== root.defaultValue - backgroundColor: Theme.surfaceContainerHigh iconColor: Theme.surfaceVariantText onClicked: { slider.value = root.defaultValue; diff --git a/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml b/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml index 94c55dd8b..ad9dc97b0 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml @@ -29,7 +29,9 @@ StyledRect { width: parent?.width ?? 0 height: Theme.spacingL * 2 + mainColumn.height radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface + border.color: Theme.outlineMedium + border.width: Theme.layerOutlineWidth Component.onCompleted: { if (!settingKey) diff --git a/quickshell/Modules/Settings/Widgets/SystemMonitorVariantCard.qml b/quickshell/Modules/Settings/Widgets/SystemMonitorVariantCard.qml index 59bbb74b1..5418ab490 100644 --- a/quickshell/Modules/Settings/Widgets/SystemMonitorVariantCard.qml +++ b/quickshell/Modules/Settings/Widgets/SystemMonitorVariantCard.qml @@ -27,7 +27,7 @@ Rectangle { width: parent?.width ?? 0 height: variantColumn.height radius: Theme.cornerRadius - color: Theme.surfaceContainerHigh + color: Theme.floatingWindowNestedSurface clip: true Column { diff --git a/quickshell/Services/CompositorService.qml b/quickshell/Services/CompositorService.qml index e00fa0b33..00c3febca 100644 --- a/quickshell/Services/CompositorService.qml +++ b/quickshell/Services/CompositorService.qml @@ -975,6 +975,45 @@ Singleton { compositorDetected = true; if (isNiri) NiriService.generateNiriBlurrule(); + Qt.callLater(applyDmsWindowFloatingRule); + } + + function applyDmsWindowFloatingRule() { + if (!compositorDetected || (!isNiri && !isHyprland && !isMango)) + return; + const floating = typeof SettingsData === "undefined" || (SettingsData.dmsWindowsFloating ?? true); + if (!floating) { + Proc.runCommand("dms-windowrule-float-remove", [Proc.dmsBin, "config", "windowrules", "remove", compositor, "dms-floating-windows"], (output, exitCode) => { + if (exitCode !== 0) { + log.warn("failed to remove DMS floating window rule", exitCode, output); + return; + } + if (isMango) + MangoService.reloadConfig(); + }); + return; + } + const ruleJson = JSON.stringify({ + "id": "dms-floating-windows", + "name": "DMS Floating Windows", + "enabled": true, + "matchCriteria": { + "appId": "^com.danklinux.dms$" + }, + "actions": { + "openFloating": true + } + }); + Proc.runCommand("dms-windowrule-float-add", [Proc.dmsBin, "config", "windowrules", "add", compositor, ruleJson], (output, exitCode) => { + if (exitCode !== 0) { + log.warn("failed to add DMS floating window rule", exitCode, output); + return; + } + if (isNiri) + NiriService.validate(); + if (isMango) + MangoService.reloadConfig(); + }); } // Fallback when the socket owner can't be resolved (no ss, unrecognized @@ -1140,5 +1179,9 @@ Singleton { return; } } + + function onDmsWindowsFloatingChanged() { + root.applyDmsWindowFloatingRule(); + } } } diff --git a/quickshell/Services/NiriService.qml b/quickshell/Services/NiriService.qml index bf0309ff2..7f625c3d7 100644 --- a/quickshell/Services/NiriService.qml +++ b/quickshell/Services/NiriService.qml @@ -1202,7 +1202,6 @@ Singleton { if (frameEnabled && SettingsData.frameMode !== "connected") excludeNamespaces.push("dms:frame"); - // Xray is niri's default blur, so only the off state needs a rule. let xrayRules = ""; if (!layoutXrayEnabled) { const excludeLines = layoutBarXrayEnabled ? excludeNamespaces.map(ns => `\n exclude namespace="^${ns}$"`).join("") : ""; @@ -1212,6 +1211,13 @@ layer-rule {${excludeLines} background-effect { xray false } +} + +window-rule { + match app-id="^com.danklinux.dms$" + background-effect { + xray false + } }`; } // Marker persists the preference even while the rule has no target diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index c47686649..bce7a384e 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -118,7 +118,7 @@ Singleton { "systemUpdate": () => _unloadPopoutNow("systemUpdatePopout", "systemUpdateLoader"), "layout": () => _unloadPopoutNow("layoutPopout", "layoutPopoutLoader"), "clipboardHistory": () => _unloadPopoutNow("clipboardHistoryPopout", "clipboardHistoryPopoutLoader"), - "settings": () => _unloadSettingsNow() + "settings": () => unloadSettingsNow() }) function setPosition(popout, x, y, width, section, screen) { @@ -491,15 +491,12 @@ Singleton { openSettingsWithTab(tabName); } - function unloadSettings() { - _scheduleUnload("settings"); - } - - function _unloadSettingsNow() { + function unloadSettingsNow() { if (!settingsModalLoader) return; if (settingsModal && settingsModal.visible) return; + delete _pendingUnloads["settings"]; settingsModal = null; settingsModalLoader.active = false; } diff --git a/quickshell/Widgets/DankFloatingWindow.qml b/quickshell/Widgets/DankFloatingWindow.qml new file mode 100644 index 000000000..3a2054bf7 --- /dev/null +++ b/quickshell/Widgets/DankFloatingWindow.qml @@ -0,0 +1,49 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import qs.Common +import qs.Services + +FloatingWindow { + id: root + + default property alias content: contentItem.data + + property color surfaceColor: Theme.floatingWindowSurface + + color: "transparent" + + Rectangle { + anchors.fill: parent + radius: Theme.cornerRadius + color: root.surfaceColor + } + + WindowBlur { + targetWindow: root + blurX: 0 + blurY: 0 + blurWidth: root.visible ? root.width : 0 + blurHeight: root.visible ? root.height : 0 + blurRadius: Theme.cornerRadius + } + + Item { + id: contentItem + + property bool disablePopupTransparency: true + + anchors.fill: parent + } + + Rectangle { + anchors.fill: parent + radius: Theme.cornerRadius + color: "transparent" + border.color: BlurService.borderColor + border.width: BlurService.borderWidth + antialiasing: true + z: 100 + } +} diff --git a/quickshell/translations/extract_settings_index.py b/quickshell/translations/extract_settings_index.py index bf2835331..357da0799 100755 --- a/quickshell/translations/extract_settings_index.py +++ b/quickshell/translations/extract_settings_index.py @@ -397,7 +397,9 @@ def find_settings_components(content, filename, wrappers): visible_raw = extract_property(block, "visible") condition_key = FILE_CONDITION_MAP.get(filename) if visible_raw: - if "CompositorService.isNiri" in visible_raw: + if all(c in visible_raw for c in ("CompositorService.isNiri", "CompositorService.isHyprland", "CompositorService.isMango")): + condition_key = "windowRulesCapable" + elif "CompositorService.isNiri" in visible_raw: condition_key = "isNiri" elif "CompositorService.isHyprland" in visible_raw: condition_key = "isHyprland" diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index 8446ed8c6..e1b581fb2 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -935,7 +935,7 @@ "workspace" ], "description": "Show the bar only when no windows are open", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "hoverPopouts", @@ -1013,7 +1013,7 @@ ], "icon": "fit_screen", "description": "Remove gaps and border when windows are maximized", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "barMaximizeWidgetIcons", @@ -1421,7 +1421,7 @@ "workspaces" ], "description": "Show workspaces of the currently focused monitor", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "workspaceSettings", @@ -1447,7 +1447,7 @@ ], "icon": "view_module", "description": "Show workspace index numbers in the top bar workspace switcher", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "groupActiveWorkspaceApps", @@ -1575,7 +1575,7 @@ "workspace" ], "description": "Reverse workspace switch direction when scrolling over the bar", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "workspaceUnfocusedMonitorSeparateAppearance", @@ -1663,7 +1663,7 @@ "workspaces" ], "description": "Display only workspaces that contain windows", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "showWorkspaceApps", @@ -1693,7 +1693,7 @@ "workspace" ], "description": "Display application icons in workspace indicators", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "showWorkspaceIndex", @@ -1987,7 +1987,7 @@ "windows" ], "description": "Show dock when floating windows don", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "dockIsolateDisplays", @@ -3530,30 +3530,6 @@ ], "description": "Hide cursor after inactivity (0 = disabled)" }, - { - "section": "automaticColorMode", - "label": "Automatic Color Mode", - "tabIndex": 10, - "category": "Theme & Colors", - "keywords": [ - "appearance", - "automatic", - "color", - "colors", - "colour", - "hue", - "look", - "mode", - "schedule", - "scheme", - "style", - "sunrise", - "sunset", - "theme", - "tint" - ], - "icon": "schedule" - }, { "section": "blurEnabled", "label": "Background Blur", @@ -3638,6 +3614,7 @@ "category": "Theme & Colors", "keywords": [ "appearance", + "automatic", "color", "colors", "colour", @@ -3650,8 +3627,11 @@ "look", "mode", "night", + "schedule", "scheme", "style", + "sunrise", + "sunset", "theme", "tint" ], @@ -3745,7 +3725,7 @@ ], "icon": "mouse", "description": "Mouse pointer appearance", - "conditionKey": "isNiri" + "conditionKey": "windowRulesCapable" }, { "section": "widgetBackgroundCustomStrength", @@ -3858,6 +3838,37 @@ "theme" ] }, + { + "section": "floatingWindows", + "label": "Floating Windows", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "authentication", + "colors", + "floating", + "follow", + "foreground", + "look", + "notepad", + "opacity", + "polkit", + "scheme", + "settings", + "style", + "styling", + "surface", + "theme", + "tile", + "tiling", + "transparency", + "window", + "windows" + ], + "icon": "open_in_new", + "description": "Floating windows follow Surface Styling settings" + }, { "section": "matugenTemplateNeovimSetBackground", "label": "Follow DMS background color", @@ -3909,6 +3920,84 @@ ], "description": "Show foreground surfaces on panels for stronger contrast" }, + { + "section": "floatingWindowForegroundLayers", + "label": "Foreground Layers", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "blur", + "cards", + "colors", + "contrast", + "floating", + "foreground", + "glass", + "inside", + "layers", + "look", + "scheme", + "show", + "style", + "surfaces", + "theme", + "window", + "windows" + ], + "description": "Show foreground surfaces on cards inside floating windows" + }, + { + "section": "floatingWindowForegroundTransparency", + "label": "Foreground Opacity", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "cards", + "colors", + "floating", + "foreground", + "inside", + "layers", + "look", + "nested", + "opacity", + "scheme", + "style", + "surfaces", + "theme", + "transparency", + "window", + "windows" + ], + "description": "Opacity of cards and nested surfaces inside floating windows" + }, + { + "section": "foregroundLayerTransparency", + "label": "Foreground Opacity", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "cards", + "colors", + "contrast", + "foreground", + "layers", + "look", + "nested", + "opacity", + "panels", + "scheme", + "shell", + "style", + "surfaces", + "theme", + "transparency" + ], + "description": "Opacity of foreground cards and nested surfaces on shell panels" + }, { "section": "matugenTemplateGtk", "label": "GTK", @@ -4290,6 +4379,34 @@ "description": "Invert touchpad scroll direction", "conditionKey": "isMango" }, + { + "section": "dmsWindowsFloating", + "label": "Open Windows Floating", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "colors", + "compositor", + "floating", + "hyprland", + "look", + "mango", + "niri", + "open", + "rule", + "scheme", + "style", + "theme", + "tile", + "tiled", + "tiling", + "window", + "windows" + ], + "description": "Open DMS windows floating instead of tiled", + "conditionKey": "windowRulesCapable" + }, { "section": "popoutElevationEnabled", "label": "Popout Shadows", @@ -4579,6 +4696,37 @@ ], "description": "Controls opacity of shell surfaces, popouts, and modals" }, + { + "section": "surfaceStyling", + "label": "Surface Styling", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "border", + "colors", + "controls", + "corner", + "look", + "modal", + "modals", + "opacity", + "outline", + "popouts", + "popup", + "radius", + "scheme", + "shell", + "style", + "styling", + "surface", + "surfaces", + "theme", + "transparency" + ], + "icon": "layers", + "description": "Controls opacity of shell surfaces, popouts, and modals" + }, { "section": "syncModeWithPortal", "label": "Sync Mode with Portal", @@ -4606,6 +4754,31 @@ ], "description": "Sync dark mode with settings portals for system-wide theme hints" }, + { + "section": "floatingWindowSyncGlobal", + "label": "Sync with Global Settings", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "colors", + "floating", + "follow", + "global", + "look", + "opacity", + "scheme", + "settings", + "style", + "styling", + "surface", + "sync", + "theme", + "window", + "windows" + ], + "description": "Floating windows follow Surface Styling settings" + }, { "section": "systemAppTheming", "label": "System App Theming", @@ -4806,6 +4979,30 @@ ], "description": "Choose neutral or accent-colored widget text" }, + { + "section": "floatingWindowTransparency", + "label": "Window Opacity", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "authentication", + "colors", + "floating", + "look", + "notepad", + "opacity", + "prompts", + "scheme", + "settings", + "style", + "theme", + "transparency", + "window", + "windows" + ], + "description": "Opacity of floating DMS windows like Settings, Notepad, and authentication prompts" + }, { "section": "matugenTemplateZed", "label": "Zed",