right sidebar: slightly nicer bottom group tab switch anim

This commit is contained in:
end-4
2025-11-25 13:12:46 +01:00
parent 08739043f6
commit b4920a7cb6
@@ -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;
} }
@@ -77,13 +94,13 @@ Rectangle {
} }
spacing: 15 spacing: 15
CalendarHeaderButton { CalendarHeaderButton {
Layout.margins: 10 Layout.margins: 10
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" : ""}`
@@ -119,10 +136,10 @@ Rectangle {
} }
} }
anchors.fill: parent anchors.fill: parent
height: tabStack.height // implicitHeight: tabStack.implicitHeight
spacing: 10 spacing: 10
// Navigation rail // Navigation rail
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true
@@ -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;
}
}
}
} }