From 88d795a73f0b8bff3d372c2676f2c532585d4202 Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Mon, 9 Mar 2026 22:17:34 +0100 Subject: [PATCH] start of refactor --- Components/CustomWindow.qml | 9 ++ Config/BarConfig.qml | 2 + Config/Config.qml | 2 + Drawers/Backgrounds.qml | 2 +- Drawers/Bar.qml | 18 +-- Drawers/Exclusions.qml | 41 +++++++ Drawers/Panels.qml | 2 +- Modules/Bar/Bar.qml | 215 ++++++++++++++++++++++++++++++++ Modules/Bar/BarLoader.qml | 239 +++++++----------------------------- Modules/Bar/Border.qml | 6 +- Modules/Drawing/Wrapper.qml | 10 +- Modules/Workspaces.qml | 4 +- 12 files changed, 329 insertions(+), 221 deletions(-) create mode 100644 Components/CustomWindow.qml create mode 100644 Drawers/Exclusions.qml create mode 100644 Modules/Bar/Bar.qml diff --git a/Components/CustomWindow.qml b/Components/CustomWindow.qml new file mode 100644 index 0000000..7e5bf2c --- /dev/null +++ b/Components/CustomWindow.qml @@ -0,0 +1,9 @@ +import Quickshell +import Quickshell.Wayland + +PanelWindow { + required property string name + + WlrLayershell.namespace: `ZShell-${name}` + color: "transparent" +} diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml index 4ec7578..7b39915 100644 --- a/Config/BarConfig.qml +++ b/Config/BarConfig.qml @@ -2,6 +2,7 @@ import Quickshell.Io JsonObject { property bool autoHide: false + property int border: 8 property list entries: [ { id: "workspaces", @@ -60,6 +61,7 @@ JsonObject { enabled: true }, ] + property int height: 34 property Popouts popouts: Popouts { } property int rounding: 8 diff --git a/Config/Config.qml b/Config/Config.qml index ef5fb65..4fc63dc 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -81,6 +81,8 @@ Singleton { return { autoHide: barConfig.autoHide, rounding: barConfig.rounding, + border: barConfig.border, + height: barConfig.height, popouts: { tray: barConfig.popouts.tray, audio: barConfig.popouts.audio, diff --git a/Drawers/Backgrounds.qml b/Drawers/Backgrounds.qml index 2eab002..5493b82 100644 --- a/Drawers/Backgrounds.qml +++ b/Drawers/Backgrounds.qml @@ -22,7 +22,7 @@ Shape { required property PersistentProperties visibilities anchors.fill: parent - // anchors.margins: 8 + anchors.margins: Config.barConfig.border anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight preferredRendererType: Shape.CurveRenderer diff --git a/Drawers/Bar.qml b/Drawers/Bar.qml index be1bd99..82e46ca 100644 --- a/Drawers/Bar.qml +++ b/Drawers/Bar.qml @@ -56,21 +56,9 @@ Variants { y: Config.barConfig.autoHide && !visibilities.bar ? 4 : backgroundRect.height } - PanelWindow { - id: exclusionZone - - WlrLayershell.exclusionMode: Config.barConfig.autoHide ? ExclusionMode.Ignore : ExclusionMode.Auto - WlrLayershell.layer: WlrLayer.Bottom - WlrLayershell.namespace: "ZShell-Bar-Exclusion" - color: "transparent" - implicitHeight: backgroundRect.height - screen: bar.screen - - anchors { - left: true - right: true - top: true - } + Exclusions { + bar: barLoader + screen: scope.modelData } anchors { diff --git a/Drawers/Exclusions.qml b/Drawers/Exclusions.qml new file mode 100644 index 0000000..7c3603f --- /dev/null +++ b/Drawers/Exclusions.qml @@ -0,0 +1,41 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Config +import qs.Components + +Scope { + id: root + + required property Item bar + required property ShellScreen screen + + ExclusionZone { + anchors.top: true + exclusiveZone: root.bar.exclusiveZone + } + + ExclusionZone { + anchors.left: true + } + + ExclusionZone { + anchors.right: true + } + + ExclusionZone { + anchors.bottom: true + } + + component ExclusionZone: CustomWindow { + exclusiveZone: Config.barConfig.border + implicitHeight: 1 + implicitWidth: 1 + name: "Bar-Exclusion" + screen: root.screen + + mask: Region { + } + } +} diff --git a/Drawers/Panels.qml b/Drawers/Panels.qml index 0c81408..1a1fce2 100644 --- a/Drawers/Panels.qml +++ b/Drawers/Panels.qml @@ -34,7 +34,7 @@ Item { required property PersistentProperties visibilities anchors.fill: parent - // anchors.margins: 8 + anchors.margins: Config.barConfig.border anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight Behavior on anchors.topMargin { diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml new file mode 100644 index 0000000..eb1dacd --- /dev/null +++ b/Modules/Bar/Bar.qml @@ -0,0 +1,215 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import QtQuick.Layouts +import qs.Components +import qs.Modules +import qs.Config +import qs.Helpers +import qs.Modules.UPower +import qs.Modules.Network + +RowLayout { + id: root + + required property Wrapper popouts + required property ShellScreen screen + readonly property int vPadding: 6 + required property PersistentProperties visibilities + + function checkPopout(x: real): void { + const ch = childAt(x, 2) as WrappedLoader; + + if (!ch || ch?.id === "spacer") { + if (!popouts.currentName.startsWith("traymenu")) + popouts.hasCurrent = false; + return; + } + + if (visibilities.sidebar || visibilities.dashboard || visibilities.resources) + return; + + const id = ch.id; + const top = ch.x; + const item = ch.item; + const itemWidth = item.implicitWidth; + + if (id === "audio" && Config.barConfig.popouts.audio) { + popouts.currentName = "audio"; + popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); + popouts.hasCurrent = true; + } else if (id === "network" && Config.barConfig.popouts.network) { + popouts.currentName = "network"; + popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); + popouts.hasCurrent = true; + } else if (id === "upower" && Config.barConfig.popouts.upower) { + popouts.currentName = "upower"; + popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); + popouts.hasCurrent = true; + } + } + + spacing: Appearance.spacing.small + + Repeater { + id: repeater + + // model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index) + model: Config.barConfig.entries + + DelegateChooser { + role: "id" + + DelegateChoice { + roleValue: "spacer" + + delegate: WrappedLoader { + Layout.fillWidth: true + } + } + + DelegateChoice { + roleValue: "workspaces" + + delegate: WrappedLoader { + sourceComponent: Workspaces { + screen: root.screen + } + } + } + + DelegateChoice { + roleValue: "audio" + + delegate: WrappedLoader { + sourceComponent: AudioWidget { + } + } + } + + DelegateChoice { + roleValue: "tray" + + delegate: WrappedLoader { + sourceComponent: TrayWidget { + bar: root.bar + loader: root + popouts: root.popouts + } + } + } + + DelegateChoice { + roleValue: "resources" + + delegate: WrappedLoader { + sourceComponent: Resources { + visibilities: root.visibilities + } + } + } + + DelegateChoice { + roleValue: "updates" + + delegate: WrappedLoader { + sourceComponent: UpdatesWidget { + } + } + } + + DelegateChoice { + roleValue: "notifBell" + + delegate: WrappedLoader { + sourceComponent: NotifBell { + popouts: root.popouts + visibilities: root.visibilities + } + } + } + + DelegateChoice { + roleValue: "clock" + + delegate: WrappedLoader { + sourceComponent: Clock { + loader: root + popouts: root.popouts + visibilities: root.visibilities + } + } + } + + DelegateChoice { + roleValue: "activeWindow" + + delegate: WrappedLoader { + sourceComponent: WindowTitle { + bar: root + } + } + } + + DelegateChoice { + roleValue: "upower" + + delegate: WrappedLoader { + sourceComponent: UPowerWidget { + } + } + } + + DelegateChoice { + roleValue: "network" + + delegate: WrappedLoader { + sourceComponent: NetworkWidget { + } + } + } + + DelegateChoice { + roleValue: "media" + + delegate: WrappedLoader { + sourceComponent: MediaWidget { + } + } + } + } + } + + component WrappedLoader: Loader { + required property bool enabled + required property string id + required property int index + + function findFirstEnabled(): Item { + const count = repeater.count; + for (let i = 0; i < count; i++) { + const item = repeater.itemAt(i); + if (item?.enabled) + return item; + } + return null; + } + + function findLastEnabled(): Item { + for (let i = repeater.count - 1; i >= 0; i--) { + const item = repeater.itemAt(i); + if (item?.enabled) + return item; + } + return null; + } + + Layout.alignment: Qt.AlignVCenter + Layout.fillHeight: true + Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0 + Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0 + active: enabled + visible: enabled + } +} diff --git a/Modules/Bar/BarLoader.qml b/Modules/Bar/BarLoader.qml index 67e27e5..560529e 100644 --- a/Modules/Bar/BarLoader.qml +++ b/Modules/Bar/BarLoader.qml @@ -11,223 +11,72 @@ import qs.Helpers import qs.Modules.UPower import qs.Modules.Network -RowLayout { +Item { id: root required property PanelWindow bar + readonly property int contentHeight: Config.barConfig.height + padding * 2 + readonly property int exclusiveZone: Config.barConfig.autoHide ? Config.barConfig.border : contentHeight + property bool isHovered + readonly property int padding: Math.max(Appearance.padding.smaller, Config.barConfig.border) required property Wrapper popouts required property ShellScreen screen + readonly property bool shouldBeVisible: (!Config.barConfig.autoHide || visibilities.bar) readonly property int vPadding: 6 required property PersistentProperties visibilities function checkPopout(x: real): void { - const ch = childAt(x, 2) as WrappedLoader; - - if (!ch || ch?.id === "spacer") { - if (!popouts.currentName.startsWith("traymenu")) - popouts.hasCurrent = false; - return; - } - - if (visibilities.sidebar || visibilities.dashboard || visibilities.resources) - return; - - const id = ch.id; - const top = ch.x; - const item = ch.item; - const itemWidth = item.implicitWidth; - - if (id === "audio" && Config.barConfig.popouts.audio) { - popouts.currentName = "audio"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } else if (id === "network" && Config.barConfig.popouts.network) { - popouts.currentName = "network"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } else if (id === "upower" && Config.barConfig.popouts.upower) { - popouts.currentName = "upower"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } + content.item?.checkPopout(x); } - implicitHeight: 34 + implicitHeight: Config.barConfig.border + visible: width > Config.barConfig.border - CustomShortcut { - name: "toggle-overview" + states: State { + name: "visible" + when: root.shouldBeVisible - onPressed: { - Hyprland.refreshWorkspaces(); - Hyprland.refreshMonitors(); - if (root.popouts.hasCurrent && root.popouts.currentName === "overview") { - root.popouts.hasCurrent = false; - } else { - root.popouts.currentName = "overview"; - root.popouts.currentCenter = root.width / 2; - root.popouts.hasCurrent = true; - } + PropertyChanges { + root.implicitHeight: root.contentHeight } } + transitions: [ + Transition { + from: "" + to: "visible" - Repeater { - id: repeater - - // model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index) - model: Config.barConfig.entries - - DelegateChooser { - role: "id" - - DelegateChoice { - roleValue: "spacer" - - delegate: WrappedLoader { - Layout.fillWidth: true - } + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + property: "implicitHeight" + target: root } + }, + Transition { + from: "visible" + to: "" - DelegateChoice { - roleValue: "workspaces" - - delegate: WrappedLoader { - sourceComponent: Workspaces { - bar: root.bar - } - } - } - - DelegateChoice { - roleValue: "audio" - - delegate: WrappedLoader { - sourceComponent: AudioWidget { - } - } - } - - DelegateChoice { - roleValue: "tray" - - delegate: WrappedLoader { - sourceComponent: TrayWidget { - bar: root.bar - loader: root - popouts: root.popouts - } - } - } - - DelegateChoice { - roleValue: "resources" - - delegate: WrappedLoader { - sourceComponent: Resources { - visibilities: root.visibilities - } - } - } - - DelegateChoice { - roleValue: "updates" - - delegate: WrappedLoader { - sourceComponent: UpdatesWidget { - } - } - } - - DelegateChoice { - roleValue: "notifBell" - - delegate: WrappedLoader { - sourceComponent: NotifBell { - popouts: root.popouts - visibilities: root.visibilities - } - } - } - - DelegateChoice { - roleValue: "clock" - - delegate: WrappedLoader { - sourceComponent: Clock { - loader: root - popouts: root.popouts - visibilities: root.visibilities - } - } - } - - DelegateChoice { - roleValue: "activeWindow" - - delegate: WrappedLoader { - sourceComponent: WindowTitle { - bar: root - } - } - } - - DelegateChoice { - roleValue: "upower" - - delegate: WrappedLoader { - sourceComponent: UPowerWidget { - } - } - } - - DelegateChoice { - roleValue: "network" - - delegate: WrappedLoader { - sourceComponent: NetworkWidget { - } - } - } - - DelegateChoice { - roleValue: "media" - - delegate: WrappedLoader { - sourceComponent: MediaWidget { - } - } + Anim { + easing.bezierCurve: Appearance.anim.curves.emphasized + property: "implicitHeight" + target: root } } - } + ] - component WrappedLoader: Loader { - required property bool enabled - required property string id - required property int index + Loader { + id: content - function findFirstEnabled(): Item { - const count = repeater.count; - for (let i = 0; i < count; i++) { - const item = repeater.itemAt(i); - if (item?.enabled) - return item; - } - return null; + active: root.shouldBeVisible || root.visible + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + sourceComponent: Bar { + height: root.contentHeight + popouts: root.popouts + screen: root.screen + visibilities: root.visibilities } - - function findLastEnabled(): Item { - for (let i = repeater.count - 1; i >= 0; i--) { - const item = repeater.itemAt(i); - if (item?.enabled) - return item; - } - return null; - } - - Layout.alignment: Qt.AlignVCenter - Layout.fillHeight: true - Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0 - Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0 - active: enabled - visible: enabled } } diff --git a/Modules/Bar/Border.qml b/Modules/Bar/Border.qml index 875d218..c091795 100644 --- a/Modules/Bar/Border.qml +++ b/Modules/Bar/Border.qml @@ -38,9 +38,11 @@ Item { Rectangle { anchors.fill: parent + anchors.margins: Config.barConfig.border anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight - topLeftRadius: 8 - topRightRadius: 8 + radius: Config.barConfig.border > 0 ? Config.barConfig.rounding : 0 + topLeftRadius: Config.barConfig.rounding + topRightRadius: Config.barConfig.rounding Behavior on anchors.topMargin { Anim { diff --git a/Modules/Drawing/Wrapper.qml b/Modules/Drawing/Wrapper.qml index 6908fd8..2705e61 100644 --- a/Modules/Drawing/Wrapper.qml +++ b/Modules/Drawing/Wrapper.qml @@ -16,11 +16,6 @@ Item { readonly property bool shouldBeActive: visibilities.isDrawing required property var visibilities - function show(): void { - visibilities.isDrawing = true; - timer.restart(); - } - implicitHeight: content.implicitHeight implicitWidth: 0 visible: width > 0 @@ -102,6 +97,11 @@ Item { } ] + onVisibleChanged: { + if (!visible) + root.expanded = true; + } + Loader { id: icon diff --git a/Modules/Workspaces.qml b/Modules/Workspaces.qml index e444508..9fd47dc 100644 --- a/Modules/Workspaces.qml +++ b/Modules/Workspaces.qml @@ -12,9 +12,9 @@ Item { id: root property real activeWorkspaceMargin: Math.ceil(Appearance.padding.small / 2) - required property PanelWindow bar readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.bar.screen) + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) + required property ShellScreen screen property int workspaceButtonWidth: bgRect.implicitHeight - root.activeWorkspaceMargin * 2 property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown readonly property list workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)