From 6e6db08726e3bc274594de6835a01fc10002def9 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 17 May 2025 00:44:00 +0800 Subject: feat: dashboard --- modules/dashboard/Background.qml | 71 ++++++++++++ modules/dashboard/Content.qml | 34 ++++++ modules/dashboard/Tabs.qml | 226 +++++++++++++++++++++++++++++++++++++++ modules/dashboard/Wrapper.qml | 58 ++++++++++ 4 files changed, 389 insertions(+) create mode 100644 modules/dashboard/Background.qml create mode 100644 modules/dashboard/Content.qml create mode 100644 modules/dashboard/Tabs.qml create mode 100644 modules/dashboard/Wrapper.qml (limited to 'modules/dashboard') 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 + } + + } + +} -- cgit v1.2.3-freya