diff options
| author | Mitch Curtis <mitch.curtis@digia.com> | 2013-07-15 22:57:52 +0200 |
|---|---|---|
| committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-14 13:13:53 +0100 |
| commit | e023dd212c81a2ad4ba4b4db22df9cde65a502e8 (patch) | |
| tree | 2c3521e8a4154d65a55fc3032254b58b181d957e /src/controls | |
| parent | e88bdffe644e53912dfbce95117555cb6a87bfd2 (diff) | |
Add Calendar to Qt Quick Controls.
Task-number: QTBUG-29948
[ChangeLog][QtQuickControls] Calendar was added. Calendar allows
selection of dates from a grid of days, similar to
QCalendarWidget.
Change-Id: I279130e704bc0dfd8dfe114ec9b6b49e111faf96
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
Diffstat (limited to 'src/controls')
| -rw-r--r-- | src/controls/Calendar.qml | 401 | ||||
| -rw-r--r-- | src/controls/Private/CalendarHeaderModel.qml | 84 | ||||
| -rw-r--r-- | src/controls/Private/CalendarUtils.js | 132 | ||||
| -rw-r--r-- | src/controls/Private/private.pri | 6 | ||||
| -rw-r--r-- | src/controls/Private/qmldir | 2 | ||||
| -rw-r--r-- | src/controls/Private/qquickcalendarmodel.cpp | 264 | ||||
| -rw-r--r-- | src/controls/Private/qquickcalendarmodel_p.h | 101 | ||||
| -rw-r--r-- | src/controls/Private/qquickrangeddate.cpp | 115 | ||||
| -rw-r--r-- | src/controls/Private/qquickrangeddate_p.h | 88 | ||||
| -rw-r--r-- | src/controls/Styles/Base/CalendarStyle.qml | 552 | ||||
| -rw-r--r-- | src/controls/Styles/Desktop/CalendarStyle.qml | 43 | ||||
| -rw-r--r-- | src/controls/Styles/qmldir | 1 | ||||
| -rw-r--r-- | src/controls/Styles/styles.pri | 2 | ||||
| -rw-r--r-- | src/controls/controls.pro | 1 | ||||
| -rw-r--r-- | src/controls/doc/images/qtquickcontrols-example-calendar.png | bin | 0 -> 25967 bytes | |||
| -rw-r--r-- | src/controls/doc/src/qtquickcontrols-examples.qdoc | 10 | ||||
| -rw-r--r-- | src/controls/plugin.cpp | 6 |
17 files changed, 1808 insertions, 0 deletions
diff --git a/src/controls/Calendar.qml b/src/controls/Calendar.qml new file mode 100644 index 000000000..07e9b4adf --- /dev/null +++ b/src/controls/Calendar.qml @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype Calendar + \inqmlmodule QtQuick.Controls + \since 5.3 + \ingroup controls + \brief Provides a way to select dates from a calendar + + Calendar allows selection of dates from a grid of days, similar to + QCalendarWidget. + + The dates on the calendar can be selected with the mouse, or navigated + with the keyboard. + + The selected date can be set through \l selectedDate. + A minimum and maximum date can be set through \l minimumDate and + \l maximumDate. The earliest minimum date that can be set is 1 January, 1 + AD. The latest maximum date that can be set is 25 October, 275759 AD. + + The selected date is displayed using the format in the application's + default locale. + + Week numbers can be displayed by setting the weekNumbersVisible property to + \c true. + + You can create a custom appearance for Calendar by assigning a + \l {QtQuick.Controls.Styles::CalendarStyle}{CalendarStyle}. +*/ + +Control { + id: calendar + + implicitWidth: 250 + implicitHeight: 250 + + /*! + \qmlproperty date Calendar::selectedDate + + The date that has been selected by the user. + + This property is subject to the following validation: + + \list + \li If selectedDate is outside the range of \l minimumDate and + \l maximumDate, it will be clamped to be within that range. + + \li selectedDate will not be changed if \c undefined or some other + invalid value is assigned. + + \li If there are hours, minutes, seconds or milliseconds set, they + will be removed. + \endlist + + The default value is the current date, which is equivalent to: + + \code + new Date() + \endcode + */ + property alias selectedDate: rangedDate.date + + /*! + \qmlproperty date Calendar::minimumDate + + The earliest date that this calendar will accept. + + By default, this property is set to the earliest minimum date + (1 January, 1 AD). + */ + property alias minimumDate: rangedDate.minimumDate + + /*! + \qmlproperty date Calendar::maximumDate + + The latest date that this calendar will accept. + + By default, this property is set to the latest maximum date + (25 October, 275759 AD). + */ + property alias maximumDate: rangedDate.maximumDate + + /*! + This property determines which month in visibleYear is shown on the + calendar. + + The month is from \c 0 to \c 11 to be consistent with the JavaScript + Date object. + + \sa visibleYear + */ + property int visibleMonth: selectedDate.getMonth() + + /*! + This property determines which year is shown on the + calendar. + + \sa visibleMonth + */ + property int visibleYear: selectedDate.getFullYear() + + onSelectedDateChanged: { + // When the selected date changes, the view should move back to that date. + visibleMonth = selectedDate.getMonth(); + visibleYear = selectedDate.getFullYear(); + } + + RangedDate { + id: rangedDate + date: new Date() + minimumDate: CalendarUtils.minimumCalendarDate + maximumDate: CalendarUtils.maximumCalendarDate + } + + /*! + This property determines the visibility of the grid. + + The default value is \c true. + */ + property bool gridVisible: true + + /*! + This property determines the visibility of week numbers. + + The default value is \c false. + */ + property bool weekNumbersVisible: false + + /*! + \qmlproperty enum Calendar::dayOfWeekFormat + + The format in which the days of the week (in the header) are displayed. + + \c Locale.ShortFormat is the default and recommended format, as + \c Locale.NarrowFormat may not be fully supported by each locale (see + \l {Locale String Format Types}) and + \c Locale.LongFormat may not fit within the header cells. + */ + property int dayOfWeekFormat: Locale.ShortFormat + + /*! + The locale that this calendar should use to display itself. + + Affects how dates and day names are localized, as well as which + day is considered the first in a week. + + To set an Australian locale, for example: + + \code + locale: Qt.locale("en_AU") + \endcode + + The default locale is \c Qt.locale(). + */ + property var __locale: Qt.locale() + + /*! + \internal + + This property holds the model that will be used by the Calendar to + populate the dates available to the user. + */ + property CalendarModel __model: CalendarModel { + locale: calendar.__locale + visibleDate: new Date(visibleYear, visibleMonth, 1) + } + + style: Qt.createComponent(Settings.style + "/CalendarStyle.qml", calendar) + + /*! + \qmlsignal Calendar::hovered(date date) + + Emitted when the mouse hovers over a valid date in the calendar. + + \a date is the date that was hovered over. + */ + signal hovered(date date) + + /*! + \qmlsignal Calendar::pressed(date date) + + Emitted when the mouse is pressed on a valid date in the calendar. + + This is also emitted when dragging the mouse to another date while it is pressed. + + \a date is the date that the mouse was pressed on. + */ + signal pressed(date date) + + /*! + \qmlsignal Calendar::released(date date) + + Emitted when the mouse is released over a valid date in the calendar. + + \a date is the date that the mouse was released over. + */ + signal released(date date) + + /*! + \qmlsignal Calendar::clicked(date date) + + Emitted when the mouse is clicked on a valid date in the calendar. + + \a date is the date that the mouse was clicked on. + */ + signal clicked(date date) + + /*! + \qmlsignal Calendar::doubleClicked(date date) + + Emitted when the mouse is double-clicked on a valid date in the calendar. + + \a date is the date that the mouse was double-clicked on. + */ + signal doubleClicked(date date) + + /*! + Sets visibleMonth to the previous month. + */ + function showPreviousMonth() { + if (visibleMonth === 0) { + visibleMonth = CalendarUtils.monthsInAYear - 1; + --visibleYear; + } else { + --visibleMonth; + } + } + + /*! + Sets visibleMonth to the next month. + */ + function showNextMonth() { + if (visibleMonth === CalendarUtils.monthsInAYear - 1) { + visibleMonth = 0; + ++visibleYear; + } else { + ++visibleMonth; + } + } + + /*! + Sets visibleYear to the previous year. + */ + function showPreviousYear() { + if (visibleYear - 1 >= minimumDate.getFullYear()) { + --visibleYear; + } + } + + /*! + Sets visibleYear to the next year. + */ + function showNextYear() { + if (visibleYear + 1 <= maximumDate.getFullYear()) { + ++visibleYear; + } + } + + /*! + Selects the month before the current month in \l selectedDate. + */ + function __selectPreviousMonth() { + calendar.selectedDate = CalendarUtils.setMonth(calendar.selectedDate, calendar.selectedDate.getMonth() - 1); + } + + /*! + Selects the month after the current month in \l selectedDate. + */ + function __selectNextMonth() { + calendar.selectedDate = CalendarUtils.setMonth(calendar.selectedDate, calendar.selectedDate.getMonth() + 1); + } + + /*! + Selects the week before the current week in \l selectedDate. + */ + function __selectPreviousWeek() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(newDate.getDate() - CalendarUtils.daysInAWeek); + calendar.selectedDate = newDate; + } + + /*! + Selects the week after the current week in \l selectedDate. + */ + function __selectNextWeek() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(newDate.getDate() + CalendarUtils.daysInAWeek); + calendar.selectedDate = newDate; + } + + /*! + Selects the first day of the current month in \l selectedDate. + */ + function __selectFirstDayOfMonth() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(1); + calendar.selectedDate = newDate; + } + + /*! + Selects the last day of the current month in \l selectedDate. + */ + function __selectLastDayOfMonth() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(CalendarUtils.daysInMonth(newDate)); + calendar.selectedDate = newDate; + } + + /*! + Selects the day before the current day in \l selectedDate. + */ + function __selectPreviousDay() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(newDate.getDate() - 1); + calendar.selectedDate = newDate; + } + + /*! + Selects the day after the current day in \l selectedDate. + */ + function __selectNextDay() { + var newDate = new Date(calendar.selectedDate); + newDate.setDate(newDate.getDate() + 1); + calendar.selectedDate = newDate; + } + + Keys.onLeftPressed: { + calendar.__selectPreviousDay(); + } + + Keys.onUpPressed: { + calendar.__selectPreviousWeek(); + } + + Keys.onDownPressed: { + calendar.__selectNextWeek(); + } + + Keys.onRightPressed: { + calendar.__selectNextDay(); + } + + Keys.onPressed: { + if (event.key === Qt.Key_Home) { + calendar.__selectFirstDayOfMonth(); + event.accepted = true; + } else if (event.key === Qt.Key_End) { + calendar.__selectLastDayOfMonth(); + event.accepted = true; + } else if (event.key === Qt.Key_PageUp) { + calendar.__selectPreviousMonth(); + event.accepted = true; + } else if (event.key === Qt.Key_PageDown) { + calendar.__selectNextMonth(); + event.accepted = true; + } + } +} diff --git a/src/controls/Private/CalendarHeaderModel.qml b/src/controls/Private/CalendarHeaderModel.qml new file mode 100644 index 000000000..ef57e65b5 --- /dev/null +++ b/src/controls/Private/CalendarHeaderModel.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +/* + CalendarHeaderModel contains a list of the days of a week, + according to a \l locale. The \l locale affects which day of the week + is first in the model. + + The only role provided by the model is \c dayOfWeek, which is one of the + following JavaScript values: + + \list + \li \c Locale.Sunday + \li \c Locale.Monday + \li \c Locale.Tuesday + \li \c Locale.Wednesday + \li \c Locale.Thursday + \li \c Locale.Friday + \li \c Locale.Saturday + \endlist + */ + +ListModel { + id: root + + /* + The locale that this model should be based on. + This affects which day of the week is first in the model. + */ + property var locale + + Component.onCompleted: { + var daysOfWeek = [Locale.Sunday, Locale.Monday, Locale.Tuesday, + Locale.Wednesday, Locale.Thursday, Locale.Friday, Locale.Saturday]; + var firstDayOfWeek = root.locale.firstDayOfWeek; + + var shifted = daysOfWeek.splice(firstDayOfWeek, daysOfWeek.length - firstDayOfWeek); + daysOfWeek = shifted.concat(daysOfWeek) + + for (var i = 0; i < daysOfWeek.length; ++i) { + var element = { dayOfWeek: daysOfWeek[i] } + root.append(element); + } + } +} diff --git a/src/controls/Private/CalendarUtils.js b/src/controls/Private/CalendarUtils.js new file mode 100644 index 000000000..c055118e5 --- /dev/null +++ b/src/controls/Private/CalendarUtils.js @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var daysInAWeek = 7; +var monthsInAYear = 12; + +// Not the number of weeks per month, but the number of weeks that are +// shown on a typical calendar. +var weeksOnACalendarMonth = 6; + +// Can't create year 1 directly... +var minimumCalendarDate = new Date(-1, 0, 1); +minimumCalendarDate.setFullYear(minimumCalendarDate.getFullYear() + 2); +var maximumCalendarDate = new Date(275759, 9, 25); + +function daysInMonth(date) { + // Passing 0 as the day will give us the previous month, which will be + // date.getMonth() since we added 1 to it. + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); +} + +/*! + Returns a copy of \a date with its month set to \a month, keeping the same + day if possible. Does not modify \a date. +*/ +function setMonth(date, month) { + var oldDay = date.getDate(); + var newDate = new Date(date); + // Set the day first, because setting the month could cause it to skip ahead + // a month if the day is larger than the latest day in that month. + newDate.setDate(1); + newDate.setMonth(month); + // We'd like to have the previous day still selected when we change + // months, but it might not be possible, so use the smallest of the two. + newDate.setDate(Math.min(oldDay, daysInMonth(newDate))); + return newDate; +} + +function cellRectAt(index, columns, rows, availableWidth, availableHeight) { + var col = Math.floor(index % columns); + var row = Math.floor(index / columns); + + var remainingHorizontalSpace = Math.floor(availableWidth % columns); + var remainingVerticalSpace = Math.floor(availableHeight % rows); + var baseCellWidth = Math.floor(availableWidth / columns); + var baseCellHeight = Math.floor(availableHeight / rows); + + var rect = Qt.rect(0, 0, 0, 0); + + rect.x = baseCellWidth * col; + rect.width = baseCellWidth; + if (remainingHorizontalSpace > 0) { + if (col < remainingHorizontalSpace) { + ++rect.width; + } + + // This cell's x position should be increased by 1 for every column above it. + rect.x += Math.min(remainingHorizontalSpace, col); + } + + rect.y = baseCellHeight * row; + rect.height = baseCellHeight; + if (remainingVerticalSpace > 0) { + if (row < remainingVerticalSpace) { + ++rect.height; + } + + // This cell's y position should be increased by 1 for every row above it. + rect.y += Math.min(remainingVerticalSpace, row); + } + + return rect; +} + +function cellIndexAt(x, y, columns, rows, availableWidth, availableHeight) { + var remainingHorizontalSpace = Math.floor(availableWidth % columns); + var remainingVerticalSpace = Math.floor(availableHeight % rows); + var baseCellWidth = Math.floor(availableWidth / columns); + var baseCellHeight = Math.floor(availableHeight / rows); + + // TODO: improve this. + for (var row = 0; row < rows; ++row) { + for (var col = 0; col < columns; ++col) { + var index = row * columns + col; + var rect = cellRectAt(index, columns, rows, availableWidth, availableHeight); + if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) { + return index; + } + } + } + + return -1; +} diff --git a/src/controls/Private/private.pri b/src/controls/Private/private.pri index b12cabd77..49227c9d3 100644 --- a/src/controls/Private/private.pri +++ b/src/controls/Private/private.pri @@ -1,8 +1,10 @@ HEADERS += \ + $$PWD/qquickcalendarmodel_p.h \ $$PWD/qquicktooltip_p.h \ $$PWD/qquickspinboxvalidator_p.h \ $$PWD/qquickrangemodel_p.h \ $$PWD/qquickrangemodel_p_p.h \ + $$PWD/qquickrangeddate_p.h \ $$PWD/qquickcontrolsettings_p.h \ $$PWD/qquickwheelarea_p.h \ $$PWD/qquickabstractstyle_p.h \ @@ -10,9 +12,11 @@ HEADERS += \ $$PWD/qquickcontrolsprivate_p.h SOURCES += \ + $$PWD/qquickcalendarmodel.cpp \ $$PWD/qquicktooltip.cpp \ $$PWD/qquickspinboxvalidator.cpp \ $$PWD/qquickrangemodel.cpp \ + $$PWD/qquickrangeddate.cpp \ $$PWD/qquickcontrolsettings.cpp \ $$PWD/qquickwheelarea.cpp \ $$PWD/qquickabstractstyle.cpp @@ -30,6 +34,8 @@ PRIVATE_QML_FILES += \ $$PWD/TabBar.qml \ $$PWD/BasicButton.qml \ $$PWD/Control.qml \ + $$PWD/CalendarHeaderModel.qml \ + $$PWD/CalendarUtils.js \ $$PWD/FastGlow.qml \ $$PWD/SourceProxy.qml\ $$PWD/Style.qml \ diff --git a/src/controls/Private/qmldir b/src/controls/Private/qmldir index 7536b7e63..997401900 100644 --- a/src/controls/Private/qmldir +++ b/src/controls/Private/qmldir @@ -1,6 +1,8 @@ module QtQuick.Controls.Private AbstractCheckable 1.0 AbstractCheckable.qml +CalendarHeaderModel 1.0 CalendarHeaderModel.qml Control 1.0 Control.qml +CalendarUtils 1.0 CalendarUtils.js FocusFrame 1.0 FocusFrame.qml Margins 1.0 Margins.qml BasicButton 1.0 BasicButton.qml diff --git a/src/controls/Private/qquickcalendarmodel.cpp b/src/controls/Private/qquickcalendarmodel.cpp new file mode 100644 index 000000000..83c2885ca --- /dev/null +++ b/src/controls/Private/qquickcalendarmodel.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcalendarmodel_p.h" + +namespace { + static const int daysInAWeek = 7; + + /* + Not the number of weeks per month, but the number of weeks that are + shown on a typical calendar. + */ + static const int weeksOnACalendarMonth = 6; + + /* + The amount of days to populate the calendar with. + */ + static const int daysOnACalendarMonth = daysInAWeek * weeksOnACalendarMonth; +} + +QT_BEGIN_NAMESPACE + +/*! + QQuickCalendarModel provides a model for the Calendar control. + It is responsible for populating itself with dates based on a given month + and year. + + The model stores a list of dates whose indices map directly to the Calendar. + For example, the model would store the following dates when any day in + January 2015 is selected on the Calendar: + + [30/12/2014][31/12/2014][01/01/2015]...[31/01/2015][01/02/2015]...[09/02/2015] + + The Calendar would then display the dates in the same order within a grid: + + January 2015 + + [30][31][01][02][03][04][05] + [06][07][08][09][10][11][12] + [13][14][15][16][17][18][19] + [20][21][22][23][24][25][26] + [27][28][29][30][31][01][02] + [03][04][05][06][07][08][09] +*/ + +QQuickCalendarModel::QQuickCalendarModel(QObject *parent) : + QAbstractListModel(parent) +{ +} + +/*! + The date that determines which dates are stored. + + We store all of the days in the month of visibleDate, as well as any days + in the previous or following month if there is enough space. +*/ +QDate QQuickCalendarModel::visibleDate() const +{ + return mVisibleDate; +} + +/*! + Sets the visible date to \a visibleDate. + + If \a visibleDate is a valid date and is different than the previously + visible date, the visible date is changed and + populateFromVisibleDate() called. +*/ +void QQuickCalendarModel::setVisibleDate(const QDate &date) +{ + if (date != mVisibleDate && date.isValid()) { + const QDate previousDate = mVisibleDate; + mVisibleDate = date; + populateFromVisibleDate(previousDate); + emit visibleDateChanged(date); + } +} + +/*! + The locale set on the Calendar. + + This affects which dates are stored for visibleDate(). For example, if + the locale is en_US, the first day of the week is Sunday. Therefore, if + visibleDate() is some day in January 2014, there will be three days + displayed before the 1st of January: + + + January 2014 + + [SO][MO][TU][WE][TH][FR][SA] + [29][30][31][01][02][03][04] + ... + + If the locale is then changed to en_GB (with the same visibleDate()), + there will be 2 days before the 1st of January, because Monday is the + first day of the week for that locale: + + January 2014 + + [MO][TU][WE][TH][FR][SA][SO] + [30][31][01][02][03][04][05] + ... +*/ +QLocale QQuickCalendarModel::locale() const +{ + return mLocale; +} + +/*! + Sets the locale to \a locale. +*/ +void QQuickCalendarModel::setLocale(const QLocale &locale) +{ + if (locale != mLocale) { + Qt::DayOfWeek previousFirstDayOfWeek = mLocale.firstDayOfWeek(); + mLocale = locale; + emit localeChanged(mLocale); + if (mLocale.firstDayOfWeek() != previousFirstDayOfWeek) { + // We don't have a previousDate, so just use our current one... + // it's ignored anyway, since we're forcing the repopulation. + populateFromVisibleDate(mVisibleDate, true); + } + } +} + +QVariant QQuickCalendarModel::data(const QModelIndex &index, int role) const +{ + if (role == DateRole) + return mVisibleDates.at(index.row()); + return QVariant(); +} + +int QQuickCalendarModel::rowCount(const QModelIndex &) const +{ + return mVisibleDates.isEmpty() ? 0 : weeksOnACalendarMonth * daysInAWeek; +} + +QHash<int, QByteArray> QQuickCalendarModel::roleNames() const +{ + QHash<int, QByteArray> names; + names[DateRole] = QByteArrayLiteral("date"); + return names; +} + +/*! + Returns the date at \a index, or an invalid date if \a index is invalid. +*/ +QDate QQuickCalendarModel::dateAt(int index) const +{ + return index >= 0 && index < mVisibleDates.size() ? mVisibleDates.at(index) : QDate(); +} + +/*! + Returns the index for \a date, or -1 if \a date is outside of our range. +*/ +int QQuickCalendarModel::indexAt(const QDate &date) +{ + if (mVisibleDates.size() == 0 || date < mFirstVisibleDate || date > mLastVisibleDate) + return -1; + + // The index of the visible date will be the days from the + // previous month that we had to display before it, plus the + // day of the visible date itself. + return qMax(qint64(0), mFirstVisibleDate.daysTo(date)); +} + +/*! + Returns the week number for the first day of the week corresponding to \a row, + or -1 if \a row is outside of our range. +*/ +int QQuickCalendarModel::weekNumberAt(int row) const +{ + const int index = row * daysInAWeek; + const QDate date = dateAt(index); + if (date.isValid()) + return date.weekNumber(); + return -1; +} + +/*! + Called before visibleDateChanged() is emitted. + + This function is called even when just the day has changed, in which case + it does nothing. + + The \a force parameter is used when the locale has changed; the month + shown doesn't change, but the days displayed do. + The \a previousDate parameter is ignored when \a force is true. +*/ +void QQuickCalendarModel::populateFromVisibleDate(const QDate &previousDate, bool force) +{ + // We don't need to populate if the year and month haven't changed. + if (!force && mVisibleDate.year() == previousDate.year() && mVisibleDate.month() == previousDate.month()) + return; + + // Since our model is of a fixed size, we fill it once and assign values each time + // the month changes, instead of clearing and appending each time. + bool isEmpty = mVisibleDates.isEmpty(); + if (isEmpty) { + beginResetModel(); + mVisibleDates.fill(QDate(), daysOnACalendarMonth); + } + + // The actual first (1st) day of the month. + QDate firstDayOfMonthDate(mVisibleDate.year(), mVisibleDate.month(), 1); + int difference = ((firstDayOfMonthDate.dayOfWeek() - mLocale.firstDayOfWeek()) + 7) % 7; + // The first day to display should never be the 1st of the month, as we want some days from + // the previous month to be visible. + if (difference == 0) + difference += daysInAWeek; + QDate firstDateToDisplay = firstDayOfMonthDate.addDays(-difference); + for (int i = 0; i < daysOnACalendarMonth; ++i) + mVisibleDates[i] = firstDateToDisplay.addDays(i); + + mFirstVisibleDate = mVisibleDates.at(0); + mLastVisibleDate = mVisibleDates.at(mVisibleDates.size() - 1); + + if (!isEmpty) { + emit dataChanged(index(0, 0), index(rowCount() - 1, 0)); + } else { + endResetModel(); + emit countChanged(rowCount()); + } +} + +QT_END_NAMESPACE diff --git a/src/controls/Private/qquickcalendarmodel_p.h b/src/controls/Private/qquickcalendarmodel_p.h new file mode 100644 index 000000000..3505f3a45 --- /dev/null +++ b/src/controls/Private/qquickcalendarmodel_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCALENDARMODEL_H +#define QQUICKCALENDARMODEL_H + +#include <QObject> +#include <QAbstractListModel> +#include <QLocale> +#include <QVariant> +#include <QDate> + +QT_BEGIN_NAMESPACE + +class QQuickCalendarModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QDate visibleDate READ visibleDate WRITE setVisibleDate NOTIFY visibleDateChanged) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + +public: + explicit QQuickCalendarModel(QObject *parent = 0); + + enum { + // If this class is made public, this will have to be changed. + DateRole = Qt::UserRole + 1 + }; + + QDate visibleDate() const; + void setVisibleDate(const QDate &visibleDate); + + QLocale locale() const; + void setLocale(const QLocale &locale); + + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + + QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; + + Q_INVOKABLE QDate dateAt(int index) const; + Q_INVOKABLE int indexAt(const QDate &visibleDate); + Q_INVOKABLE int weekNumberAt(int row) const; + +Q_SIGNALS: + void visibleDateChanged(const QDate &visibleDate); + void localeChanged(const QLocale &locale); + void countChanged(int count); + +protected: + void populateFromVisibleDate(const QDate &previousDate, bool force = false); + + QDate mVisibleDate; + QDate mFirstVisibleDate; + QDate mLastVisibleDate; + QVector<QDate> mVisibleDates; + QLocale mLocale; +}; + +QT_END_NAMESPACE + +#endif // QQUICKCALENDARMODEL_H diff --git a/src/controls/Private/qquickrangeddate.cpp b/src/controls/Private/qquickrangeddate.cpp new file mode 100644 index 000000000..e76e3cfbc --- /dev/null +++ b/src/controls/Private/qquickrangeddate.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickrangeddate_p.h" + +QT_BEGIN_NAMESPACE + +// JavaScript Date > QDate conversion is not correct for large negative dates. +Q_GLOBAL_STATIC_WITH_ARGS(const QDate, jsMinimumDate, (QDate(1, 1, 1))) +Q_GLOBAL_STATIC_WITH_ARGS(const QDate, jsMaximumDate, (QDate(275759, 10, 25))) + +QQuickRangedDate::QQuickRangedDate() : + QObject(0), + mDate(QDate::currentDate()), + mMinimumDate(*jsMinimumDate), + mMaximumDate(*jsMaximumDate) +{ +} + +/*! \internal + \qmlproperty date QQuickRangedDate::date +*/ +void QQuickRangedDate::setDate(const QDate &date) +{ + if (date == mDate) + return; + + if (date < mMinimumDate) { + mDate = mMinimumDate; + } else if (date > mMaximumDate) { + mDate = mMaximumDate; + } else { + mDate = date; + } + + emit dateChanged(); +} + +/*! \internal + \qmlproperty date QQuickRangedDate::minimumDate +*/ +void QQuickRangedDate::setMinimumDate(const QDate &minimumDate) +{ + if (minimumDate == mMinimumDate) + return; + + mMinimumDate = qMax(minimumDate, *jsMinimumDate); + emit minimumDateChanged(); + + // If the new minimumDate makes date invalid, clamp date to it. + if (mDate < mMinimumDate) { + mDate = mMinimumDate; + emit dateChanged(); + } +} + +/*! \internal + \qmlproperty date QQuickRangedDate::maximumDate +*/ +void QQuickRangedDate::setMaximumDate(const QDate &maximumDate) +{ + if (maximumDate == mMaximumDate) + return; + + // If the new maximumDate is smaller than minimumDate, clamp maximumDate to it. + // If the new maximumDate is larger than jsMaximumDate, also clamp it. + mMaximumDate = maximumDate < mMinimumDate ? mMinimumDate : qMin(maximumDate, *jsMaximumDate); + emit maximumDateChanged(); + + // If the new maximumDate makes the date invalid, clamp it. + if (mDate > mMaximumDate) { + mDate = mMaximumDate; + emit dateChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/controls/Private/qquickrangeddate_p.h b/src/controls/Private/qquickrangeddate_p.h new file mode 100644 index 000000000..975bd27bf --- /dev/null +++ b/src/controls/Private/qquickrangeddate_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRANGEDDATE_H +#define QQUICKRANGEDDATE_H + +#include <QDate> + +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class QQuickRangedDate : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged RESET resetDate) + Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate NOTIFY minimumDateChanged RESET resetMinimumDate) + Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate NOTIFY maximumDateChanged RESET resetMaximumDate) +public: + QQuickRangedDate(); + ~QQuickRangedDate() {} + + QDate date() const { return mDate; } + void setDate(const QDate &date); + void resetDate() {} + + QDate minimumDate() const { return mMinimumDate; } + void setMinimumDate(const QDate &minimumDate); + void resetMinimumDate() {} + + QDate maximumDate() const { return mMaximumDate; } + void setMaximumDate(const QDate &maximumDate); + void resetMaximumDate() {} + +Q_SIGNALS: + void dateChanged(); + void minimumDateChanged(); + void maximumDateChanged(); + +private: + QDate mDate; + QDate mMinimumDate; + QDate mMaximumDate; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRangedDate) + +#endif // QQUICKRANGEDDATE_H diff --git a/src/controls/Styles/Base/CalendarStyle.qml b/src/controls/Styles/Base/CalendarStyle.qml new file mode 100644 index 000000000..99330ffe8 --- /dev/null +++ b/src/controls/Styles/Base/CalendarStyle.qml @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype CalendarStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.3 + \ingroup controlsstyling + \brief Provides custom styling for \l Calendar + + Example: + \qml + Calendar { + anchors.centerIn: parent + gridVisible: false + + style: CalendarStyle { + dayDelegate: Rectangle { + gradient: Gradient { + GradientStop { + position: 0.00 + color: styleData.selected ? "#111" : (styleData.visibleMonth && styleData.valid ? "#444" : "#666"); + } + GradientStop { + position: 1.00 + color: styleData.selected ? "#444" : (styleData.visibleMonth && styleData.valid ? "#111" : "#666"); + } + GradientStop { + position: 1.00 + color: styleData.selected ? "#777" : (styleData.visibleMonth && styleData.valid ? "#111" : "#666"); + } + } + + Label { + text: styleData.date.getDate() + anchors.centerIn: parent + color: styleData.valid ? "white" : "grey" + } + + Rectangle { + width: parent.width + height: 1 + color: "#555" + anchors.bottom: parent.bottom + } + + Rectangle { + width: 1 + height: parent.height + color: "#555" + anchors.right: parent.right + } + } + } + } + \endqml +*/ + +Style { + id: calendarStyle + + /*! + The Calendar attached to this style. + */ + property Calendar control: __control + + /*! + The color of the grid lines. + */ + property color gridColor: "#f0f0f0" + + /*! + \internal + + The width of each grid line. + */ + property real __gridLineWidth: 1 + + function __cellRectAt(index) { + return CalendarUtils.cellRectAt(index, control.__panel.columns, control.__panel.rows, + control.__panel.availableWidth, control.__panel.availableHeight); + } + + function __cellIndexAt(mouseX, mouseY) { + return CalendarUtils.cellIndexAt(mouseX, mouseY, control.__panel.columns, control.__panel.rows, + control.__panel.availableWidth, control.__panel.availableHeight); + } + + function __isValidDate(date) { + return date !== undefined + && date.getTime() >= control.minimumDate.getTime() + && date.getTime() <= control.maximumDate.getTime(); + } + + /*! + The background of the calendar. + */ + property Component background: Rectangle { + color: "#fff" + } + + /*! + The navigation bar of the calendar. + + Styles the bar at the top of the calendar that contains the + next month/previous month buttons and the selected date label. + */ + property Component navigationBar: Item { + height: 50 + + Button { + id: previousMonth + width: parent.height * 0.6 + height: width + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: (parent.height - height) / 2 + iconSource: "images/arrow-left.png" + + onClicked: control.showPreviousMonth() + } + Label { + id: dateText + text: styleData.title + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pointSize: 14 + anchors.verticalCenter: parent.verticalCenter + anchors.left: previousMonth.right + anchors.leftMargin: 2 + anchors.right: nextMonth.left + anchors.rightMargin: 2 + } + Button { + id: nextMonth + width: parent.height * 0.6 + height: width + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: (parent.height - height) / 2 + iconSource: "images/arrow-right.png" + + onClicked: control.showNextMonth() + } + } + + /*! + The delegate that styles each date in the calendar. + + The properties provided to each delegate are: + \table + \row \li readonly property date \b styleData.date + \li The date this delegate represents. + \row \li readonly property bool \b styleData.selected + \li \c true if this is the selected date. + \row \li readonly property int \b styleData.index + \li The index of this delegate. + \row \li readonly property bool \b styleData.valid + \li \c true if this date is greater than or equal to than \l {Calendar::minimumDate}{minimumDate} and + less than or equal to \l {Calendar::maximumDate}{maximumDate}. + \row \li readonly property bool \b styleData.today + \li \c true if this date is equal to today's date. + \row \li readonly property bool \b styleData.visibleMonth + \li \c true if the month in this date is the visible month. + \row \li readonly property bool \b styleData.hovered + \li \c true if the mouse is over this cell. + \note This property is \c true even when the mouse is hovered over an invalid date. + \row \li readonly property bool \b styleData.pressed + \li \c true if the mouse is pressed on this cell. + \note This property is \c true even when the mouse is pressed on an invalid date. + \endtable + */ + property Component dayDelegate: Rectangle { + color: styleData.date !== undefined && styleData.selected ? selectedDateColor : "white"/*"transparent"*/ + readonly property color sameMonthDateTextColor: "black" + readonly property color selectedDateColor: __syspal.highlight + readonly property color selectedDateTextColor: "white" + readonly property color differentMonthDateTextColor: Qt.darker("darkgrey", 1.4); + readonly property color invalidDateColor: "#dddddd" + + Label { + id: dayDelegateText + text: styleData.date.getDate() + anchors.centerIn: parent + horizontalAlignment: Text.AlignRight + color: { + var theColor = invalidDateColor; + if (styleData.valid) { + // Date is within the valid range. + theColor = styleData.visibleMonth ? sameMonthDateTextColor : differentMonthDateTextColor; + if (styleData.selected) + theColor = selectedDateTextColor; + } + theColor; + } + } + } + + /*! + The delegate that styles each weekday. + */ + property Component dayOfWeekDelegate: Rectangle { + color: "white" + Label { + text: control.__locale.dayName(styleData.dayOfWeek, control.dayOfWeekFormat) + anchors.centerIn: parent + } + } + + /*! + The delegate that styles each week number. + */ + property Component weekNumberDelegate: Rectangle { + color: "white" + Label { + text: styleData.weekNumber + anchors.centerIn: parent + } + } + + /*! \internal */ + property Component panel: Item { + id: panelItem + + implicitWidth: 200 + implicitHeight: 200 + + property alias navigationBarItem: navigationBarLoader.item + + readonly property real dayOfWeekHeaderRowHeight: 40 + + readonly property int weeksToShow: 6 + readonly property int rows: weeksToShow + readonly property int columns: CalendarUtils.daysInAWeek + + // The combined available width and height to be shared amongst each cell. + readonly property real availableWidth: (viewContainer.width - (control.gridVisible ? __gridLineWidth : 0)) + readonly property real availableHeight: (viewContainer.height - (control.gridVisible ? __gridLineWidth : 0)) + + property int hoveredCellIndex: -1 + property int pressedCellIndex: -1 + + Loader { + id: backgroundLoader + anchors.fill: parent + sourceComponent: background + } + + Loader { + id: navigationBarLoader + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + sourceComponent: navigationBar + + property QtObject styleData: QtObject { + readonly property string title: control.__locale.standaloneMonthName(control.visibleMonth) + + new Date(control.visibleYear, control.visibleMonth, 1).toLocaleDateString(control.__locale, " yyyy") + } + } + + Row { + id: dayOfWeekHeaderRow + spacing: (control.gridVisible ? __gridLineWidth : 0) + anchors.top: navigationBarLoader.bottom + anchors.left: parent.left + anchors.leftMargin: (control.weekNumbersVisible ? weekNumbersItem.width : 0) + (control.gridVisible ? __gridLineWidth : 0) + anchors.right: parent.right + height: dayOfWeekHeaderRowHeight + + Repeater { + id: repeater + model: CalendarHeaderModel { + locale: control.__locale + } + Loader { + id: dayOfWeekDelegateLoader + sourceComponent: dayOfWeekDelegate + width: __cellRectAt(index).width - (control.gridVisible ? __gridLineWidth : 0) + height: dayOfWeekHeaderRow.height + + readonly property var __dayOfWeek: dayOfWeek + + property QtObject styleData: QtObject { + readonly property alias dayOfWeek: dayOfWeekDelegateLoader.__dayOfWeek + } + } + } + } + + Row { + id: gridRow + width: weekNumbersItem.width + viewContainer.width + height: viewContainer.height + anchors.top: dayOfWeekHeaderRow.bottom + + Item { + id: weekNumbersItem + visible: control.weekNumbersVisible + width: 30 + height: viewContainer.height + + Repeater { + id: weekNumberRepeater + model: panelItem.weeksToShow + + Loader { + id: weekNumberDelegateLoader + y: __cellRectAt(index * panelItem.columns).y + (control.gridVisible ? __gridLineWidth : 0) + width: weekNumbersItem.width + height: __cellRectAt(index * panelItem.columns).height - (control.gridVisible ? __gridLineWidth : 0) + sourceComponent: weekNumberDelegate + + readonly property int __index: index + property int __weekNumber: control.__model.weekNumberAt(index) + + Connections { + target: control + onVisibleMonthChanged: __weekNumber = control.__model.weekNumberAt(index) + onVisibleYearChanged: __weekNumber = control.__model.weekNumberAt(index) + } + + Connections { + target: control.__model + onCountChanged: __weekNumber = control.__model.weekNumberAt(index) + } + + property QtObject styleData: QtObject { + readonly property alias index: weekNumberDelegateLoader.__index + readonly property int weekNumber: weekNumberDelegateLoader.__weekNumber + } + } + } + } + + // Contains the grid lines and the grid itself. + Item { + id: viewContainer + width: panelItem.width - (control.weekNumbersVisible ? weekNumbersItem.width : 0) + height: panelItem.height - navigationBarLoader.height - dayOfWeekHeaderRow.height + + Repeater { + id: verticalGridLineRepeater + model: panelItem.columns + 1 + delegate: Rectangle { + // The last line will be an invalid index, so we must handle it + x: index < panelItem.columns + ? __cellRectAt(index).x + : __cellRectAt(panelItem.columns - 1).x + __cellRectAt(panelItem.columns - 1).width + y: 0 + width: __gridLineWidth + height: viewContainer.height + color: gridColor + visible: control.gridVisible + } + } + + Repeater { + id: horizontalGridLineRepeater + model: panelItem.rows + 1 + delegate: Rectangle { + x: 0 + // The last line will be an invalid index, so we must handle it + y: index < panelItem.columns - 1 + ? __cellRectAt(index * panelItem.columns).y + : __cellRectAt((panelItem.rows - 1) * panelItem.columns).y + __cellRectAt((panelItem.rows - 1) * panelItem.columns).height + width: viewContainer.width + height: __gridLineWidth + color: gridColor + visible: control.gridVisible + } + } + + Connections { + target: control + onSelectedDateChanged: view.selectedDateChanged() + } + + Repeater { + id: view + + property int currentIndex: -1 + + model: control.__model + + Component.onCompleted: selectedDateChanged() + + function selectedDateChanged() { + if (model !== undefined && model.locale !== undefined) { + currentIndex = model.indexAt(control.selectedDate); + } + } + + delegate: Loader { + id: delegateLoader + + x: __cellRectAt(index).x + (control.gridVisible ? __gridLineWidth : 0) + y: __cellRectAt(index).y + (control.gridVisible ? __gridLineWidth : 0) + width: __cellRectAt(index).width - (control.gridVisible ? __gridLineWidth : 0) + height: __cellRectAt(index).height - (control.gridVisible ? __gridLineWidth : 0) + + sourceComponent: dayDelegate + + readonly property int __index: index + readonly property date __date: date + // We rely on the fact that an invalid QDate will be converted to a Date + // whose year is -4713, which is always an invalid date since our + // earliest minimum date is the year 1. + readonly property bool valid: __isValidDate(date) + + property QtObject styleData: QtObject { + readonly property alias index: delegateLoader.__index + readonly property bool selected: control.selectedDate.getTime() === date.getTime() + readonly property alias date: delegateLoader.__date + readonly property bool valid: delegateLoader.valid + // TODO: this will not be correct if the app is running when a new day begins. + readonly property bool today: date.getTime() === new Date().setHours(0, 0, 0, 0) + readonly property bool visibleMonth: date.getMonth() === control.visibleMonth + readonly property bool hovered: panelItem.hoveredCellIndex == index + readonly property bool pressed: panelItem.pressedCellIndex == index + // todo: pressed property here, clicked and doubleClicked in the control itself + } + } + } + + MouseArea { + anchors.fill: parent + + hoverEnabled: true + + onEntered: { + var indexOfCell = __cellIndexAt(mouseX, mouseY); + hoveredCellIndex = indexOfCell; + var date = view.model.dateAt(indexOfCell); + if (__isValidDate(date)) { + control.hovered(date); + } + } + + onExited: { + hoveredCellIndex = -1; + } + + onPositionChanged: { + var indexOfCell = __cellIndexAt(mouse.x, mouse.y); + var previousHoveredCellIndex = hoveredCellIndex; + hoveredCellIndex = indexOfCell; + if (indexOfCell !== -1) { + var date = view.model.dateAt(indexOfCell); + if (__isValidDate(date)) { + if (hoveredCellIndex !== previousHoveredCellIndex) + control.hovered(date); + + if (pressed && date.getTime() !== control.selectedDate.getTime()) { + control.selectedDate = date; + pressedCellIndex = indexOfCell; + control.pressed(date); + } + } + } + } + + onPressed: { + var indexOfCell = __cellIndexAt(mouse.x, mouse.y); + if (indexOfCell !== -1) { + var date = view.model.dateAt(indexOfCell); + pressedCellIndex = indexOfCell; + if (__isValidDate(date)) { + control.selectedDate = date; + control.pressed(date); + } + } + } + + onReleased: { + var indexOfCell = __cellIndexAt(mouse.x, mouse.y); + if (indexOfCell !== -1) { + // The cell index might be valid, but the date has to be too. We could let the + // selected date validation take care of this, but then the selected date would + // change to the earliest day if a day before the minimum date is clicked, for example. + var date = view.model.dateAt(indexOfCell); + if (__isValidDate(date)) { + control.released(date); + } + } + pressedCellIndex = -1; + } + + onClicked: { + var indexOfCell = __cellIndexAt(mouse.x, mouse.y); + if (indexOfCell !== -1) { + var date = view.model.dateAt(indexOfCell); + if (__isValidDate(date)) + control.clicked(date); + } + } + + onDoubleClicked: { + var indexOfCell = __cellIndexAt(mouse.x, mouse.y); + if (indexOfCell !== -1) { + var date = view.model.dateAt(indexOfCell); + if (__isValidDate(date)) + control.doubleClicked(date); + } + } + } + } + } + } +} diff --git a/src/controls/Styles/Desktop/CalendarStyle.qml b/src/controls/Styles/Desktop/CalendarStyle.qml new file mode 100644 index 000000000..657947c84 --- /dev/null +++ b/src/controls/Styles/Desktop/CalendarStyle.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick.Controls.Styles 1.1 + +CalendarStyle {} diff --git a/src/controls/Styles/qmldir b/src/controls/Styles/qmldir index 4ffb08ee0..f0e6fcc10 100644 --- a/src/controls/Styles/qmldir +++ b/src/controls/Styles/qmldir @@ -1,6 +1,7 @@ module QtQuick.Controls.Styles ButtonStyle 1.0 Base/ButtonStyle.qml BusyIndicatorStyle 1.1 Base/BusyIndicatorStyle.qml +CalendarStyle 1.1 Base/CalendarStyle.qml CheckBoxStyle 1.0 Base/CheckBoxStyle.qml ComboBoxStyle 1.0 Base/ComboBoxStyle.qml MenuStyle 1.2 Base/MenuStyle.qml diff --git a/src/controls/Styles/styles.pri b/src/controls/Styles/styles.pri index d695893a0..9199dfc58 100644 --- a/src/controls/Styles/styles.pri +++ b/src/controls/Styles/styles.pri @@ -3,6 +3,7 @@ STYLES_QML_FILES = \ $$PWD/Base/ButtonStyle.qml \ $$PWD/Base/BusyIndicatorStyle.qml \ + $$PWD/Base/CalendarStyle.qml \ $$PWD/Base/CheckBoxStyle.qml \ $$PWD/Base/ComboBoxStyle.qml \ $$PWD/Base/FocusFrameStyle.qml \ @@ -28,6 +29,7 @@ STYLES_QML_FILES += \ $$PWD/Desktop/qmldir \ $$PWD/Desktop/RowItemSingleton.qml \ $$PWD/Desktop/ButtonStyle.qml \ + $$PWD/Desktop/CalendarStyle.qml \ $$PWD/Desktop/BusyIndicatorStyle.qml \ $$PWD/Desktop/CheckBoxStyle.qml \ $$PWD/Desktop/ComboBoxStyle.qml \ diff --git a/src/controls/controls.pro b/src/controls/controls.pro index e31f86838..e19d5d18d 100644 --- a/src/controls/controls.pro +++ b/src/controls/controls.pro @@ -10,6 +10,7 @@ CONTROLS_QML_FILES = \ ApplicationWindow.qml \ Button.qml \ BusyIndicator.qml \ + Calendar.qml \ CheckBox.qml \ ComboBox.qml \ GroupBox.qml \ diff --git a/src/controls/doc/images/qtquickcontrols-example-calendar.png b/src/controls/doc/images/qtquickcontrols-example-calendar.png Binary files differnew file mode 100644 index 000000000..57db0a122 --- /dev/null +++ b/src/controls/doc/images/qtquickcontrols-example-calendar.png diff --git a/src/controls/doc/src/qtquickcontrols-examples.qdoc b/src/controls/doc/src/qtquickcontrols-examples.qdoc index 4bb99d8ed..1531e3ff5 100644 --- a/src/controls/doc/src/qtquickcontrols-examples.qdoc +++ b/src/controls/doc/src/qtquickcontrols-examples.qdoc @@ -105,3 +105,13 @@ for touch input using \l{Qt Quick Controls}. */ +/*! + \example controls/calendar + \title Qt Quick Controls - Calendar Example + \ingroup qtquickcontrols_examples + \brief Demonstrates the use of Calendar to display events + \image qtquickcontrols-example-calendar.png + + This example shows how Calendar can be used to view events retrieved from + an SQL database. +*/ diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp index aba186590..40cc38e33 100644 --- a/src/controls/plugin.cpp +++ b/src/controls/plugin.cpp @@ -49,6 +49,9 @@ #include "qquickstack_p.h" #include "qquickdesktopiconprovider_p.h" #include "qquickselectionmode_p.h" + +#include "Private/qquickcalendarmodel_p.h" +#include "Private/qquickrangeddate_p.h" #include "Private/qquickrangemodel_p.h" #include "Private/qquickwheelarea_p.h" #include "Private/qquicktooltip_p.h" @@ -75,6 +78,7 @@ static const struct { } qmldir [] = { { "ApplicationWindow", 1, 0 }, { "Button", 1, 0 }, + { "Calendar", 1, 2 }, { "CheckBox", 1, 0 }, { "ComboBox", 1, 0 }, { "GroupBox", 1, 0 }, @@ -131,7 +135,9 @@ void QtQuickControlsPlugin::initializeEngine(QQmlEngine *engine, const char *uri // Register private API const char *private_uri = "QtQuick.Controls.Private"; qmlRegisterType<QQuickAbstractStyle>(private_uri, 1, 0, "AbstractStyle"); + qmlRegisterType<QQuickCalendarModel>(private_uri, 1, 0, "CalendarModel"); qmlRegisterType<QQuickPadding>(); + qmlRegisterType<QQuickRangedDate>(private_uri, 1, 0, "RangedDate"); qmlRegisterType<QQuickRangeModel>(private_uri, 1, 0, "RangeModel"); qmlRegisterType<QQuickWheelArea>(private_uri, 1, 0, "WheelArea"); qmlRegisterType<QQuickSpinBoxValidator>(private_uri, 1, 0, "SpinBoxValidator"); |
