forked from Shinonome/dots-hyprland
right sidebar: slightly nicer bottom group tab switch anim
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.services
|
import qs.services
|
||||||
@@ -12,32 +13,47 @@ Rectangle {
|
|||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
color: Appearance.colors.colLayer1
|
color: Appearance.colors.colLayer1
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : bottomWidgetGroupRow.implicitHeight
|
implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : 350
|
||||||
property int selectedTab: Persistent.states.sidebar.bottomGroup.tab
|
property int selectedTab: Persistent.states.sidebar.bottomGroup.tab
|
||||||
|
property int previousIndex: -1
|
||||||
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
|
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
|
||||||
property var tabs: [
|
property var tabs: [
|
||||||
{"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": "calendar/CalendarWidget.qml"},
|
{
|
||||||
{"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": "todo/TodoWidget.qml"},
|
"type": "calendar",
|
||||||
{"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": "pomodoro/PomodoroWidget.qml"},
|
"name": Translation.tr("Calendar"),
|
||||||
|
"icon": "calendar_month",
|
||||||
|
"widget": "calendar/CalendarWidget.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "todo",
|
||||||
|
"name": Translation.tr("To Do"),
|
||||||
|
"icon": "done_outline",
|
||||||
|
"widget": "todo/TodoWidget.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "timer",
|
||||||
|
"name": Translation.tr("Timer"),
|
||||||
|
"icon": "schedule",
|
||||||
|
"widget": "pomodoro/PomodoroWidget.qml"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Appearance.animation.elementMove.duration
|
duration: Appearance.animation.elementMove.duration
|
||||||
easing.type: Appearance.animation.elementMove.type
|
easing.type: Appearance.animation.elementMove.type
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCollapsed(state) {
|
function setCollapsed(state) {
|
||||||
Persistent.states.sidebar.bottomGroup.collapsed = state
|
Persistent.states.sidebar.bottomGroup.collapsed = state;
|
||||||
if (collapsed) {
|
if (collapsed) {
|
||||||
bottomWidgetGroupRow.opacity = 0
|
bottomWidgetGroupRow.opacity = 0;
|
||||||
|
} else {
|
||||||
|
collapsedBottomWidgetGroupRow.opacity = 0;
|
||||||
}
|
}
|
||||||
else {
|
collapseCleanFadeTimer.start();
|
||||||
collapsedBottomWidgetGroupRow.opacity = 0
|
|
||||||
}
|
|
||||||
collapseCleanFadeTimer.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -45,18 +61,19 @@ Rectangle {
|
|||||||
interval: Appearance.animation.elementMove.duration / 2
|
interval: Appearance.animation.elementMove.duration / 2
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if(collapsed) collapsedBottomWidgetGroupRow.opacity = 1
|
if (collapsed)
|
||||||
else bottomWidgetGroupRow.opacity = 1
|
collapsedBottomWidgetGroupRow.opacity = 1;
|
||||||
|
else
|
||||||
|
bottomWidgetGroupRow.opacity = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: event => {
|
||||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp)
|
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.ControlModifier) {
|
||||||
&& event.modifiers === Qt.ControlModifier) {
|
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabs.length - 1)
|
root.selectedTab = Math.min(root.selectedTab + 1, root.tabs.length - 1);
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
root.selectedTab = Math.max(root.selectedTab - 1, 0);
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
@@ -83,7 +100,7 @@ Rectangle {
|
|||||||
Layout.rightMargin: 0
|
Layout.rightMargin: 0
|
||||||
forceCircle: true
|
forceCircle: true
|
||||||
downAction: () => {
|
downAction: () => {
|
||||||
root.setCollapsed(false)
|
root.setCollapsed(false);
|
||||||
}
|
}
|
||||||
contentItem: MaterialSymbol {
|
contentItem: MaterialSymbol {
|
||||||
text: "keyboard_arrow_up"
|
text: "keyboard_arrow_up"
|
||||||
@@ -94,7 +111,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
property int remainingTasks: Todo.list.filter(task => !task.done).length;
|
property int remainingTasks: Todo.list.filter(task => !task.done).length
|
||||||
Layout.margins: 10
|
Layout.margins: 10
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
// text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}`
|
// text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}`
|
||||||
@@ -120,7 +137,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
height: tabStack.height
|
// implicitHeight: tabStack.implicitHeight
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
// Navigation rail
|
// Navigation rail
|
||||||
@@ -141,13 +158,15 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabs
|
model: root.tabs
|
||||||
NavigationRailButton {
|
NavigationRailButton {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
showToggledHighlight: false
|
showToggledHighlight: false
|
||||||
toggled: root.selectedTab == index
|
toggled: root.selectedTab == index
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
buttonIcon: modelData.icon
|
||||||
onPressed: {
|
onPressed: {
|
||||||
root.selectedTab = index
|
root.selectedTab = index;
|
||||||
Persistent.states.sidebar.bottomGroup.tab = index
|
Persistent.states.sidebar.bottomGroup.tab = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +177,7 @@ Rectangle {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
forceCircle: true
|
forceCircle: true
|
||||||
downAction: () => {
|
downAction: () => {
|
||||||
root.setCollapsed(true)
|
root.setCollapsed(true);
|
||||||
}
|
}
|
||||||
contentItem: MaterialSymbol {
|
contentItem: MaterialSymbol {
|
||||||
text: "keyboard_arrow_down"
|
text: "keyboard_arrow_down"
|
||||||
@@ -170,56 +189,96 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Content area
|
// Content area
|
||||||
Loader {
|
Item {
|
||||||
id: tabStack
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
source: root.tabs[root.selectedTab].widget
|
// implicitHeight: tabStack.implicitHeight
|
||||||
|
|
||||||
Behavior on source {
|
Loader {
|
||||||
id: switchBehavior
|
id: tabStack
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.bottomMargin: -anchors.topMargin
|
||||||
|
|
||||||
SequentialAnimation {
|
Component.onCompleted: {
|
||||||
id: switchAnim
|
tabStack.source = root.tabs[root.selectedTab].widget;
|
||||||
ParallelAnimation {
|
}
|
||||||
PropertyAnimation {
|
|
||||||
target: tabStack.item
|
Connections {
|
||||||
properties: "opacity"
|
target: root
|
||||||
to: 0
|
function onSelectedTabChanged() {
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
if (root.currentTab > root.previousIndex)
|
||||||
easing.type: Easing.BezierSpline
|
tabSwitchBehavior.animation.down = true;
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
else if (root.currentTab < root.previousIndex)
|
||||||
}
|
tabSwitchBehavior.animation.down = false;
|
||||||
PropertyAnimation {
|
tabStack.source = root.tabs[root.selectedTab].widget;
|
||||||
target: tabStack.item
|
|
||||||
properties: "y"
|
|
||||||
from: 0
|
|
||||||
to: 20
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Easing.InExpo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PropertyAction {} // The source change happens here
|
}
|
||||||
ParallelAnimation {
|
|
||||||
PropertyAnimation {
|
Behavior on source {
|
||||||
target: tabStack.item
|
id: tabSwitchBehavior
|
||||||
properties: "opacity"
|
animation: TabSwitchAnim {
|
||||||
to: 1
|
id: upAnim
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
down: true
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
PropertyAnimation {
|
|
||||||
target: tabStack.item
|
|
||||||
properties: "y"
|
|
||||||
from: 20
|
|
||||||
to: 0
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component TabSwitchAnim: SequentialAnimation {
|
||||||
|
id: switchAnim
|
||||||
|
property bool down: false
|
||||||
|
// ScriptAction {
|
||||||
|
// script: {
|
||||||
|
// switchAnim.down = root.selectedTab > root.previousIndex;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
ParallelAnimation {
|
||||||
|
PropertyAnimation {
|
||||||
|
target: tabStack
|
||||||
|
properties: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
target: tabStack.anchors
|
||||||
|
properties: "topMargin"
|
||||||
|
to: 10 * (switchAnim.down ? -1 : 1)
|
||||||
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
target: tabStack
|
||||||
|
property: "source"
|
||||||
|
value: root.tabs[root.selectedTab].widget
|
||||||
|
} // The source change happens here
|
||||||
|
ParallelAnimation {
|
||||||
|
PropertyAnimation {
|
||||||
|
target: tabStack.anchors
|
||||||
|
properties: "topMargin"
|
||||||
|
from: 10 * -(switchAnim.down ? -1 : 1)
|
||||||
|
to: 0
|
||||||
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
target: tabStack
|
||||||
|
properties: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
root.previousIndex = root.selectedTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user