Files
illogical-impulse/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml
T
2026-03-21 11:54:47 +01:00

139 lines
5.2 KiB
QML

pragma ComponentBehavior: Bound
import QtQml
import QtQuick
import QtQuick.Layouts
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
Item {
id: root
// Expose delegate
property Component delegate: Text {
required property var model
text: model.day
}
// Configuration
property int paddingWeeks: 2 // 1 should be sufficient with proper clipping and no padding
property var locale: Qt.locale() // Should be of type Locale but QML is being funny
// Scrolling
function scrollMonthsAndSnap(x) { // Scroll x months and snap to month
const focusedDate = root.focusedDate;
const focusedMonth = focusedDate.getMonth();
const focusedYear = focusedDate.getFullYear();
const targetMonth = focusedMonth + x;
const targetDate = new Date(focusedYear, targetMonth, 1);
const currentFirstShownDate = new Date(root.dateInFirstWeek.getTime() + (root.paddingWeeks * root.millisPerWeek));
const diffMillis = targetDate.getTime() - currentFirstShownDate.getTime();
const diffWeeks = Math.round(diffMillis / root.millisPerWeek);
root.targetWeekDiff += diffWeeks;
}
function scrollToToday() {
root.targetWeekDiff = 0;
}
property int weeksPerScroll: 1
property real targetWeekDiff: 0
property real weekDiff: targetWeekDiff
property int contentWeekDiff: weekDiff // whole part of weekDiff
property bool scrolling: false
property Animation scrollAnimation: NumberAnimation {
duration: Config.options.calendar.animate ? Appearance.animation.scroll.duration : 0
easing.type: Appearance.animation.scroll.type
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
}
Behavior on weekDiff {
id: weekScrollBehavior
animation: root.scrollAnimation
}
Timer {
id: scrollAnimationCheckTimer
interval: 30 // Should be plenty for 60fps
onTriggered: root.scrolling = false;
}
onWeekDiffChanged: {
scrolling = true;
scrollAnimationCheckTimer.restart();
}
property var wheelAction: (wheel) => {
// Reverse cuz scrolling down should advance
const sign = wheel.angleDelta.y / 120 * -1;
if (Config.options.calendar.weekScrollPrecision) {
root.targetWeekDiff += sign * root.weeksPerScroll;
} else {
scrollMonthsAndSnap(sign);
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
onWheel: (wheel) => root.wheelAction(wheel)
}
// Date calculations
readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000
readonly property int totalWeeks: 6 + (paddingWeeks * 2)
readonly property int focusedWeekIndex: 2 // The third row, 0-indexed
readonly property int focusDayOfWeekIndex: 6
property date dateInFirstWeek: {
const currentDate = new Date();
const currentMonth = currentDate.getMonth();
const currentYear = currentDate.getFullYear();
const firstDayThisMonth = new Date(currentYear, currentMonth, 1);
return new Date(firstDayThisMonth.getTime() - (paddingWeeks * millisPerWeek) + contentWeekDiff * millisPerWeek);
}
property date focusedDate: {
// The last day of 3rd week shown is considered the focused month
const addedTime = (root.paddingWeeks + root.focusedWeekIndex) * root.millisPerWeek
const dateInTargetWeek = new Date(root.dateInFirstWeek.getTime() + addedTime);
return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday
}
property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed
property string title: locale.toString(focusedDate, "MMMM yyyy")
// Sizes
property real verticalPadding: 0
property real horizontalPadding: 0
property real buttonSize: 40
property real buttonSpacing: 2
property real buttonVerticalSpacing: buttonSpacing
implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding)
implicitWidth: weeksColumn.implicitWidth + (2 * horizontalPadding)
clip: true
ColumnLayout {
id: weeksColumn
anchors {
left: parent.left
right: parent.right
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
}
y: {
const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing;
const origin = -(spacePerExtraRow * root.paddingWeeks);
const diff = root.weekDiff * spacePerExtraRow;
return origin + (-diff % spacePerExtraRow) + root.verticalPadding;
}
spacing: root.buttonVerticalSpacing
Repeater {
model: root.totalWeeks
WeekRow {
required property int index
locale: root.locale
date: new Date(root.dateInFirstWeek.getTime() + (index * root.millisPerWeek))
Layout.fillWidth: true
spacing: root.buttonSpacing
delegate: root.delegate
}
}
}
}