hefty: bar: time: calendar

This commit is contained in:
end-4
2026-03-07 21:47:00 +01:00
parent 7a4468258b
commit a33122cd3c
9 changed files with 326 additions and 90 deletions
@@ -375,7 +375,7 @@ Singleton {
property QtObject scroll: QtObject {
property int duration: 200
property int type: Easing.BezierSpline
property list<real> bezierCurve: animationCurves.standardDecel
property list<real> bezierCurve: root.animationCurves.standardDecel
}
property QtObject menuDecel: QtObject {
@@ -301,6 +301,9 @@ Singleton {
property JsonObject calendar: JsonObject {
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 {
@@ -18,7 +18,4 @@ 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 JsonObject calendar: JsonObject {
property bool force2CharDayOfWeek: true
}
}
@@ -1,15 +1,10 @@
pragma ComponentBehavior: Bound
import QtQml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
Item {
id: root
@@ -36,15 +31,23 @@ Item {
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: Looks.transition.scroll.createObject(this)
animation: root.scrollAnimation
}
Timer {
id: scrollAnimationCheckTimer
@@ -56,12 +59,20 @@ Item {
scrollAnimationCheckTimer.restart();
}
MouseArea {
anchors.fill: parent
onWheel: wheel => {
root.targetWeekDiff += wheel.angleDelta.y / 120 * -root.weeksPerScroll; // Reverse cuz scrolling down should advance
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
@@ -82,14 +93,16 @@ Item {
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
implicitWidth: weeksColumn.implicitWidth + (2 * horizontalPadding)
clip: true
ColumnLayout {
@@ -97,6 +110,8 @@ Item {
anchors {
left: parent.left
right: parent.right
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
}
y: {
const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing;
@@ -10,7 +10,7 @@ GridLayout {
property real totalDuration: 250
property real interval: totalDuration / count
default property list<AbstractChoreographable> choreographableChildren
default property list<QtObject> choreographableChildren
readonly property int count: choreographableChildren.length
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
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.modules.common as C
@@ -70,38 +71,8 @@ HBarWidgetContainer {
anchors.fill: parent
shown: !contentRoot.vertical
sourceComponent: Item {
sourceComponent: HorizontalClock {
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
shown: contentRoot.vertical
sourceComponent: Item {
sourceComponent: VerticalClock {
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
W.ChoreographerGrid {
PopupContent {
id: popupContent
anchors {
top: root.vertical ? verticalContent.top : horizontalContent.top
@@ -159,21 +98,211 @@ HBarWidgetContainer {
}
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 {
text: "Kalender"
font.pixelSize: 25
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)
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 {
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 {
text: "BAJLANDO\nUUOOOUUUOOOUUOOUOU"
}
W.FlyFadeEnterChoreographable {
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
text: {
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;
}
color: Looks.colors.fg
@@ -70,6 +70,7 @@ BodyRectangle {
buttonSpacing: 6
buttonVerticalSpacing: 1
Layout.fillWidth: true
scrollAnimation: Looks.transition.scroll.createObject(this)
delegate: DayButton {}
}
}