summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-18 15:19:15 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-18 15:19:15 +1000
commit85d575f6c18017693cb2f3ab07314a0cf43adbe6 (patch)
tree89540859a71162c28c8419af49c75cc3634e6b70 /modules
parentlauncher: fix open anim (diff)
downloadcaelestia-shell-85d575f6c18017693cb2f3ab07314a0cf43adbe6.tar.gz
caelestia-shell-85d575f6c18017693cb2f3ab07314a0cf43adbe6.tar.bz2
caelestia-shell-85d575f6c18017693cb2f3ab07314a0cf43adbe6.zip
feat: add sidebar
WIP
Diffstat (limited to 'modules')
-rw-r--r--modules/drawers/Backgrounds.qml17
-rw-r--r--modules/drawers/Drawers.qml4
-rw-r--r--modules/drawers/Interactions.qml77
-rw-r--r--modules/drawers/Panels.qml23
-rw-r--r--modules/notifications/Background.qml15
-rw-r--r--modules/notifications/Content.qml8
-rw-r--r--modules/notifications/Wrapper.qml28
-rw-r--r--modules/session/Wrapper.qml6
-rw-r--r--modules/sidebar/Background.qml52
-rw-r--r--modules/sidebar/Content.qml19
-rw-r--r--modules/sidebar/Wrapper.qml66
-rw-r--r--modules/utilities/Background.qml7
-rw-r--r--modules/utilities/Wrapper.qml11
13 files changed, 278 insertions, 55 deletions
diff --git a/modules/drawers/Backgrounds.qml b/modules/drawers/Backgrounds.qml
index e1773a1..7fa2ca1 100644
--- a/modules/drawers/Backgrounds.qml
+++ b/modules/drawers/Backgrounds.qml
@@ -7,6 +7,7 @@ import qs.modules.launcher as Launcher
import qs.modules.dashboard as Dashboard
import qs.modules.bar.popouts as BarPopouts
import qs.modules.utilities as Utilities
+import qs.modules.sidebar as Sidebar
import QtQuick
import QtQuick.Shapes
@@ -24,12 +25,13 @@ Shape {
Osd.Background {
wrapper: root.panels.osd
- startX: root.width - root.panels.session.width
+ startX: root.width - root.panels.session.width - root.panels.sidebar.width
startY: (root.height - wrapper.height) / 2 - rounding
}
Notifications.Background {
wrapper: root.panels.notifications
+ sidebar: sidebar
startX: root.width
startY: 0
@@ -38,7 +40,7 @@ Shape {
Session.Background {
wrapper: root.panels.session
- startX: root.width
+ startX: root.width - root.panels.sidebar.width
startY: (root.height - wrapper.height) / 2 - rounding
}
@@ -66,8 +68,19 @@ Shape {
Utilities.Background {
wrapper: root.panels.utilities
+ sidebar: sidebar
startX: root.width
startY: root.height
}
+
+ Sidebar.Background {
+ id: sidebar
+
+ wrapper: root.panels.sidebar
+ panels: root.panels
+
+ startX: root.width
+ startY: root.panels.notifications.height
+ }
}
diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml
index 5e5e992..3503697 100644
--- a/modules/drawers/Drawers.qml
+++ b/modules/drawers/Drawers.qml
@@ -72,11 +72,12 @@ Variants {
}
HyprlandFocusGrab {
- active: (visibilities.launcher && Config.launcher.enabled) || (visibilities.session && Config.session.enabled)
+ active: (visibilities.launcher && Config.launcher.enabled) || (visibilities.session && Config.session.enabled) || (visibilities.sidebar && Config.sidebar.enabled)
windows: [win]
onCleared: {
visibilities.launcher = false;
visibilities.session = false;
+ visibilities.sidebar = false;
}
}
@@ -119,6 +120,7 @@ Variants {
property bool launcher
property bool dashboard
property bool utilities
+ property bool sidebar
Component.onCompleted: Visibilities.load(scope.modelData, this)
}
diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml
index 99aa267..84714b5 100644
--- a/modules/drawers/Interactions.qml
+++ b/modules/drawers/Interactions.qml
@@ -82,6 +82,8 @@ CustomMouseArea {
const x = event.x;
const y = event.y;
+ const dragX = x - dragStart.x;
+ const dragY = y - dragStart.y;
// Show bar in non-exclusive mode on hover
if (!visibilities.bar && Config.bar.showOnHover && x < bar.implicitWidth)
@@ -89,33 +91,68 @@ CustomMouseArea {
// Show/hide bar on drag
if (pressed && dragStart.x < bar.implicitWidth) {
- const dragX = x - dragStart.x;
if (dragX > Config.bar.dragThreshold)
visibilities.bar = true;
else if (dragX < -Config.bar.dragThreshold)
visibilities.bar = false;
}
- // Show osd on hover
- const showOsd = inRightPanel(panels.osd, x, y);
+ if (panels.sidebar.width === 0) {
+ // Show osd on hover
+ const showOsd = inRightPanel(panels.osd, x, y);
- // Always update visibility based on hover if not in shortcut mode
- if (!osdShortcutActive) {
- visibilities.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;
- }
+ // Always update visibility based on hover if not in shortcut mode
+ if (!osdShortcutActive) {
+ visibilities.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 > bar.implicitWidth + panels.sidebar.x;
+
+ // Show/hide session on drag
+ if (pressed && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
+ if (dragX < -Config.session.dragThreshold)
+ visibilities.session = true;
+ else if (dragX > Config.session.dragThreshold)
+ visibilities.session = false;
+
+ // Show sidebar on drag if in session area and session is nearly fully visible
+ if (showSidebar && panels.session.width >= panels.session.nonAnimWidth && dragX < -Config.sidebar.dragThreshold)
+ visibilities.sidebar = true;
+ } else if (showSidebar && dragX < -Config.sidebar.dragThreshold) {
+ // Show sidebar on drag if not in session area
+ visibilities.sidebar = true;
+ }
+ } else {
+ const outOfSidebar = x < width - panels.sidebar.width;
+ // Show osd on hover
+ const showOsd = outOfSidebar && inRightPanel(panels.osd, x, y);
+
+ // Always update visibility based on hover if not in shortcut mode
+ if (!osdShortcutActive) {
+ visibilities.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.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
+ if (dragX < -Config.session.dragThreshold)
+ visibilities.session = true;
+ else if (dragX > Config.session.dragThreshold)
+ visibilities.session = false;
+ }
- // Show/hide session on drag
- if (pressed && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
- const dragX = x - dragStart.x;
- if (dragX < -Config.session.dragThreshold)
- visibilities.session = true;
- else if (dragX > Config.session.dragThreshold)
- visibilities.session = false;
+ // Hide sidebar on drag
+ if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold)
+ visibilities.sidebar = false;
}
// Show launcher on hover, or show/hide on drag if hover is disabled
@@ -123,7 +160,6 @@ CustomMouseArea {
if (!visibilities.launcher && inBottomPanel(panels.launcher, x, y))
visibilities.launcher = true;
} else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) {
- const dragY = y - dragStart.y;
if (dragY < -Config.launcher.dragThreshold)
visibilities.launcher = true;
else if (dragY > Config.launcher.dragThreshold)
@@ -143,7 +179,6 @@ CustomMouseArea {
// Show/hide dashboard on drag (for touchscreen devices)
if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
- const dragY = y - dragStart.y;
if (dragY > Config.dashboard.dragThreshold)
visibilities.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml
index ed21f80..7896ddb 100644
--- a/modules/drawers/Panels.qml
+++ b/modules/drawers/Panels.qml
@@ -6,6 +6,7 @@ import qs.modules.launcher as Launcher
import qs.modules.dashboard as Dashboard
import qs.modules.bar.popouts as BarPopouts
import qs.modules.utilities as Utilities
+import qs.modules.sidebar as Sidebar
import Quickshell
import QtQuick
@@ -23,6 +24,7 @@ Item {
readonly property Dashboard.Wrapper dashboard: dashboard
readonly property BarPopouts.Wrapper popouts: popouts
readonly property Utilities.Wrapper utilities: utilities
+ readonly property Sidebar.Wrapper sidebar: sidebar
anchors.fill: parent
anchors.margins: Config.border.thickness
@@ -31,20 +33,20 @@ Item {
Osd.Wrapper {
id: osd
- clip: root.visibilities.session
+ clip: session.width > 0 || sidebar.width > 0
screen: root.screen
visibilities: root.visibilities
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
- anchors.rightMargin: session.width
+ anchors.rightMargin: session.width + sidebar.width
}
Notifications.Wrapper {
id: notifications
visibilities: root.visibilities
- panel: root
+ panels: root
anchors.top: parent.top
anchors.right: parent.right
@@ -53,10 +55,13 @@ Item {
Session.Wrapper {
id: session
+ clip: sidebar.width > 0
visibilities: root.visibilities
+ panels: root
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
+ anchors.rightMargin: sidebar.width
}
Launcher.Wrapper {
@@ -101,8 +106,20 @@ Item {
id: utilities
visibilities: root.visibilities
+ sidebar: sidebar
anchors.bottom: parent.bottom
anchors.right: parent.right
}
+
+ Sidebar.Wrapper {
+ id: sidebar
+
+ visibilities: root.visibilities
+ panels: root
+
+ anchors.top: notifications.bottom
+ anchors.bottom: utilities.top
+ anchors.right: parent.right
+ }
}
diff --git a/modules/notifications/Background.qml b/modules/notifications/Background.qml
index 47ec54a..a44cb19 100644
--- a/modules/notifications/Background.qml
+++ b/modules/notifications/Background.qml
@@ -1,7 +1,6 @@
import qs.components
import qs.services
import qs.config
-import Quickshell
import QtQuick
import QtQuick.Shapes
@@ -9,10 +8,10 @@ ShapePath {
id: root
required property Wrapper wrapper
+ required property var sidebar
readonly property real rounding: Config.border.rounding
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
- property real fullHeightRounding: wrapper.height >= QsWindow.window?.height - Config.border.thickness * 2 ? -rounding : rounding
strokeWidth: -1
fillColor: Colours.palette.m3surface
@@ -32,14 +31,14 @@ ShapePath {
relativeY: root.wrapper.height - root.roundingY * 2
}
PathArc {
- relativeX: root.fullHeightRounding
+ relativeX: root.sidebar.notifsRoundingX
relativeY: root.roundingY
- radiusX: Math.abs(root.fullHeightRounding)
+ radiusX: root.sidebar.notifsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
- direction: root.fullHeightRounding < 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ direction: PathArc.Counterclockwise
}
PathLine {
- relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.fullHeightRounding : root.wrapper.width
+ relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
@@ -52,8 +51,4 @@ ShapePath {
Behavior on fillColor {
CAnim {}
}
-
- Behavior on fullHeightRounding {
- Anim {}
- }
}
diff --git a/modules/notifications/Content.qml b/modules/notifications/Content.qml
index 2ece7f4..828c3a8 100644
--- a/modules/notifications/Content.qml
+++ b/modules/notifications/Content.qml
@@ -10,7 +10,7 @@ Item {
id: root
required property PersistentProperties visibilities
- required property Item panel
+ required property Item panels
readonly property int padding: Appearance.padding.large
anchors.top: parent.top
@@ -27,15 +27,15 @@ Item {
for (let i = 0; i < count; i++)
height += list.itemAtIndex(i)?.nonAnimHeight ?? 0;
- if (visibilities && panel) {
+ if (visibilities && panels) {
if (visibilities.osd) {
- const h = panel.osd.y - Config.border.rounding * 2 - padding * 2;
+ const h = panels.osd.y - Config.border.rounding * 2 - padding * 2;
if (height > h)
height = h;
}
if (visibilities.session) {
- const h = panel.session.y - Config.border.rounding * 2 - padding * 2;
+ const h = panels.session.y - Config.border.rounding * 2 - padding * 2;
if (height > h)
height = h;
}
diff --git a/modules/notifications/Wrapper.qml b/modules/notifications/Wrapper.qml
index 47655ac..61acc56 100644
--- a/modules/notifications/Wrapper.qml
+++ b/modules/notifications/Wrapper.qml
@@ -1,21 +1,39 @@
+import qs.components
import qs.config
-import Quickshell
import QtQuick
Item {
id: root
- required property PersistentProperties visibilities
- required property Item panel
+ required property var visibilities
+ required property Item panels
visible: height > 0
+ implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth)
implicitHeight: content.implicitHeight
- implicitWidth: content.implicitWidth
+
+ states: State {
+ name: "hidden"
+ when: root.visibilities.sidebar && Config.sidebar.enabled
+
+ PropertyChanges {
+ root.implicitHeight: 0
+ }
+ }
+
+ transitions: Transition {
+ Anim {
+ target: root
+ property: "implicitHeight"
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
Content {
id: content
visibilities: root.visibilities
- panel: root.panel
+ panels: root.panels
}
}
diff --git a/modules/session/Wrapper.qml b/modules/session/Wrapper.qml
index 967d8c3..14b03a8 100644
--- a/modules/session/Wrapper.qml
+++ b/modules/session/Wrapper.qml
@@ -9,6 +9,8 @@ Item {
id: root
required property PersistentProperties visibilities
+ required property var panels
+ readonly property real nonAnimWidth: content.implicitWidth
visible: width > 0
implicitWidth: 0
@@ -19,7 +21,7 @@ Item {
when: root.visibilities.session && Config.session.enabled
PropertyChanges {
- root.implicitWidth: content.implicitWidth
+ root.implicitWidth: root.nonAnimWidth
}
}
@@ -41,7 +43,7 @@ Item {
Anim {
target: root
property: "implicitWidth"
- easing.bezierCurve: root.visibilities.osd ? Appearance.anim.curves.expressiveDefaultSpatial : Appearance.anim.curves.emphasized
+ easing.bezierCurve: root.panels.osd.width > 0 ? Appearance.anim.curves.expressiveDefaultSpatial : Appearance.anim.curves.emphasized
}
}
]
diff --git a/modules/sidebar/Background.qml b/modules/sidebar/Background.qml
new file mode 100644
index 0000000..beefdf5
--- /dev/null
+++ b/modules/sidebar/Background.qml
@@ -0,0 +1,52 @@
+import qs.components
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Shapes
+
+ShapePath {
+ id: root
+
+ required property Wrapper wrapper
+ required property var panels
+
+ readonly property real rounding: Config.border.rounding
+
+ readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
+ readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
+
+ readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
+ readonly property real utilsRoundingX: utilsWidthDiff < rounding * 2 ? utilsWidthDiff / 2 : rounding
+
+ strokeWidth: -1
+ fillColor: Colours.palette.m3surface
+
+ PathLine {
+ relativeX: -root.wrapper.width - root.notifsRoundingX
+ relativeY: 0
+ }
+ PathArc {
+ relativeX: root.notifsRoundingX
+ relativeY: root.rounding
+ radiusX: root.notifsRoundingX
+ radiusY: root.rounding
+ }
+ PathLine {
+ relativeX: 0
+ relativeY: root.wrapper.height - root.rounding * 2
+ }
+ PathArc {
+ relativeX: -root.utilsRoundingX
+ relativeY: root.rounding
+ radiusX: root.utilsRoundingX
+ radiusY: root.rounding
+ }
+ PathLine {
+ relativeX: root.wrapper.width + root.utilsRoundingX
+ relativeY: 0
+ }
+
+ Behavior on fillColor {
+ CAnim {}
+ }
+}
diff --git a/modules/sidebar/Content.qml b/modules/sidebar/Content.qml
new file mode 100644
index 0000000..13829bd
--- /dev/null
+++ b/modules/sidebar/Content.qml
@@ -0,0 +1,19 @@
+import qs.config
+import QtQuick
+import QtQuick.Layouts
+
+Item {
+ id: root
+
+ required property var visibilities
+
+ implicitWidth: layout.implicitWidth
+ implicitHeight: layout.implicitHeight
+
+ ColumnLayout {
+ id: layout
+
+ anchors.fill: parent
+ spacing: Appearance.spacing.normal
+ }
+}
diff --git a/modules/sidebar/Wrapper.qml b/modules/sidebar/Wrapper.qml
new file mode 100644
index 0000000..ddbb39c
--- /dev/null
+++ b/modules/sidebar/Wrapper.qml
@@ -0,0 +1,66 @@
+pragma ComponentBehavior: Bound
+
+import qs.components
+import qs.config
+import QtQuick
+
+Item {
+ id: root
+
+ required property var visibilities
+ required property var panels
+
+ visible: width > 0
+ implicitWidth: 0
+ implicitHeight: 0
+
+ states: State {
+ name: "visible"
+ when: root.visibilities.sidebar && Config.sidebar.enabled
+
+ PropertyChanges {
+ root.implicitWidth: Config.sidebar.sizes.width
+ }
+ }
+
+ transitions: [
+ Transition {
+ from: ""
+ to: "visible"
+
+ Anim {
+ target: root
+ property: "implicitWidth"
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ },
+ Transition {
+ from: "visible"
+ to: ""
+
+ Anim {
+ target: root
+ property: "implicitWidth"
+ easing.bezierCurve: root.panels.osd.width > 0 || root.panels.session.width > 0 ? Appearance.anim.curves.expressiveDefaultSpatial : Appearance.anim.curves.emphasized
+ }
+ }
+ ]
+
+ Loader {
+ id: content
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: Appearance.padding.large
+
+ visible: false
+ active: true
+ Component.onCompleted: active = Qt.binding(() => (root.visibilities.sidebar && Config.sidebar.enabled) || root.visible)
+
+ sourceComponent: Content {
+ visibilities: root.visibilities
+ }
+ }
+}
diff --git a/modules/utilities/Background.qml b/modules/utilities/Background.qml
index 5dc2c13..fbce896 100644
--- a/modules/utilities/Background.qml
+++ b/modules/utilities/Background.qml
@@ -8,6 +8,7 @@ ShapePath {
id: root
required property Wrapper wrapper
+ required property var sidebar
readonly property real rounding: Config.border.rounding
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
@@ -31,13 +32,13 @@ ShapePath {
relativeY: -(root.wrapper.height - root.roundingY * 2)
}
PathArc {
- relativeX: root.rounding
+ relativeX: root.sidebar.utilsRoundingX
relativeY: -root.roundingY
- radiusX: root.rounding
+ radiusX: root.sidebar.utilsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
}
PathLine {
- relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding * 2 : root.wrapper.width
+ relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
diff --git a/modules/utilities/Wrapper.qml b/modules/utilities/Wrapper.qml
index 3f84319..a3ba53d 100644
--- a/modules/utilities/Wrapper.qml
+++ b/modules/utilities/Wrapper.qml
@@ -9,6 +9,8 @@ Item {
id: root
required property var visibilities
+ required property Item sidebar
+
readonly property PersistentProperties props: PersistentProperties {
property bool recordingListExpanded: false
property string recordingConfirmDelete
@@ -16,10 +18,11 @@ Item {
reloadableId: "utilities"
}
+ readonly property bool shouldBeActive: visibilities.sidebar || (visibilities.utilities && Config.utilities.enabled)
visible: height > 0
implicitHeight: 0
- implicitWidth: Config.utilities.sizes.width
+ implicitWidth: Math.max(sidebar.width, Config.utilities.sizes.width)
onStateChanged: {
if (state === "visible" && timer.running) {
@@ -30,7 +33,7 @@ Item {
states: State {
name: "visible"
- when: root.visibilities.utilities
+ when: root.shouldBeActive
PropertyChanges {
root.implicitHeight: content.implicitHeight + Appearance.padding.large * 2
@@ -67,7 +70,7 @@ Item {
running: true
interval: Appearance.anim.durations.extraLarge
onTriggered: {
- content.active = Qt.binding(() => (root.visibilities.utilities && Config.utilities.enabled) || root.visible);
+ content.active = Qt.binding(() => root.shouldBeActive || root.visible);
content.visible = true;
}
}
@@ -77,13 +80,13 @@ Item {
anchors.top: parent.top
anchors.left: parent.left
- anchors.right: parent.right
anchors.margins: Appearance.padding.large
visible: false
active: true
sourceComponent: Content {
+ implicitWidth: Config.utilities.sizes.width - Appearance.padding.large * 2
props: root.props
visibilities: root.visibilities
}