hefty: bar: time: calendar

This commit is contained in:
end-4
2026-03-07 21:47:00 +01:00
parent c155bde816
commit 9ffe4dfb11
9 changed files with 326 additions and 90 deletions
@@ -375,7 +375,7 @@ Singleton {
property QtObject scroll: QtObject { property QtObject scroll: QtObject {
property int duration: 200 property int duration: 200
property int type: Easing.BezierSpline property int type: Easing.BezierSpline
property list<real> bezierCurve: animationCurves.standardDecel property list<real> bezierCurve: root.animationCurves.standardDecel
} }
property QtObject menuDecel: QtObject { property QtObject menuDecel: QtObject {
@@ -301,6 +301,9 @@ Singleton {
property JsonObject calendar: JsonObject { property JsonObject calendar: JsonObject {
property string locale: "en-GB" property string locale: "en-GB"
property bool force2CharDayOfWeek: true
property bool animate: false // Disabled by default cuz laggy
property bool weekScrollPrecision: false // One scroll advances 1 week instead of 1 month
} }
property JsonObject cheatsheet: JsonObject { property JsonObject cheatsheet: JsonObject {
@@ -18,7 +18,4 @@ JsonObject {
property JsonObject actionCenter: JsonObject { property JsonObject actionCenter: JsonObject {
property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ] property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
} }
property JsonObject calendar: JsonObject {
property bool force2CharDayOfWeek: true
}
} }
@@ -1,15 +1,10 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQml import QtQml
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import qs.modules.common.functions import qs.modules.common.functions
import qs.modules.waffle.looks
Item { Item {
id: root id: root
@@ -36,15 +31,23 @@ Item {
const diffWeeks = Math.round(diffMillis / root.millisPerWeek); const diffWeeks = Math.round(diffMillis / root.millisPerWeek);
root.targetWeekDiff += diffWeeks; root.targetWeekDiff += diffWeeks;
} }
function scrollToToday() {
root.targetWeekDiff = 0;
}
property int weeksPerScroll: 1 property int weeksPerScroll: 1
property real targetWeekDiff: 0 property real targetWeekDiff: 0
property real weekDiff: targetWeekDiff property real weekDiff: targetWeekDiff
property int contentWeekDiff: weekDiff // whole part of weekDiff property int contentWeekDiff: weekDiff // whole part of weekDiff
property bool scrolling: false 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 { Behavior on weekDiff {
id: weekScrollBehavior id: weekScrollBehavior
animation: Looks.transition.scroll.createObject(this) animation: root.scrollAnimation
} }
Timer { Timer {
id: scrollAnimationCheckTimer id: scrollAnimationCheckTimer
@@ -56,12 +59,20 @@ Item {
scrollAnimationCheckTimer.restart(); scrollAnimationCheckTimer.restart();
} }
MouseArea { property var wheelAction: (wheel) => {
anchors.fill: parent // Reverse cuz scrolling down should advance
onWheel: wheel => { const sign = wheel.angleDelta.y / 120 * -1;
root.targetWeekDiff += wheel.angleDelta.y / 120 * -root.weeksPerScroll; // Reverse cuz scrolling down should advance 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 // Date calculations
readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000 readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000
@@ -82,14 +93,16 @@ Item {
return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday 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 int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed
property string title: locale.toString(focusedDate, "MMMM yyyy")
// Sizes // Sizes
property real verticalPadding: 0 property real verticalPadding: 0
property real horizontalPadding: 0
property real buttonSize: 40 property real buttonSize: 40
property real buttonSpacing: 2 property real buttonSpacing: 2
property real buttonVerticalSpacing: buttonSpacing property real buttonVerticalSpacing: buttonSpacing
implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding) implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding)
implicitWidth: weeksColumn.implicitWidth implicitWidth: weeksColumn.implicitWidth + (2 * horizontalPadding)
clip: true clip: true
ColumnLayout { ColumnLayout {
@@ -97,6 +110,8 @@ Item {
anchors { anchors {
left: parent.left left: parent.left
right: parent.right right: parent.right
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
} }
y: { y: {
const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing; const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing;
@@ -10,7 +10,7 @@ GridLayout {
property real totalDuration: 250 property real totalDuration: 250
property real interval: totalDuration / count property real interval: totalDuration / count
default property list<AbstractChoreographable> choreographableChildren default property list<QtObject> choreographableChildren
readonly property int count: choreographableChildren.length readonly property int count: choreographableChildren.length
children: choreographableChildren children: choreographableChildren
@@ -0,0 +1,72 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import ".."
import "../functions"
Button {
id: root
property alias radius: bg.radius
property alias contentLayer: bg.contentLayer
property color colFocusRing: Appearance.colors.colOnSecondaryContainer
property color colBackground: checked ? colBackgroundChecked : colBackgroundUnchecked
property color colForeground: checked ? colForegroundChecked : colForegroundUnchecked
property color colBackgroundUnchecked: ColorUtils.transparentize(Appearance.colors.colLayer4Base, 1)
property color colBackgroundChecked: Appearance.colors.colPrimary
property color colForegroundUnchecked: Appearance.colors.colOnLayer4
property color colForegroundChecked: Appearance.colors.colOnPrimary
hoverEnabled: true
opacity: root.enabled ? 1 : 0.5
HoverHandler {
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
}
background: StyledRectangle {
id: bg
implicitHeight: root.contentItem.implicitHeight
implicitWidth: root.contentItem.implicitWidth
radius: Math.min(width, height) / 2
color: root.colBackground
StateOverlay {
anchors.fill: parent
hover: root.hovered && root.enabled
press: root.pressed && root.enabled
focus: false // We use a ring instead
radius: bg.radius
}
Rectangle {
id: focusRing
radius: bg.radius - anchors.margins
visible: root.visualFocus
color: "transparent"
anchors {
fill: parent
margins: -4
}
border {
color: root.colFocusRing
width: 2
}
}
}
contentItem: Item {
implicitWidth: buttonText.implicitWidth
implicitHeight: buttonText.implicitHeight
VisuallyCenteredStyledText {
id: buttonText
anchors.centerIn: parent
text: root.text
color: root.colForeground
}
}
}
@@ -0,0 +1,19 @@
pragma ComponentBehavior: Bound
import QtQuick
StyledButton {
id: root
property alias implicitSize: root.implicitHeight
implicitWidth: implicitHeight
property alias iconSize: icon.iconSize
contentItem: Item {
MaterialSymbol {
id: icon
anchors.centerIn: parent
color: root.colForeground
text: root.text
}
}
}
@@ -1,5 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.modules.common as C import qs.modules.common as C
@@ -70,38 +71,8 @@ HBarWidgetContainer {
anchors.fill: parent anchors.fill: parent
shown: !contentRoot.vertical shown: !contentRoot.vertical
sourceComponent: Item { sourceComponent: HorizontalClock {
anchors.fill: parent anchors.fill: parent
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
RowLayout {
id: contentLayout
anchors.fill: parent
W.VisuallyCenteredStyledText {
Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.fillHeight: true
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.time
}
W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1
text: "•"
}
W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.longDate
}
}
} }
} }
@@ -111,45 +82,13 @@ HBarWidgetContainer {
anchors.fill: parent anchors.fill: parent
shown: contentRoot.vertical shown: contentRoot.vertical
sourceComponent: Item { sourceComponent: VerticalClock {
anchors.fill: parent anchors.fill: parent
implicitWidth: contentLayoutVertical.implicitWidth
implicitHeight: contentLayoutVertical.implicitHeight
ColumnLayout {
id: contentLayoutVertical
anchors.fill: parent
spacing: -6
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: {
var hrs = S.DateTime.clock.hours;
if (root.is12h) hrs %= 12;
return hrs.toString().padStart(2, '0')
}
}
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.clock.minutes.toString().padStart(2, '0')
}
W.StyledText {
visible: root.hasAmPm
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.smaller
color: C.Appearance.colors.colOnLayer1
text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap")
}
}
} }
} }
// Popup content // Popup content
W.ChoreographerGrid { PopupContent {
id: popupContent id: popupContent
anchors { anchors {
top: root.vertical ? verticalContent.top : horizontalContent.top top: root.vertical ? verticalContent.top : horizontalContent.top
@@ -159,21 +98,211 @@ HBarWidgetContainer {
} }
shown: root.showPopup shown: root.showPopup
}
}
component HorizontalClock: Item {
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
RowLayout {
id: contentLayout
anchors.fill: parent
W.VisuallyCenteredStyledText {
Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.fillHeight: true
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.time
}
W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1
text: "•"
}
W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.longDate
}
}
}
component VerticalClock: Item {
implicitWidth: contentLayoutVertical.implicitWidth
implicitHeight: contentLayoutVertical.implicitHeight
ColumnLayout {
id: contentLayoutVertical
anchors.fill: parent
spacing: amPmText.visible ? -2 : -4
ColumnLayout {
id: verticalTime
Layout.alignment: Qt.AlignHCenter
spacing: -4
W.FlyFadeEnterChoreographable {
W.StyledText { W.StyledText {
text: "Kalender" Layout.alignment: Qt.AlignHCenter
font.pixelSize: 25 font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: {
var hrs = S.DateTime.clock.hours;
if (root.is12h && hrs != 12)
hrs %= 12;
return hrs.toString().padStart(2, '0');
}
}
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.clock.minutes.toString().padStart(2, '0')
}
W.StyledText {
id: amPmText
visible: root.hasAmPm
Layout.topMargin: -2
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.smaller
color: C.Appearance.colors.colOnLayer1
text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap")
} }
} }
W.FlyFadeEnterChoreographable {
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.smallest
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.shortDate
}
}
}
component PopupContent: W.ChoreographerGridLayout {
id: popupRoot
property real buttonSize: C.Appearance.rounding.normal * 2
property real buttonSpacing: 4
rowSpacing: 0
W.FlyFadeEnterChoreographable {
Layout.fillWidth: true
RowLayout {
width: parent.width
spacing: 0
W.StyledText { W.StyledText {
text: "Lorem ipsum okakumalum tung\ntung tung tung" Layout.leftMargin: 6
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
text: calendarView.title
font.pixelSize: C.Appearance.font.pixelSize.larger
elide: Text.ElideRight
color: C.Appearance.colors.colSecondary
}
W.StyledIconButton {
implicitSize: 30
text: "chevron_left"
iconSize: 20
onClicked: calendarView.scrollMonthsAndSnap(-1)
colForeground: C.Appearance.colors.colPrimary
}
W.StyledIconButton {
implicitSize: 30
text: "chevron_right"
iconSize: 20
onClicked: calendarView.scrollMonthsAndSnap(1)
colForeground: C.Appearance.colors.colPrimary
}
W.StyledIconButton {
implicitSize: 30
text: "rotate_left"
iconSize: 20
onClicked: calendarView.scrollToToday()
colForeground: C.Appearance.colors.colPrimary
enabled: calendarView.targetWeekDiff != 0
} }
} }
W.FlyFadeEnterChoreographable { }
W.StyledText { W.FlyFadeEnterChoreographable {
text: "BAJLANDO\nUUOOOUUUOOOUUOOUOU" Layout.fillWidth: true
DayOfWeekRow {
id: dayOfWeekRow
width: parent.width
locale: calendarView.locale
spacing: popupRoot.buttonSpacing
delegate: Item {
id: dayOfWeekItem
required property var model
implicitHeight: popupRoot.buttonSize
implicitWidth: popupRoot.buttonSize
W.VisuallyCenteredStyledText {
anchors.centerIn: parent
text: {
var result = dayOfWeekItem.model.shortName;
if (C.Config.options.calendar.force2CharDayOfWeek)
result = result.substring(0, 2);
return result;
}
}
}
}
}
W.FlyFadeEnterChoreographable {
Item {
implicitWidth: calendarView.implicitWidth - calendarView.horizontalPadding * 2
implicitHeight: calendarView.implicitHeight - calendarView.verticalPadding * 2
W.CalendarView {
id: calendarView
anchors.centerIn: parent
locale: Qt.locale(C.Config.options.calendar.locale)
verticalPadding: 4
horizontalPadding: 4
buttonSize: popupRoot.buttonSize
buttonSpacing: popupRoot.buttonSpacing
buttonVerticalSpacing: popupRoot.buttonSpacing
Layout.fillWidth: true
delegate: W.StyledButton {
id: dayButton
required property var model
focus: model.today
checked: model.today
enabled: model.month === calendarView.focusedMonth
implicitWidth: popupRoot.buttonSize
implicitHeight: popupRoot.buttonSize
width: implicitWidth
height: implicitHeight
text: model.day
Connections {
target: popupRoot
enabled: dayButton.model.today
function onShownChanged() {
if (popupRoot.shown) dayButton.forceActiveFocus();
}
}
contentItem: Item {
W.VisuallyCenteredStyledText {
anchors.centerIn: parent
text: dayButton.text
color: dayButton.colForeground
}
}
}
} }
} }
} }
@@ -54,7 +54,7 @@ BodyRectangle {
anchors.centerIn: parent anchors.centerIn: parent
text: { text: {
var result = dayOfWeekItem.model.shortName; var result = dayOfWeekItem.model.shortName;
if (Config.options.waffles.calendar.force2CharDayOfWeek) result = result.substring(0,2); if (Config.options.calendar.force2CharDayOfWeek) result = result.substring(0,2);
return result; return result;
} }
color: Looks.colors.fg color: Looks.colors.fg
@@ -70,6 +70,7 @@ BodyRectangle {
buttonSpacing: 6 buttonSpacing: 6
buttonVerticalSpacing: 1 buttonVerticalSpacing: 1
Layout.fillWidth: true Layout.fillWidth: true
scrollAnimation: Looks.transition.scroll.createObject(this)
delegate: DayButton {} delegate: DayButton {}
} }
} }