summaryrefslogtreecommitdiff
path: root/src/controls/Styles/Base/CalendarStyle.qml
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@digia.com>2013-07-15 22:57:52 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-14 13:13:53 +0100
commite023dd212c81a2ad4ba4b4db22df9cde65a502e8 (patch)
tree2c3521e8a4154d65a55fc3032254b58b181d957e /src/controls/Styles/Base/CalendarStyle.qml
parente88bdffe644e53912dfbce95117555cb6a87bfd2 (diff)
downloadqtquickcontrols-e023dd212c81a2ad4ba4b4db22df9cde65a502e8.tar.gz
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/Styles/Base/CalendarStyle.qml')
-rw-r--r--src/controls/Styles/Base/CalendarStyle.qml552
1 files changed, 552 insertions, 0 deletions
diff --git a/src/controls/Styles/Base/CalendarStyle.qml b/src/controls/Styles/Base/CalendarStyle.qml
new file mode 100644
index 00000000..99330ffe
--- /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);
+ }
+ }
+ }
+ }
+ }
+ }
+}