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
11QT_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
75class QQuickMonthGridPrivate : public QQuickControlPrivate
76{
77 Q_DECLARE_PUBLIC(QQuickMonthGrid)
78
79public:
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
104void 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
120QQuickItem *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
130QDate QQuickMonthGridPrivate::dateOf(QQuickItem *cell) const
131{
132 if (contentItem)
133 return model->dateAt(index: contentItem->childItems().indexOf(t: cell));
134 return QDate();
135}
136
137void 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
147void 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
159bool 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
169bool QQuickMonthGridPrivate::handleMove(const QPointF &point, ulong timestamp)
170{
171 QQuickControlPrivate::handleMove(point, timestamp);
172 updatePress(pos: point);
173 return true;
174}
175
176bool QQuickMonthGridPrivate::handleRelease(const QPointF &point, ulong timestamp)
177{
178 QQuickControlPrivate::handleRelease(point, timestamp);
179 clearPress(clicked: true);
180 return true;
181}
182
183void QQuickMonthGridPrivate::handleUngrab()
184{
185 QQuickControlPrivate::handleUngrab();
186 clearPress(clicked: false);
187}
188
189QQuickMonthGrid::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*/
217int QQuickMonthGrid::month() const
218{
219 Q_D(const QQuickMonthGrid);
220 return d->model->month() - 1;
221}
222
223void 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*/
241int QQuickMonthGrid::year() const
242{
243 Q_D(const QQuickMonthGrid);
244 return d->model->year();
245}
246
247void 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*/
264QVariant QQuickMonthGrid::source() const
265{
266 Q_D(const QQuickMonthGrid);
267 return d->source;
268}
269
270void 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*/
288QString 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
296void 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*/
327QQmlComponent *QQuickMonthGrid::delegate() const
328{
329 Q_D(const QQuickMonthGrid);
330 return d->delegate;
331}
332
333void QQuickMonthGrid::setDelegate(QQmlComponent *delegate)
334{
335 Q_D(QQuickMonthGrid);
336 if (d->delegate != delegate) {
337 d->delegate = delegate;
338 emit delegateChanged();
339 }
340}
341
342void QQuickMonthGrid::componentComplete()
343{
344 Q_D(QQuickMonthGrid);
345 QQuickControl::componentComplete();
346 d->resizeItems();
347}
348
349void 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
357void 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
364void 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
372void QQuickMonthGrid::updatePolish()
373{
374 Q_D(QQuickMonthGrid);
375 QQuickControl::updatePolish();
376 d->resizeItems();
377}
378
379void 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
389QT_END_NAMESPACE
390
391#include "moc_qquickmonthgrid_p.cpp"
392

source code of qtdeclarative/src/quicktemplates/qquickmonthgrid.cpp