feat: dashboard
This commit is contained in:
parent
25e6f17c60
commit
6e6db08726
9 changed files with 430 additions and 1 deletions
13
config/DashboardConfig.qml
Normal file
13
config/DashboardConfig.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
71
modules/dashboard/Background.qml
Normal file
71
modules/dashboard/Background.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
34
modules/dashboard/Content.qml
Normal file
34
modules/dashboard/Content.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
226
modules/dashboard/Tabs.qml
Normal file
226
modules/dashboard/Tabs.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
58
modules/dashboard/Wrapper.qml
Normal file
58
modules/dashboard/Wrapper.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue