Skip to content

Commit 8306089

Browse files
committed
Calendar Refactored in separate small components
1 parent 84e4861 commit 8306089

File tree

12 files changed

+1252
-1119
lines changed

12 files changed

+1252
-1119
lines changed

qml/CalendarGrid.qml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import QtQuick 2.15
2+
import QtQuick.Layouts 1.15
3+
import QtQuick.Controls 2.15
4+
import "qrc:/qml/"
5+
6+
// CalendarGrid.qml
7+
GridLayout {
8+
id: calendarGrid
9+
columns: 7
10+
rowSpacing: 8
11+
columnSpacing: 8
12+
Layout.fillWidth: true
13+
Layout.fillHeight: true
14+
Layout.margins: 20
15+
Layout.topMargin: 10
16+
17+
property alias calendarModel: repeater.model
18+
property var theme
19+
20+
signal dayClicked(var panchanga)
21+
22+
Repeater {
23+
id: repeater
24+
delegate: Item {
25+
Layout.fillWidth: true
26+
Layout.fillHeight: modelData.type === "day" || modelData.type === "empty"
27+
Layout.preferredHeight: modelData.type === "header" ? 35 : -1
28+
29+
// Use a Loader to conditionally instantiate the correct component.
30+
// This is the key to fixing the "Unable to assign [undefined]" errors.
31+
Loader {
32+
id: delegateLoader
33+
anchors.fill: parent
34+
35+
// Set the source component based on the model data type
36+
sourceComponent: {
37+
if (modelData.type === "header") {
38+
return headerComponent;
39+
} else if (modelData.type === "day") {
40+
return dayComponent;
41+
} else { // for "empty" type
42+
return emptyComponent;
43+
}
44+
}
45+
46+
// After the correct component is loaded, set its specific properties.
47+
onLoaded: {
48+
if (modelData.type === "day") {
49+
item.bsDay = modelData.bsDay;
50+
item.adDay = modelData.adDay;
51+
item.tithi = modelData.tithi;
52+
item.isToday = modelData.isToday;
53+
item.isSaturday = modelData.isSaturday;
54+
item.theme = calendarGrid.theme;
55+
// Connect the signal from the loaded DayCell to the CalendarGrid's signal
56+
item.clicked.connect(function() {
57+
calendarGrid.dayClicked(modelData.panchanga)
58+
});
59+
} else if (modelData.type === "header") {
60+
item.text = modelData.text;
61+
item.theme = calendarGrid.theme;
62+
}
63+
}
64+
}
65+
}
66+
}
67+
68+
// --- Component Definitions for the Loader ---
69+
70+
Component {
71+
id: headerComponent
72+
Rectangle {
73+
// These properties are set by the Loader
74+
property string text
75+
property var theme
76+
77+
radius: 8
78+
color: theme && theme.isDark ? theme.secondaryBg : (theme ? theme.tertiaryBg : "lightgrey")
79+
border.width: 0
80+
81+
Label {
82+
text: parent.text
83+
color: theme && theme.isDark ? theme.secondaryText : (theme ? theme.accentText : "blue")
84+
font.bold: true
85+
font.pixelSize: 12
86+
anchors.fill: parent
87+
horizontalAlignment: Text.AlignHCenter
88+
verticalAlignment: Text.AlignVCenter
89+
}
90+
}
91+
}
92+
93+
Component {
94+
id: dayComponent
95+
// The DayCell itself. Its properties will be set by the Loader's onLoaded handler.
96+
DayCell {}
97+
}
98+
99+
Component {
100+
id: emptyComponent
101+
// An empty item for the blank days at the start of the month.
102+
Item {}
103+
}
104+
}

