From d32bedda31524e9d38cc4dd3a5637d0de693db8b Mon Sep 17 00:00:00 2001 From: kossLAN Date: Sun, 10 Aug 2025 01:41:30 -0400 Subject: [PATCH] progress save --- shell/ShellSettings.qml | 33 +++-- shell/bar/Bar.qml | 167 ++++++++++++++---------- shell/bar/Controller.qml | 10 -- shell/bar/PopupHandler.qml | 173 ++++++++++++++----------- shell/bar/PopupItem.qml | 8 ++ shell/bar/power/BatteryIndicator.qml | 1 - shell/bar/systray/SysTray.qml | 84 ++---------- shell/bar/systray/TrayMenuLauncher.qml | 62 +++++++++ shell/shell.qml | 4 +- 9 files changed, 293 insertions(+), 249 deletions(-) delete mode 100644 shell/bar/Controller.qml create mode 100644 shell/bar/PopupItem.qml create mode 100644 shell/bar/systray/TrayMenuLauncher.qml diff --git a/shell/ShellSettings.qml b/shell/ShellSettings.qml index a4fa7b0..133e16d 100644 --- a/shell/ShellSettings.qml +++ b/shell/ShellSettings.qml @@ -7,13 +7,26 @@ import Quickshell.Io Singleton { property alias settings: jsonAdapter.settings property alias sizing: jsonAdapter.sizing - property alias colors: jsonAdapter.colors + + property QtObject colors: QtObject { + property color surface: Qt.rgba(1.0, 1.0, 1.0, 1.0) + property color surface_translucent: Qt.rgba(0.0, 0.0, 0.0, 0.15) + property color surface_container: Qt.rgba(0.25, 0.25, 0.25, 1.0) + property color surface_container_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.25) + property color highlight: Qt.rgba(1.0, 1.0, 1.0, 0.85) + // property color primary: "#2EADC6" + property color active: Qt.rgba(1.0, 1.0, 1.0, 1.0) + property color active_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.15) + property color border_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.05) + property color inactive: Qt.rgba(0.25, 0.25, 0.25, 1.0) + property color inactive_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.15) + } FileView { path: `${Quickshell.dataPath("settings")}/quickshell/settings.json` watchChanges: true - // onFileChanged: reload() - // onAdapterUpdated: writeAdapter() + onFileChanged: reload() + onAdapterUpdated: writeAdapter() blockLoading: true JsonAdapter { @@ -28,20 +41,6 @@ Singleton { property JsonObject sizing: JsonObject { property int barHeight: 25 } - - property JsonObject colors: JsonObject { - property color surface: Qt.rgba(1.0, 1.0, 1.0, 1.0) - property color surface_translucent: Qt.rgba(0.0, 0.0, 0.0, 0.15) - property color surface_container: Qt.rgba(0.25, 0.25, 0.25, 1.0) - property color surface_container_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.25) - property color highlight: Qt.rgba(1.0, 1.0, 1.0, 0.85) - // property color primary: "#2EADC6" - property color active: Qt.rgba(1.0, 1.0, 1.0, 1.0) - property color active_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.15) - property color border_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.05) - property color inactive: Qt.rgba(0.25, 0.25, 0.25, 1.0) - property color inactive_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.15) - } } } } diff --git a/shell/bar/Bar.qml b/shell/bar/Bar.qml index 00d7ccf..bc727d9 100644 --- a/shell/bar/Bar.qml +++ b/shell/bar/Bar.qml @@ -7,91 +7,120 @@ import "systray" // import qs.widgets import qs -PanelWindow { - id: root - color: "transparent" - implicitHeight: ShellSettings.sizing.barHeight - property alias popup: popupHandler +Variants { + model: Quickshell.screens - anchors { - top: true - left: true - right: true - } - - Rectangle { + delegate: PanelWindow { + id: root color: ShellSettings.colors.surface_translucent - anchors.fill: parent - } + implicitHeight: ShellSettings.sizing.barHeight + screen: modelData - PopupHandler { - id: popupHandler - bar: root - } - - RowLayout { - spacing: 0 + required property var modelData anchors { - fill: parent - leftMargin: 5 - rightMargin: 5 + top: true + left: true + right: true } - // Left side of bar - RowLayout { - spacing: 15 - Layout.fillWidth: true - Layout.fillHeight: true - - Workspaces { - screen: root.screen - Layout.fillHeight: true - } - - ActiveWindow { - id: activeWindow - Layout.preferredWidth: 400 - } + PopupHandler { + id: popupHandler + bar: root } - // Right side of bar RowLayout { - spacing: 10 - Layout.fillWidth: true - Layout.fillHeight: true - Layout.alignment: Qt.AlignRight + spacing: 0 - SysTray { - id: sysTray - popup: root.popup - // bar: root - Layout.fillHeight: true + anchors { + fill: parent + leftMargin: 5 + rightMargin: 5 } - // VolumeIndicator { - // id: volumeIndicator - // popup: root.popup - // Layout.preferredWidth: this.height - // Layout.fillHeight: true - // Layout.topMargin: 2 - // Layout.bottomMargin: 2 - // } + // Left side of bar + RowLayout { + spacing: 15 + Layout.fillWidth: true + Layout.fillHeight: true - // BatteryIndicator { - // id: batteryIndicator - // popup: root.popup - // Layout.fillHeight: true - // } + Workspaces { + screen: root.screen + Layout.fillHeight: true + } - // Widgets.Separator { - // Layout.leftMargin: 5 - // Layout.rightMargin: 5 - // } + ActiveWindow { + id: activeWindow + Layout.preferredWidth: 400 + } + } - Clock { - id: clock - color: ShellSettings.colors.active + // Right side of bar + RowLayout { + spacing: 10 + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignRight + + SysTray { + id: sysTray + popup: popupHandler + Layout.fillHeight: true + } + + PopupItem { + id: test + Layout.preferredWidth: 20 + Layout.fillHeight: true + + onClicked: { + popupHandler.set(test); + } + + menu: Rectangle { + implicitWidth: 100 + implicitHeight: 100 + } + } + + PopupItem { + id: test2 + Layout.preferredWidth: 20 + Layout.fillHeight: true + + onClicked: { + popupHandler.set(test2); + } + + menu: Rectangle { + implicitWidth: 200 + implicitHeight: 200 + } + } + + // VolumeIndicator { + // id: volumeIndicator + // popup: root.popup + // Layout.preferredWidth: this.height + // Layout.fillHeight: true + // Layout.topMargin: 2 + // Layout.bottomMargin: 2 + // } + + // BatteryIndicator { + // id: batteryIndicator + // Layout.fillHeight: true + // } + + // Widgets.Separator { + // Layout.leftMargin: 5 + // Layout.rightMargin: 5 + // } + + Clock { + id: clock + color: ShellSettings.colors.active + } } } } diff --git a/shell/bar/Controller.qml b/shell/bar/Controller.qml deleted file mode 100644 index 9724241..0000000 --- a/shell/bar/Controller.qml +++ /dev/null @@ -1,10 +0,0 @@ -import Quickshell - -Variants { - model: Quickshell.screens - - Bar { - required property var modelData - screen: modelData - } -} diff --git a/shell/bar/PopupHandler.qml b/shell/bar/PopupHandler.qml index 8424265..0f4565e 100644 --- a/shell/bar/PopupHandler.qml +++ b/shell/bar/PopupHandler.qml @@ -1,17 +1,19 @@ +pragma ComponentBehavior: Bound + import Quickshell -import Quickshell.Hyprland import Quickshell.Widgets +import Quickshell.Hyprland import QtQuick import qs PopupWindow { id: root + visible: false color: "transparent" implicitWidth: bar.width - implicitHeight: Math.max(popupContainer.height, 800) + 20 - + implicitHeight: Math.max(1000, bar.height) mask: Region { - item: popupContainer + item: surface } anchor { @@ -23,103 +25,126 @@ PopupWindow { } required property var bar - property var isOpen: false - property var padding: 5 - property var item - property var content + property var currentMenu + property real padding: 5 - function set(item, content) { - root.item = item; - root.content = content; - popupContent.data = content; + function set(item) { + + // Clear surface + if (content.children.includes(item.menu)) { + console.log("Clearing popup surface."); + root.currentMenu = undefined; + content.children = []; + // surface.implicitHeight = 0; + surface.opacity = 0; + contentOpacity.restart(); + grab.active = false; + return; + } + + // Set surface + console.log("Setting popup surface."); + root.visible = true; + root.currentMenu = item.menu + content.children = [item.menu]; + // content.implicitWidth = item.menu.implicitWidth; + // content.implicitHeight = item.menu.implicitHeight; let itemPos = item.mapToItem(root.bar.contentItem, 0, root.bar.height, item.width, 0).x; - position(itemPos); - popupContainer.visible = false; - } - - function position(itemPos) { - if (itemPos === undefined) - return; - - let rightEdge = itemPos + popupContainer.implicitWidth; + // Check right edge + let rightEdge = itemPos + surface.implicitWidth; let maxRightEdge = root.width - padding; let isTouchingRightEdge = rightEdge > maxRightEdge; if (isTouchingRightEdge) { // touching right edge, reposition - // console.log("touching right edge"); - popupContainer.x = maxRightEdge - popupContainer.implicitWidth; - popupContainer.y = padding; + surface.x = maxRightEdge - surface.implicitWidth; + surface.y = padding; } else { // not touching right edge - popupContainer.x = itemPos; - popupContainer.y = padding; + surface.x = itemPos; + surface.y = padding; } - } - function show() { + surface.opacity = 1; + contentOpacity.restart(); grab.active = true; - isOpen = true; - root.visible = true; // set and leave open - root.content.visible = true; - popupContainer.visible = true; } - function hide() { - grab.active = false; - isOpen = false; - popupContainer.visible = false; - - root.item = undefined; - root.content = undefined; - popupContent.data = []; - } - - function toggle() { - if (isOpen) { - hide(); - } else { - show(); + HyprlandFocusGrab { + id: grab + windows: [root, root.bar] + onCleared: { + surface.opacity = 0; + contentOpacity.restart(); + root.currentMenu = undefined; + content.children = []; } } - Rectangle { + WrapperRectangle { + id: surface + opacity: 0 + visible: opacity > 0 color: ShellSettings.colors.surface_translucent - opacity: 0.15 - radius: 12 - anchors.fill: popupContainer - border.color: ShellSettings.colors.active - } - - WrapperItem { - id: popupContainer - margin: 8 clip: true - x: root.bar.width - onVisibleChanged: root.visible = visible + margin: 5 + radius: 12 - // needed to handle occurences where items are resized while open - onImplicitWidthChanged: { - if (root.isOpen && popupContent.data !== []) { - // console.log("repositioning popup"); - let itemPos = root.item.mapToItem(root.bar.contentItem, 0, root.bar.height, root.item.width, 0).x; - root.position(itemPos); + border { + width: 1 + color: ShellSettings.colors.active_translucent + } + + // Animating implicit widht/height causes issues, this works but + // is kind of cursed, but fuck it. Better solutions welcome. + width: implicitWidth + height: implicitHeight + + Item { + id: content + implicitWidth: Math.max(root.currentMenu?.width, 60) + implicitHeight: root.currentMenu?.height ?? 0 + + NumberAnimation { + id: contentOpacity + target: content + property: "opacity" + from: 0 + to: 1 + duration: 300 + easing.type: Easing.InOutQuad } } - Item { - id: popupContent - implicitWidth: Math.max(root.content?.width, 60) - implicitHeight: Math.max(childrenRect.height, 60) + Behavior on opacity { + NumberAnimation { + duration: 250 + easing.type: Easing.InOutQuad + } } - HyprlandFocusGrab { - id: grab - windows: [root, root.bar] - onCleared: { - root.hide(); + Behavior on width { + enabled: root.visible + SmoothedAnimation { + duration: 250 + easing.type: Easing.InOutQuad + } + } + + Behavior on height { + SmoothedAnimation { + duration: 250 + easing.type: Easing.InOutQuad + } + } + + Behavior on x { + enabled: root.visible + SmoothedAnimation { + duration: 250 + easing.type: Easing.InOutQuad } } } diff --git a/shell/bar/PopupItem.qml b/shell/bar/PopupItem.qml new file mode 100644 index 0000000..26b4b6d --- /dev/null +++ b/shell/bar/PopupItem.qml @@ -0,0 +1,8 @@ +import QtQuick +import qs.widgets + +StyledMouseArea { + id: root + + property QtObject menu +} diff --git a/shell/bar/power/BatteryIndicator.qml b/shell/bar/power/BatteryIndicator.qml index 0603017..c4b260a 100644 --- a/shell/bar/power/BatteryIndicator.qml +++ b/shell/bar/power/BatteryIndicator.qml @@ -26,7 +26,6 @@ Item { } root.popup.set(this, powerMenu); - root.popup.show(); } anchors { diff --git a/shell/bar/systray/SysTray.qml b/shell/bar/systray/SysTray.qml index 56c2a40..c6b8818 100644 --- a/shell/bar/systray/SysTray.qml +++ b/shell/bar/systray/SysTray.qml @@ -2,95 +2,27 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts -import Quickshell -import Quickshell.Widgets import Quickshell.Services.SystemTray -import qs.widgets +import ".." RowLayout { id: root spacing: 5 visible: SystemTray.items.values.length > 0 - required property var popup + required property PopupHandler popup Repeater { + id: repeater model: SystemTray.items - delegate: Item { - id: trayField + delegate: TrayMenuLauncher { + id: trayItem + required property SystemTrayItem modelData + trayItem: modelData + popup: root.popup Layout.preferredWidth: parent.height Layout.fillHeight: true - required property SystemTrayItem modelData - - StyledMouseArea { - id: trayButton - hoverEnabled: true - onClicked: { - menuOpener.menu = trayField.modelData.menu; - - if (root.popup.content == trayMenu) { - root.popup.hide(); - return; - } - - root.popup.set(this, trayMenu); - root.popup.show(); - } - - anchors { - fill: parent - margins: 2 - } - - IconImage { - id: trayIcon - anchors.fill: parent - source: { - // console.log(trayField.modelData.id); - switch (trayField.modelData.id) { - case "obs": - return "image://icon/obs-tray"; - default: - return trayField.modelData.icon; - } - } - } - } - - QsMenuOpener { - id: menuOpener - } - - WrapperItem { - id: trayMenu - visible: false - - property var leftItem: false - property var rightItem: false - - ColumnLayout { - id: menuContainer - spacing: 2 - - Repeater { - model: menuOpener.children - - delegate: TrayMenuItem { - id: sysTrayContent - Layout.fillWidth: true - Layout.fillHeight: true - - rootMenu: trayMenu - - onInteracted: { - root.popup.hide(); - menuOpener.menu = null; - } - } - } - } - } } } } diff --git a/shell/bar/systray/TrayMenuLauncher.qml b/shell/bar/systray/TrayMenuLauncher.qml new file mode 100644 index 0000000..44976cd --- /dev/null +++ b/shell/bar/systray/TrayMenuLauncher.qml @@ -0,0 +1,62 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.SystemTray +import ".." + +PopupItem { + id: root + onClicked: { + menuOpener.menu = trayItem.menu; + popup.set(menu); + } + + required property PopupHandler popup + required property SystemTrayItem trayItem + + menu: ColumnLayout { + id: trayMenu + spacing: 2 + // visible: false + + property var leftItem: false + property var rightItem: false + + Repeater { + model: menuOpener.children + + delegate: TrayMenuItem { + id: sysTrayContent + Layout.fillWidth: true + Layout.fillHeight: true + + rootMenu: trayMenu + + onInteracted: { + menuOpener.menu = null; + } + } + } + } + + QsMenuOpener { + id: menuOpener + } + + IconImage { + id: trayIcon + anchors.fill: parent + source: { + // console.log(trayField.modelData.id); + switch (root.trayItem.id) { + case "obs": + return "image://icon/obs-tray"; + default: + return root.trayItem.icon; + } + } + } +} diff --git a/shell/shell.qml b/shell/shell.qml index 1372c45..8527c2d 100644 --- a/shell/shell.qml +++ b/shell/shell.qml @@ -3,7 +3,7 @@ import Quickshell import QtQuick -import "bar" as Bar +import "bar" import "notifications" as Notifications import "mpris" as Mpris import "volume-osd" as VolumeOSD @@ -14,7 +14,7 @@ import "wallpaper" as Wallpaper import "screencapture" as ScreenCapture ShellRoot { - Bar.Controller {} + Bar {} Wallpaper.Controller {} Notifications.Controller {} VolumeOSD.Controller {}