refactor: move drawers window -> ContentWindow.qml

This commit is contained in:
2 * r + 2 * t 2026-04-06 23:32:17 +10:00
parent c2221a6d8e
commit 0e50b62413
3 changed files with 328 additions and 344 deletions

View file

@ -4,6 +4,7 @@ import QtQuick
import Quickshell import Quickshell
import qs.components import qs.components
import qs.config import qs.config
import qs.utils
import qs.modules.bar.popouts as BarPopouts import qs.modules.bar.popouts as BarPopouts
Item { Item {
@ -12,9 +13,10 @@ Item {
required property ShellScreen screen required property ShellScreen screen
required property DrawerVisibilities visibilities required property DrawerVisibilities visibilities
required property BarPopouts.Wrapper popouts required property BarPopouts.Wrapper popouts
required property bool disabled
required property bool fullscreen required property bool fullscreen
readonly property bool disabled: Strings.testRegexList(Config.bar.excludedScreens, screen.name)
readonly property int clampedWidth: Math.max(Config.border.minThickness, implicitWidth) readonly property int clampedWidth: Math.max(Config.border.minThickness, implicitWidth)
readonly property int padding: Math.max(Appearance.padding.smaller, Config.border.thickness) readonly property int padding: Math.max(Appearance.padding.smaller, Config.border.thickness)
readonly property int contentWidth: Config.bar.sizes.innerWidth + padding * 2 readonly property int contentWidth: Config.bar.sizes.innerWidth + padding * 2

View file

@ -0,0 +1,322 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import Caelestia.Blobs
import qs.components
import qs.components.containers
import qs.services
import qs.config
import qs.modules.bar
StyledWindow {
id: root
readonly property alias bar: bar
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject.specialWorkspace?.name.length ?? 0) > 0
readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
if (!specialName)
return false;
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
return monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
property real borderThickness: hasFullscreen ? 0 : Config.border.thickness
readonly property real borderLayoutThickness: hasFullscreen ? 0 : Config.border.thickness
property real borderRounding: hasFullscreen ? 0 : Config.border.rounding
property real shadowOpacity: hasFullscreen ? 0 : 0.7
readonly property int dragMaskPadding: {
if (focusGrab.active || panels.popouts.isDetached)
return 0;
if (monitor?.lastIpcObject.specialWorkspace?.name || monitor?.activeWorkspace.lastIpcObject.windows > 0)
return 0;
const thresholds = [];
for (const panel of ["dashboard", "launcher", "session", "sidebar"])
if (Config[panel].enabled)
thresholds.push(Config[panel].dragThreshold);
return Math.max(...thresholds);
}
onHasFullscreenChanged: {
visibilities.launcher = false;
visibilities.session = false;
visibilities.dashboard = false;
}
name: "drawers"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.session || panels.dashboard.needsKeyboard ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
mask: Regions {
bar: bar
panels: panels
win: root
}
anchors.top: true
anchors.bottom: true
anchors.left: true
anchors.right: true
Behavior on borderThickness {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on borderRounding {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on shadowOpacity {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
HyprlandFocusGrab {
id: focusGrab
active: (visibilities.launcher && Config.launcher.enabled) || (visibilities.session && Config.session.enabled) || (visibilities.sidebar && Config.sidebar.enabled) || (!Config.dashboard.showOnHover && visibilities.dashboard && Config.dashboard.enabled) || (panels.popouts.currentName.startsWith("traymenu") && (panels.popouts.current as StackView)?.depth > 1)
windows: [root]
onCleared: {
visibilities.launcher = false;
visibilities.session = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
panels.popouts.hasCurrent = false;
bar.closeTray();
}
}
StyledRect {
anchors.fill: parent
opacity: visibilities.session && Config.session.enabled ? 0.5 : 0
color: Colours.palette.m3scrim
Behavior on opacity {
Anim {}
}
}
Item {
anchors.fill: parent
opacity: Colours.transparency.enabled ? Colours.transparency.base : 1
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
blurMax: 15
shadowColor: Qt.alpha(Colours.palette.m3shadow, Math.max(0, root.shadowOpacity))
}
// Border {
// bar: bar
// }
// Backgrounds {
// panels: panels
// bar: bar
// }
BlobGroup {
id: blobGroup
color: Colours.palette.m3surface
Behavior on color {
CAnim {}
}
}
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50 // Make border thicker to smooth out bulge from closed drawers
group: blobGroup
radius: root.borderRounding
borderLeft: bar.implicitWidth - anchors.margins
borderRight: root.borderThickness - anchors.margins
borderTop: root.borderThickness - anchors.margins
borderBottom: root.borderThickness - anchors.margins
}
PanelBg {
id: dashBg
panel: panels.dashboard
deformAmount: 0.1
}
PanelBg {
id: launcherBg
panel: panels.launcher
deformAmount: 0.1
}
PanelBg {
id: sessionBg
panel: panels.sessionWrapper
deformAmount: 0.25
x: panels.sessionWrapper.x + panels.session.x + bar.implicitWidth
implicitWidth: panels.session.width
}
PanelBg {
id: sidebarBg
panel: panels.sidebar
deformAmount: 0.05
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
bottomLeftRadius: Math.max(0, Math.min(1, panels.sidebar.offsetScale / 0.3)) * radius
}
PanelBg {
id: osdBg
panel: panels.osdWrapper
deformAmount: 0.3
x: panels.osdWrapper.x + panels.osd.x + bar.implicitWidth
implicitWidth: panels.osd.width
}
PanelBg {
id: notifsBg
panel: panels.notifications
}
PanelBg {
id: utilsBg
panel: panels.utilities
deformAmount: panels.sidebar.visible ? 0.1 : 0.15
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
topLeftRadius: Math.max(0, Math.min(1, panels.sidebar.offsetScale / 0.3)) * radius
}
PanelBg {
id: popoutBg
panel: panels.popouts
x: bar.implicitWidth - (panels.popouts.isDetached ? -(root.width - panels.popouts.shownWidth) / 2 : panels.popouts.hasCurrent ? 0 : panels.popouts.shownWidth + 5)
implicitWidth: panels.popouts.shownWidth
Behavior on x {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on implicitWidth {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
}
}
DrawerVisibilities {
id: visibilities
Component.onCompleted: Visibilities.load(root.screen, this)
}
Interactions {
screen: root.screen
popouts: panels.popouts
visibilities: visibilities
panels: panels
bar: bar
borderThickness: root.borderLayoutThickness
fullscreen: root.hasFullscreen
Panels {
id: panels
screen: root.screen
visibilities: visibilities
bar: bar
borderThickness: root.borderThickness
utilities.horizontalStretch: (sidebarBg.rawDeformMatrix.m11 - 1) / 2 + 1
dashboard.transform: Matrix4x4 {
matrix: dashBg.deformMatrix
}
launcher.transform: Matrix4x4 {
matrix: launcherBg.deformMatrix
}
session.transform: Matrix4x4 {
matrix: sessionBg.deformMatrix
}
sidebar.transform: Matrix4x4 {
matrix: sidebarBg.deformMatrix
}
osd.transform: Matrix4x4 {
matrix: osdBg.deformMatrix
}
notifications.transform: Matrix4x4 {
matrix: notifsBg.deformMatrix
}
utilities.transform: Matrix4x4 {
matrix: utilsBg.deformMatrix
}
popouts.transform: Matrix4x4 {
matrix: popoutBg.deformMatrix
}
}
BarWrapper {
id: bar
anchors.top: parent.top
anchors.bottom: parent.bottom
screen: root.screen
visibilities: visibilities
popouts: panels.popouts
fullscreen: root.hasFullscreen
Component.onCompleted: Visibilities.bars.set(root.screen, this)
}
}
component PanelBg: BlobRect {
required property Item panel
property real deformAmount: 0.15
group: panel.width > 0 && panel.height > 0 ? blobGroup : null
x: panel.x + bar.implicitWidth
y: panel.y + root.borderThickness
implicitWidth: panel.width
implicitHeight: panel.height
radius: Config.border.rounding
deformScale: deformAmount / 10000
}
}

View file

@ -1,18 +1,9 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import Caelestia.Blobs
import qs.components
import qs.components.containers
import qs.services import qs.services
import qs.config import qs.config
import qs.utils
import qs.modules.bar
Variants { Variants {
model: Screens.screens model: Screens.screens
@ -21,348 +12,17 @@ Variants {
id: scope id: scope
required property ShellScreen modelData required property ShellScreen modelData
readonly property bool barDisabled: Strings.testRegexList(Config.bar.excludedScreens, modelData.name)
Exclusions { Exclusions {
screen: scope.modelData screen: scope.modelData
bar: bar bar: content.bar
borderThickness: Config.border.thickness borderThickness: Config.border.thickness
} }
StyledWindow { ContentWindow {
id: win id: content
readonly property var monitor: Hypr.monitorFor(screen)
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject?.specialWorkspace?.name.length ?? 0) > 0
readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject?.specialWorkspace?.name;
if (!specialName)
return false;
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
return monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
property real borderThickness: hasFullscreen ? 0 : Config.border.thickness
readonly property real borderLayoutThickness: hasFullscreen ? 0 : Config.border.thickness
property real borderRounding: hasFullscreen ? 0 : Config.border.rounding
property real shadowOpacity: hasFullscreen ? 0 : 0.7
readonly property int dragMaskPadding: {
if (focusGrab.active || panels.popouts.isDetached)
return 0;
const mon = Hypr.monitorFor(screen);
if (mon?.lastIpcObject.specialWorkspace?.name || mon?.activeWorkspace.lastIpcObject.windows > 0)
return 0;
const thresholds = [];
for (const panel of ["dashboard", "launcher", "session", "sidebar"])
if (Config[panel].enabled)
thresholds.push(Config[panel].dragThreshold);
return Math.max(...thresholds);
}
onHasFullscreenChanged: {
visibilities.launcher = false;
visibilities.session = false;
visibilities.dashboard = false;
}
screen: scope.modelData screen: scope.modelData
name: "drawers"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.session || panels.dashboard.needsKeyboard ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
mask: Regions {
bar: bar
panels: panels
win: win
} }
anchors.top: true
anchors.bottom: true
anchors.left: true
anchors.right: true
Behavior on borderThickness {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on borderRounding {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on shadowOpacity {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
HyprlandFocusGrab {
id: focusGrab
active: (visibilities.launcher && Config.launcher.enabled) || (visibilities.session && Config.session.enabled) || (visibilities.sidebar && Config.sidebar.enabled) || (!Config.dashboard.showOnHover && visibilities.dashboard && Config.dashboard.enabled) || (panels.popouts.currentName.startsWith("traymenu") && (panels.popouts.current as StackView)?.depth > 1)
windows: [win]
onCleared: {
visibilities.launcher = false;
visibilities.session = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
panels.popouts.hasCurrent = false;
bar.closeTray();
}
}
StyledRect {
anchors.fill: parent
opacity: visibilities.session && Config.session.enabled ? 0.5 : 0
color: Colours.palette.m3scrim
Behavior on opacity {
Anim {}
}
}
Item {
anchors.fill: parent
opacity: Colours.transparency.enabled ? Colours.transparency.base : 1
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
blurMax: 15
shadowColor: Qt.alpha(Colours.palette.m3shadow, Math.max(0, win.shadowOpacity))
}
// Border {
// bar: bar
// }
// Backgrounds {
// panels: panels
// bar: bar
// }
BlobGroup {
id: blobGroup
color: Colours.palette.m3surface
Behavior on color {
CAnim {}
}
}
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50 // Make border thicker to smooth out bulge from closed drawers
group: blobGroup
radius: win.borderRounding
borderLeft: bar.implicitWidth - anchors.margins
borderRight: win.borderThickness - anchors.margins
borderTop: win.borderThickness - anchors.margins
borderBottom: win.borderThickness - anchors.margins
}
PanelBg {
id: dashBg
blobGroup: blobGroup
panel: panels.dashboard
bar: bar
borderThickness: win.borderThickness
deformAmount: 0.1
}
PanelBg {
id: launcherBg
blobGroup: blobGroup
panel: panels.launcher
bar: bar
borderThickness: win.borderThickness
deformAmount: 0.1
}
PanelBg {
id: sessionBg
blobGroup: blobGroup
panel: panels.sessionWrapper
bar: bar
borderThickness: win.borderThickness
deformAmount: 0.25
x: panels.sessionWrapper.x + panels.session.x + bar.implicitWidth
implicitWidth: panels.session.width
}
PanelBg {
id: sidebarBg
blobGroup: blobGroup
panel: panels.sidebar
bar: bar
borderThickness: win.borderThickness
deformAmount: 0.05
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
bottomLeftRadius: Math.max(0, Math.min(1, panels.sidebar.offsetScale / 0.3)) * radius
}
PanelBg {
id: osdBg
blobGroup: blobGroup
panel: panels.osdWrapper
bar: bar
borderThickness: win.borderThickness
deformAmount: 0.3
x: panels.osdWrapper.x + panels.osd.x + bar.implicitWidth
implicitWidth: panels.osd.width
}
PanelBg {
id: notifsBg
blobGroup: blobGroup
panel: panels.notifications
bar: bar
borderThickness: win.borderThickness
}
PanelBg {
id: utilsBg
blobGroup: blobGroup
panel: panels.utilities
bar: bar
borderThickness: win.borderThickness
deformAmount: panels.sidebar.visible ? 0.1 : 0.15
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
topLeftRadius: Math.max(0, Math.min(1, panels.sidebar.offsetScale / 0.3)) * radius
}
PanelBg {
id: popoutBg
blobGroup: blobGroup
panel: panels.popouts
bar: bar
borderThickness: win.borderThickness
x: bar.implicitWidth - (panels.popouts.isDetached ? -(win.width - panels.popouts.shownWidth) / 2 : panels.popouts.hasCurrent ? 0 : panels.popouts.shownWidth + 5)
implicitWidth: panels.popouts.shownWidth
Behavior on x {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on implicitWidth {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
}
}
DrawerVisibilities {
id: visibilities
Component.onCompleted: Visibilities.load(scope.modelData, this)
}
Interactions {
screen: scope.modelData
popouts: panels.popouts
visibilities: visibilities
panels: panels
bar: bar
borderThickness: win.borderLayoutThickness
fullscreen: win.hasFullscreen
Panels {
id: panels
screen: scope.modelData
visibilities: visibilities
bar: bar
borderThickness: win.borderThickness
utilities.horizontalStretch: (sidebarBg.rawDeformMatrix.m11 - 1) / 2 + 1
dashboard.transform: Matrix4x4 {
matrix: dashBg.deformMatrix
}
launcher.transform: Matrix4x4 {
matrix: launcherBg.deformMatrix
}
session.transform: Matrix4x4 {
matrix: sessionBg.deformMatrix
}
sidebar.transform: Matrix4x4 {
matrix: sidebarBg.deformMatrix
}
osd.transform: Matrix4x4 {
matrix: osdBg.deformMatrix
}
notifications.transform: Matrix4x4 {
matrix: notifsBg.deformMatrix
}
utilities.transform: Matrix4x4 {
matrix: utilsBg.deformMatrix
}
popouts.transform: Matrix4x4 {
matrix: popoutBg.deformMatrix
}
}
BarWrapper {
id: bar
anchors.top: parent.top
anchors.bottom: parent.bottom
screen: scope.modelData
visibilities: visibilities
popouts: panels.popouts
disabled: scope.barDisabled
fullscreen: win.hasFullscreen
Component.onCompleted: Visibilities.bars.set(scope.modelData, this)
}
}
}
}
component PanelBg: BlobRect {
required property BlobGroup blobGroup
required property Item panel
required property Item bar
required property real borderThickness
property real deformAmount: 0.15
group: panel.width > 0 && panel.height > 0 ? blobGroup : null
x: panel.x + bar.implicitWidth
y: panel.y + borderThickness
implicitWidth: panel.width
implicitHeight: panel.height
radius: Config.border.rounding
deformScale: deformAmount / 10000
} }
} }