import QtQuick import "../components" // Calendar grid – a plain Item so it can live inside an expanding section border. Item { id: root property var now: new Date() property int viewYear: now.getFullYear() property int viewMonth: now.getMonth() // 0-based readonly property var monthNames: [ "January","February","March","April","May","June", "July","August","September","October","November","December" ] readonly property int daysInMonth: new Date(viewYear, viewMonth + 1, 0).getDate() // First weekday of month: Mon=0 … Sun=6 readonly property int startOffset: (new Date(viewYear, viewMonth, 1).getDay() + 6) % 7 readonly property int totalCells: Math.ceil((startOffset + daysInMonth) / 7) * 7 readonly property int todayYear: now.getFullYear() readonly property int todayMonth: now.getMonth() readonly property int todayDay: now.getDate() // All widths derived from this constant — never from parent.width — // to avoid Column polish loops when the popup is wider than the grid. readonly property int cellW: 28 readonly property int cellGap: 2 readonly property int gridW: 7 * cellW + 6 * cellGap // 208 implicitWidth: gridW + 24 implicitHeight: calLayout.implicitHeight + 16 Column { id: calLayout // Centre the fixed-width grid inside the (potentially wider) popup. // No left+right anchors → implicitWidth stays at gridW, no resize loop. anchors { top: parent.top; topMargin: 8; horizontalCenter: parent.horizontalCenter } spacing: 6 // Month navigation — all child widths are explicit constants Row { spacing: 0 Text { text: "‹" color: Theme.text font.pixelSize: 16 width: 20 horizontalAlignment: Text.AlignHCenter MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { if (root.viewMonth === 0) { root.viewMonth = 11; root.viewYear-- } else root.viewMonth-- } } } Text { text: root.monthNames[root.viewMonth] + " " + root.viewYear color: Theme.text font.pixelSize: 13 font.bold: true width: root.gridW - 40 // fixed constant, not parent.width horizontalAlignment: Text.AlignHCenter } Text { text: "›" color: Theme.text font.pixelSize: 16 width: 20 horizontalAlignment: Text.AlignHCenter MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { if (root.viewMonth === 11) { root.viewMonth = 0; root.viewYear++ } else root.viewMonth++ } } } } // Weekday headers Row { spacing: root.cellGap Repeater { model: ["Mo","Tu","We","Th","Fr","Sa","Su"] Text { text: modelData color: Theme.textDim font.pixelSize: 10 width: root.cellW horizontalAlignment: Text.AlignHCenter } } } // Day grid Grid { columns: 7 spacing: root.cellGap Repeater { model: root.totalCells delegate: Item { width: root.cellW height: 22 readonly property int dayNum: index - root.startOffset + 1 readonly property bool valid: index >= root.startOffset && index < root.startOffset + root.daysInMonth readonly property bool isToday: valid && root.viewYear === root.todayYear && root.viewMonth === root.todayMonth && dayNum === root.todayDay Rectangle { anchors.fill: parent radius: 3 color: isToday ? Theme.accent : "transparent" visible: isToday } Text { anchors.centerIn: parent text: parent.valid ? String(parent.dayNum) : "" color: parent.isToday ? Theme.text : Theme.textDim font.pixelSize: 11 font.bold: parent.isToday } } } } } }