Merge branch 'main' into feat/fullscreen-reveal-bar
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 31s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 56s
Python / typecheck (pull_request) Successful in 1m23s
Rust / fmt (pull_request) Successful in 41s
C++ / build (pull_request) Successful in 2m35s
Rust / build (pull_request) Successful in 1m49s
Rust / clippy (pull_request) Successful in 59s
Python / buildcheck (pull_request) Successful in 2m19s
C++ / clang-tidy (pull_request) Successful in 6m2s

This commit is contained in:
2026-08-16 11:20:59 +02:00
52 changed files with 1174 additions and 859 deletions
+33 -2
View File
@@ -11,15 +11,46 @@ 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 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
}
}
}
+55
View File
@@ -0,0 +1,55 @@
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 configPosition
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`);
}
}
+7 -2
View File
@@ -10,6 +10,7 @@ Scope {
id: root
required property Item bar
required property EdgeGeometry geometry
required property ShellScreen screen
ExclusionZone {
@@ -19,13 +20,14 @@ Scope {
anchors.left: true
anchors.right: true
anchors.top: true
exclusiveZone: root.bar.exclusiveZone
hasBar: root.geometry.barOnTop
}
ExclusionZone {
id: left
anchors.left: true
hasBar: root.geometry.barOnLeft
}
ExclusionZone {
@@ -38,6 +40,7 @@ Scope {
id: bottom
anchors.bottom: true
hasBar: root.geometry.barOnBottom
}
Timer {
@@ -48,7 +51,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"
+24 -20
View File
@@ -3,7 +3,7 @@ import QtQuick
import qs.Components
import ZShell.Config
import qs.Helpers
import qs.Modules as BarPopouts
import qs.Modules.Bar.Popouts
Item {
id: root
@@ -12,43 +12,47 @@ 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
required property Wrapper popouts
required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
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);
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 < 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);
}
function onWheel(event: WheelEvent): void {
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
}
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,7 +85,7 @@ Item {
if (root.singleGestureTriggered)
return;
if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (root.geometry.insetTop(root.borderThickness) > centroid.pressPosition.y) {
if (dragY > 20) {
root.visibilities.settings = true;
root.singleGestureTriggered = true;
@@ -91,10 +95,10 @@ Item {
}
}
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;
@@ -206,7 +210,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.bar.fullscreen && !root.bar.isHovered)
@@ -235,8 +239,8 @@ Item {
if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.visibilities.dock = true;
if (y < root.bar.implicitHeight)
root.bar.checkPopout(x);
if (root.geometry.barContains(x, y))
root.bar.checkPopout(root.geometry.axisPos(x, y));
}
}
+19 -16
View File
@@ -1,7 +1,7 @@
import Quickshell
import QtQuick
import qs.Components
import qs.Modules as Modules
import qs.Modules.Bar.Popouts as Popouts
import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils
@@ -27,6 +27,7 @@ Item {
readonly property alias dock: dock
readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
@@ -43,24 +44,26 @@ Item {
readonly property alias utilities: utilities
required property PersistentProperties visibilities
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness)
anchors.margins: borderThickness
anchors.topMargin: bar.implicitHeight
anchors.topMargin: geometry.insetTop(borderThickness)
Item {
id: resourcesWrapper
anchors.bottom: root.geometry.position === "bottom" ? parent.bottom : undefined
anchors.left: parent.left
anchors.top: parent.top
anchors.top: root.geometry.position !== "bottom" ? parent.top : undefined
clip: true
implicitHeight: resources.implicitHeight * (1 - resources.offsetScale)
implicitWidth: resources.implicitWidth
implicitHeight: root.geometry.horizontal ? resources.implicitHeight * (1 - resources.offsetScale) : resources.implicitHeight
implicitWidth: root.geometry.horizontal ? resources.implicitWidth : resources.implicitWidth * (1 - resources.offsetScale)
Resources.Wrapper {
id: resources
anchors.left: parent.left
anchors.top: parent.top
position: root.geometry.position
visibilities: root.visibilities
}
}
@@ -96,11 +99,11 @@ Item {
visibilities: root.visibilities
}
Modules.ClipWrapper {
Popouts.ClipWrapper {
id: popouts
anchors.top: parent.top
borderThickness: root.borderThickness
position: root.geometry.position
screen: root.screen
}
@@ -147,11 +150,13 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: root.geometry.barOnLeft || root.geometry.barOnBottom ? parent.bottom : undefined
anchors.left: root.geometry.barOnLeft ? parent.left : undefined
anchors.right: root.geometry.barOnLeft ? undefined : parent.right
anchors.top: root.geometry.barOnBottom || root.geometry.barOnLeft ? undefined : parent.top
clip: true
implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: dashboard.implicitWidth
implicitHeight: root.geometry.horizontal ? dashboard.implicitHeight * (1 - offsetScale) : dashboard.implicitHeight
implicitWidth: root.geometry.horizontal ? dashboard.implicitWidth : dashboard.implicitWidth * (1 - offsetScale)
Behavior on offsetScale {
Anim {
@@ -163,10 +168,8 @@ Item {
Dashboard.Wrapper {
id: dashboard
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * offsetScale
offsetScale: dashboardWrapper.offsetScale
position: root.geometry.position
visibilities: root.visibilities
}
}
+13 -8
View File
@@ -7,17 +7,18 @@ import qs.Modules.Bar as Bar
Region {
id: root
required property Bar.BarLoader bar
// required property Bar.BarLoader bar
readonly property real borderThickness: win.borderThickness
required property EdgeGeometry geometry
readonly property alias menuPopoutRegion: menuPopoutRegion
required property Panels panels
required property var win
height: win.height - bar.implicitHeight - win.borderThickness - win.dragMaskPadding * 2
height: win.height - geometry.insetTop(borderThickness, true) - geometry.insetBottom(borderThickness, true) - win.dragMaskPadding * 2
intersection: Intersection.Xor
width: win.width - win.borderThickness * 2 - win.dragMaskPadding * 2
x: win.borderThickness + win.dragMaskPadding
y: bar.implicitHeight + win.dragMaskPadding + (bar.fullscreen ? 1 : 0)
width: win.width - geometry.insetLeft(borderThickness, true) * 2 - win.dragMaskPadding * 2
x: geometry.insetLeft(borderThickness, true) + win.dragMaskPadding
y: geometry.insetTop(borderThickness, true) + win.dragMaskPadding
R {
panel: root.panels.dashboardWrapper
@@ -41,17 +42,21 @@ Region {
}
R {
height: panel.height + root.geometry.insetTop(root.borderThickness)
panel: root.panels.notifications
y: 0
}
R {
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.borderThickness
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.geometry.insetBottom(root.borderThickness)
panel: root.panels.utilities
y: root.win.height - height
}
R {
height: root.geometry.horizontal ? panel.height * (1 - root.panels.popoutsWrapper.offsetScale) : panel.height
panel: root.panels.popoutsWrapper
width: root.geometry.horizontal ? panel.width : panel.width * (1 - root.panels.popoutsWrapper.offsetScale)
}
R {
@@ -82,7 +87,7 @@ Region {
height: panel.height
intersection: Intersection.Subtract
width: panel.width
x: panel.x + root.borderThickness
y: panel.y + root.bar.implicitHeight
x: panel.x + root.geometry.insetLeft(root.borderThickness)
y: panel.y + root.geometry.insetTop(root.borderThickness)
}
}
+42 -27
View File
@@ -33,6 +33,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;
@@ -95,13 +96,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
@@ -111,15 +120,16 @@ CustomWindow {
}
Region {
height: 1
width: root.screen.width
height: geometry.horizontal ? 1 : root.screen.height
width: geometry.horizontal ? root.screen.width : 1
y: geometry.position === "bottom" ? root.screen.height - 1 : 0
}
}
Regions {
id: regions
bar: bar
geometry: geometry
panels: panels
win: root
}
@@ -198,10 +208,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
}
@@ -209,15 +219,15 @@ CustomWindow {
PanelBg {
id: dashBg
property real extraHeight: 0.2
property real extraExtent: 0.2
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width
implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent)
panel: panels.dashboardWrapper
radius: Tokens.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
x: panels.dashboardWrapper.x + panels.dashboard.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.dashboard.width * extraExtent)
y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - (geometry.horizontal && geometry.position !== "bottom" ? panels.dashboard.height * extraExtent : 0)
}
PanelBg {
@@ -229,7 +239,7 @@ CustomWindow {
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher
radius: Tokens.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
y: panels.launcher.y + geometry.insetTop(root.borderThickness)
}
PanelBg {
@@ -250,8 +260,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 {
@@ -273,17 +282,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 ?? Tokens.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 {
}
}
@@ -297,8 +306,8 @@ CustomWindow {
implicitWidth: panels.resources.width
panel: panels.resourcesWrapper
radius: Tokens.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + root.borderThickness
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness)
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness)
}
PanelBg {
@@ -376,6 +385,7 @@ CustomWindow {
borderThickness: root.borderLayoutThickness
drawing: drawing
enabled: true
geometry: geometry
panels: panels
popouts: panels.popouts
screen: root.screen
@@ -387,6 +397,7 @@ CustomWindow {
bar: bar
borderThickness: root.borderThickness
drawingItem: drawing
geometry: geometry
screen: root.screen
visibilities: visibilities
@@ -428,14 +439,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
}
}
@@ -448,7 +463,7 @@ CustomWindow {
implicitHeight: panel.height
implicitWidth: panel.width
radius: Tokens.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)
}
}