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/Tabs.qml | 226 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 modules/dashboard/Tabs.qml (limited to 'modules/dashboard/Tabs.qml') 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 + } +} -- cgit v1.2.3-freya