From 1d150292d3dfd296308a8ab24d051ca0f9506060 Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 12 Aug 2026 20:15:55 +0200 Subject: [PATCH] initial commit for repositioning bar, currently broken --- Config/BarConfig.qml | 1 + Drawers/Drawers.qml | 39 +++++++++++++++++++++++++-- Drawers/EdgeGeometry.qml | 57 +++++++++++++++++++++++++++++++++++++++ Drawers/Exclusions.qml | 9 +++++-- Drawers/Interactions.qml | 33 ++++++++++++++--------- Drawers/Windows.qml | 50 +++++++++++++++++++++------------- Modules/Bar/Bar.qml | 55 +++++++++++++++++++++++++------------ Modules/Bar/BarLoader.qml | 35 ++++++++++++++++-------- 8 files changed, 217 insertions(+), 62 deletions(-) create mode 100644 Drawers/EdgeGeometry.qml diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml index 4f3697f..6f5997d 100644 --- a/Config/BarConfig.qml +++ b/Config/BarConfig.qml @@ -61,6 +61,7 @@ JsonObject { property bool hideWhenNotif: false property Popouts popouts: Popouts { } + property string position: "top" property int rounding: 8 property int smoothing: 32 property Tray tray: Tray { diff --git a/Drawers/Drawers.qml b/Drawers/Drawers.qml index b026775..490bc0e 100644 --- a/Drawers/Drawers.qml +++ b/Drawers/Drawers.qml @@ -11,15 +11,50 @@ Variants { required property ShellScreen modelData + function rebuild(): void { + loader.active = false; + loader.active = true; + } + + LazyLoader { + id: loader + + active: true + + Drawer { + screen: scope.modelData + } + } + + Connections { + function onConfigDashboardPositionChanged(): void { + Qt.callLater(scope.rebuild); + } + + function onConfigPositionChanged(): void { + Qt.callLater(scope.rebuild); + } + + target: (loader.item as Drawer)?.geometry ?? null + } + } + + component Drawer: Scope { + id: drawer + + readonly property alias geometry: content.geometry + required property ShellScreen screen + Exclusions { bar: content.bar - screen: scope.modelData + geometry: content.geometry + screen: drawer.screen } Windows { id: content - screen: scope.modelData + screen: drawer.screen } } } diff --git a/Drawers/EdgeGeometry.qml b/Drawers/EdgeGeometry.qml new file mode 100644 index 0000000..47bc341 --- /dev/null +++ b/Drawers/EdgeGeometry.qml @@ -0,0 +1,57 @@ +import QtQuick +import qs.Modules.Bar as Bar + +QtObject { + id: root + + required property Bar.BarLoader bar + readonly property real barClamped: bar.clampedExtent + readonly property real barExtent: bar.extent + readonly property bool barOnBottom: position === "bottom" + readonly property bool barOnLeft: position === "left" + readonly property bool barOnTop: position === "top" + required property string configDashboardPosition + required property string configPosition + readonly property bool dashboardOnLeft: configDashboardPosition === "left" ? !barOnLeft : barOnTop + readonly property bool horizontal: position !== "left" + readonly property string position: configPosition === "top" || configPosition === "bottom" ? configPosition : "left" + required property var win + + function axisPos(x: real, y: real): real { + return horizontal ? x : y; + } + + function barContains(x: real, y: real, clamped = false): bool { + const extent = clamped ? barClamped : barExtent; + if (barOnTop) + return y < extent; + if (barOnBottom) + return y > win.height - extent; + return x < extent; + } + + function insetBottom(border: real, clamped = false): real { + return barOnBottom ? (clamped ? barClamped : barExtent) : border; + } + + function insetLeft(border: real, clamped = false): real { + return barOnLeft ? (clamped ? barClamped : barExtent) : border; + } + + function insetTop(border: real, clamped = false): real { + return barOnTop ? (clamped ? barClamped : barExtent) : border; + } + + function inwardDrag(dragX: real, dragY: real): real { + if (barOnTop) + return dragY; + if (barOnBottom) + return -dragY; + return dragX; + } + + onConfigPositionChanged: { + if (!["left", "top", "bottom"].includes(configPosition)) + console.warn(`Invalid bar position '${configPosition}', falling back to left`); + } +} diff --git a/Drawers/Exclusions.qml b/Drawers/Exclusions.qml index 3373e34..259b616 100644 --- a/Drawers/Exclusions.qml +++ b/Drawers/Exclusions.qml @@ -9,19 +9,21 @@ Scope { id: root required property Item bar + required property EdgeGeometry geometry required property ShellScreen screen ExclusionZone { id: top anchors.top: true - exclusiveZone: root.bar.exclusiveZone + hasBar: root.geometry.barOnTop } ExclusionZone { id: left anchors.left: true + hasBar: root.geometry.barOnLeft } ExclusionZone { @@ -34,6 +36,7 @@ Scope { id: bottom anchors.bottom: true + hasBar: root.geometry.barOnBottom } Timer { @@ -44,7 +47,9 @@ Scope { } component ExclusionZone: CustomWindow { - exclusiveZone: Config.bar.border + property bool hasBar + + exclusiveZone: hasBar ? root.bar.exclusiveZone : Config.bar.border implicitHeight: 1 implicitWidth: 1 name: "Bar-Exclusion" diff --git a/Drawers/Interactions.qml b/Drawers/Interactions.qml index c72f55f..73570cf 100644 --- a/Drawers/Interactions.qml +++ b/Drawers/Interactions.qml @@ -12,6 +12,7 @@ Item { required property real borderThickness property bool dashboardShortcutActive required property Drawing drawing + required property EdgeGeometry geometry property bool osdShortcutActive required property Panels panels required property BarPopouts.Wrapper popouts @@ -21,19 +22,26 @@ Item { required property PersistentProperties visibilities function inBottomPanel(panel: Item, x: real, y: real): bool { - return y > root.height - panel.height - Config.bar.border && withinPanelWidth(panel, x, y); + return y > root.height - panel.height - geometry.insetBottom(borderThickness) && withinPanelWidth(panel, x, y); } function inLeftPanel(panel: Item, x: real, y: real): bool { - return x < panel.x + panel.width + Config.bar.border && 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 > panel.x - Config.bar.border && withinPanelHeight(panel, x, y); + return x > panel.x - geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y); } function inTopPanel(panel: Item, x: real, y: real): bool { - return y < bar.implicitHeight + panel.height && withinPanelWidth(panel, x, y); + return y < geometry.insetTop(borderThickness) + panel.height && withinPanelWidth(panel, x, y); } function onWheel(event: WheelEvent): void { @@ -43,12 +51,12 @@ Item { } function withinPanelHeight(panel: Item, x: real, y: real): bool { - const panelY = panel.y + bar.implicitHeight; + const panelY = panel.y + geometry.insetTop(borderThickness); return y >= panelY && y <= panelY + panel.height; } function withinPanelWidth(panel: Item, x: real, y: real): bool { - const panelX = panel.x + root.borderThickness; + const panelX = panel.x + geometry.insetLeft(borderThickness); return x >= panelX && x <= panelX + panel.width; } @@ -81,20 +89,21 @@ Item { if (root.singleGestureTriggered) return; - if (centroid.pressPosition.y < root.bar.implicitHeight) { - if (dragY > 20) { + if (root.geometry.barContains(centroid.pressPosition.x, centroid.pressPosition.y, true)) { + const barDrag = root.geometry.inwardDrag(dragX, dragY); + if (barDrag > 20) { root.visibilities.settings = true; root.singleGestureTriggered = true; - } else if (dragY < -20) { + } else if (barDrag < -20) { root.visibilities.settings = false; root.singleGestureTriggered = true; } } - if (centroid.pressPosition.y > root.screen.height - Config.bar.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50) + if (centroid.pressPosition.y > root.screen.height - root.geometry.insetBottom(root.borderThickness) && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50) root.visibilities.clipboard = true; - if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher) + if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.geometry.insetTop(root.borderThickness) && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher) if (dragY < -10) { root.visibilities.dock = true; root.singleGestureTriggered = true; @@ -193,7 +202,7 @@ Item { return; } - if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight) + if (!root.visibilities.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true)) root.bar.isHovered = true; if (root.panels.sidebar.offsetScale === 1) { diff --git a/Drawers/Windows.qml b/Drawers/Windows.qml index 23f9028..e3874e2 100644 --- a/Drawers/Windows.qml +++ b/Drawers/Windows.qml @@ -32,6 +32,7 @@ CustomWindow { return 100; } property real fsTransitionProg: hasFullscreen ? 1 : 0 + readonly property alias geometry: geometry readonly property bool hasFullscreen: { if (hasSpecialWorkspace) { const specialName = monitor?.lastIpcObject.specialWorkspace?.name; @@ -94,13 +95,21 @@ CustomWindow { panels.popouts.hasCurrent = false; } + EdgeGeometry { + id: geometry + + bar: bar + configPosition: Config.bar.position + win: root + } + Region { id: emptyRegion height: panels.notifications.height width: panels.notifications.width - x: panels.notifications.x + root.borderThickness - y: panels.notifications.y + bar.implicitHeight + x: panels.notifications.x + geometry.insetLeft(root.borderThickness) + y: panels.notifications.y + geometry.insetTop(root.borderThickness) Region { height: panels.osd.height @@ -113,7 +122,7 @@ CustomWindow { Regions { id: regions - bar: bar + geometry: geometry panels: panels win: root } @@ -192,10 +201,10 @@ CustomWindow { BlobInvertedRect { anchors.fill: parent anchors.margins: -50 - borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset - borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset + borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset + borderLeft: geometry.insetLeft(root.borderThickness) - anchors.margins - root.sdfBorderOffset borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset - borderTop: bar.implicitHeight - anchors.margins - root.sdfBorderOffset + borderTop: geometry.insetTop(root.borderThickness) - anchors.margins - root.sdfBorderOffset group: blobGroup radius: root.borderRounding } @@ -211,7 +220,7 @@ CustomWindow { panel: panels.dashboardWrapper radius: Appearance.rounding.normal x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness - y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight + y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - panels.dashboard.height * extraHeight } PanelBg { @@ -244,8 +253,7 @@ CustomWindow { implicitWidth: panels.osd.width panel: panels.osdWrapper radius: 20 - x: panels.osdWrapper.x + panels.osd.x + root.borderThickness - y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight + x: panels.osdWrapper.x + panels.osd.x + geometry.insetLeft(root.borderThickness) } PanelBg { @@ -267,17 +275,17 @@ CustomWindow { PanelBg { id: popoutBg - property real extraHeight: 0.2 + property real extraExtent: 0.2 deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08 - implicitHeight: panels.popouts.height * (1 + extraHeight) - implicitWidth: panels.popouts.width + implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1) + implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent) panel: panels.popoutsWrapper radius: panels.popouts.current?.panelRadius ?? Appearance.rounding.normal - x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness - y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight + x: panels.popoutsWrapper.x + panels.popouts.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.popouts.width * extraExtent) + y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0) - Behavior on extraHeight { + Behavior on extraExtent { Anim { } } @@ -370,6 +378,7 @@ CustomWindow { borderThickness: root.borderLayoutThickness drawing: drawing enabled: true + geometry: geometry panels: panels popouts: panels.popouts screen: root.screen @@ -381,6 +390,7 @@ CustomWindow { bar: bar borderThickness: root.borderThickness drawingItem: drawing + geometry: geometry screen: root.screen visibilities: visibilities @@ -422,14 +432,18 @@ CustomWindow { BarLoader { id: bar + anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined anchors.left: parent.left - anchors.right: parent.right + anchors.top: geometry.barOnBottom ? undefined : parent.top enabled: !visibilities.isDrawing fullscreen: root.hasFullscreen + height: geometry.horizontal ? implicitHeight : parent.height popouts: panels.popouts popoutsWrapper: panels.popoutsWrapper + position: geometry.position screen: root.screen visibilities: visibilities + width: geometry.horizontal ? parent.width : implicitWidth } } @@ -442,7 +456,7 @@ CustomWindow { implicitHeight: panel.height implicitWidth: panel.width radius: Appearance.rounding.smallest - x: panel.x + root.borderThickness - y: panel.y + bar.implicitHeight + x: panel.x + geometry.insetLeft(root.borderThickness) + y: panel.y + geometry.insetTop(root.borderThickness) } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index b2a1842..14471ef 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -9,18 +9,24 @@ import qs.Modules.SysTray import qs.Modules.Network import qs.Modules.Updates -RowLayout { +GridLayout { id: root required property bool fullscreen + required property bool horizontal required property Wrapper popouts required property ClipWrapper popoutsWrapper required property ShellScreen screen readonly property int vPadding: 6 required property PersistentProperties visibilities - function checkPopout(x: real): void { - const ch = childAt(x, height / 2) as EntryWrapper; + function axisCenterOf(item: Item): real { + const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2); + return horizontal ? c.x : c.y; + } + + function checkPopout(pos: real): void { + const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper; const id = ch?.entryId; if (!ch || id === undefined) { @@ -34,26 +40,21 @@ RowLayout { if (id === "statusIcons" && Config.bar.popouts.statusIcons) { const items = (ch.item as StatusIcons).items; - const localX = mapToItem(items, x, 0).x; - const icon = items.childAt(localX, items.height / 2); + const icon = horizontal ? items.childAt(mapToItem(items, pos, 0).x, items.height / 2) : items.childAt(items.width / 2, mapToItem(items, 0, pos).y); if (icon) { popouts.currentName = icon.name; - popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, icon.implicitWidth / 2, 0).x); + popouts.currentCenter = Qt.binding(() => axisCenterOf(icon)); popouts.hasCurrent = true; } } else if (id === "tray" && Config.bar.popouts.tray && Config.bar.tray.showOnHover) { const tray = ch.item as TrayIcons; const layout = tray.layout; - const localX = mapToItem(layout, x, 0).x; - const trayItem = layout.childAt(localX, layout.height / 2); + const trayItem = horizontal ? layout.childAt(mapToItem(layout, pos, 0).x, layout.height / 2) : layout.childAt(items.width / 2, mapToItem(layout, 0, pos).y); if (trayItem) { const idx = trayItem.index; popouts.currentName = `traymenu${idx}`; - popouts.currentCenter = Qt.binding(() => { - const it = tray.items.itemAt(idx); - return it ? it.mapToItem(root, it.implicitWidth / 2, 0).x : popouts.currentCenter; - }); + popouts.currentCenter = Qt.binding(() => axisCenterOf(trayItem)); popouts.hasCurrent = true; } @@ -63,12 +64,19 @@ RowLayout { if (id === "updates") { popouts.currentName = "updates"; - popouts.currentCenter = Qt.binding(() => ch.item.mapToItem(root, (ch.item as Item).implicitWidth / 2, 0).x); + popouts.currentCenter = Qt.binding(() => axisCenterOf(ch.item as Item)); popouts.hasCurrent = true; } } - spacing: Appearance.spacing.small + function entryAt(pos: real): string { + const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper; + return ch?.entryId ?? ""; + } + + columnSpacing: Appearance.spacing.small + columns: horizontal ? -1 : 1 + rowSpacing: Appearance.spacing.small Repeater { id: repeater @@ -94,7 +102,8 @@ RowLayout { roleValue: "spacer" delegate: EntryWrapper { - Layout.fillWidth: true + Layout.fillHeight: !root.horizontal + Layout.fillWidth: root.horizontal } } @@ -103,6 +112,7 @@ RowLayout { delegate: EntryWrapper { Workspaces { + horizontal: root.horizontal screen: root.screen visible: !root.fullscreen } @@ -114,6 +124,7 @@ RowLayout { delegate: EntryWrapper { TrayIcons { + horizontal: root.horizontal loader: root popouts: root.popouts visible: !root.fullscreen @@ -126,6 +137,7 @@ RowLayout { delegate: EntryWrapper { StatusIcons { + horizontal: root.horizontal } } } @@ -135,6 +147,7 @@ RowLayout { delegate: EntryWrapper { Resources { + horizontal: root.horizontal visibilities: root.visibilities visible: !root.fullscreen } @@ -146,6 +159,7 @@ RowLayout { delegate: EntryWrapper { UpdatesWidget { + horizontal: root.horizontal visible: !root.fullscreen } } @@ -156,6 +170,7 @@ RowLayout { delegate: EntryWrapper { NotifBell { + horizontal: root.horizontal popouts: root.popouts visibilities: root.visibilities visible: !root.fullscreen @@ -168,6 +183,7 @@ RowLayout { delegate: EntryWrapper { Clock { + horizontal: root.horizontal loader: root popouts: root.popouts visibilities: root.visibilities @@ -182,6 +198,7 @@ RowLayout { delegate: EntryWrapper { WindowTitle { bar: root + horizontal: root.horizontal visible: !root.fullscreen } } @@ -192,6 +209,7 @@ RowLayout { delegate: EntryWrapper { NetworkWidget { + horizontal: root.horizontal } } } @@ -201,6 +219,7 @@ RowLayout { delegate: EntryWrapper { MediaWidget { + horizontal: root.horizontal visible: !root.fullscreen } } @@ -215,8 +234,10 @@ RowLayout { required property var modelData Layout.alignment: Qt.AlignVCenter - Layout.leftMargin: index === 0 ? root.vPadding : (entryId === "statusIcons") ? -root.spacing + Appearance.spacing.extraSmall : 0 - Layout.rightMargin: index === repeater.count - 1 ? root.vPadding : 0 + Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0 + Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons") ? -root.rowSpacing + Appearance.spacing.extraSmall : 0 + Layout.rightMargin: root.horizontal && index === repeater.count - 1 ? root.vPadding : 0 + Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons") ? -root.columnSpacing + Appearance.spacing.extraSmall : 0 children: item implicitHeight: item?.implicitHeight ?? 0 implicitWidth: item?.implicitWidth ?? 0 diff --git a/Modules/Bar/BarLoader.qml b/Modules/Bar/BarLoader.qml index f5baba8..240417c 100644 --- a/Modules/Bar/BarLoader.qml +++ b/Modules/Bar/BarLoader.qml @@ -15,31 +15,40 @@ import qs.Modules.Network Item { id: root - readonly property int contentHeight: Config.bar.height + padding * 2 - readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentHeight + readonly property int clampedExtent: Math.max(Config.bar.border, extent) + readonly property int contentThickness: Config.bar.height + padding * 2 + readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentThickness + property real extent: fullscreen ? 0 : Config.bar.border required property bool fullscreen + readonly property bool horizontal: position !== "left" property bool isHovered readonly property int padding: Math.max(Appearance.padding.smaller, Config.bar.border) required property Wrapper popouts required property ClipWrapper popoutsWrapper + required property string position required property ShellScreen screen readonly property bool shouldBeVisible: !fullscreen && (!Config.bar.autoHide || visibilities.bar || isHovered) readonly property int vPadding: 6 required property PersistentProperties visibilities - function checkPopout(x: real): void { - content.item?.checkPopout(x); + function checkPopout(pos: real): void { + (content.item as Bar)?.checkPopout(pos); } - implicitHeight: fullscreen ? 0 : Config.bar.border - visible: height > Config.bar.border + function entryAt(pos: real): string { + return (content.item as Bar)?.entryAt(pos) ?? ""; + } + + implicitHeight: extent + implicitWidth: extent + visible: extent > Config.bar.border states: State { name: "visible" when: root.shouldBeVisible PropertyChanges { - root.implicitHeight: root.contentHeight + root.extent: root.contentThickness } } transitions: [ @@ -50,7 +59,7 @@ Item { Anim { duration: MaterialEasing.expressiveEffectsTime easing.bezierCurve: MaterialEasing.expressiveEffects - property: "implicitHeight" + property: "extent" target: root } }, @@ -61,7 +70,7 @@ Item { Anim { duration: MaterialEasing.expressiveEffectsTime easing.bezierCurve: MaterialEasing.expressiveEffects - property: "implicitHeight" + property: "extent" target: root } } @@ -71,13 +80,17 @@ Item { id: content active: root.shouldBeVisible || root.visible - anchors.bottom: parent.bottom - anchors.left: parent.left + anchors.bottom: root.position !== "bottom" ? parent.bottom : undefined + anchors.left: root.horizontal ? parent.left : undefined anchors.right: parent.right + anchors.top: root.position !== "top" ? parent.top : undefined + height: root.contentThickness + width: root.contentThickness sourceComponent: Bar { fullscreen: root.fullscreen height: root.contentHeight + horizontal: root.horizontal popouts: root.popouts popoutsWrapper: root.popoutsWrapper screen: root.screen