summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-05-17 00:44:00 +0800
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-05-17 00:44:00 +0800
commit6e6db08726e3bc274594de6835a01fc10002def9 (patch)
tree272dcaa27b96690c449cdf61c8e4e6ea21559f48
parentlauncher: fallback app icon (diff)
downloadcaelestia-shell-6e6db08726e3bc274594de6835a01fc10002def9.tar.gz
caelestia-shell-6e6db08726e3bc274594de6835a01fc10002def9.tar.bz2
caelestia-shell-6e6db08726e3bc274594de6835a01fc10002def9.zip
feat: dashboard
-rw-r--r--config/DashboardConfig.qml13
-rw-r--r--modules/dashboard/Background.qml71
-rw-r--r--modules/dashboard/Content.qml34
-rw-r--r--modules/dashboard/Tabs.qml226
-rw-r--r--modules/dashboard/Wrapper.qml58
-rw-r--r--modules/drawers/Backgrounds.qml8
-rw-r--r--modules/drawers/Drawers.qml3
-rw-r--r--modules/drawers/Interactions.qml7
-rw-r--r--modules/drawers/Panels.qml11
9 files changed, 430 insertions, 1 deletions
diff --git a/config/DashboardConfig.qml b/config/DashboardConfig.qml
new file mode 100644
index 0000000..a0b2106
--- /dev/null
+++ b/config/DashboardConfig.qml
@@ -0,0 +1,13 @@
+pragma Singleton
+
+import Quickshell
+import QtQuick
+
+Singleton {
+ readonly property Sizes sizes: Sizes {}
+
+ component Sizes: QtObject {
+ readonly property int tabIndicatorHeight: 3
+ readonly property int tabIndicatorSpacing: 5
+ }
+}
diff --git a/modules/dashboard/Background.qml b/modules/dashboard/Background.qml
new file mode 100644
index 0000000..0ab6658
--- /dev/null
+++ b/modules/dashboard/Background.qml
@@ -0,0 +1,71 @@
+import QtQuick
+import QtQuick.Shapes
+import "root:/config"
+import "root:/services"
+
+ShapePath {
+ id: root
+
+ required property Wrapper wrapper
+ readonly property real rounding: BorderConfig.rounding
+ readonly property bool flatten: wrapper.height < rounding * 2
+ readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
+
+ strokeWidth: -1
+ fillColor: BorderConfig.colour
+
+ PathArc {
+ relativeX: root.rounding
+ relativeY: root.roundingY
+ radiusX: root.rounding
+ radiusY: Math.min(root.rounding, root.wrapper.height)
+ }
+
+ PathLine {
+ relativeX: 0
+ relativeY: root.wrapper.height - root.roundingY * 2
+ }
+
+ PathArc {
+ relativeX: root.rounding
+ relativeY: root.roundingY
+ radiusX: root.rounding
+ radiusY: Math.min(root.rounding, root.wrapper.height)
+ direction: PathArc.Counterclockwise
+ }
+
+ PathLine {
+ relativeX: root.wrapper.width - root.rounding * 4
+ relativeY: 0
+ }
+
+ PathArc {
+ relativeX: root.rounding
+ relativeY: -root.roundingY
+ radiusX: root.rounding
+ radiusY: Math.min(root.rounding, root.wrapper.height)
+ direction: PathArc.Counterclockwise
+ }
+
+ PathLine {
+ relativeX: 0
+ relativeY: -(root.wrapper.height - root.roundingY * 2)
+ }
+
+ PathArc {
+ relativeX: root.rounding
+ relativeY: -root.roundingY
+ radiusX: root.rounding
+ radiusY: Math.min(root.rounding, root.wrapper.height)
+ }
+
+ Behavior on fillColor {
+ ColorAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+
+ }
+
+}
diff --git a/modules/dashboard/Content.qml b/modules/dashboard/Content.qml
new file mode 100644
index 0000000..f3f102a
--- /dev/null
+++ b/modules/dashboard/Content.qml
@@ -0,0 +1,34 @@
+import "root:/widgets"
+import "root:/services"
+import "root:/config"
+import Quickshell
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ id: root
+
+ required property PersistentProperties visibilities
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+
+ implicitWidth: 800
+ implicitHeight: 300
+
+ Tabs {
+ id: tabs
+
+ anchors.fill: parent
+ anchors.topMargin: Appearance.padding.normal
+ anchors.margins: Appearance.padding.large
+
+ currentIndex: view.currentIndex
+ }
+
+ SwipeView {
+ id: view
+
+ currentIndex: tabs.currentIndex
+ }
+}
diff --git a/modules/dashboard/Tabs.qml b/modules/dashboard/Tabs.qml
new file mode 100644
index 0000000..49f836b
--- /dev/null
+++ b/modules/dashboard/Tabs.qml
@@ -0,0 +1,226 @@
+import "root:/widgets"
+import "root:/services"
+import "root:/config"
+import Quickshell.Widgets
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ id: root
+
+ property alias currentIndex: bar.currentIndex
+
+ implicitHeight: bar.height + indicator.height + indicator.anchors.topMargin + separator.height
+
+ TabBar {
+ id: bar
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+
+ background: null
+
+ Tab {
+ iconName: "dashboard"
+ text: qsTr("Dashboard")
+ }
+
+ Tab {
+ iconName: "queue_music"
+ text: qsTr("Media")
+ }
+
+ Tab {
+ iconName: "speed"
+ text: qsTr("Performance")
+ }
+
+ Tab {
+ iconName: "workspaces"
+ text: qsTr("Workspaces")
+ }
+ }
+
+ StyledRect {
+ id: indicator
+
+ anchors.top: bar.bottom
+ anchors.topMargin: DashboardConfig.sizes.tabIndicatorSpacing
+
+ implicitWidth: bar.currentItem.implicitWidth
+ implicitHeight: DashboardConfig.sizes.tabIndicatorHeight
+
+ x: {
+ const tab = bar.currentItem;
+ return tab.x + (tab.width - tab.implicitWidth) / 2;
+ }
+
+ color: Colours.palette.m3primary
+ radius: Appearance.rounding.full
+
+ Behavior on x {
+ Anim {}
+ }
+
+ Behavior on implicitWidth {
+ Anim {}
+ }
+ }
+
+ StyledRect {
+ id: separator
+
+ anchors.top: indicator.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ implicitHeight: 1
+ color: Colours.palette.m3outlineVariant
+ }
+
+ component Tab: TabButton {
+ id: tab
+
+ required property string iconName
+ readonly property bool current: TabBar.tabBar.currentItem === this
+
+ background: null
+
+ contentItem: MouseArea {
+ id: mouse
+
+ implicitWidth: Math.max(icon.width, label.width)
+ implicitHeight: icon.height + label.height
+
+ cursorShape: Qt.PointingHandCursor
+
+ onPressed: ({x,y}) => {
+ tab.TabBar.tabBar.setCurrentIndex(tab.TabBar.index);
+
+ const stateY = stateWrapper.y;
+ rippleAnim.x = x;
+ rippleAnim.y = y - stateY;
+
+ const dist = (ox,oy) => ox*ox + oy*oy
+ const stateEndY = stateY + stateWrapper.height
+ rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY)))
+
+ rippleAnim.restart();
+ }
+ onWheel: event => {
+ if (event.angleDelta.y > 0)
+ tab.TabBar.tabBar.incrementCurrentIndex();
+ else if (event.angleDelta.y < 0)
+ tab.TabBar.tabBar.decrementCurrentIndex();
+ }
+
+ SequentialAnimation {
+ id: rippleAnim
+
+ property real x
+ property real y
+ property real radius
+
+ PropertyAction {
+ target: ripple
+ property: "x"
+ value: rippleAnim.x
+ }
+ PropertyAction {
+ target: ripple
+ property: "y"
+ value: rippleAnim.y
+ }
+ PropertyAction {
+ target: ripple
+ property: "opacity"
+ value: 0.1
+ }
+ ParallelAnimation {
+ Anim {
+ target: ripple
+ properties: "implicitWidth,implicitHeight"
+ from: 0
+ to: rippleAnim.radius * 2
+ duration: Appearance.anim.durations.large
+ easing.bezierCurve: Appearance.anim.curves.standardDecel
+ }
+ Anim {
+ target: ripple
+ property: "opacity"
+ to: 0
+ duration: Appearance.anim.durations.large
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standardDecel
+ }
+ }
+ }
+
+ ClippingRectangle {
+ id: stateWrapper
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ implicitHeight: parent.height + DashboardConfig.sizes.tabIndicatorSpacing * 2
+
+ color: "transparent"
+ radius: Appearance.rounding.small
+
+ StyledRect {
+ id: stateLayer
+
+ anchors.fill: parent
+
+ color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurface
+ opacity: mouse.pressed ? 0.1 : tab.hovered ? 0.08 : 0
+
+ Behavior on opacity {
+ Anim {}
+ }
+ }
+
+ StyledRect {
+ id: ripple
+
+ radius: Appearance.rounding.full
+ color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurface
+ opacity: 0
+
+ transform: Translate {
+ x: -ripple.width / 2
+ y: -ripple.height / 2
+ }
+ }
+ }
+
+ MaterialIcon {
+ id: icon
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: label.top
+
+ text: tab.iconName
+ color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
+ font.pointSize: Appearance.font.size.large
+ }
+
+ StyledText {
+ id: label
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+
+ text: tab.text
+ color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
+ }
+ }
+ }
+
+ component Anim: NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+}
diff --git a/modules/dashboard/Wrapper.qml b/modules/dashboard/Wrapper.qml
new file mode 100644
index 0000000..c4388e6
--- /dev/null
+++ b/modules/dashboard/Wrapper.qml
@@ -0,0 +1,58 @@
+import QtQuick
+import Quickshell
+import "root:/config"
+
+Item {
+ id: root
+
+ required property PersistentProperties visibilities
+
+ visible: height > 0
+ implicitHeight: 0
+ implicitWidth: content.width + BorderConfig.rounding * 2
+ transitions: [
+ Transition {
+ from: ""
+ to: "visible"
+
+ NumberAnimation {
+ target: root
+ property: "implicitHeight"
+ duration: Appearance.anim.durations.large
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
+ }
+
+ },
+ Transition {
+ from: "visible"
+ to: ""
+
+ NumberAnimation {
+ target: root
+ property: "implicitHeight"
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
+ }
+
+ }
+ ]
+
+ Content {
+ id: content
+
+ visibilities: root.visibilities
+ }
+
+ states: State {
+ name: "visible"
+ when: root.visibilities.dashboard
+
+ PropertyChanges {
+ root.implicitHeight: content.height
+ }
+
+ }
+
+}
diff --git a/modules/drawers/Backgrounds.qml b/modules/drawers/Backgrounds.qml
index 3e0156f..a78c667 100644
--- a/modules/drawers/Backgrounds.qml
+++ b/modules/drawers/Backgrounds.qml
@@ -4,6 +4,7 @@ import "root:/modules/osd" as Osd
import "root:/modules/notifications" as Notifications
import "root:/modules/session" as Session
import "root:/modules/launcher" as Launcher
+import "root:/modules/dashboard" as Dashboard
import QtQuick.Shapes
Shape {
@@ -43,4 +44,11 @@ Shape {
startX: (root.width - wrapper.width) / 2
startY: root.height
}
+
+ Dashboard.Background {
+ wrapper: panels.dashboard
+
+ startX: (root.width - wrapper.width) / 2
+ startY: 0
+ }
}
diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml
index 51374e9..91c6fc8 100644
--- a/modules/drawers/Drawers.qml
+++ b/modules/drawers/Drawers.qml
@@ -89,10 +89,11 @@ Variants {
PersistentProperties {
id: visibilities
- property bool launcher
property bool osd
property bool notifications: Notifs.popups.length > 0
property bool session
+ property bool launcher
+ property bool dashboard
Component.onCompleted: Visibilities.screens[scope.modelData] = this
}
diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml
index e937326..0dfda50 100644
--- a/modules/drawers/Interactions.qml
+++ b/modules/drawers/Interactions.qml
@@ -21,6 +21,10 @@ MouseArea {
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > BorderConfig.thickness + panel.x && withinPanelHeight(panel, x, y);
}
+
+ function inTopPanel(panel: Item, x: real, y: real): bool {
+ const panelX = BorderConfig.thickness + panel.x;
+ return y < BorderConfig.thickness + panel.y + panel.height && x >= panelX && x <= panelX + panel.width;
}
anchors.fill: parent
@@ -50,6 +54,9 @@ MouseArea {
else if (dragX > SessionConfig.dragThreshold)
root.visibilities.session = false;
}
+
+ const showDashboard = root.inTopPanel(panels.dashboard, x, y);
+ root.visibilities.dashboard = showDashboard;
}
}
diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml
index ab5f8de..9bde4b2 100644
--- a/modules/drawers/Panels.qml
+++ b/modules/drawers/Panels.qml
@@ -3,6 +3,7 @@ import "root:/modules/osd" as Osd
import "root:/modules/notifications" as Notifications
import "root:/modules/session" as Session
import "root:/modules/launcher" as Launcher
+import "root:/modules/dashboard" as Dashboard
import Quickshell
import QtQuick
@@ -16,6 +17,7 @@ Item {
readonly property Notifications.Wrapper notifications: notifications
readonly property Session.Wrapper session: session
readonly property Launcher.Wrapper launcher: launcher
+ readonly property Dashboard.Wrapper dashboard: dashboard
anchors.fill: parent
anchors.margins: BorderConfig.thickness
@@ -58,4 +60,13 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
}
+
+ Dashboard.Wrapper {
+ id: dashboard
+
+ visibilities: root.visibilities
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ }
}