qml/CalendarLogic.qml

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import QtQuick 2.15
2+
import "qrc:/PanchangaCalculator.js" as Panchanga
3+
4+
// CalendarLogic.qml
5+
QtObject {
6+
id: calendarLogic
7+
8+
// State Properties
9+
property int currentAdYear: new Date().getFullYear()
10+
property int currentAdMonth: new Date().getMonth()
11+
property string prevAdMonthName: ""
12+
property string nextAdMonthName: ""
13+
14+
property int currentBsYear: 2081
15+
property int currentBsMonthIndex: 1
16+
property var calendarModel: []
17+
property string currentBsLabelStr: ""
18+
property string currentAdLabelStr: ""
19+
property string prevMonthName: ""
20+
property string nextMonthName: ""
21+
property bool isCurrentMonthComputed: false
22+
23+
24+
function toDevanagari(num) {
25+
return Panchanga.toDevanagari(String(num))
26+
}
27+
28+
function fromDevanagari(devanagariStr) {
29+
if (typeof devanagariStr !== 'string') return "";
30+
const digitMap = {
31+
'': '0', '': '1', '': '2', '': '3', '': '4',
32+
'': '5', '': '6', '': '7', '': '8', '': '9'
33+
};
34+
return devanagariStr.replace(/[०-९]/g, (match) => digitMap[match]);
35+
}
36+
37+
function initializeApp() {
38+
var today = new Date();
39+
var bsInfo = Panchanga.calculate(today);
40+
renderCalendarByBs(bsInfo.bsYear, bsInfo.bsMonthIndex);
41+
}
42+
43+
function getGregorianRange(bsYear, monthIndex) {
44+
var first = Panchanga.fromBikramSambat(bsYear, monthIndex, 1);
45+
var info = Panchanga.getBikramMonthInfo(bsYear, monthIndex);
46+
var last = Panchanga.fromBikramSambat(bsYear, monthIndex, info.totalDays);
47+
var firstMonth = Panchanga.nepaliGregorianMonths[first.getUTCMonth()];
48+
var lastMonth = Panchanga.nepaliGregorianMonths[last.getUTCMonth()];
49+
var firstYear = first.getUTCFullYear();
50+
var lastYear = last.getUTCFullYear();
51+
52+
if (firstYear !== lastYear) {
53+
return firstMonth + " " + firstYear + "" + lastMonth + " " + lastYear;
54+
} else if (firstMonth !== lastMonth) {
55+
return firstMonth + " - " + lastMonth + " " + firstYear;
56+
} else {
57+
return firstMonth + " " + firstYear;
58+
}
59+
}
60+
61+
function renderCalendarByBs(year, monthIndex, preserveAdState = false) {
62+
calendarModel = [];
63+
currentBsYear = year;
64+
currentBsMonthIndex = monthIndex;
65+
var info = Panchanga.getBikramMonthInfo(year, monthIndex);
66+
if (!info) {
67+
console.error("Failed to get Bikram month info for", year, monthIndex);
68+
return;
69+
}
70+
71+
if (!preserveAdState) {
72+
// LOGIC to determine the representative AD month
73+
var bsMonthStartDate = Panchanga.fromBikramSambat(year, monthIndex, 1);
74+
var startAdYear = bsMonthStartDate.getUTCFullYear();
75+
var startAdMonth = bsMonthStartDate.getUTCMonth();
76+
77+
var nextAdMonth = (startAdMonth + 1) % 12;
78+
var nextAdYear = (startAdMonth === 11) ? startAdYear + 1 : startAdYear;
79+
var firstOfNextAdMonth = new Date(Date.UTC(nextAdYear, nextAdMonth, 1));
80+
81+
var bsMonthEndDate = Panchanga.fromBikramSambat(year, monthIndex, info.totalDays);
82+
83+
if (firstOfNextAdMonth <= bsMonthEndDate) {
84+
// Prefer the later AD month if its first day is in the BS month.
85+
currentAdYear = nextAdYear;
86+
currentAdMonth = nextAdMonth;
87+
} else {
88+
// Otherwise, use the starting AD month.
89+
currentAdYear = startAdYear;
90+
currentAdMonth = startAdMonth;
91+
}
92+
}
93+
94+
var prevAdMonthIndex = (currentAdMonth - 1 + 12) % 12;
95+
var nextAdMonthIndex = (currentAdMonth + 1) % 12;
96+
prevAdMonthName = Panchanga.nepaliGregorianMonths[prevAdMonthIndex];
97+
nextAdMonthName = Panchanga.nepaliGregorianMonths[nextAdMonthIndex];
98+
99+
var daysInMonth = info.totalDays;
100+
var startDay = info.startDayOfWeek;
101+
var model = [];
102+
var weekdaysNe = ["आइतबार", "सोमबार", "मङ्गलबार", "बुधबार", "बिहीबार", "शुक्रबार", "शनिबार"];
103+
for (var i = 0; i < 7; ++i) {
104+
model.push({ type: "header", text: weekdaysNe[i] });
105+
}
106+
for (i = 0; i < startDay; ++i) {
107+
model.push({ type: "empty" });
108+
}
109+
for (var day = 1; day <= daysInMonth; ++day) {
110+
var adDate = Panchanga.fromBikramSambat(year, monthIndex, day);
111+
var result = Panchanga.calculate(adDate);
112+
if (day === 1) {
113+
isCurrentMonthComputed = result.isComputed;
114+
}
115+
var isToday = adDate.toDateString() === new Date().toDateString();
116+
var isSaturday = (startDay + day - 1) % 7 === 6;
117+
result.monthName = info.monthName;
118+
model.push({
119+
type: "day", bsDay: day, adDay: adDate.getDate(),
120+
tithi: result.tithi, isToday: isToday, isSaturday: isSaturday,
121+
gregorianDate: result.gregorianDate, panchanga: result
122+
});
123+
}
124+
calendarModel = model;
125+
currentBsLabelStr = toDevanagari(year) + " " + info.monthName;
126+
currentAdLabelStr = getGregorianRange(year, monthIndex);
127+
var prevMonthIndex = monthIndex - 1;
128+
var prevYear = year;
129+
if (prevMonthIndex < 0) {
130+
prevMonthIndex = 11;
131+
prevYear--;
132+
}
133+
prevMonthName = Panchanga.solarMonths[prevMonthIndex] || "";
134+
var nextMonthIndex = monthIndex + 1;
135+
var nextYear = year;
136+
if (nextMonthIndex > 11) {
137+
nextMonthIndex = 0;
138+
nextYear++;
139+
}
140+
nextMonthName = Panchanga.solarMonths[nextMonthIndex] || "";
141+
}
142+
143+
function renderCalendarByAd(year, monthIndex) {
144+
currentAdYear = year;
145+
currentAdMonth = monthIndex;
146+
147+
var date = new Date(Date.UTC(year, monthIndex, 1));
148+
var bsInfo = Panchanga.calculate(date);
149+
150+
renderCalendarByBs(bsInfo.bsYear, bsInfo.bsMonthIndex, true);
151+
}
152+
153+
function navigateBsMonth(direction) {
154+
var newMonth = currentBsMonthIndex + direction;
155+
var newYear = currentBsYear;
156+
if (newMonth > 11) {
157+
newMonth = 0;
158+
newYear++;
159+
} else if (newMonth < 0) {
160+
newMonth = 11;
161+
newYear--;
162+
}
163+
renderCalendarByBs(newYear, newMonth);
164+
}
165+
166+
function navigateAdMonth(direction) {
167+
var newAdMonth = currentAdMonth + direction;
168+
var newAdYear = currentAdYear;
169+
if (newAdMonth > 11) {
170+
newAdMonth = 0;
171+
newAdYear++;
172+
} else if (newAdMonth < 0) {
173+
newAdMonth = 11;
174+
newAdYear--;
175+
}
176+
renderCalendarByAd(newAdYear, newAdMonth);
177+
}
178+
}

