From 958d07a3ffd40813511c37066ebd4b55a670c8fa Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:28:18 +1000 Subject: [PATCH] dashboard/calendar: add scroll --- modules/dashboard/dash/Calendar.qml | 383 ++++++++++++++-------------- 1 file changed, 198 insertions(+), 185 deletions(-) diff --git a/modules/dashboard/dash/Calendar.qml b/modules/dashboard/dash/Calendar.qml index f8cf04a4..56c04938 100644 --- a/modules/dashboard/dash/Calendar.qml +++ b/modules/dashboard/dash/Calendar.qml @@ -2,13 +2,14 @@ pragma ComponentBehavior: Bound import qs.components import qs.components.effects +import qs.components.controls import qs.services import qs.config import QtQuick import QtQuick.Controls import QtQuick.Layouts -Column { +CustomMouseArea { id: root required property var state @@ -18,221 +19,233 @@ Column { anchors.left: parent.left anchors.right: parent.right - padding: Appearance.padding.large - spacing: Appearance.spacing.small + implicitHeight: inner.implicitHeight + inner.anchors.margins * 2 - RowLayout { - id: monthNavigationRow + acceptedButtons: Qt.MiddleButton + onClicked: root.state.currentDate = new Date() - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: parent.padding + function onWheel(event: WheelEvent): void { + if (event.angleDelta.y > 0) + root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1); + else if (event.angleDelta.y < 0) + root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1); + } + + ColumnLayout { + id: inner + + anchors.fill: parent + anchors.margins: Appearance.padding.large spacing: Appearance.spacing.small - Item { - implicitWidth: implicitHeight - implicitHeight: prevMonthText.implicitHeight + Appearance.padding.small * 2 + RowLayout { + id: monthNavigationRow - StateLayer { - id: prevMonthStateLayer + Layout.fillWidth: true + spacing: Appearance.spacing.small - radius: Appearance.rounding.full + Item { + implicitWidth: implicitHeight + implicitHeight: prevMonthText.implicitHeight + Appearance.padding.small * 2 - function onClicked(): void { - root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1); + StateLayer { + id: prevMonthStateLayer + + radius: Appearance.rounding.full + + function onClicked(): void { + root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1); + } + } + + MaterialIcon { + id: prevMonthText + + anchors.centerIn: parent + text: "chevron_left" + color: Colours.palette.m3tertiary + font.pointSize: Appearance.font.size.normal + font.weight: 700 } } - MaterialIcon { - id: prevMonthText + Item { + Layout.fillWidth: true - anchors.centerIn: parent - text: "chevron_left" - color: Colours.palette.m3tertiary - font.pointSize: Appearance.font.size.normal - font.weight: 700 + implicitWidth: monthYearDisplay.implicitWidth + Appearance.padding.small * 2 + implicitHeight: monthYearDisplay.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + anchors.fill: monthYearDisplay + anchors.margins: -Appearance.padding.small + anchors.leftMargin: -Appearance.padding.normal + anchors.rightMargin: -Appearance.padding.normal + + radius: Appearance.rounding.full + disabled: { + const now = new Date(); + return root.currMonth === now.getMonth() && root.currYear === now.getFullYear(); + } + + function onClicked(): void { + root.state.currentDate = new Date(); + } + } + + StyledText { + id: monthYearDisplay + + anchors.centerIn: parent + text: grid.title + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.normal + font.weight: 500 + font.capitalization: Font.Capitalize + } + } + + Item { + implicitWidth: implicitHeight + implicitHeight: nextMonthText.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + id: nextMonthStateLayer + + radius: Appearance.rounding.full + + function onClicked(): void { + root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1); + } + } + + MaterialIcon { + id: nextMonthText + + anchors.centerIn: parent + text: "chevron_right" + color: Colours.palette.m3tertiary + font.pointSize: Appearance.font.size.normal + font.weight: 700 + } + } + } + + DayOfWeekRow { + id: daysRow + + Layout.fillWidth: true + locale: grid.locale + + delegate: StyledText { + required property var model + + horizontalAlignment: Text.AlignHCenter + text: model.shortName + font.weight: 500 + color: (model.day === 0 || model.day === 6) ? Colours.palette.m3secondary : Colours.palette.m3onSurfaceVariant } } Item { Layout.fillWidth: true + implicitHeight: grid.implicitHeight - implicitWidth: monthYearDisplay.implicitWidth + Appearance.padding.small * 2 - implicitHeight: monthYearDisplay.implicitHeight + Appearance.padding.small * 2 + MonthGrid { + id: grid - StateLayer { - anchors.fill: monthYearDisplay - anchors.margins: -Appearance.padding.small - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal + month: root.currMonth + year: root.currYear - radius: Appearance.rounding.full - disabled: root.state.currentDate.toDateString() == new Date().toDateString() + anchors.fill: parent - function onClicked(): void { - root.state.currentDate = new Date(); - } - } + spacing: 3 + locale: Qt.locale() - StyledText { - id: monthYearDisplay + delegate: Item { + id: dayItem - anchors.centerIn: parent - text: grid.title - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.normal - font.weight: 500 - font.capitalization: Font.Capitalize - } - } + required property var model - Item { - implicitWidth: implicitHeight - implicitHeight: nextMonthText.implicitHeight + Appearance.padding.small * 2 + implicitWidth: implicitHeight + implicitHeight: text.implicitHeight + Appearance.padding.small * 2 - StateLayer { - id: nextMonthStateLayer + StyledText { + id: text - radius: Appearance.rounding.full + anchors.centerIn: parent - function onClicked(): void { - root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1); - } - } + horizontalAlignment: Text.AlignHCenter + text: grid.locale.toString(dayItem.model.day) + color: { + const dayOfWeek = dayItem.model.date.getUTCDay(); + if (dayOfWeek === 0 || dayOfWeek === 6) + return Colours.palette.m3secondary; - MaterialIcon { - id: nextMonthText - - anchors.centerIn: parent - text: "chevron_right" - color: Colours.palette.m3tertiary - font.pointSize: Appearance.font.size.normal - font.weight: 700 - } - } - } - - DayOfWeekRow { - id: daysRow - - locale: grid.locale - - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: parent.padding - - delegate: StyledText { - required property var model - - horizontalAlignment: Text.AlignHCenter - text: model.shortName - font.weight: 500 - color: (model.day === 0 || model.day === 6) ? Colours.palette.m3secondary : Colours.palette.m3onSurfaceVariant - } - } - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: parent.padding - - implicitHeight: grid.implicitHeight - - MonthGrid { - id: grid - - month: root.currMonth - year: root.currYear - - anchors.fill: parent - - spacing: 3 - locale: Qt.locale() - - delegate: Item { - id: dayItem - - required property var model - - implicitWidth: implicitHeight - implicitHeight: text.implicitHeight + Appearance.padding.small * 2 - - StyledText { - id: text - - anchors.centerIn: parent - - horizontalAlignment: Text.AlignHCenter - text: grid.locale.toString(dayItem.model.day) - color: { - const dayOfWeek = dayItem.model.date.getUTCDay(); - if (dayOfWeek === 0 || dayOfWeek === 6) - return Colours.palette.m3secondary; - - return Colours.palette.m3onSurfaceVariant; + return Colours.palette.m3onSurfaceVariant; + } + opacity: dayItem.model.today || dayItem.model.month === grid.month ? 1 : 0.4 + font.pointSize: Appearance.font.size.normal + font.weight: 500 } - opacity: dayItem.model.today || dayItem.model.month === grid.month ? 1 : 0.4 - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } - } - } - - StyledRect { - id: todayIndicator - - readonly property Item todayItem: grid.contentItem.children.find(c => c.model.today) ?? null - property Item today - - onTodayItemChanged: { - if (todayItem) - today = todayItem; - } - - x: today ? today.x + (today.width - implicitWidth) / 2 : 0 - y: today?.y ?? 0 - - implicitWidth: today?.implicitWidth ?? 0 - implicitHeight: today?.implicitHeight ?? 0 - - clip: true - radius: Appearance.rounding.full - color: Colours.palette.m3primary - - opacity: todayItem ? 1 : 0 - scale: todayItem ? 1 : 0.7 - - Colouriser { - x: -todayIndicator.x - y: -todayIndicator.y - - implicitWidth: grid.width - implicitHeight: grid.height - - source: grid - sourceColor: Colours.palette.m3onSurface - colorizationColor: Colours.palette.m3onPrimary - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim {} - } - - Behavior on x { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } } - Behavior on y { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + StyledRect { + id: todayIndicator + + readonly property Item todayItem: grid.contentItem.children.find(c => c.model.today) ?? null + property Item today + + onTodayItemChanged: { + if (todayItem) + today = todayItem; + } + + x: today ? today.x + (today.width - implicitWidth) / 2 : 0 + y: today?.y ?? 0 + + implicitWidth: today?.implicitWidth ?? 0 + implicitHeight: today?.implicitHeight ?? 0 + + clip: true + radius: Appearance.rounding.full + color: Colours.palette.m3primary + + opacity: todayItem ? 1 : 0 + scale: todayItem ? 1 : 0.7 + + Colouriser { + x: -todayIndicator.x + y: -todayIndicator.y + + implicitWidth: grid.width + implicitHeight: grid.height + + source: grid + sourceColor: Colours.palette.m3onSurface + colorizationColor: Colours.palette.m3onPrimary + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + Behavior on x { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + + Behavior on y { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } } } }