Files
illogical-impulse/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml
T
2026-01-13 10:19:33 +01:00

280 lines
9.6 KiB
QML

pragma ComponentBehavior: Bound
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import qs.modules.ii.sidebarRight.calendar
import qs.modules.ii.sidebarRight.todo
import qs.modules.ii.sidebarRight.pomodoro
import QtQuick
import QtQuick.Layouts
Rectangle {
id: root
radius: Appearance.rounding.normal
color: Appearance.colors.colLayer1
clip: true
implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : 350
property int selectedTab: Persistent.states.sidebar.bottomGroup.tab
property int previousIndex: -1
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
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": "timer",
"name": Translation.tr("Timer"),
"icon": "schedule",
"widget": "pomodoro/PomodoroWidget.qml"
},
]
Behavior on implicitHeight {
NumberAnimation {
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
}
function setCollapsed(state) {
Persistent.states.sidebar.bottomGroup.collapsed = state;
if (collapsed) {
bottomWidgetGroupRow.opacity = 0;
} else {
collapsedBottomWidgetGroupRow.opacity = 0;
}
collapseCleanFadeTimer.start();
}
Timer {
id: collapseCleanFadeTimer
interval: Appearance.animation.elementMove.duration / 2
repeat: false
onTriggered: {
if (collapsed)
collapsedBottomWidgetGroupRow.opacity = 1;
else
bottomWidgetGroupRow.opacity = 1;
}
}
Keys.onPressed: event => {
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) {
root.selectedTab = Math.min(root.selectedTab + 1, root.tabs.length - 1);
} else if (event.key === Qt.Key_PageUp) {
root.selectedTab = Math.max(root.selectedTab - 1, 0);
}
event.accepted = true;
}
}
// The thing when collapsed
RowLayout {
id: collapsedBottomWidgetGroupRow
opacity: collapsed ? 1 : 0
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
id: collapsedBottomWidgetGroupRowFade
duration: Appearance.animation.elementMove.duration / 2
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
}
spacing: 15
CalendarHeaderButton {
Layout.margins: 10
Layout.rightMargin: 0
forceCircle: true
downAction: () => {
root.setCollapsed(false);
}
contentItem: MaterialSymbol {
text: "keyboard_arrow_up"
iconSize: Appearance.font.pixelSize.larger
horizontalAlignment: Text.AlignHCenter
color: Appearance.colors.colOnLayer1
}
}
StyledText {
property int remainingTasks: Todo.list.filter(task => !task.done).length
Layout.margins: 10
Layout.leftMargin: 0
// text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}`
text: Translation.tr("%1 • %2 tasks").arg(DateTime.collapsedCalendarFormat).arg(remainingTasks)
font.pixelSize: Appearance.font.pixelSize.large
color: Appearance.colors.colOnLayer1
}
}
// The thing when expanded
RowLayout {
id: bottomWidgetGroupRow
opacity: collapsed ? 0 : 1
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
id: bottomWidgetGroupRowFade
duration: Appearance.animation.elementMove.duration / 2
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
}
anchors.fill: parent
// implicitHeight: tabStack.implicitHeight
spacing: 20
// Navigation rail
Item {
Layout.fillHeight: true
Layout.fillWidth: false
Layout.leftMargin: 10
Layout.topMargin: 10
implicitWidth: tabBar.implicitWidth
// Navigation rail buttons
NavigationRailTabArray {
id: tabBar
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
currentIndex: root.selectedTab
expanded: false
Repeater {
model: root.tabs
NavigationRailButton {
required property int index
required property var modelData
showToggledHighlight: false
toggled: root.selectedTab == index
buttonText: modelData.name
buttonIcon: modelData.icon
onPressed: {
root.selectedTab = index;
Persistent.states.sidebar.bottomGroup.tab = index;
}
}
}
}
// Collapse button
CalendarHeaderButton {
anchors.left: parent.left
anchors.top: parent.top
forceCircle: true
downAction: () => {
root.setCollapsed(true);
}
contentItem: MaterialSymbol {
text: "keyboard_arrow_down"
iconSize: Appearance.font.pixelSize.larger
horizontalAlignment: Text.AlignHCenter
color: Appearance.colors.colOnLayer1
}
}
}
// Content area
Item {
Layout.fillWidth: true
Layout.fillHeight: true
// implicitHeight: tabStack.implicitHeight
Loader {
id: tabStack
anchors.fill: parent
anchors.bottomMargin: -anchors.topMargin
Component.onCompleted: {
tabStack.source = root.tabs[root.selectedTab].widget;
}
Connections {
target: root
function onSelectedTabChanged() {
if (root.currentTab > root.previousIndex)
tabSwitchBehavior.animation.down = true;
else if (root.currentTab < root.previousIndex)
tabSwitchBehavior.animation.down = false;
tabStack.source = root.tabs[root.selectedTab].widget;
}
}
Behavior on source {
id: tabSwitchBehavior
animation: TabSwitchAnim {
id: upAnim
down: true
}
}
}
}
}
component TabSwitchAnim: SequentialAnimation {
id: switchAnim
property bool down: false
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;
}
}
}
}