feat: progress on changing interactions to mousearea again
C++ / fmt (pull_request) Successful in 5s
C++ / clang-tidy (pull_request) Failing after 9s
C++ / build (pull_request) Failing after 19s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 7s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Successful in 1m33s
Rust / fmt (pull_request) Successful in 43s
Rust / clippy (pull_request) Successful in 57s

This commit is contained in:
2026-09-29 00:02:14 +02:00
parent 613242ae50
commit dc77ea44a4
5 changed files with 212 additions and 220 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import qs.Components
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
+198 -208
View File
@@ -1,309 +1,299 @@
import QtQuick
import QtQuick.Controls
import Quickshell import Quickshell
import QtQuick
import ZShell.Config import ZShell.Config
import ZShell.Internal
import qs.Components import qs.Components
import qs.Modules.Bar as Bar import qs.Helpers
import qs.Modules.Bar.Popouts as BarPopouts import qs.Modules.Bar.Popouts
CustomMouseArea { CustomMouseArea {
id: root id: root
required property ShellScreen screen required property Item bar
required property BarPopouts.Wrapper popouts
required property ScreenState screenState
required property Panels panels
required property Bar.BarLoader bar
required property real borderThickness 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 dashboardShortcutActive
property bool osdShortcutActive property bool osdShortcutActive
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
property bool setInitialPoint: false
property point dragStart
function withinPanelHeight(panel: Item, x: real, y: real): bool { function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelY = root.borderThickness + panel.y; const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding; return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
}
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 { 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 { 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 { function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) && withinPanelWidth(panel, x, y); return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
} }
function inBottomPanel(panel: Item, x: real, y: real, isCorner = false): bool { function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property const panelY = panel.y + geometry.insetTop(borderThickness);
return y > height - Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) - (isCorner ? Config.border.rounding : 0) && withinPanelWidth(panel, x, y); return y >= panelY && y <= panelY + panel.height;
} }
function onWheel(event: WheelEvent): void { function withinPanelWidth(panel: Item, x: real, y: real): bool {
if (fullscreen) const panelX = panel.x + geometry.insetLeft(borderThickness);
return; return x >= panelX && x <= panelX + panel.width;
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
} }
anchors.fill: parent anchors.fill: parent
acceptedButtons: fullscreen ? Qt.NoButton : Qt.AllButtons acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
hoverEnabled: true 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: { onContainsMouseChanged: {
if (!containsMouse) { if (!containsMouse) {
// Only hide if not activated by shortcut
if (!osdShortcutActive) { if (!osdShortcutActive) {
screenState.osd = false; screenState.osd = false;
root.panels.osd.hovered = false; panels.osd.hovered = false;
} }
if (!dashboardShortcutActive) if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
screenState.dashboard = false;
if (!utilitiesShortcutActive)
screenState.utilities = false;
if (!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) {
popouts.hasCurrent = false; popouts.hasCurrent = false;
bar.closeTray();
} }
if (Config.bar.showOnHover) if (Config.bar.autoHide || bar.fullscreen)
bar.isHovered = false; bar.isHovered = false;
if (Config.sidebar.showOnHover) if (screenState.dashboard && Config.dashboard.showOnHover)
screenState.sidebar = false; screenState.dashboard = false;
} }
} }
onPositionChanged: event => { onPositionChanged: event => {
if (popouts.isDetached)
return;
const x = event.x; const x = event.x;
const y = event.y; const y = event.y;
const dragX = x - dragStart.x; const dragX = x - dragStart.x;
const dragY = y - dragStart.y; const dragY = y - dragStart.y;
if (fullscreen) { if (screenState.isDrawing) {
root.panels.osd.hovered = inRightPanel(panels.osdWrapper, x, y); 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; return;
} }
// Show bar in non-exclusive mode on hover if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
if (!screenState.bar && Config.bar.showOnHover && x < bar.clampedWidth) screenState.launcher = true;
bar.isHovered = true;
// Show/hide bar on drag const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y);
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) { if (!dashboardShortcutActive) {
screenState.dashboard = showDashboard; screenState.dashboard = showDashboard;
} else if (showDashboard) { } else if (showDashboard) {
// If hovering over dashboard area while in shortcut mode, transition to hover control
dashboardShortcutActive = false; dashboardShortcutActive = false;
} }
// Show/hide dashboard on drag (for touchscreen devices) if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
if (dragY > Config.dashboard.dragThreshold) if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true; screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold) else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false; screenState.dashboard = false;
} }
// Show utilities on hover if (pressed && geometry.insetTop(borderThickness) > dragStart.y && (Config.dashboard.showOnHover || !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y))) {
const showUtilities = inBottomPanel(panels.utilities, x, y, true); if (dragY > 20) {
screenState.settings = true;
// Always update visibility based on hover if not in shortcut mode } else if (dragY < -20) {
if (!utilitiesShortcutActive) { screenState.settings = false;
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 (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
if (x < bar.implicitWidth) { screenState.clipboard = true;
bar.checkPopout(y);
} else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popoutsWrapper, x, y)) { if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
popouts.hasCurrent = false; if (dragY < -10)
bar.closeTray(); 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 { Connections {
function onLauncherChanged() { function onDashboardChanged(): void {
// If launcher is hidden, clear shortcut flags for dashboard and OSD 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) { if (!root.screenState.launcher) {
root.dashboardShortcutActive = false; root.dashboardShortcutActive = false;
root.osdShortcutActive = false; root.osdShortcutActive = false;
root.utilitiesShortcutActive = false; root.utilitiesShortcutActive = false;
// Also hide dashboard and OSD if they're not being hovered const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
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) { if (!inOsdArea) {
root.screenState.osd = false; root.screenState.osd = false;
root.panels.osd.hovered = false; root.panels.osd.hovered = false;
} }
} }
}
function onDashboardChanged() { if (root.screenState.launcher) {
if (root.screenState.dashboard) { root.screenState.dashboard = false;
// Dashboard became visible, immediately check if this should be shortcut mode root.screenState.dock = false;
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY); root.screenState.settings = false;
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
} else {
// Dashboard hidden, clear shortcut flag
root.dashboardShortcutActive = false;
} }
} }
function onOsdChanged() { function onOsdChanged(): void {
if (root.screenState.osd) { if (root.screenState.osd) {
// OSD became visible, immediately check if this should be shortcut mode const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osdWrapper, root.mouseX, root.mouseY);
if (!inOsdArea) { if (!inOsdArea) {
root.osdShortcutActive = true; root.osdShortcutActive = true;
} }
} else { } else {
// OSD hidden, clear shortcut flag
root.osdShortcutActive = false; root.osdShortcutActive = false;
} }
} }
function onUtilitiesChanged() { function onResourcesChanged(): void {
if (root.screenState.utilities) { if (root.screenState.resources) {
// Utilities became visible, immediately check if this should be shortcut mode if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY); root.popouts.hasCurrent = false;
if (!inUtilitiesArea) {
root.utilitiesShortcutActive = true; root.screenState.settings = false;
} root.screenState.dashboard = false;
} else { }
// Utilities hidden, clear shortcut flag }
root.utilitiesShortcutActive = 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;
} }
} }
+1 -1
View File
@@ -372,7 +372,7 @@ CustomWindow {
} }
} }
Interactions { Interactions2 {
id: interactions id: interactions
anchors.fill: parent anchors.fill: parent
+3 -5
View File
@@ -27,7 +27,7 @@ Item {
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0; 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; return 0;
if (screenState.osd) { if (screenState.osd) {
@@ -47,8 +47,7 @@ Item {
implicitWidth: Config.notifs.sizes.width + padding * 2 implicitWidth: Config.notifs.sizes.width + padding * 2
Behavior on implicitHeight { Behavior on implicitHeight {
Anim { Anim {}
}
} }
ClippingWrapperRectangle { ClippingWrapperRectangle {
@@ -65,8 +64,7 @@ Item {
orientation: Qt.Vertical orientation: Qt.Vertical
spacing: 0 spacing: 0
delegate: NotifWrapper { delegate: NotifWrapper {}
}
displaced: Transition { displaced: Transition {
Anim { Anim {
property: "y" property: "y"
+9 -5
View File
@@ -2,6 +2,8 @@
#include <qcursor.h> #include <qcursor.h>
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@@ -14,23 +16,25 @@ class Cursor : public QObject {
public: public:
Q_INVOKABLE void setOverride(Qt::CursorShape shape) { Q_INVOKABLE void setOverride(Qt::CursorShape shape) {
if (m_override) if (m_shape == shape) return;
if (m_shape)
QGuiApplication::changeOverrideCursor(QCursor(shape)); QGuiApplication::changeOverrideCursor(QCursor(shape));
else { else {
QGuiApplication::setOverrideCursor(QCursor(shape)); QGuiApplication::setOverrideCursor(QCursor(shape));
m_override = true;
} }
m_shape = shape;
} }
Q_INVOKABLE void clearOverride() { Q_INVOKABLE void clearOverride() {
if (!m_override) return; if (!m_shape) return;
QGuiApplication::restoreOverrideCursor(); QGuiApplication::restoreOverrideCursor();
m_override = false; m_shape.reset();
} }
private: private:
bool m_override = false; std::optional<Qt::CursorShape> m_shape;
}; };
} // namespace ZShell::internal } // namespace ZShell::internal