1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | #include "qquickmonthgrid_p.h" |
4 | #include "qquickmonthmodel_p.h" |
5 | |
6 | #include <QtGui/qstylehints.h> |
7 | #include <QtGui/qguiapplication.h> |
8 | #include <QtQuickTemplates2/private/qquickcontrol_p_p.h> |
9 | #include <QtQml/qqmlinfo.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \qmltype MonthGrid |
15 | \inherits Control |
16 | //! \instantiates QQuickMonthGrid |
17 | \inqmlmodule QtQuick.Controls |
18 | \brief A grid of days for a calendar month. |
19 | |
20 | MonthGrid presents a calendar month in a grid. The contents are |
21 | calculated for a given \l month and \l year, using the specified |
22 | \l {Control::locale}{locale}. |
23 | |
24 | \image qtquickcontrols-monthgrid.png |
25 | \snippet qtquickcontrols-monthgrid.qml 1 |
26 | |
27 | MonthGrid can be used as a standalone control, but it is most often |
28 | used in conjunction with DayOfWeekRow and WeekNumberColumn. Regardless |
29 | of the use case, positioning of the grid is left to the user. |
30 | |
31 | \image qtquickcontrols-monthgrid-layout.png |
32 | \snippet qtquickcontrols-monthgrid-layout.qml 1 |
33 | |
34 | The visual appearance of MonthGrid can be changed by |
35 | implementing a \l {delegate}{custom delegate}. |
36 | |
37 | When viewing any given month, MonthGrid shows days from the previous and |
38 | next month. This means it always shows six rows, even when first or last |
39 | row is entirely within an adjacent month. |
40 | |
41 | \section1 Localizing days |
42 | |
43 | To localize days, use \l {Locale::toString()}{Locale.toString()}. |
44 | For example, to display day numbers in an Arabic locale: |
45 | |
46 | \snippet qtquickcontrols-monthgrid-localization.qml 1 |
47 | |
48 | \sa DayOfWeekRow, WeekNumberColumn, CalendarModel |
49 | */ |
50 | |
51 | /*! |
52 | \qmlsignal QtQuick.Controls::MonthGrid::pressed(date date) |
53 | |
54 | This signal is emitted when \a date is pressed. |
55 | */ |
56 | |
57 | /*! |
58 | \qmlsignal QtQuick.Controls::MonthGrid::released(date date) |
59 | |
60 | This signal is emitted when \a date is released. |
61 | */ |
62 | |
63 | /*! |
64 | \qmlsignal QtQuick.Controls::MonthGrid::clicked(date date) |
65 | |
66 | This signal is emitted when \a date is clicked. |
67 | */ |
68 | |
69 | /*! |
70 | \qmlsignal QtQuick.Controls::MonthGrid::pressAndHold(date date) |
71 | |
72 | This signal is emitted when \a date is pressed and held down. |
73 | */ |
74 | |
75 | class QQuickMonthGridPrivate : public QQuickControlPrivate |
76 | { |
77 | Q_DECLARE_PUBLIC(QQuickMonthGrid) |
78 | |
79 | public: |
80 | QQuickMonthGridPrivate() : pressTimer(0), pressedItem(nullptr), model(nullptr), delegate(nullptr) { } |
81 | |
82 | void resizeItems(); |
83 | |
84 | QQuickItem *cellAt(const QPointF &pos) const; |
85 | QDate dateOf(QQuickItem *cell) const; |
86 | |
87 | void updatePress(const QPointF &pos); |
88 | void clearPress(bool clicked); |
89 | |
90 | bool handlePress(const QPointF &point, ulong timestamp) override; |
91 | bool handleMove(const QPointF &point, ulong timestamp) override; |
92 | bool handleRelease(const QPointF &point, ulong timestamp) override; |
93 | void handleUngrab() override; |
94 | |
95 | QString title; |
96 | QVariant source; |
97 | QDate pressedDate; |
98 | int pressTimer; |
99 | QQuickItem *pressedItem; |
100 | QQuickMonthModel *model; |
101 | QQmlComponent *delegate; |
102 | }; |
103 | |
104 | void QQuickMonthGridPrivate::resizeItems() |
105 | { |
106 | if (!contentItem) |
107 | return; |
108 | |
109 | QSizeF itemSize; |
110 | itemSize.setWidth((contentItem->width() - 6 * spacing) / 7); |
111 | itemSize.setHeight((contentItem->height() - 5 * spacing) / 6); |
112 | |
113 | const auto childItems = contentItem->childItems(); |
114 | for (QQuickItem *item : childItems) { |
115 | if (!QQuickItemPrivate::get(item)->isTransparentForPositioner()) |
116 | item->setSize(itemSize); |
117 | } |
118 | } |
119 | |
120 | QQuickItem *QQuickMonthGridPrivate::cellAt(const QPointF &pos) const |
121 | { |
122 | Q_Q(const QQuickMonthGrid); |
123 | if (contentItem) { |
124 | QPointF mapped = q->mapToItem(item: contentItem, point: pos); |
125 | return contentItem->childAt(x: mapped.x(), y: mapped.y()); |
126 | } |
127 | return nullptr; |
128 | } |
129 | |
130 | QDate QQuickMonthGridPrivate::dateOf(QQuickItem *cell) const |
131 | { |
132 | if (contentItem) |
133 | return model->dateAt(index: contentItem->childItems().indexOf(t: cell)); |
134 | return QDate(); |
135 | } |
136 | |
137 | void QQuickMonthGridPrivate::updatePress(const QPointF &pos) |
138 | { |
139 | Q_Q(QQuickMonthGrid); |
140 | clearPress(clicked: false); |
141 | pressedItem = cellAt(pos); |
142 | pressedDate = dateOf(cell: pressedItem); |
143 | if (pressedDate.isValid()) |
144 | emit q->pressed(date: pressedDate); |
145 | } |
146 | |
147 | void QQuickMonthGridPrivate::clearPress(bool clicked) |
148 | { |
149 | Q_Q(QQuickMonthGrid); |
150 | if (pressedDate.isValid()) { |
151 | emit q->released(date: pressedDate); |
152 | if (clicked) |
153 | emit q->clicked(date: pressedDate); |
154 | } |
155 | pressedDate = QDate(); |
156 | pressedItem = nullptr; |
157 | } |
158 | |
159 | bool QQuickMonthGridPrivate::handlePress(const QPointF &point, ulong timestamp) |
160 | { |
161 | Q_Q(QQuickMonthGrid); |
162 | QQuickControlPrivate::handlePress(point, timestamp); |
163 | updatePress(pos: point); |
164 | if (pressedDate.isValid()) |
165 | pressTimer = q->startTimer(qGuiApp->styleHints()->mousePressAndHoldInterval()); |
166 | return true; |
167 | } |
168 | |
169 | bool QQuickMonthGridPrivate::handleMove(const QPointF &point, ulong timestamp) |
170 | { |
171 | QQuickControlPrivate::handleMove(point, timestamp); |
172 | updatePress(pos: point); |
173 | return true; |
174 | } |
175 | |
176 | bool QQuickMonthGridPrivate::handleRelease(const QPointF &point, ulong timestamp) |
177 | { |
178 | QQuickControlPrivate::handleRelease(point, timestamp); |
179 | clearPress(clicked: true); |
180 | return true; |
181 | } |
182 | |
183 | void QQuickMonthGridPrivate::handleUngrab() |
184 | { |
185 | QQuickControlPrivate::handleUngrab(); |
186 | clearPress(clicked: false); |
187 | } |
188 | |
189 | QQuickMonthGrid::QQuickMonthGrid(QQuickItem *parent) : |
190 | QQuickControl(*(new QQuickMonthGridPrivate), parent) |
191 | { |
192 | Q_D(QQuickMonthGrid); |
193 | setFlag(flag: ItemIsFocusScope); |
194 | setActiveFocusOnTab(true); |
195 | setAcceptedMouseButtons(Qt::LeftButton); |
196 | #if QT_CONFIG(cursor) |
197 | setCursor(Qt::ArrowCursor); |
198 | #endif |
199 | |
200 | d->model = new QQuickMonthModel(this); |
201 | d->source = QVariant::fromValue(value: d->model); |
202 | connect(sender: d->model, signal: &QQuickMonthModel::monthChanged, context: this, slot: &QQuickMonthGrid::monthChanged); |
203 | connect(sender: d->model, signal: &QQuickMonthModel::yearChanged, context: this, slot: &QQuickMonthGrid::yearChanged); |
204 | connect(sender: d->model, signal: &QQuickMonthModel::titleChanged, context: this, slot: &QQuickMonthGrid::titleChanged); |
205 | } |
206 | |
207 | /*! |
208 | \qmlproperty int QtQuick.Controls::MonthGrid::month |
209 | |
210 | This property holds the number of the month. The default value is the |
211 | current month. |
212 | |
213 | \include zero-based-months.qdocinc |
214 | |
215 | \sa Calendar |
216 | */ |
217 | int QQuickMonthGrid::month() const |
218 | { |
219 | Q_D(const QQuickMonthGrid); |
220 | return d->model->month() - 1; |
221 | } |
222 | |
223 | void QQuickMonthGrid::setMonth(int month) |
224 | { |
225 | Q_D(QQuickMonthGrid); |
226 | if (month < 0 || month > 11) { |
227 | qmlWarning(me: this) << "month " << month << " is out of range [0...11]" ; |
228 | return; |
229 | } |
230 | d->model->setMonth(month + 1); |
231 | } |
232 | |
233 | /*! |
234 | \qmlproperty int QtQuick.Controls::MonthGrid::year |
235 | |
236 | This property holds the number of the year. |
237 | |
238 | The value must be in the range from \c -271820 to \c 275759. The default |
239 | value is the current year. |
240 | */ |
241 | int QQuickMonthGrid::year() const |
242 | { |
243 | Q_D(const QQuickMonthGrid); |
244 | return d->model->year(); |
245 | } |
246 | |
247 | void QQuickMonthGrid::setYear(int year) |
248 | { |
249 | Q_D(QQuickMonthGrid); |
250 | if (year < -271820 || year > 275759) { |
251 | qmlWarning(me: this) << "year " << year << " is out of range [-271820...275759]" ; |
252 | return; |
253 | } |
254 | d->model->setYear(year); |
255 | } |
256 | |
257 | /*! |
258 | \internal |
259 | \qmlproperty model QtQuick.Controls::MonthGrid::source |
260 | |
261 | This property holds the source model that is used as a data model |
262 | for the internal content column. |
263 | */ |
264 | QVariant QQuickMonthGrid::source() const |
265 | { |
266 | Q_D(const QQuickMonthGrid); |
267 | return d->source; |
268 | } |
269 | |
270 | void QQuickMonthGrid::setSource(const QVariant &source) |
271 | { |
272 | Q_D(QQuickMonthGrid); |
273 | if (d->source != source) { |
274 | d->source = source; |
275 | emit sourceChanged(); |
276 | } |
277 | } |
278 | |
279 | /*! |
280 | \qmlproperty string QtQuick.Controls::MonthGrid::title |
281 | |
282 | This property holds a title for the calendar. |
283 | |
284 | This property is provided for convenience. MonthGrid itself does |
285 | not visualize the title. The default value consists of the month name, |
286 | formatted using \l {Control::locale}{locale}, and the year number. |
287 | */ |
288 | QString QQuickMonthGrid::title() const |
289 | { |
290 | Q_D(const QQuickMonthGrid); |
291 | if (d->title.isNull()) |
292 | return d->model->title(); |
293 | return d->title; |
294 | } |
295 | |
296 | void QQuickMonthGrid::setTitle(const QString &title) |
297 | { |
298 | Q_D(QQuickMonthGrid); |
299 | if (d->title != title) { |
300 | d->title = title; |
301 | emit titleChanged(); |
302 | } |
303 | } |
304 | |
305 | /*! |
306 | \qmlproperty Component QtQuick.Controls::MonthGrid::delegate |
307 | |
308 | This property holds the item delegate that visualizes each day. |
309 | |
310 | In addition to the \c index property, a list of model data roles |
311 | are available in the context of each delegate: |
312 | \table |
313 | \row \li \b model.date : date \li The date of the cell |
314 | \row \li \b model.day : int \li The number of the day |
315 | \row \li \b model.today : bool \li Whether the delegate represents today |
316 | \row \li \b model.weekNumber : int \li The week number |
317 | \row \li \b model.month : int \li The number of the month |
318 | \row \li \b model.year : int \li The number of the year |
319 | \endtable |
320 | |
321 | The following snippet presents the default implementation of the item |
322 | delegate. It can be used as a starting point for implementing custom |
323 | delegates. |
324 | |
325 | \snippet basic/MonthGrid.qml delegate |
326 | */ |
327 | QQmlComponent *QQuickMonthGrid::delegate() const |
328 | { |
329 | Q_D(const QQuickMonthGrid); |
330 | return d->delegate; |
331 | } |
332 | |
333 | void QQuickMonthGrid::setDelegate(QQmlComponent *delegate) |
334 | { |
335 | Q_D(QQuickMonthGrid); |
336 | if (d->delegate != delegate) { |
337 | d->delegate = delegate; |
338 | emit delegateChanged(); |
339 | } |
340 | } |
341 | |
342 | void QQuickMonthGrid::componentComplete() |
343 | { |
344 | Q_D(QQuickMonthGrid); |
345 | QQuickControl::componentComplete(); |
346 | d->resizeItems(); |
347 | } |
348 | |
349 | void QQuickMonthGrid::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
350 | { |
351 | Q_D(QQuickMonthGrid); |
352 | QQuickControl::geometryChange(newGeometry, oldGeometry); |
353 | if (isComponentComplete()) |
354 | d->resizeItems(); |
355 | } |
356 | |
357 | void QQuickMonthGrid::localeChange(const QLocale &newLocale, const QLocale &oldLocale) |
358 | { |
359 | Q_D(QQuickMonthGrid); |
360 | QQuickControl::localeChange(newLocale, oldLocale); |
361 | d->model->setLocale(newLocale); |
362 | } |
363 | |
364 | void QQuickMonthGrid::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) |
365 | { |
366 | Q_D(QQuickMonthGrid); |
367 | QQuickControl::paddingChange(newPadding, oldPadding); |
368 | if (isComponentComplete()) |
369 | d->resizeItems(); |
370 | } |
371 | |
372 | void QQuickMonthGrid::updatePolish() |
373 | { |
374 | Q_D(QQuickMonthGrid); |
375 | QQuickControl::updatePolish(); |
376 | d->resizeItems(); |
377 | } |
378 | |
379 | void QQuickMonthGrid::timerEvent(QTimerEvent *event) |
380 | { |
381 | Q_D(QQuickMonthGrid); |
382 | if (event->timerId() == d->pressTimer) { |
383 | if (d->pressedDate.isValid()) |
384 | emit pressAndHold(date: d->pressedDate); |
385 | killTimer(id: d->pressTimer); |
386 | } |
387 | } |
388 | |
389 | QT_END_NAMESPACE |
390 | |
391 | #include "moc_qquickmonthgrid_p.cpp" |
392 | |