From 0bfda04e919004fcf262f231fb8762f6d18be5a1 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 22 Sep 2026 02:35:18 +0200 Subject: [PATCH] intermediary changes, plan to revert to mousearea for gestures + config load bugfixes --- Drawers/Interactions.qml | 50 +-- Drawers/Interactions2.qml | 312 ++++++++++++++++++ Modules/Bar/Components/Clock.qml | 12 +- Modules/Dashboard/Media/LyricList.qml | 2 +- Modules/Launcher/Items/FilesItem.qml | 6 +- Modules/Lock/InputField.qml | 2 +- .../Sidebar/Utils/Cards/RecordingList.qml | 2 +- Modules/Settings/Pages/Apps/AllApps.qml | 2 +- Modules/Settings/Pages/Apps/AppInfo.qml | 2 +- Modules/Settings/Pages/AppsPage.qml | 2 +- Plugins/ZShell/Services/gpu.cpp | 2 +- Plugins/ZShell/Services/tickingservice.cpp | 2 +- 12 files changed, 351 insertions(+), 45 deletions(-) create mode 100644 Drawers/Interactions2.qml diff --git a/Drawers/Interactions.qml b/Drawers/Interactions.qml index a8a1bbc..327289a 100644 --- a/Drawers/Interactions.qml +++ b/Drawers/Interactions.qml @@ -87,15 +87,27 @@ Item { if (root.singleGestureTriggered) return; - // if (root.geometry.insetTop(root.borderThickness) > pressY) { - // if (dragY > 20) { - // root.screenState.settings = true; - // root.singleGestureTriggered = true; - // } else if (dragY < -20) { - // root.screenState.settings = false; - // root.singleGestureTriggered = true; - // } - // } + const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY); + + if (inDashboard && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) { + if (dragY > Config.dashboard.dragThreshold) { + root.screenState.dashboard = true; + root.singleGestureTriggered = true; + } else if (dragY < -Config.dashboard.dragThreshold) { + root.screenState.dashboard = false; + root.singleGestureTriggered = true; + } + } + + if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) { + if (dragY > 20) { + root.screenState.settings = true; + root.singleGestureTriggered = true; + } else if (dragY < -20) { + root.screenState.settings = false; + root.singleGestureTriggered = true; + } + } if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50) root.screenState.clipboard = true; @@ -120,16 +132,6 @@ Item { Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`); root.singleGestureTriggered = true; } - - if (root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY) && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) { - if (dragY > Config.dashboard.dragThreshold) { - root.screenState.dashboard = true; - root.singleGestureTriggered = true; - } else if (dragY < -Config.dashboard.dragThreshold) { - root.screenState.dashboard = false; - root.singleGestureTriggered = true; - } - } } } @@ -276,6 +278,7 @@ Item { root.screenState.settings = false; root.screenState.sidebar = false; + root.screenState.resources = false; root.popouts.hasCurrent = false; } else { root.dashboardShortcutActive = false; @@ -323,12 +326,13 @@ Item { } function onResourcesChanged() { - if (root.screenState.resources && (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))) { - root.popouts.hasCurrent = false; - } + if (root.screenState.resources) { + if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates")) + root.popouts.hasCurrent = false; - if (root.screenState.resources) root.screenState.settings = false; + root.screenState.dashboard = false; + } } function onSettingsChanged() { diff --git a/Drawers/Interactions2.qml b/Drawers/Interactions2.qml new file mode 100644 index 0000000..b35cf72 --- /dev/null +++ b/Drawers/Interactions2.qml @@ -0,0 +1,312 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import ZShell.Config +import qs.Components +import qs.Modules.Bar as Bar +import qs.Modules.Bar.Popouts as BarPopouts + +CustomMouseArea { + id: root + + required property ShellScreen screen + required property BarPopouts.Wrapper popouts + required property ScreenState screenState + required property Panels panels + required property Bar.BarLoader bar + required property real borderThickness + required property bool fullscreen + + property point dragStart + property bool dashboardShortcutActive + property bool osdShortcutActive + property bool utilitiesShortcutActive + + function withinPanelHeight(panel: Item, x: real, y: real): bool { + const panelY = root.borderThickness + panel.y; + return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding; + } + + function withinPanelWidth(panel: Item, x: real, y: real): bool { + const panelX = bar.implicitWidth + panel.x; + return x >= panelX - Config.border.rounding && x <= panelX + panel.width + Config.border.rounding; + } + + function inLeftPanel(panel: Item, x: real, y: real): bool { + return x < bar.implicitWidth + panel.x + panel.width && withinPanelHeight(panel, x, y); + } + + function inRightPanel(panel: Item, x: real, y: real): bool { + return x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panel.x) && withinPanelHeight(panel, x, y); + } + + function inTopPanel(panel: Item, x: real, y: real): bool { + const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property + return y < Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) && withinPanelWidth(panel, x, y); + } + + function inBottomPanel(panel: Item, x: real, y: real, isCorner = false): bool { + const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property + return y > height - Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) - (isCorner ? Config.border.rounding : 0) && withinPanelWidth(panel, x, y); + } + + function onWheel(event: WheelEvent): void { + if (fullscreen) + return; + if (event.x < bar.implicitWidth) { + bar.handleWheel(event.y, event.angleDelta); + } + } + + anchors.fill: parent + acceptedButtons: fullscreen ? Qt.NoButton : Qt.AllButtons + hoverEnabled: true + + onPressed: event => dragStart = Qt.point(event.x, event.y) + onContainsMouseChanged: { + if (!containsMouse) { + // Only hide if not activated by shortcut + if (!osdShortcutActive) { + screenState.osd = false; + root.panels.osd.hovered = false; + } + + if (!dashboardShortcutActive) + screenState.dashboard = false; + + if (!utilitiesShortcutActive) + screenState.utilities = false; + + if (!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) { + popouts.hasCurrent = false; + bar.closeTray(); + } + + if (Config.bar.showOnHover) + bar.isHovered = false; + + if (Config.sidebar.showOnHover) + screenState.sidebar = false; + } + } + + onPositionChanged: event => { + if (popouts.isDetached) + return; + + const x = event.x; + const y = event.y; + const dragX = x - dragStart.x; + const dragY = y - dragStart.y; + + if (fullscreen) { + root.panels.osd.hovered = inRightPanel(panels.osdWrapper, x, y); + return; + } + + // Show bar in non-exclusive mode on hover + if (!screenState.bar && Config.bar.showOnHover && x < bar.clampedWidth) + bar.isHovered = true; + + // Show/hide bar on drag + if (pressed && dragStart.x < bar.clampedWidth) { + if (dragX > Config.bar.dragThreshold) + screenState.bar = true; + else if (dragX < -Config.bar.dragThreshold) + screenState.bar = false; + } + + if (panels.sidebar.offsetScale === 1) { + // Show osd on hover + const showOsd = inRightPanel(panels.osdWrapper, x, y); + + // Always update visibility based on hover if not in shortcut mode + if (!osdShortcutActive) { + screenState.osd = showOsd; + root.panels.osd.hovered = showOsd; + } else if (showOsd) { + // If hovering over OSD area while in shortcut mode, transition to hover control + osdShortcutActive = false; + root.panels.osd.hovered = true; + } + + const showSidebar = pressed && dragStart.x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x); + + // Show sidebar on hover (top-right corner, bounded by notification panel height) + if (Config.sidebar.showOnHover) { + const sidebarTriggerY = Math.max(Config.sidebar.minHoverThreshold, panels.notifications.y + panels.notifications.height + borderThickness); + const showSidebarHover = x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x) && y <= sidebarTriggerY; + if (showSidebarHover && !screenState.sidebar) + screenState.sidebar = true; + } + + // Show/hide session on drag + if (pressed && inRightPanel(panels.sessionWrapper, dragStart.x, dragStart.y) && withinPanelHeight(panels.sessionWrapper, x, y)) { + if (dragX < -Config.session.dragThreshold) + screenState.session = true; + else if (dragX > Config.session.dragThreshold) + screenState.session = false; + + // Show sidebar on drag if in session area and session is nearly fully visible + if (showSidebar && panels.session.offsetScale <= 0 && dragX < -Config.sidebar.dragThreshold) + screenState.sidebar = true; + } else if (showSidebar && dragX < -Config.sidebar.dragThreshold) { + // Show sidebar on drag if not in session area + screenState.sidebar = true; + } + } else { + const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale); + // Show osd on hover + const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y); + + // Always update visibility based on hover if not in shortcut mode + if (!osdShortcutActive) { + screenState.osd = showOsd; + root.panels.osd.hovered = showOsd; + } else if (showOsd) { + // If hovering over OSD area while in shortcut mode, transition to hover control + osdShortcutActive = false; + root.panels.osd.hovered = true; + } + + // Show/hide session on drag + if (pressed && outOfSidebar && inRightPanel(panels.sessionWrapper, dragStart.x, dragStart.y) && withinPanelHeight(panels.sessionWrapper, x, y)) { + if (dragX < -Config.session.dragThreshold) + screenState.session = true; + else if (dragX > Config.session.dragThreshold) + screenState.session = false; + } + + // Show/hide sidebar on hover + if (Config.sidebar.showOnHover && !pressed) { + const sidebarTriggerY = Math.max(Config.sidebar.minHoverThreshold, panels.notifications.y + panels.notifications.height + borderThickness); + const showSidebarHover = x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x) && y <= sidebarTriggerY; + if (showSidebarHover && !screenState.sidebar) { + screenState.sidebar = true; + } else { + const inSidebarArea = inRightPanel(panels.sidebar, x, y) || inRightPanel(panels.sessionWrapper, x, y); + if (!inSidebarArea) + screenState.sidebar = false; + } + } + + // Hide sidebar on drag + if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold) + screenState.sidebar = false; + } + + // Show launcher on hover, or show/hide on drag if hover is disabled + if (Config.launcher.showOnHover) { + if (!screenState.launcher && inBottomPanel(panels.launcher, x, y)) + screenState.launcher = true; + } else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) { + if (dragY < -Config.launcher.dragThreshold) + screenState.launcher = true; + else if (dragY > Config.launcher.dragThreshold) + screenState.launcher = false; + } + + // Show dashboard on hover + const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboard, x, y); + + // Always update visibility based on hover if not in shortcut mode + if (!dashboardShortcutActive) { + screenState.dashboard = showDashboard; + } else if (showDashboard) { + // If hovering over dashboard area while in shortcut mode, transition to hover control + dashboardShortcutActive = false; + } + + // Show/hide dashboard on drag (for touchscreen devices) + if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) { + if (dragY > Config.dashboard.dragThreshold) + screenState.dashboard = true; + else if (dragY < -Config.dashboard.dragThreshold) + screenState.dashboard = false; + } + + // Show utilities on hover + const showUtilities = inBottomPanel(panels.utilities, x, y, true); + + // Always update visibility based on hover if not in shortcut mode + if (!utilitiesShortcutActive) { + screenState.utilities = showUtilities; + } else if (showUtilities) { + // If hovering over utilities area while in shortcut mode, transition to hover control + utilitiesShortcutActive = false; + } + + // Show popouts on hover + if (x < bar.implicitWidth) { + bar.checkPopout(y); + } else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popoutsWrapper, x, y)) { + popouts.hasCurrent = false; + bar.closeTray(); + } + } + + // Monitor individual visibility changes + Connections { + function onLauncherChanged() { + // If launcher is hidden, clear shortcut flags for dashboard and OSD + if (!root.screenState.launcher) { + root.dashboardShortcutActive = false; + root.osdShortcutActive = false; + root.utilitiesShortcutActive = false; + + // Also hide dashboard and OSD if they're not being hovered + const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY); + const inOsdArea = root.inRightPanel(root.panels.osdWrapper, root.mouseX, root.mouseY); + + if (!inDashboardArea) { + root.screenState.dashboard = false; + } + if (!inOsdArea) { + root.screenState.osd = false; + root.panels.osd.hovered = false; + } + } + } + + function onDashboardChanged() { + if (root.screenState.dashboard) { + // Dashboard became visible, immediately check if this should be shortcut mode + const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY); + if (!inDashboardArea) { + root.dashboardShortcutActive = true; + } + } else { + // Dashboard hidden, clear shortcut flag + root.dashboardShortcutActive = false; + } + } + + function onOsdChanged() { + if (root.screenState.osd) { + // OSD became visible, immediately check if this should be shortcut mode + const inOsdArea = root.inRightPanel(root.panels.osdWrapper, root.mouseX, root.mouseY); + if (!inOsdArea) { + root.osdShortcutActive = true; + } + } else { + // OSD hidden, clear shortcut flag + root.osdShortcutActive = false; + } + } + + function onUtilitiesChanged() { + if (root.screenState.utilities) { + // Utilities became visible, immediately check if this should be shortcut mode + const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY); + if (!inUtilitiesArea) { + root.utilitiesShortcutActive = true; + } + } else { + // Utilities hidden, clear shortcut flag + root.utilitiesShortcutActive = false; + } + } + + target: root.screenState + } +} diff --git a/Modules/Bar/Components/Clock.qml b/Modules/Bar/Components/Clock.qml index 4791c73..3f9f3e3 100644 --- a/Modules/Bar/Components/Clock.qml +++ b/Modules/Bar/Components/Clock.qml @@ -14,7 +14,7 @@ WidgetBase { id: root readonly property font font: Tokens.font.clock.family(Config.appearance.font.family.clock).build() - readonly property color contentColor: screenState.dashboard ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface + readonly property color contentColor: Colors.palette.m3onSurface readonly property string effectiveFormat: { let fmt = Config.general.dateFormat; if (!root.horizontal) @@ -48,7 +48,6 @@ WidgetBase { readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2 required property ScreenState screenState - color: screenState.dashboard ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer implicitHeight: horizontal ? baseSize : size implicitWidth: horizontal ? size : baseSize radius: Tokens.rounding.full @@ -146,13 +145,4 @@ WidgetBase { } } } - - StateLayer { - acceptedButtons: Qt.LeftButton - color: root.contentColor - - onClicked: { - root.screenState.dashboard = !root.screenState.dashboard; - } - } } diff --git a/Modules/Dashboard/Media/LyricList.qml b/Modules/Dashboard/Media/LyricList.qml index 69ce845..cdec3dc 100644 --- a/Modules/Dashboard/Media/LyricList.qml +++ b/Modules/Dashboard/Media/LyricList.qml @@ -179,7 +179,7 @@ Item { CustomText { text: qsTr("Loading lyrics...") color: Colors.palette.m3onSurfaceVariant - font: Tokens.font.body.normal + font: Tokens.font.body.medium } } diff --git a/Modules/Launcher/Items/FilesItem.qml b/Modules/Launcher/Items/FilesItem.qml index b878e6e..e84ca5a 100644 --- a/Modules/Launcher/Items/FilesItem.qml +++ b/Modules/Launcher/Items/FilesItem.qml @@ -76,7 +76,7 @@ Item { id: icon anchors.verticalCenter: parent.verticalCenter - implicitSize: Tokens.font.size.extraLarge + implicitSize: Tokens.font.icon.extraLarge.pointSize source: Quickshell.iconPath(root.modelData?.icon) ?? "" visible: !root.modelData?.isImage } @@ -94,14 +94,14 @@ Item { } sourceComponent: LoadingIndicator { color: Colors.palette.m3primaryContainer - implicitSize: Tokens.font.size.extraLarge + implicitSize: Tokens.font.icon.extraLarge.pointSize } } FadeImage { id: image - property int implicitSize: Tokens.font.size.extraLarge + property int implicitSize: Tokens.font.icon.extraLarge.pointSize width: implicitSize fillMode: Image.PreserveAspectFit diff --git a/Modules/Lock/InputField.qml b/Modules/Lock/InputField.qml index b2ca56c..c7ac527 100644 --- a/Modules/Lock/InputField.qml +++ b/Modules/Lock/InputField.qml @@ -70,7 +70,7 @@ Item { anchors.centerIn: parent anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0 - implicitHeight: Tokens.font.size.normal + implicitHeight: Tokens.font.body.medium.pointSize implicitWidth: fullWidth interactive: false orientation: Qt.Horizontal diff --git a/Modules/Notifications/Sidebar/Utils/Cards/RecordingList.qml b/Modules/Notifications/Sidebar/Utils/Cards/RecordingList.qml index 5c28149..2b2d344 100644 --- a/Modules/Notifications/Sidebar/Utils/Cards/RecordingList.qml +++ b/Modules/Notifications/Sidebar/Utils/Cards/RecordingList.qml @@ -56,7 +56,7 @@ ColumnLayout { Layout.fillWidth: true Layout.rightMargin: -Tokens.spacing.small clip: true - implicitHeight: (Tokens.font.size.larger + Tokens.padding.extraSmall) * (root.screenState.recordingListExpanded ? 10 : 3) + implicitHeight: (Tokens.font.body.large.pointSize + Tokens.padding.extraSmall) * (root.screenState.recordingListExpanded ? 10 : 3) CustomScrollBar.vertical: CustomScrollBar { flickable: list diff --git a/Modules/Settings/Pages/Apps/AllApps.qml b/Modules/Settings/Pages/Apps/AllApps.qml index 0dde289..4f9e685 100644 --- a/Modules/Settings/Pages/Apps/AllApps.qml +++ b/Modules/Settings/Pages/Apps/AllApps.qml @@ -56,7 +56,7 @@ PageBase { IconImage { asynchronous: true - implicitSize: Math.round(Tokens.font.size.large * 1.8) + implicitSize: Math.round(Tokens.font.icon.large * 1.8) source: Quickshell.iconPath(appItem.modelData.icon, "image-missing") } diff --git a/Modules/Settings/Pages/Apps/AppInfo.qml b/Modules/Settings/Pages/Apps/AppInfo.qml index 333a880..4f549da 100644 --- a/Modules/Settings/Pages/Apps/AppInfo.qml +++ b/Modules/Settings/Pages/Apps/AppInfo.qml @@ -47,7 +47,7 @@ PageBase { IconImage { asynchronous: true - implicitSize: Math.round(Tokens.font.size.large * 3) + implicitSize: Math.round(Tokens.font.icon.large.pointSize * 3) source: Quickshell.iconPath(root.app?.icon, "image-missing") } diff --git a/Modules/Settings/Pages/AppsPage.qml b/Modules/Settings/Pages/AppsPage.qml index 43cf99f..32206a0 100644 --- a/Modules/Settings/Pages/AppsPage.qml +++ b/Modules/Settings/Pages/AppsPage.qml @@ -143,7 +143,7 @@ PageBase { IconImage { asynchronous: true - implicitSize: Math.round(Tokens.font.size.large * 1.8) + implicitSize: Math.round(Tokens.font.icon.large.pointSize * 1.8) source: Quickshell.iconPath(appItem.modelData.icon, "image-missing") } diff --git a/Plugins/ZShell/Services/gpu.cpp b/Plugins/ZShell/Services/gpu.cpp index 8d3d1da..c12d896 100644 --- a/Plugins/ZShell/Services/gpu.cpp +++ b/Plugins/ZShell/Services/gpu.cpp @@ -60,7 +60,7 @@ Gpu::Gpu(QObject* parent) : TickingService(parent) { [this, configPath]() { QTimer::singleShot(100, this, [this, configPath]() { QFile file(configPath); - if (file.exists()) { + if (file.open(QIODevice::ReadOnly)) { QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); if (!doc.isNull()) { diff --git a/Plugins/ZShell/Services/tickingservice.cpp b/Plugins/ZShell/Services/tickingservice.cpp index 5c51cdc..96083d5 100644 --- a/Plugins/ZShell/Services/tickingservice.cpp +++ b/Plugins/ZShell/Services/tickingservice.cpp @@ -44,7 +44,7 @@ TickingService::TickingService(QObject* parent) [this, configPath]() { QTimer::singleShot(100, this, [this, configPath]() { QFile file(configPath); - if (!file.exists()) { + if (!file.open(QIODevice::ReadOnly)) { return; } QJsonDocument doc = QJsonDocument::fromJson(file.readAll());