From cd40f2f7ed7044ddfd5b403a6fa1534de130920a Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 30 Apr 2026 15:19:06 -0400 Subject: [PATCH] re-organize settings --- quickshell/Common/AnimVariants.qml | 2 +- quickshell/Common/SettingsData.qml | 24 +-- quickshell/Common/settings/SettingsSpec.js | 5 +- .../Modals/Common/DankModalConnected.qml | 4 +- .../DankLauncherV2ModalConnected.qml | 2 +- .../Modals/Settings/SettingsContent.qml | 8 +- quickshell/Modules/Settings/DankBarTab.qml | 117 +++--------- quickshell/Modules/Settings/DockTab.qml | 48 ++--- quickshell/Modules/Settings/FrameTab.qml | 175 +++++++++--------- .../Modules/Settings/ThemeColorsTab.qml | 25 ++- .../Modules/Settings/TypographyMotionTab.qml | 40 ---- .../Widgets/SettingsControlledByFrame.qml | 79 ++++++++ quickshell/Widgets/DankPopoutConnected.qml | 9 +- 13 files changed, 240 insertions(+), 298 deletions(-) create mode 100644 quickshell/Modules/Settings/Widgets/SettingsControlledByFrame.qml diff --git a/quickshell/Common/AnimVariants.qml b/quickshell/Common/AnimVariants.qml index 44545001..676af6d1 100644 --- a/quickshell/Common/AnimVariants.qml +++ b/quickshell/Common/AnimVariants.qml @@ -55,7 +55,7 @@ Singleton { readonly property bool isDirectionalEffect: isConnectedEffect || _effect === 1 readonly property bool isDepthEffect: _effect === 2 - readonly property bool isConnectedEffect: (typeof SettingsData !== "undefined") && SettingsData.frameEnabled && _effect === 1 && SettingsData.directionalAnimationMode === 3 + readonly property bool isConnectedEffect: (typeof SettingsData !== "undefined") && SettingsData.connectedFrameModeActive readonly property real effectScaleCollapsed: _effectScaleCollapsed[_effect] !== undefined ? _effectScaleCollapsed[_effect] : 0.96 readonly property real effectAnimOffset: _effectAnimOffsets[_effect] !== undefined ? _effectAnimOffsets[_effect] : 16 diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 3cc33adc..eb511a55 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -185,15 +185,6 @@ Singleton { onAnimationVariantChanged: saveSettings() property int motionEffect: SettingsData.AnimationEffect.Standard onMotionEffectChanged: saveSettings() - property int directionalAnimationMode: 1 - onDirectionalAnimationModeChanged: { - const normalized = directionalAnimationMode === 3 ? 3 : 1; - if (directionalAnimationMode !== normalized) { - directionalAnimationMode = normalized; - return; - } - saveSettings(); - } property bool m3ElevationEnabled: true onM3ElevationEnabledChanged: saveSettings() property int m3ElevationIntensity: 12 @@ -252,17 +243,11 @@ Singleton { property string frameLauncherEmergeSide: "bottom" onFrameLauncherEmergeSideChanged: saveSettings() readonly property string frameModalEmergeSide: frameLauncherEmergeSide === "top" ? "bottom" : "top" - property int previousDirectionalMode: 1 - onPreviousDirectionalModeChanged: { - if (previousDirectionalMode !== 1) { - previousDirectionalMode = 1; - return; - } - saveSettings(); - } + property string frameMode: "separate" + onFrameModeChanged: saveSettings() property var connectedFrameBarStyleBackups: ({}) onConnectedFrameBarStyleBackupsChanged: saveSettings() - readonly property bool connectedFrameModeActive: frameEnabled && motionEffect === SettingsData.AnimationEffect.Directional && directionalAnimationMode === 3 + readonly property bool connectedFrameModeActive: frameEnabled && frameMode === "connected" onConnectedFrameModeActiveChanged: { if (_loading) return; @@ -1353,6 +1338,9 @@ Singleton { Store.parse(root, obj); + if (obj?.directionalAnimationMode === 3 && frameMode !== "connected") + frameMode = "connected"; + if (obj?.weatherLocation !== undefined) _legacyWeatherLocation = obj.weatherLocation; if (obj?.weatherCoordinates !== undefined) diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index 218b1305..a65a8e3e 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -51,8 +51,6 @@ var SPEC = { enableRippleEffects: { def: true }, animationVariant: { def: 0 }, motionEffect: { def: 0 }, - directionalAnimationMode: { def: 1 }, - previousDirectionalMode: { def: 1 }, m3ElevationEnabled: { def: true }, m3ElevationIntensity: { def: 12 }, m3ElevationOpacity: { def: 30 }, @@ -566,7 +564,8 @@ var SPEC = { frameShowOnOverview: { def: false }, frameBlurEnabled: { def: true }, frameCloseGaps: { def: false }, - frameLauncherEmergeSide: { def: "bottom" } + frameLauncherEmergeSide: { def: "bottom" }, + frameMode: { def: "separate" } }; function getValidKeys() { diff --git a/quickshell/Modals/Common/DankModalConnected.qml b/quickshell/Modals/Common/DankModalConnected.qml index 4614194f..42023cf0 100644 --- a/quickshell/Modals/Common/DankModalConnected.qml +++ b/quickshell/Modals/Common/DankModalConnected.qml @@ -311,8 +311,8 @@ Item { readonly property real shadowMotionPadding: { if (Theme.isConnectedEffect) return 0; - if (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0 && Theme.isDirectionalEffect) - return 0; // Wayland native overlap mask + if (Theme.isDirectionalEffect) + return 0; if (animationType === "slide") return 30; if (Theme.isDirectionalEffect) diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml index 9431062a..d6dbb897 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml @@ -746,7 +746,7 @@ Item { Item { id: directionalClipMask - readonly property bool shouldClip: Theme.isDirectionalEffect && typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0 + readonly property bool shouldClip: Theme.isDirectionalEffect readonly property real clipOversize: 2000 clip: shouldClip diff --git a/quickshell/Modals/Settings/SettingsContent.qml b/quickshell/Modals/Settings/SettingsContent.qml index e16bb637..e1d6373c 100644 --- a/quickshell/Modals/Settings/SettingsContent.qml +++ b/quickshell/Modals/Settings/SettingsContent.qml @@ -112,7 +112,9 @@ FocusScope { focus: active sourceComponent: Component { - DockTab {} + DockTab { + parentModal: root.parentModal + } } onActiveChanged: { @@ -218,7 +220,9 @@ FocusScope { visible: active focus: active - sourceComponent: ThemeColorsTab {} + sourceComponent: ThemeColorsTab { + parentModal: root.parentModal + } onActiveChanged: { if (active && item) diff --git a/quickshell/Modules/Settings/DankBarTab.qml b/quickshell/Modules/Settings/DankBarTab.qml index ff7b304b..ab3d1a57 100644 --- a/quickshell/Modules/Settings/DankBarTab.qml +++ b/quickshell/Modules/Settings/DankBarTab.qml @@ -708,42 +708,18 @@ Item { } } - Item { + SettingsControlledByFrame { visible: SettingsData.frameEnabled - width: parent.width - implicitHeight: frameNote.implicitHeight + Theme.spacingS * 2 - - Row { - id: frameNote - x: Theme.spacingM - width: parent.width - Theme.spacingM * 2 - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingS - - DankIcon { - name: "frame_source" - size: Theme.fontSizeMedium - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Spacing and size are managed by Frame mode") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceVariantText - wrapMode: Text.WordWrap - width: parent.width - Theme.fontSizeMedium - Theme.spacingS - } - } + parentModal: dankBarTab.parentModal + settingLabel: I18n.tr("Bar spacing and size") + reason: I18n.tr("Managed by Frame") } SettingsCard { iconName: "space_bar" title: I18n.tr("Spacing") settingKey: "barSpacing" - visible: selectedBarConfig?.enabled - enabled: !SettingsData.frameEnabled - opacity: SettingsData.frameEnabled ? 0.5 : 1.0 + visible: (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled SettingsSliderRow { id: edgeSpacingSlider @@ -894,8 +870,7 @@ Item { SettingsSliderRow { id: barTransparencySlider - enabled: !SettingsData.frameEnabled - opacity: SettingsData.frameEnabled ? 0.5 : 1.0 + visible: !SettingsData.frameEnabled text: I18n.tr("Bar Transparency") value: (selectedBarConfig?.transparency ?? 1.0) * 100 minimum: 0 @@ -938,33 +913,11 @@ Item { } } - Item { + SettingsControlledByFrame { visible: SettingsData.frameEnabled - width: parent.width - implicitHeight: transparencyFrameNote.implicitHeight + Theme.spacingS * 2 - - Row { - id: transparencyFrameNote - x: Theme.spacingM - width: parent.width - Theme.spacingM * 2 - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingS - - DankIcon { - name: "frame_source" - size: Theme.fontSizeMedium - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Opacity is controlled by Frame Border Opacity in Frame settings") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceVariantText - wrapMode: Text.WordWrap - width: parent.width - Theme.fontSizeMedium - Theme.spacingS - } - } + parentModal: dankBarTab.parentModal + settingLabel: I18n.tr("Bar transparency") + reason: I18n.tr("Managed by Frame") } } @@ -1026,10 +979,16 @@ Item { expanded: false visible: selectedBarConfig?.enabled + SettingsControlledByFrame { + visible: SettingsData.frameEnabled + parentModal: dankBarTab.parentModal + settingLabel: I18n.tr("Bar corners and background") + reason: I18n.tr("Managed by Frame") + } + SettingsToggleRow { text: I18n.tr("Square Corners") - enabled: !SettingsData.frameEnabled - opacity: SettingsData.frameEnabled ? 0.5 : 1.0 + visible: !SettingsData.frameEnabled checked: selectedBarConfig?.squareCorners ?? false onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { squareCorners: checked @@ -1038,8 +997,7 @@ Item { SettingsToggleRow { text: I18n.tr("No Background") - enabled: !SettingsData.frameEnabled - opacity: SettingsData.frameEnabled ? 0.5 : 1.0 + visible: !SettingsData.frameEnabled checked: selectedBarConfig?.noBackground ?? false onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { noBackground: checked @@ -1079,8 +1037,7 @@ Item { SettingsToggleRow { text: I18n.tr("Goth Corners") - enabled: !SettingsData.frameEnabled - opacity: SettingsData.frameEnabled ? 0.5 : 1.0 + visible: !SettingsData.frameEnabled checked: selectedBarConfig?.gothCornersEnabled ?? false onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { gothCornersEnabled: checked @@ -1416,33 +1373,11 @@ Item { } } - Item { + SettingsControlledByFrame { visible: dankBarTab.connectedFrameModeActive - width: parent.width - implicitHeight: connectedFrameStyleNote.implicitHeight + Theme.spacingS * 2 - - Row { - id: connectedFrameStyleNote - x: Theme.spacingM - width: parent.width - Theme.spacingM * 2 - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingS - - DankIcon { - name: "frame_source" - size: Theme.fontSizeMedium - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Connected Frame mode keeps bar shadow override, border, and corner overrides off while active") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceVariantText - wrapMode: Text.WordWrap - width: parent.width - Theme.fontSizeMedium - Theme.spacingS - } - } + parentModal: dankBarTab.parentModal + settingLabel: I18n.tr("Bar shadow, border, and corners") + reason: I18n.tr("Managed by Frame in Connected Mode") } SettingsCard { @@ -1452,9 +1387,7 @@ Item { settingKey: "barShadow" collapsible: true expanded: false - visible: selectedBarConfig?.enabled - enabled: !dankBarTab.connectedFrameModeActive - opacity: dankBarTab.connectedFrameModeActive ? 0.5 : 1.0 + visible: (selectedBarConfig?.enabled ?? false) && !dankBarTab.connectedFrameModeActive readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0 readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom" diff --git a/quickshell/Modules/Settings/DockTab.qml b/quickshell/Modules/Settings/DockTab.qml index c5d3ecb7..8ade7e07 100644 --- a/quickshell/Modules/Settings/DockTab.qml +++ b/quickshell/Modules/Settings/DockTab.qml @@ -7,9 +7,9 @@ import qs.Modules.Settings.Widgets Item { id: root - readonly property bool connectedFrameModeActive: SettingsData.frameEnabled - && SettingsData.motionEffect === 1 - && SettingsData.directionalAnimationMode === 3 + + property var parentModal: null + readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive FileBrowserModal { id: dockLogoFileBrowser @@ -607,8 +607,7 @@ Item { SettingsSliderRow { text: I18n.tr("Exclusive Zone Offset") - enabled: !root.connectedFrameModeActive - opacity: root.connectedFrameModeActive ? 0.5 : 1.0 + visible: !root.connectedFrameModeActive value: SettingsData.dockBottomGap minimum: -100 maximum: 100 @@ -618,8 +617,7 @@ Item { SettingsSliderRow { text: I18n.tr("Margin") - enabled: !root.connectedFrameModeActive - opacity: root.connectedFrameModeActive ? 0.5 : 1.0 + visible: !root.connectedFrameModeActive value: SettingsData.dockMargin minimum: 0 maximum: 100 @@ -628,33 +626,11 @@ Item { } } - Item { + SettingsControlledByFrame { visible: root.connectedFrameModeActive - width: parent.width - implicitHeight: dockConnectedNote.implicitHeight + Theme.spacingS * 2 - - Row { - id: dockConnectedNote - x: Theme.spacingM - width: parent.width - Theme.spacingM * 2 - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingS - - DankIcon { - name: "frame_source" - size: Theme.fontSizeMedium - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Connected Frame mode manages dock edge offset, transparency, blur, and border styling") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceVariantText - wrapMode: Text.WordWrap - width: parent.width - Theme.fontSizeMedium - Theme.spacingS - } - } + parentModal: root.parentModal + settingLabel: I18n.tr("Dock spacing, transparency, and border") + reason: I18n.tr("Managed by Frame in Connected Mode") } SettingsCard { @@ -662,8 +638,7 @@ Item { iconName: "opacity" title: I18n.tr("Transparency") settingKey: "dockTransparency" - enabled: !root.connectedFrameModeActive - opacity: root.connectedFrameModeActive ? 0.5 : 1.0 + visible: !root.connectedFrameModeActive SettingsSliderRow { text: I18n.tr("Dock Transparency") @@ -683,8 +658,7 @@ Item { settingKey: "dockBorder" collapsible: true expanded: false - enabled: !root.connectedFrameModeActive - opacity: root.connectedFrameModeActive ? 0.5 : 1.0 + visible: !root.connectedFrameModeActive SettingsToggleRow { text: I18n.tr("Border") diff --git a/quickshell/Modules/Settings/FrameTab.qml b/quickshell/Modules/Settings/FrameTab.qml index 07968840..ca74e2f0 100644 --- a/quickshell/Modules/Settings/FrameTab.qml +++ b/quickshell/Modules/Settings/FrameTab.qml @@ -25,7 +25,6 @@ Item { anchors.horizontalCenter: parent.horizontalCenter spacing: Theme.spacingXL - // ── Enable Frame ────────────────────────────────────────────────── SettingsCard { width: parent.width iconName: "frame_source" @@ -42,7 +41,35 @@ Item { } } - // ── Border ──────────────────────────────────────────────────────── + SettingsCard { + width: parent.width + iconName: "tune" + title: I18n.tr("Mode") + settingKey: "frameMode" + visible: SettingsData.frameEnabled + + SettingsButtonGroupRow { + settingKey: "frameModeSelector" + tags: ["frame", "mode", "connected", "separate", "popout"] + text: I18n.tr("Surface Behavior") + description: SettingsData.frameMode === "connected" ? I18n.tr("Surfaces emerge flush from the bar") : I18n.tr("Surfaces float independently of the frame") + model: [I18n.tr("Separate"), I18n.tr("Connected")] + currentIndex: SettingsData.frameMode === "connected" ? 1 : 0 + onSelectionChanged: (index, selected) => { + if (!selected) + return; + switch (index) { + case 1: + SettingsData.set("frameMode", "connected"); + break; + default: + SettingsData.set("frameMode", "separate"); + break; + } + } + } + } + SettingsCard { width: parent.width iconName: "border_outer" @@ -56,9 +83,6 @@ Item { settingKey: "frameRounding" tags: ["frame", "border", "rounding", "radius", "corner"] text: I18n.tr("Border Radius") - description: SettingsData.connectedFrameModeActive - ? I18n.tr("Controls the radius of the frame and all connected popout, dock, and modal surfaces while Connected Mode is active") - : I18n.tr("Controls the frame border radius. This also becomes the connected surface radius whenever Connected Mode is active") unit: "px" minimum: 0 maximum: 100 @@ -99,7 +123,7 @@ Item { settingKey: "frameBarSize" tags: ["frame", "bar", "thickness", "size", "height", "width"] text: I18n.tr("Size") - description: I18n.tr("Height of horizontal bars / width of vertical bars in frame mode") + description: I18n.tr("Horizontal and vertical bar thickness") unit: "px" minimum: 24 maximum: 100 @@ -120,7 +144,6 @@ Item { settingKey: "frameOpacity" tags: ["frame", "border", "surface", "popup", "opacity", "transparency"] text: I18n.tr("Surface Opacity") - description: I18n.tr("Frame border opacity. Controls all surface opacity globally when Connected Mode is active") unit: "%" minimum: 0 maximum: 100 @@ -140,9 +163,7 @@ Item { settingKey: "frameBlurEnabled" tags: ["frame", "blur", "background", "glass", "transparency", "frosted"] text: I18n.tr("Frame Blur") - description: !BlurService.available - ? I18n.tr("Requires a newer version of Quickshell") - : I18n.tr("Apply compositor blur behind the frame border") + description: !BlurService.available ? I18n.tr("Requires a newer version of Quickshell") : I18n.tr("Apply compositor blur behind the frame border") checked: SettingsData.frameBlurEnabled onToggled: checked => SettingsData.set("frameBlurEnabled", checked) enabled: BlurService.available && SettingsData.blurEnabled @@ -170,7 +191,7 @@ Item { } StyledText { - text: I18n.tr("Frame Blur is controlled by Background Blur in Theme & Colors") + text: I18n.tr("Frame Blur follows Background Blur in Theme & Colors") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText wrapMode: Text.WordWrap @@ -179,35 +200,47 @@ Item { } } - // Color mode buttons SettingsButtonGroupRow { settingKey: "frameColor" tags: ["frame", "border", "color", "theme", "primary", "surface", "default"] - text: I18n.tr("Border color") + text: I18n.tr("Border Color") model: [I18n.tr("Default"), I18n.tr("Primary"), I18n.tr("Surface"), I18n.tr("Custom")] currentIndex: { const fc = SettingsData.frameColor; - if (!fc || fc === "default") return 0; - if (fc === "primary") return 1; - if (fc === "surface") return 2; - return 3; + if (!fc || fc === "default") + return 0; + switch (fc) { + case "primary": + return 1; + case "surface": + return 2; + default: + return 3; + } } onSelectionChanged: (index, selected) => { - if (!selected) return; + if (!selected) + return; switch (index) { - case 0: SettingsData.set("frameColor", ""); break; - case 1: SettingsData.set("frameColor", "primary"); break; - case 2: SettingsData.set("frameColor", "surface"); break; + case 0: + SettingsData.set("frameColor", ""); + break; + case 1: + SettingsData.set("frameColor", "primary"); + break; + case 2: + SettingsData.set("frameColor", "surface"); + break; case 3: const cur = SettingsData.frameColor; const isPreset = !cur || cur === "primary" || cur === "surface"; - if (isPreset) SettingsData.set("frameColor", "#2a2a2a"); + if (isPreset) + SettingsData.set("frameColor", "#2a2a2a"); break; } } } - // Custom color swatch — only visible when a hex color is stored (Custom mode) Item { visible: { const fc = SettingsData.frameColor; @@ -225,14 +258,13 @@ Item { StyledText { anchors.verticalCenter: parent.verticalCenter - text: I18n.tr("Custom color") + text: I18n.tr("Custom Color") font.pixelSize: Theme.fontSizeMedium font.weight: Font.Medium color: Theme.surfaceText } Rectangle { - id: colorSwatch anchors.verticalCenter: parent.verticalCenter width: 32 height: 32 @@ -258,7 +290,39 @@ Item { } } - // ── Bar Integration ─────────────────────────────────────────────── + SettingsCard { + width: parent.width + iconName: "blur_linear" + title: I18n.tr("Connected Options") + settingKey: "frameConnectedOptions" + collapsible: true + expanded: true + visible: SettingsData.frameEnabled && SettingsData.frameMode === "connected" + + SettingsToggleRow { + settingKey: "frameCloseGaps" + tags: ["frame", "connected", "gap", "edge", "flush", "popout", "notification"] + text: I18n.tr("Close the Gaps") + description: I18n.tr("Connected popouts sit flush with the frame") + checked: SettingsData.frameCloseGaps + onToggled: checked => SettingsData.set("frameCloseGaps", checked) + } + + SettingsButtonGroupRow { + settingKey: "frameLauncherEmergeSide" + tags: ["frame", "connected", "launcher", "modal", "emerge", "direction", "bottom", "top"] + text: I18n.tr("Launcher Emerge Side") + description: I18n.tr("Edge the launcher slides from") + model: [I18n.tr("Bottom"), I18n.tr("Top")] + currentIndex: SettingsData.frameLauncherEmergeSide === "top" ? 1 : 0 + onSelectionChanged: (index, selected) => { + if (!selected) + return; + SettingsData.set("frameLauncherEmergeSide", index === 1 ? "top" : "bottom"); + } + } + } + SettingsCard { width: parent.width iconName: "toolbar" @@ -266,73 +330,18 @@ Item { settingKey: "frameBarIntegration" collapsible: true expanded: true - visible: SettingsData.frameEnabled + visible: SettingsData.frameEnabled && CompositorService.isNiri SettingsToggleRow { - visible: CompositorService.isNiri settingKey: "frameShowOnOverview" tags: ["frame", "overview", "show", "hide", "niri"] text: I18n.tr("Show on Overview") - description: I18n.tr("Show the bar and frame during Niri overview mode") + description: I18n.tr("Show during Niri overview") checked: SettingsData.frameShowOnOverview onToggled: checked => SettingsData.set("frameShowOnOverview", checked) } - - SettingsToggleRow { - visible: SettingsData.frameEnabled - settingKey: "directionalAnimationMode" - tags: ["frame", "connected", "popout", "corner", "animation"] - text: I18n.tr("Connected Mode") - description: I18n.tr("Popouts emerge flush from the bar edge as one continuous piece") - checked: SettingsData.connectedFrameModeActive - onToggled: checked => { - if (checked) { - if (SettingsData.directionalAnimationMode !== 3) - SettingsData.set("previousDirectionalMode", SettingsData.directionalAnimationMode); - SettingsData.set("motionEffect", 1); - SettingsData.set("directionalAnimationMode", 3); - } else { - SettingsData.set("directionalAnimationMode", SettingsData.previousDirectionalMode); - } - } - - Connections { - target: SettingsData - function onDirectionalAnimationModeChanged() {} - function onMotionEffectChanged() {} - } - } - - SettingsToggleRow { - visible: SettingsData.frameEnabled - settingKey: "frameCloseGaps" - tags: ["frame", "connected", "gap", "edge", "flush", "popout", "notification"] - text: I18n.tr("Close the Gaps") - description: I18n.tr("Connected popouts and notification corners sit flush against the frame edge") - checked: SettingsData.frameCloseGaps - enabled: SettingsData.connectedFrameModeActive - opacity: enabled ? 1.0 : 0.5 - onToggled: checked => SettingsData.set("frameCloseGaps", checked) - } - - SettingsButtonGroupRow { - visible: SettingsData.frameEnabled - settingKey: "frameLauncherEmergeSide" - tags: ["frame", "connected", "launcher", "modal", "emerge", "direction", "bottom", "top"] - text: I18n.tr("Launcher Emerge Side") - description: I18n.tr("Which frame edge the Launcher slides in from. Other modals emerge from the opposite side.") - model: [I18n.tr("Bottom"), I18n.tr("Top")] - currentIndex: SettingsData.frameLauncherEmergeSide === "top" ? 1 : 0 - enabled: SettingsData.connectedFrameModeActive - opacity: enabled ? 1.0 : 0.5 - onSelectionChanged: (index, selected) => { - if (!selected) return; - SettingsData.set("frameLauncherEmergeSide", index === 1 ? "top" : "bottom"); - } - } } - // ── Display Assignment ──────────────────────────────────────────── SettingsCard { width: parent.width iconName: "monitor" diff --git a/quickshell/Modules/Settings/ThemeColorsTab.qml b/quickshell/Modules/Settings/ThemeColorsTab.qml index 49b2bfee..e88408b2 100644 --- a/quickshell/Modules/Settings/ThemeColorsTab.qml +++ b/quickshell/Modules/Settings/ThemeColorsTab.qml @@ -11,6 +11,7 @@ import qs.Modules.Settings.Widgets Item { id: themeColorsTab + property var parentModal: null readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive property var cachedIconThemes: SettingsData.availableIconThemes property var cachedCursorThemes: SettingsData.availableCursorThemes @@ -1614,16 +1615,20 @@ Item { } } + SettingsControlledByFrame { + visible: themeColorsTab.connectedFrameModeActive + parentModal: themeColorsTab.parentModal + settingLabel: I18n.tr("Surface Opacity") + reason: I18n.tr("Managed by Frame in Connected Mode") + } + SettingsSliderRow { tab: "theme" tags: ["surface", "popup", "transparency", "opacity", "modal"] settingKey: "popupTransparency" text: I18n.tr("Surface Opacity") - description: themeColorsTab.connectedFrameModeActive - ? I18n.tr("Connected Frame mode follows Surface Opacity from the Frame tab for connected popouts, docks, and modal surfaces") - : I18n.tr("Controls opacity of all popouts, modals, and their content layers") - enabled: !themeColorsTab.connectedFrameModeActive - opacity: themeColorsTab.connectedFrameModeActive ? 0.5 : 1.0 + description: I18n.tr("Controls opacity of all popouts, modals, and their content layers") + visible: !themeColorsTab.connectedFrameModeActive value: Math.round(SettingsData.popupTransparency * 100) minimum: 0 maximum: 100 @@ -1637,9 +1642,7 @@ Item { tags: ["corner", "radius", "rounded", "square"] settingKey: "cornerRadius" text: I18n.tr("Corner Radius") - description: themeColorsTab.connectedFrameModeActive - ? I18n.tr("Controls general UI rounding. Connected frame popouts, docks, and modal surfaces follow Border Radius in the Frame tab while Connected Frame mode is active") - : I18n.tr("0 = square corners") + description: I18n.tr("0 = square corners") value: SettingsData.cornerRadius minimum: 0 maximum: 32 @@ -1844,11 +1847,7 @@ Item { tags: ["blur", "background", "transparency", "glass", "frosted"] settingKey: "blurEnabled" text: I18n.tr("Background Blur") - description: !BlurService.available - ? I18n.tr("Requires a newer version of Quickshell") - : (themeColorsTab.connectedFrameModeActive - ? I18n.tr("Connected Frame mode follows Frame Blur for connected surfaces while this remains the master blur availability toggle") - : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.")) + description: !BlurService.available ? I18n.tr("Requires a newer version of Quickshell") : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.") checked: SettingsData.blurEnabled ?? false enabled: BlurService.available onToggled: checked => SettingsData.set("blurEnabled", checked) diff --git a/quickshell/Modules/Settings/TypographyMotionTab.qml b/quickshell/Modules/Settings/TypographyMotionTab.qml index 5b6e1668..4ad421cd 100644 --- a/quickshell/Modules/Settings/TypographyMotionTab.qml +++ b/quickshell/Modules/Settings/TypographyMotionTab.qml @@ -191,46 +191,6 @@ Item { } } } - - Rectangle { - width: parent.width - height: 1 - color: Theme.outline - opacity: 0.15 - visible: SettingsData.motionEffect === 1 - } - - SettingsDropdownRow { - visible: SettingsData.motionEffect === 1 - tab: "typography" - tags: ["animation", "directional", "behavior", "fluid", "connected"] - settingKey: "directionalAnimationMode" - text: I18n.tr("Directional Behavior") - description: { - if (SettingsData.connectedFrameModeActive) - return I18n.tr("Popouts emerge flush from the bar edge as a single continuous piece, with corner connectors bridging the junction"); - return I18n.tr("How the popout emerges from the DankBar"); - } - options: SettingsData.frameEnabled ? [I18n.tr("Fluid"), I18n.tr("Connected")] : [I18n.tr("Fluid")] - currentValue: { - switch (SettingsData.directionalAnimationMode) { - case 3: - return SettingsData.frameEnabled ? I18n.tr("Connected") : I18n.tr("Fluid"); - default: - return I18n.tr("Fluid"); - } - } - onValueChanged: value => { - if (value === I18n.tr("Fluid")) - SettingsData.set("directionalAnimationMode", 1); - else if (value === I18n.tr("Connected") && SettingsData.frameEnabled) { - if (SettingsData.directionalAnimationMode !== 3) - SettingsData.set("previousDirectionalMode", 1); - SettingsData.set("directionalAnimationMode", 3); - } else - SettingsData.set("directionalAnimationMode", 1); - } - } } SettingsCard { diff --git a/quickshell/Modules/Settings/Widgets/SettingsControlledByFrame.qml b/quickshell/Modules/Settings/Widgets/SettingsControlledByFrame.qml new file mode 100644 index 00000000..fe09f90c --- /dev/null +++ b/quickshell/Modules/Settings/Widgets/SettingsControlledByFrame.qml @@ -0,0 +1,79 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import qs.Common +import qs.Widgets + +StyledRect { + id: root + + LayoutMirroring.enabled: I18n.isRtl + LayoutMirroring.childrenInherit: true + + property string settingLabel: "" + property string reason: "" + property var parentModal: null + + width: parent?.width ?? 0 + height: contentRow.implicitHeight + Theme.spacingM * 2 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.primary, 0.08) + border.color: Theme.withAlpha(Theme.primary, 0.18) + border.width: 1 + + Row { + id: contentRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: Theme.spacingM + anchors.rightMargin: Theme.spacingM + spacing: Theme.spacingM + + DankIcon { + name: "frame_source" + size: Theme.iconSize + color: Theme.primary + anchors.verticalCenter: parent.verticalCenter + } + + Column { + anchors.verticalCenter: parent.verticalCenter + width: parent.width - Theme.iconSize - openButton.width - Theme.spacingM * 2 + spacing: 2 + + StyledText { + text: root.settingLabel + font.pixelSize: Theme.fontSizeMedium + font.weight: Font.Medium + color: Theme.surfaceText + width: parent.width + wrapMode: Text.WordWrap + } + + StyledText { + text: root.reason + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + width: parent.width + wrapMode: Text.WordWrap + visible: root.reason !== "" + } + } + + DankButton { + id: openButton + anchors.verticalCenter: parent.verticalCenter + text: I18n.tr("Open Frame") + backgroundColor: Theme.primary + textColor: Theme.primaryText + buttonHeight: 32 + horizontalPadding: Theme.spacingM + onClicked: { + if (!root.parentModal) + return; + root.parentModal.showWithTabName("frame"); + } + } + } +} diff --git a/quickshell/Widgets/DankPopoutConnected.qml b/quickshell/Widgets/DankPopoutConnected.qml index 2f4d27bb..a478940a 100644 --- a/quickshell/Widgets/DankPopoutConnected.qml +++ b/quickshell/Widgets/DankPopoutConnected.qml @@ -500,11 +500,8 @@ Item { readonly property real shadowMotionPadding: { if (Theme.isConnectedEffect) return Math.max(storedBarSpacing + Theme.connectedCornerRadius + 4, 40); - if (Theme.isDirectionalEffect) { - if (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0) - return 16; // Fluid uses strict Wayland clipping instead of extra motion padding. - return Math.max(0, animationOffset) + 16; - } + if (Theme.isDirectionalEffect) + return 16; if (Theme.isDepthEffect) return Math.max(0, animationOffset) + 8; return Math.max(0, animationOffset); @@ -901,7 +898,7 @@ Item { Item { id: directionalClipMask - readonly property bool shouldClip: (Theme.isDirectionalEffect && typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0) || Theme.isConnectedEffect + readonly property bool shouldClip: Theme.isDirectionalEffect || Theme.isConnectedEffect readonly property real clipOversize: 1000 readonly property real connectedClipAllowance: { if (!Theme.isConnectedEffect)