From be3c938c3952c6974ac56c15afb18c53fb695630 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 29 Sep 2026 00:02:14 +0200 Subject: [PATCH] feat: progress on changing interactions to mousearea again --- Drawers/Interactions.qml | 2 +- Drawers/Interactions2.qml | 406 ++++++++++++++--------------- Drawers/Windows.qml | 2 +- Modules/Notifications/Content.qml | 8 +- Plugins/ZShell/Internal/cursor.hpp | 14 +- 5 files changed, 212 insertions(+), 220 deletions(-) diff --git a/Drawers/Interactions.qml b/Drawers/Interactions.qml index 327289a..4e0d6a5 100644 --- a/Drawers/Interactions.qml +++ b/Drawers/Interactions.qml @@ -1,7 +1,7 @@ import Quickshell import QtQuick -import qs.Components import ZShell.Config +import qs.Components import qs.Helpers import qs.Modules.Bar.Popouts diff --git a/Drawers/Interactions2.qml b/Drawers/Interactions2.qml index b35cf72..25c3ed7 100644 --- a/Drawers/Interactions2.qml +++ b/Drawers/Interactions2.qml @@ -1,309 +1,299 @@ -import QtQuick -import QtQuick.Controls import Quickshell +import QtQuick import ZShell.Config +import ZShell.Internal import qs.Components -import qs.Modules.Bar as Bar -import qs.Modules.Bar.Popouts as BarPopouts +import qs.Helpers +import qs.Modules.Bar.Popouts 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 Item bar required property real borderThickness - required property bool fullscreen + required property ScreenState screenState + required property Drawing drawing + required property EdgeGeometry geometry + required property Panels panels + required property Wrapper popouts + required property ShellScreen screen - property point dragStart property bool dashboardShortcutActive property bool osdShortcutActive + property bool singleGestureTriggered: false property bool utilitiesShortcutActive + property bool setInitialPoint: false + property point dragStart - 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 inBottomPanel(panel: Item, x: real, y: real): bool { + const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); + return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y); } function inLeftPanel(panel: Item, x: real, y: real): bool { - return x < bar.implicitWidth + panel.x + panel.width && withinPanelHeight(panel, x, y); + return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y); + } + + function inPopoutArea(x: real, y: real): bool { + const panel = panels.popoutsWrapper; + if (!geometry.horizontal) + return inLeftPanel(panel, x, y); + return withinPanelWidth(panel, x, y) && 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); + return x > panel.x + geometry.insetLeft(borderThickness) && 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); + const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); + return y < geometry.insetTop(borderThickness) + 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 withinPanelHeight(panel: Item, x: real, y: real): bool { + const panelY = panel.y + geometry.insetTop(borderThickness); + return y >= panelY && y <= panelY + panel.height; } - function onWheel(event: WheelEvent): void { - if (fullscreen) - return; - if (event.x < bar.implicitWidth) { - bar.handleWheel(event.y, event.angleDelta); - } + function withinPanelWidth(panel: Item, x: real, y: real): bool { + const panelX = panel.x + geometry.insetLeft(borderThickness); + return x >= panelX && x <= panelX + panel.width; } anchors.fill: parent - acceptedButtons: fullscreen ? Qt.NoButton : Qt.AllButtons + acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons hoverEnabled: true - onPressed: event => dragStart = Qt.point(event.x, event.y) + onPressed: event => { + dragStart = Qt.point(event.x, event.y); + + if (screenState.isDrawing) { + if (event.button & Qt.RightButton) { + drawing.content.clear(); + return; + } + + if (!inLeftPanel(panels.drawing, mouseX, mouseY)) + drawing.content.beginStroke(event.x, event.y); + } + } + + onPressedChanged: { + if (!pressed) { + if (screenState.isDrawing) { + setInitialPoint = false; + drawing.content.endStroke(); + if (!inLeftPanel(panels.drawing, mouseX, mouseY)) + drawing.content.showHover(mouseX, mouseY); + } + } else { + if (screenState.isDrawing) { + panels.drawing.collapse(); + } + } + } + onContainsMouseChanged: { if (!containsMouse) { - // Only hide if not activated by shortcut if (!osdShortcutActive) { screenState.osd = false; - root.panels.osd.hovered = false; + 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) { + if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) { popouts.hasCurrent = false; - bar.closeTray(); } - if (Config.bar.showOnHover) + if (Config.bar.autoHide || bar.fullscreen) bar.isHovered = false; - if (Config.sidebar.showOnHover) - screenState.sidebar = false; + if (screenState.dashboard && Config.dashboard.showOnHover) + screenState.dashboard = 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); + if (screenState.isDrawing) { + if (pressed) { + drawing.content.appendPoint(x, y); + } else { + if (inLeftPanel(panels.drawing, x, y)) { + if (!(pressedButtons & Qt.LeftButton)) { + panels.drawing.expand(); + drawing.content.hideHover(); + Cursor.clearOverride(); + } + } else { + Cursor.setOverride(Qt.BlankCursor); + drawing.content.showHover(x, y); + } + } + if (drawing.content.hoverVisible) + drawing.content.showHover(x, y); return; } - // Show bar in non-exclusive mode on hover - if (!screenState.bar && Config.bar.showOnHover && x < bar.clampedWidth) - bar.isHovered = true; + if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold) + screenState.launcher = 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; - } + const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y); - 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 (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, 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; + if (pressed && geometry.insetTop(borderThickness) > dragStart.y && (Config.dashboard.showOnHover || !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y))) { + if (dragY > 20) { + screenState.settings = true; + } else if (dragY < -20) { + screenState.settings = 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(); + if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50) + screenState.clipboard = true; + + if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher) + if (dragY < -10) + screenState.dock = true; + + if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold) + screenState.sidebar = true; + + if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20) + Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`); + + if (pressed && dragStart.x <= Config.bar.border && dragX > 20) + Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`); + + if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true)) + bar.isHovered = true; + + if (panels.sidebar.offsetScale === 1) { + const showOsd = inRightPanel(panels.osdWrapper, x, y); + + if (showOsd) { + osdShortcutActive = false; + panels.osd.hovered = true; + } + } else { + const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale); + const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y); + + if (!osdShortcutActive) { + screenState.osd = showOsd; + panels.osd.hovered = showOsd; + } else if (showOsd) { + osdShortcutActive = false; + panels.osd.hovered = true; + } } + + if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y)) + screenState.dock = true; + + if (geometry.barContains(x, y)) + bar.checkPopout(geometry.axisPos(x, y)); } - // Monitor individual visibility changes Connections { - function onLauncherChanged() { - // If launcher is hidden, clear shortcut flags for dashboard and OSD + function onDashboardChanged(): void { + if (root.screenState.dashboard) { + const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY); + if (!inDashboardArea) { + root.dashboardShortcutActive = true; + } + + root.screenState.launcher = false; + root.screenState.settings = false; + root.screenState.sidebar = false; + root.screenState.resources = false; + root.popouts.hasCurrent = false; + } else { + root.dashboardShortcutActive = false; + } + } + + function onIsDrawingChanged(): void { + if (root.screenState.isDrawing) { + Cursor.setOverride(Qt.BlankCursor); + } else { + Cursor.clearOverride(); + } + } + + function onLauncherChanged(): void { 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); + const inOsdArea = root.inRightPanel(root.panels.osd, 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; + if (root.screenState.launcher) { + root.screenState.dashboard = false; + root.screenState.dock = false; + root.screenState.settings = false; } } - function onOsdChanged() { + function onOsdChanged(): void { 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); + const inOsdArea = root.inRightPanel(root.panels.osd, 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; + function onResourcesChanged(): void { + if (root.screenState.resources) { + if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates")) + root.popouts.hasCurrent = false; + + root.screenState.settings = false; + root.screenState.dashboard = false; + } + } + + function onSettingsChanged(): void { + if (root.screenState.settings) { + root.screenState.resources = false; + root.screenState.dashboard = false; + root.screenState.sidebar = false; + root.panels.popouts.hasCurrent = false; + root.screenState.launcher = false; + } + } + + function onSidebarChanged(): void { + if (root.screenState.sidebar) { + if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x) + root.screenState.dashboard = false; + root.screenState.settings = false; + root.popouts.hasCurrent = false; } } diff --git a/Drawers/Windows.qml b/Drawers/Windows.qml index b2c9887..2121c27 100644 --- a/Drawers/Windows.qml +++ b/Drawers/Windows.qml @@ -375,7 +375,7 @@ CustomWindow { } } - Interactions { + Interactions2 { id: interactions anchors.fill: parent diff --git a/Modules/Notifications/Content.qml b/Modules/Notifications/Content.qml index bad138a..c2b6c48 100644 --- a/Modules/Notifications/Content.qml +++ b/Modules/Notifications/Content.qml @@ -27,7 +27,7 @@ Item { for (let i = 0; i < count; i++) height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0; - if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x || screenState.dashboard) + if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x) return 0; if (screenState.osd) { @@ -47,8 +47,7 @@ Item { implicitWidth: Config.notifs.sizes.width + padding * 2 Behavior on implicitHeight { - Anim { - } + Anim {} } ClippingWrapperRectangle { @@ -65,8 +64,7 @@ Item { orientation: Qt.Vertical spacing: 0 - delegate: NotifWrapper { - } + delegate: NotifWrapper {} displaced: Transition { Anim { property: "y" diff --git a/Plugins/ZShell/Internal/cursor.hpp b/Plugins/ZShell/Internal/cursor.hpp index 7b1356e..7496195 100644 --- a/Plugins/ZShell/Internal/cursor.hpp +++ b/Plugins/ZShell/Internal/cursor.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -14,23 +16,25 @@ class Cursor : public QObject { public: Q_INVOKABLE void setOverride(Qt::CursorShape shape) { - if (m_override) + if (m_shape == shape) return; + + if (m_shape) QGuiApplication::changeOverrideCursor(QCursor(shape)); else { QGuiApplication::setOverrideCursor(QCursor(shape)); - m_override = true; } + m_shape = shape; } Q_INVOKABLE void clearOverride() { - if (!m_override) return; + if (!m_shape) return; QGuiApplication::restoreOverrideCursor(); - m_override = false; + m_shape.reset(); } private: - bool m_override = false; + std::optional m_shape; }; } // namespace ZShell::internal