Garden was welded onto the notification list in CenterWidgetGroup — always on, unrelated, stealing space from notifications that said "Nothing". Moved it into the BottomWidgetGroup drawer as a fifth tab alongside Calendar, To Do, Pomodoro, and Stopwatch. CenterWidgetGroup is now just the notification list. Lens events (note_created, synced) emit through notify-send so they land in the notification system properly. Forked calendar, todo, pomodoro, notifications out of ii-base into our tree — the right sidebar no longer borrows any widget files from ii. Toggle dialogs (bluetooth, wifi, nightLight) still symlinked as infrastructure.
294 lines
9.8 KiB
QML
294 lines
9.8 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 : 430
|
|
property int selectedTab: Persistent.states.sidebar.bottomGroup.tab
|
|
property int previousIndex: -1
|
|
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
|
|
readonly property int collapsedHeight: Math.max(
|
|
Appearance.font.pixelSize.larger + 24,
|
|
Appearance.font.pixelSize.large + 24
|
|
)
|
|
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": "pomodoro",
|
|
"name": Translation.tr("Pomodoro"),
|
|
"icon": "search_activity",
|
|
"widget": "pomodoro/PomodoroWidget.qml"
|
|
},
|
|
{
|
|
"type": "stopwatch",
|
|
"name": Translation.tr("Stopwatch"),
|
|
"icon": "timer",
|
|
"widget": "pomodoro/StopwatchWidget.qml"
|
|
},
|
|
{
|
|
"type": "garden",
|
|
"name": Translation.tr("Garden"),
|
|
"icon": "hub",
|
|
"widget": "lens/LensWidget.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();
|
|
}
|
|
|
|
Connections {
|
|
target: Persistent.states.sidebar.bottomGroup
|
|
|
|
function onTabChanged() {
|
|
root.selectedTab = Persistent.states.sidebar.bottomGroup.tab;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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: Translation.tr("%1 • %2 tasks").arg(DateTime.collapsedCalendarFormat).arg(remainingTasks)
|
|
font.pixelSize: Appearance.font.pixelSize.large
|
|
color: Appearance.colors.colOnLayer1
|
|
}
|
|
}
|
|
|
|
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
|
|
spacing: 20
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
Layout.fillWidth: false
|
|
Layout.leftMargin: 10
|
|
Layout.topMargin: 10
|
|
implicitWidth: tabBar.implicitWidth
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
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.selectedTab > root.previousIndex)
|
|
tabSwitchBehavior.animation.down = true;
|
|
else if (root.selectedTab < 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
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|