qml/DayCell.qml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import QtQuick 2.15
2+
import QtQuick.Controls 2.15
3+
import "qrc:/PanchangaCalculator.js" as Panchanga
4+
import "qrc:/qml/"
5+
6+
// DayCell.qml
7+
Rectangle {
8+
id: dayCell
9+
radius: 12
10+
scale: cellMouseArea.containsMouse ? 1.2 : 1.0
11+
z: cellMouseArea.containsMouse ? 1 : 0
12+
Behavior on scale {
13+
NumberAnimation { duration: 150; easing.type: Easing.InOutQuad }
14+
}
15+
16+
// --- Added default values to prevent "Unable to assign [undefined]" errors ---
17+
property int bsDay: 0
18+
property int adDay: 0
19+
property string tithi: ""
20+
property bool isToday: false
21+
property bool isSaturday: false
22+
property var theme
23+
24+
signal clicked()
25+
26+
color: {
27+
if (!theme) return "white";
28+
if (cellMouseArea.containsMouse) return theme.tertiaryBg;
29+
if (isSaturday) return theme.saturdayBg;
30+
if (isToday) return theme.todayBg;
31+
return theme.secondaryBg;
32+
}
33+
34+
border.color: {
35+
if (!theme) return "grey";
36+
if (isToday) return theme.todayBorder;
37+
if (isSaturday) return theme.saturdayBorder;
38+
return theme.borderColor;
39+
}
40+
border.width: isToday ? 2 : 1
41+
42+
Label {
43+
text: Panchanga.toDevanagari(bsDay || 0)
44+
font.bold: true
45+
font.pixelSize: cellMouseArea.containsMouse ? 28 : 25
46+
color: {
47+
if (!theme) return "black";
48+
if (isToday) return theme.accentText;
49+
if (isSaturday) return theme.saturdayText;
50+
return theme.primaryText;
51+
}
52+
anchors.centerIn: parent
53+
}
54+
55+
Label {
56+
text: tithi || ""
57+
font.pixelSize: 11
58+
color: theme && (tithi === "पूर्णिमा" || tithi === "अमावस्या") ? theme.purnimaText : (theme ? theme.secondaryText : "grey")
59+
elide: Text.ElideRight
60+
font.bold: tithi === "पूर्णिमा" || tithi === "अमावस्या"
61+
anchors.left: parent.left
62+
anchors.bottom: parent.bottom
63+
anchors.leftMargin: 8
64+
anchors.bottomMargin: 4
65+
}
66+
67+
Rectangle {
68+
width: 12; height: 12; radius: 12; color: theme ? theme.tertiaryBg : "lightgrey"
69+
anchors.right: parent.right; anchors.top: parent.top; anchors.margins: 4
70+
Label { anchors.centerIn: parent; text: adDay || 0; font.pixelSize: 10; color: theme ? theme.adDayText : "blue" }
71+
}
72+
73+
Text {
74+
visible: tithi === "पूर्णिमा" || tithi === "अमावस्या"
75+
text: tithi === "पूर्णिमा" ? "🌕" : "🌑"
76+
font.pixelSize: 14
77+
anchors.right: parent.right
78+
anchors.bottom: parent.bottom
79+
anchors.rightMargin: 4
80+
anchors.bottomMargin: 4
81+
}
82+
83+
MouseArea {
84+
id: cellMouseArea
85+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor; hoverEnabled: true
86+
onClicked: dayCell.clicked()
87+
}
88+
}

0 commit comments

Comments
 (0)