Calendar: add conditional week number column. New option is in the Location tab of the settings.

This commit is contained in:
LemmyCook
2025-09-17 22:08:46 -04:00
parent 75b7f0fcb0
commit 6e88118ca9
3 changed files with 157 additions and 39 deletions
+1
View File
@@ -316,6 +316,7 @@ Singleton {
property bool useFahrenheit: false
property bool use12hourFormat: false
property bool monthBeforeDay: false
property bool showWeekNumberInCalendar: false
}
// screen recorder
+148 -38
View File
@@ -10,7 +10,7 @@ import qs.Widgets
NPanel {
id: root
preferredWidth: 340
preferredWidth: Settings.data.location.showWeekNumberInCalendar ? 350 : 330
preferredHeight: 320
// Main Column
@@ -60,7 +60,7 @@ NPanel {
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginS * scaling
Layout.bottomMargin: Style.marginM * scaling
Layout.bottomMargin: Style.marginL * scaling
}
// Columns label (respects locale's first day of week)
@@ -68,62 +68,172 @@ NPanel {
Layout.fillWidth: true
Layout.leftMargin: Style.marginS * scaling // Align with grid
Layout.rightMargin: Style.marginS * scaling
Layout.bottomMargin: Style.marginM * scaling
spacing: 0
Repeater {
model: 7
// Week header spacer or label (same width as week number column)
Item {
visible: Settings.data.location.showWeekNumberInCalendar
Layout.preferredWidth: visible ? Style.baseWidgetSize * scaling : 0
NText {
text: {
// Use the locale's first day of week setting
let firstDay = Qt.locale().firstDayOfWeek
let dayIndex = (firstDay + index) % 7
return Qt.locale().dayName(dayIndex, Locale.ShortFormat)
}
color: Color.mSecondary
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
anchors.centerIn: parent
text: "Week"
color: Color.mOutline
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightRegular
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
Layout.preferredWidth: Style.baseWidgetSize * scaling
}
}
// Day name headers - now properly aligned with calendar grid
GridLayout {
Layout.fillWidth: true
Layout.fillHeight: true
columns: 7
rows: 1
columnSpacing: 0
rowSpacing: 0
Repeater {
model: 7
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: Style.baseWidgetSize * scaling
NText {
anchors.centerIn: parent
text: {
// Use the locale's first day of week setting
let firstDay = Qt.locale().firstDayOfWeek
let dayIndex = (firstDay + index) % 7
return Qt.locale().dayName(dayIndex, Locale.ShortFormat)
}
color: Color.mSecondary
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
horizontalAlignment: Text.AlignHCenter
}
}
}
}
}
// Grids: days
MonthGrid {
id: grid
// Grids: days with optional week numbers
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true // Take remaining space
Layout.fillHeight: true
Layout.leftMargin: Style.marginS * scaling
Layout.rightMargin: Style.marginS * scaling
spacing: 0
month: Time.date.getMonth()
year: Time.date.getFullYear()
locale: Qt.locale() // Use system locale
delegate: Rectangle {
width: (Style.baseWidgetSize * scaling)
height: (Style.baseWidgetSize * scaling)
radius: Style.radiusS * scaling
color: model.today ? Color.mPrimary : Color.transparent
// Week numbers column (only visible when enabled)
GridLayout {
visible: Settings.data.location.showWeekNumberInCalendar
Layout.preferredWidth: visible ? Style.baseWidgetSize * scaling : 0
Layout.fillHeight: true
columns: 1
rows: 6
columnSpacing: 0
rowSpacing: 0
NText {
anchors.centerIn: parent
text: model.day
color: model.today ? Color.mOnPrimary : Color.mOnSurface
opacity: model.month === grid.month ? Style.opacityHeavy : Style.opacityLight
font.pointSize: (Style.fontSizeM * scaling)
font.weight: model.today ? Style.fontWeightBold : Style.fontWeightRegular
Repeater {
model: 6 // Maximum 6 weeks in a month view
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Color.transparent
NText {
anchors.centerIn: parent
color: Color.mOutline
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
text: {
// Calculate the first day shown in the calendar grid
let firstDay = new Date(grid.year, grid.month, 1)
let firstDayOfWeek = Qt.locale().firstDayOfWeek
let startOffset = (firstDay.getDay() - firstDayOfWeek + 7) % 7
let gridStartDate = new Date(grid.year, grid.month, 1 - startOffset)
// Get the date for the start of this specific row
let rowDate = new Date(gridStartDate)
rowDate.setDate(gridStartDate.getDate() + (index * 7))
// Calculate week number based on the Thursday of the visual row
// This correctly handles rows that span two different ISO weeks.
let thursdayOfRow = new Date(rowDate)
let offsetToThursday = (4 - thursdayOfRow.getDay() + 7) % 7
thursdayOfRow.setDate(thursdayOfRow.getDate() + offsetToThursday)
// Check if this row is visible (contains days from current month)
let rowEndDate = new Date(rowDate)
rowEndDate.setDate(rowDate.getDate() + 6)
if (rowDate.getMonth() === grid.month || rowEndDate.getMonth() === grid.month || (rowDate.getMonth() < grid.month && rowEndDate.getMonth() > grid.month)) {
return `${getISOWeekNumber(thursdayOfRow)}`
}
return ""
}
}
}
}
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
// The actual calendar grid
MonthGrid {
id: grid
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
month: Time.date.getMonth()
year: Time.date.getFullYear()
locale: Qt.locale()
delegate: Rectangle {
width: Style.baseWidgetSize * scaling
height: Style.baseWidgetSize * scaling
radius: Style.radiusS * scaling
color: model.today ? Color.mPrimary : Color.transparent
NText {
anchors.centerIn: parent
text: model.day
color: model.today ? Color.mOnPrimary : Color.mOnSurface
opacity: model.month === grid.month ? Style.opacityHeavy : Style.opacityLight
font.pointSize: Style.fontSizeM * scaling
font.weight: model.today ? Style.fontWeightBold : Style.fontWeightRegular
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
}
}
}
}
}
}
function getISOWeekNumber(date) {
// Create a copy of the date and normalize to noon to prevent DST issues
const targetDate = new Date(date.getTime())
targetDate.setHours(12, 0, 0, 0)
// Roll the date to the Thursday of the week.
// getDay() is 0 for Sunday, we want Monday to be 1 and Sunday to be 7.
const dayOfWeek = targetDate.getDay() || 7
targetDate.setDate(targetDate.getDate() - dayOfWeek + 4)
// Get the first day of that Thursday's year
const yearStart = new Date(targetDate.getFullYear(), 0, 1)
// Calculate the difference in days and find the week number
const dayOfYear = ((targetDate - yearStart) / 86400000) + 1
return Math.ceil(dayOfYear / 7)
}
}
+8 -1
View File
@@ -100,11 +100,18 @@ ColumnLayout {
}
NToggle {
label: "Show Month Before Day"
label: "Show month before day"
description: "Organize your dates. On for 09/17/2025, off for 17/09/2025."
checked: Settings.data.location.monthBeforeDay
onToggled: checked => Settings.data.location.monthBeforeDay = checked
}
NToggle {
label: "Show week number in calendar"
description: "Displays the week number of the year in calendar view."
checked: Settings.data.location.showWeekNumberInCalendar
onToggled: checked => Settings.data.location.showWeekNumberInCalendar = checked
}
}
NDivider {