import QtQuick import QtQuick.Layouts import "../services" import "../theme" Item { id: root implicitWidth: 210 implicitHeight: mainLayout.implicitHeight property date currentDate: new Date() property int selectedYear: currentDate.getFullYear() property int selectedMonth: currentDate.getMonth() function getMonthName(monthIndex) { var names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] return names[monthIndex] } function generateGrid() { var result = [] var firstDayIndex = new Date(selectedYear, selectedMonth, 1).getDay() var startOffset = (firstDayIndex + 6) % 7 var daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate() var prevMonthDays = new Date(selectedYear, selectedMonth, 0).getDate() // Leading days from previous month for (var i = startOffset - 1; i >= 0; i--) { result.push({ day: prevMonthDays - i, isCurrentMonth: false, isToday: false }) } // Active month days var today = new Date() for (var d = 1; d <= daysInMonth; d++) { var isToday = (d === today.getDate() && selectedMonth === today.getMonth() && selectedYear === today.getFullYear()) result.push({ day: d, isCurrentMonth: true, isToday: isToday }) } // Trailing days from next month to complete fixed 42 cells (6 rows x 7 cols) var totalNeeded = 42 - result.length for (var n = 1; n <= totalNeeded; n++) { result.push({ day: n, isCurrentMonth: false, isToday: false }) } return result } property var gridData: generateGrid() function prevMonth() { if (selectedMonth === 0) { selectedMonth = 11 selectedYear -= 1 } else { selectedMonth -= 1 } gridData = generateGrid() } function nextMonth() { if (selectedMonth === 11) { selectedMonth = 0 selectedYear += 1 } else { selectedMonth += 1 } gridData = generateGrid() } ColumnLayout { id: mainLayout anchors.fill: parent spacing: 8 // Month & Year Header Navigation RowLayout { Layout.fillWidth: true spacing: 0 Rectangle { width: 24; height: 24; radius: 4 color: prevMonthMouse.containsMouse ? Theme.currentLine : "transparent" Text { text: "‹"; color: Theme.fg; font.pixelSize: 14; anchors.centerIn: parent } MouseArea { id: prevMonthMouse anchors.fill: parent hoverEnabled: true onClicked: root.prevMonth() } } Text { text: root.getMonthName(root.selectedMonth) + " " + root.selectedYear color: Theme.fg font.pixelSize: 12 font.weight: Font.Bold Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } Rectangle { width: 24; height: 24; radius: 4 color: nextMonthMouse.containsMouse ? Theme.currentLine : "transparent" Text { text: "›"; color: Theme.fg; font.pixelSize: 14; anchors.centerIn: parent } MouseArea { id: nextMonthMouse anchors.fill: parent hoverEnabled: true onClicked: root.nextMonth() } } } // Days of Week Header Row RowLayout { Layout.fillWidth: true spacing: 4 Repeater { model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] Text { text: modelData color: Theme.accent font.pixelSize: 10 font.weight: Font.Bold Layout.preferredWidth: 26 horizontalAlignment: Text.AlignHCenter } } } // Calendar Days Fixed Grid (6 x 7) GridLayout { columns: 7 columnSpacing: 4 rowSpacing: 4 Layout.fillWidth: true Repeater { model: root.gridData Rectangle { width: 26 height: 22 radius: 4 color: modelData.isToday ? Theme.accent : "transparent" Text { anchors.centerIn: parent text: modelData.day.toString() color: modelData.isToday ? (Theme.isDark ? Theme.bg : "#ffffff") : (modelData.isCurrentMonth ? Theme.fg : Qt.rgba(98/255, 114/255, 164/255, 0.4)) font.pixelSize: 10 font.weight: modelData.isToday ? Font.Bold : Font.Normal } } } } // Solid Separator Line Rectangle { Layout.fillWidth: true Layout.preferredHeight: 1 color: Theme.currentLine Layout.topMargin: 4 Layout.bottomMargin: 2 } // Weather Report Section RowLayout { Layout.fillWidth: true spacing: 8 WeatherIcon { weatherCode: WeatherService.weatherCode isDay: WeatherService.isDay implicitWidth: 18 implicitHeight: 18 } ColumnLayout { spacing: 1 Layout.fillWidth: true Text { text: WeatherService.city color: Theme.fg font.pixelSize: 11 font.weight: Font.Bold elide: Text.ElideRight Layout.fillWidth: true } Text { text: WeatherService.condition color: Theme.comment font.pixelSize: 10 elide: Text.ElideRight Layout.fillWidth: true } } Text { text: WeatherService.currentTempStr color: Theme.accent font.pixelSize: 12 font.weight: Font.Bold } } } }