diff --git a/src/declarativeimports/calendar/qml/DaysCalendar.qml b/src/declarativeimports/calendar/qml/DaysCalendar.qml index eb4d308bc..00b52b9a0 100644 --- a/src/declarativeimports/calendar/qml/DaysCalendar.qml +++ b/src/declarativeimports/calendar/qml/DaysCalendar.qml @@ -20,37 +20,27 @@ Item { signal headerClicked - signal previous - signal next - signal activated(int index, var date, var item) // so it forwards it to the delegate which then emits activated with all the necessary data signal activateHighlightedItem readonly property int gridColumns: showWeekNumbers ? calendarGrid.columns + 1 : calendarGrid.columns - property alias previousLabel: previousButton.tooltip - property alias nextLabel: nextButton.tooltip - property int rows property int columns property bool showWeekNumbers - onShowWeekNumbersChanged: canvas.requestPaint() - // how precise date matching should be, 3 = day+month+year, 2 = month+year, 1 = just year property int dateMatchingPrecision property alias headerModel: days.model property alias gridModel: repeater.model - property alias title: heading.text - // Take the calendar width, subtract the inner and outer spacings and divide by number of columns (==days in week) - readonly property int cellWidth: Math.floor((stack.width - (daysCalendar.columns + 1) * root.borderWidth) / (daysCalendar.columns + (showWeekNumbers ? 1 : 0))) + readonly property int cellWidth: Math.floor((swipeView.width - (daysCalendar.columns + 1) * root.borderWidth) / (daysCalendar.columns + (showWeekNumbers ? 1 : 0))) // Take the calendar height, subtract the inner spacings and divide by number of rows (root.weeks + one row for day names) - readonly property int cellHeight: Math.floor((stack.height - heading.height - calendarGrid.rows * root.borderWidth) / calendarGrid.rows) + readonly property int cellHeight: Math.floor((swipeView.height - heading.height - calendarGrid.rows * root.borderWidth) / calendarGrid.rows) property real transformScale: 1 property point transformOrigin: Qt.point(width / 2, height / 2) @@ -76,166 +66,6 @@ Item { } } - RowLayout { - anchors { - top: parent.top - left: canvas.left - right: canvas.right - } - spacing: PlasmaCore.Units.smallSpacing - - PlasmaExtras.Heading { - id: heading - - Layout.fillWidth: true - - level: 2 - elide: Text.ElideRight - font.capitalization: Font.Capitalize - //SEE QTBUG-58307 - //try to make all heights an even number, otherwise the layout engine gets confused - Layout.preferredHeight: implicitHeight + implicitHeight%2 - - MouseArea { - id: monthMouse - property int previousPixelDelta - - anchors.fill: parent - onClicked: { - if (!stack.busy) { - daysCalendar.headerClicked() - } - } - onExited: previousPixelDelta = 0 - onWheel: { - var delta = wheel.angleDelta.y || wheel.angleDelta.x - var pixelDelta = wheel.pixelDelta.y || wheel.pixelDelta.x - - // For high-precision touchpad scrolling, we get a wheel event for basically every slightest - // finger movement. To prevent the view from suddenly ending up in the next century, we - // cumulate all the pixel deltas until they're larger than the label and then only change - // the month. Standard mouse wheel scrolling is unaffected since it's fine. - if (pixelDelta) { - if (Math.abs(previousPixelDelta) < monthMouse.height) { - previousPixelDelta += pixelDelta - return - } - } - - if (delta >= 15) { - daysCalendar.previous() - } else if (delta <= -15) { - daysCalendar.next() - } - previousPixelDelta = 0 - } - } - } - - Components.ToolButton { - id: previousButton - property string tooltip - - icon.name: Qt.application.layoutDirection === Qt.RightToLeft ? "go-next" : "go-previous" - onClicked: daysCalendar.previous() - Accessible.name: tooltip - Components.ToolTip { text: parent.tooltip } - //SEE QTBUG-58307 - Layout.preferredHeight: implicitHeight + implicitHeight%2 - } - - Components.ToolButton { - icon.name: "go-jump-today" - property string tooltip - - onClicked: root.resetToToday() - tooltip: i18ndc("libplasma5", "Reset calendar to today", "Today") - Accessible.name: tooltip - Accessible.description: i18nd("libplasma5", "Reset calendar to today") - Components.ToolTip { text: parent.tooltip } - //SEE QTBUG-58307 - Layout.preferredHeight: implicitHeight + implicitHeight%2 - } - - Components.ToolButton { - id: nextButton - property string tooltip - - icon.name: Qt.application.layoutDirection === Qt.RightToLeft ? "go-previous" : "go-next" - onClicked: daysCalendar.next() - Accessible.name: tooltip - Components.ToolTip { text: parent.tooltip } - //SEE QTBUG-58307 - Layout.preferredHeight: implicitHeight + implicitHeight%2 - } - } - - // Paints the inner grid and the outer frame - Canvas { - id: canvas - - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - } - width: (daysCalendar.cellWidth + root.borderWidth) * gridColumns + root.borderWidth - height: (daysCalendar.cellHeight + root.borderWidth) * calendarGrid.rows + root.borderWidth - - opacity: root.borderOpacity - antialiasing: false - clip: false - onPaint: { - var ctx = getContext("2d"); - // this is needed as otherwise the canvas seems to have some sort of - // inner clip region which does not update on size changes - ctx.reset(); - ctx.save(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.strokeStyle = PlasmaCore.Theme.textColor; - ctx.lineWidth = root.borderWidth - ctx.globalAlpha = 1.0; - - ctx.beginPath(); - - // When line is more wide than 1px, it is painted with 1px line at the actual coords - // and then 1px lines are added first to the left of the middle then right (then left again) - // So all the lines need to be offset a bit to have their middle point in the center - // of the grid spacing rather than on the left most pixel, otherwise they will be painted - // over the days grid which will be visible on eg. mouse hover - var lineBasePoint = Math.floor(root.borderWidth / 2) - - // horizontal lines - for (var i = 0; i < calendarGrid.rows + 1; i++) { - var lineY = lineBasePoint + (daysCalendar.cellHeight + root.borderWidth) * (i); - - if (i == 0 || i == calendarGrid.rows) { - ctx.moveTo(0, lineY); - } else { - ctx.moveTo(showWeekNumbers ? daysCalendar.cellWidth + root.borderWidth : root.borderWidth, lineY); - } - ctx.lineTo(width, lineY); - } - - // vertical lines - for (var i = 0; i < gridColumns + 1; i++) { - var lineX = lineBasePoint + (daysCalendar.cellWidth + root.borderWidth) * (i); - - // Draw the outer vertical lines in full height so that it closes - // the outer rectangle - if (i == 0 || i == gridColumns || !daysCalendar.headerModel) { - ctx.moveTo(lineX, 0); - } else { - ctx.moveTo(lineX, root.borderWidth + daysCalendar.cellHeight); - } - ctx.lineTo(lineX, height); - } - - ctx.closePath(); - ctx.stroke(); - ctx.restore(); - } - } - PlasmaCore.Svg { id: calendarSvg imagePath: "widgets/calendar" @@ -251,20 +81,13 @@ Item { } } - Connections { - target: theme - function onTextColorChanged() { - canvas.requestPaint(); - } - } - Column { id: weeksColumn visible: showWeekNumbers anchors { - top: canvas.top + top: parent.top left: parent.left - bottom: canvas.bottom + bottom: parent.bottom // The borderWidth needs to be counted twice here because it goes // in fact through two lines - the topmost one (the outer edge) // and then the one below weekday strings @@ -291,7 +114,8 @@ Item { id: calendarGrid anchors { - right: canvas.right + top: parent.top + right: parent.right rightMargin: root.borderWidth bottom: parent.bottom bottomMargin: root.borderWidth diff --git a/src/declarativeimports/calendar/qml/MonthView.qml b/src/declarativeimports/calendar/qml/MonthView.qml index fb6c2ce32..624da671b 100644 --- a/src/declarativeimports/calendar/qml/MonthView.qml +++ b/src/declarativeimports/calendar/qml/MonthView.qml @@ -6,15 +6,15 @@ SPDX-License-Identifier: GPL-2.0-or-later */ import QtQuick 2.0 -import QtQuick.Controls 1.1 +import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.1 import org.kde.plasma.calendar 2.0 import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.components 3.0 as Components import org.kde.plasma.extras 2.0 as PlasmaExtras -PinchArea { +Item { id: root anchors.fill: parent @@ -39,76 +39,130 @@ PinchArea { property int firstDay: new Date(showDate.getFullYear(), showDate.getMonth(), 1).getDay() property alias today: calendarBackend.today property bool showWeekNumbers: false + property bool showCustomHeader: false + + /** + * Current index of the internal swipeView. Usefull for binding + * a TabBar to it. + */ + property alias currentIndex: swipeView.currentIndex property alias cellHeight: mainDaysCalendar.cellHeight property QtObject daysModel: calendarBackend.daysModel - onPinchStarted: stack.currentItem.transformOrigin = pinch.center - onPinchUpdated: { - var item = stack.currentItem - if (stack.depth < 3 && pinch.scale < 1) { - item.transformScale = pinch.scale - item.opacity = pinch.scale - } else if (stack.depth > 1 && pinch.scale > 1) { - item.transformScale = pinch.scale - item.opacity = (2 - pinch.scale / 2) - } - } - onPinchFinished: { - var item = stack.currentItem - if (item.transformScale < 0.7) { - item.headerClicked() - } else if (item.transformScale > 1.4) { - item.activateHighlightedItem() - } else { - item.transformScale = 1 - item.opacity = 1 - } - } - function isToday(date) { - if (date.toDateString() == new Date().toDateString()) { - return true; - } - - return false; + return date.toDateString() === new Date().toDateString(); } function eventDate(yearNumber,monthNumber,dayNumber) { - var d = new Date(yearNumber, monthNumber-1, dayNumber); + const d = new Date(yearNumber, monthNumber-1, dayNumber); return Qt.formatDate(d, "dddd dd MMM yyyy"); } + /** + * Move calendar to month view showing today's date. + */ function resetToToday() { calendarBackend.resetToToday(); root.currentDate = root.today; - stack.pop(null); + swipeView.currentIndex = 0; } function updateYearOverview() { - var date = calendarBackend.displayedDate; - var day = date.getDate(); - var year = date.getFullYear(); + const date = calendarBackend.displayedDate; + const day = date.getDate(); + const year = date.getFullYear(); - for (var i = 0, j = monthModel.count; i < j; ++i) { + for (let i = 0, j = monthModel.count; i < j; ++i) { monthModel.setProperty(i, "yearNumber", year); } } function updateDecadeOverview() { - var date = calendarBackend.displayedDate; - var day = date.getDate(); - var month = date.getMonth() + 1; - var year = date.getFullYear(); - var decade = year - year % 10; + const date = calendarBackend.displayedDate; + const day = date.getDate(); + const month = date.getMonth() + 1; + const year = date.getFullYear(); + const decade = year - year % 10; - for (var i = 0, j = yearModel.count; i < j; ++i) { - var label = decade - 1 + i; + for (let i = 0, j = yearModel.count; i < j; ++i) { + const label = decade - 1 + i; yearModel.setProperty(i, "yearNumber", label); yearModel.setProperty(i, "label", label); } } + /** + * Possible calendar views + */ + enum CalendarView { + DayView, + MonthView, + YearView + } + + /** + * Go to the next month/year/decade depending on the current + * calendar view displayed. + */ + function nextFrame() { + if (swipeView.currentIndex === 0) { + calendarBackend.nextMonth(); + } else if (swipeView.currentIndex === 1) { + calendarBackend.nextYear(); + } else if (swipeView.currentIndex === 2) { + calendarBackend.nextDecade(); + } + } + + /** + * Go to the previous month/year/decade depending on the current + * calendar view displayed. + */ + function previousFrame() { + if (swipeView.currentIndex === 0) { + calendarBackend.previousMonth(); + } else if (swipeView.currentIndex === 1) { + calendarBackend.previousYear(); + } else if (swipeView.currentIndex === 2) { + calendarBackend.previousDecade(); + } + } + + /** + * \return CalendarView + */ + readonly property var calendarViewDisplayed: { + if (swipeView.currentIndex === 0) { + return MonthView.CalendarView.DayView; + } else if (swipeView.currentIndex === 1) { + return MonthView.CalendarView.MonthView; + } else if (swipeView.currentIndex === 2) { + return MonthView.CalendarView.YearView; + } + } + + /** + * Show month view. + */ + function showMonthView() { + swipeView.currentIndex = 0; + } + + /** + * Show year view. + */ + function showYearView() { + swipeView.currentIndex = 1; + } + + /** + * Show month view. + */ + function showDecadeView() { + swipeView.currentIndex = 2; + } + Calendar { id: calendarBackend @@ -148,6 +202,8 @@ PinchArea { Component.onCompleted: { for (var i = 0; i < 12; ++i) { append({ + label: 2050, // this value will be overwritten, but it set the type of the property to int + yearNumber: 2050, isCurrent: (i > 0 && i < 11) // first and last year are outside the decade }) } @@ -165,63 +221,136 @@ PinchArea { onPressed: mouse.accepted = false } - StackView { - id: stack + ColumnLayout { + id: viewHeader + visible: !showCustomHeader + height: !visible ? 0 : implicitHeight + width: parent.width + anchors { + top: parent.top + } - anchors.fill: parent + RowLayout { + PlasmaExtras.Heading { + id: heading + text: i18ndc("libplasma5", "Format: **month** year", "%1 %2", root.selectedMonth, root.selectedYear.toString()) - delegate: StackViewDelegate { - pushTransition: StackViewTransition { - NumberAnimation { - target: exitItem - duration: PlasmaCore.Units.longDuration - property: "opacity" - from: 1 - to: 0 - } - NumberAnimation { - target: enterItem - duration: PlasmaCore.Units.longDuration - property: "opacity" - from: 0 - to: 1 - } - NumberAnimation { - target: enterItem - duration: PlasmaCore.Units.longDuration - property: "transformScale" - from: 1.5 - to: 1 - } + level: 2 + elide: Text.ElideRight + font.capitalization: Font.Capitalize + //SEE QTBUG-58307 + //try to make all heights an even number, otherwise the layout engine gets confused + Layout.preferredHeight: implicitHeight + implicitHeight%2 + Layout.fillWidth: true } - popTransition: StackViewTransition { - NumberAnimation { - target: exitItem - duration: PlasmaCore.Units.longDuration - property: "opacity" - from: 1 - to: 0 + Row { + spacing: 0 + Components.ToolButton { + id: previousButton + property string tooltip: { + switch(root.calendarViewDisplayed) { + case MonthView.CalendarView.DayView: + return i18nd("libplasma5", "Previous Month") + case MonthView.CalendarView.MonthView: + return i18nd("libplasma5", "Previous Year") + case MonthView.CalendarView.YearView: + return i18nd("libplasma5", "Previous Decade") + default: + return ""; + } + } + + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight % 2 + icon.name: Qt.application.layoutDirection === Qt.RightToLeft ? "go-next" : "go-previous" + onClicked: root.previousFrame() + Accessible.name: tooltip + Components.ToolTip { text: parent.tooltip } } - NumberAnimation { - target: exitItem - duration: PlasmaCore.Units.longDuration - property: "transformScale" - // so no matter how much you scaled, it would still fly towards you - to: exitItem.transformScale * 1.5 + + Components.ToolButton { + icon.name: "go-jump-today" + property string tooltip + + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight % 2 + onClicked: root.resetToToday() + Components.ToolTip { + text: i18ndc("libplasma5", "Reset calendar to today", "Today") + } + Accessible.name: tooltip + Accessible.description: i18nd("libplasma5", "Reset calendar to today") } - NumberAnimation { - target: enterItem - duration: PlasmaCore.Units.longDuration - property: "opacity" - from: 0 - to: 1 + + Components.ToolButton { + id: nextButton + property string tooltip: { + switch(root.calendarViewDisplayed) { + case MonthView.CalendarView.DayView: + return i18nd("libplasma5", "Next Month") + case MonthView.CalendarView.MonthView: + return i18nd("libplasma5", "Next Year") + case MonthView.CalendarView.YearView: + return i18nd("libplasma5", "Next Decade") + default: + return ""; + } + } + + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight % 2 + icon.name: Qt.application.layoutDirection === Qt.RightToLeft ? "go-previous" : "go-next" + Components.ToolTip { text: parent.tooltip } + onClicked: root.nextFrame(); + Accessible.name: tooltip } } } - initialItem: DaysCalendar { + Components.TabBar { + id: tabBar + currentIndex: swipeView.currentIndex + Layout.preferredWidth: contentWidth + Layout.alignment: Qt.AlignRight + Components.TabButton { + text: i18nc("libplasma5", "Days"); + onClicked: root.showMonthView(); + opacity: root.calendarViewDisplayed === MonthView.CalendarView.MonthView ? 1 : 0.8 + width: implicitWidth + } + Components.TabButton { + text: i18nd("libplasma5", "Months"); + onClicked: root.showYearView(); + opacity: root.calendarViewDisplayed === MonthView.CalendarView.YearView ? 1 : 0.7 + width: implicitWidth + } + Components.TabButton { + text: i18nd("libplasma5", "Years"); + onClicked: root.showDecadeView(); + opacity: root.calendarViewDisplayed === MonthView.CalendarView.DecadeView ? 1 : 0.7 + width: implicitWidth + } + } + } + + QQC2.SwipeView { + id: swipeView + orientation: Qt.Vertical + anchors { + top: viewHeader.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + clip: true + + onCurrentIndexChanged: if (currentIndex > 1) { + updateDecadeOverview(); + } + + // MonthView + DaysCalendar { id: mainDaysCalendar - title: calendarBackend.displayedDate.getFullYear() == new Date().getFullYear() ? root.selectedMonth : root.selectedMonth + ", " + root.selectedYear columns: calendarBackend.days rows: calendarBackend.weeks @@ -233,75 +362,43 @@ PinchArea { dateMatchingPrecision: Calendar.MatchYearMonthAndDay - previousLabel: i18nd("libplasma5", "Previous Month") - nextLabel: i18nd("libplasma5", "Next Month") - - onPrevious: calendarBackend.previousMonth() - onNext: calendarBackend.nextMonth() - onHeaderClicked: { - stack.push(yearOverview) - } onActivated: { - var rowNumber = Math.floor(index / columns); + const rowNumber = Math.floor(index / columns); week = 1 + calendarBackend.weeksModel[rowNumber]; root.currentDate = new Date(date.yearNumber, date.monthNumber - 1, date.dayNumber) } } - } - - Component { - id: yearOverview + // YearView DaysCalendar { - title: calendarBackend.displayedDate.getFullYear() columns: 3 rows: 4 dateMatchingPrecision: Calendar.MatchYearAndMonth gridModel: monthModel - - previousLabel: i18nd("libplasma5", "Previous Year") - nextLabel: i18nd("libplasma5", "Next Year") - - onPrevious: calendarBackend.previousYear() - onNext: calendarBackend.nextYear() - onHeaderClicked: { - updateDecadeOverview(); - stack.push(decadeOverview) - } onActivated: { - calendarBackend.goToMonth(date.monthNumber) - stack.pop() + calendarBackend.goToMonth(date.monthNumber); + swipeView.currentIndex = 0; } } - } - - Component { - id: decadeOverview + // DecadeView DaysCalendar { readonly property int decade: { - var year = calendarBackend.displayedDate.getFullYear() + const year = calendarBackend.displayedDate.getFullYear() return year - year % 10 } - title: decade + " – " + (decade + 9) columns: 3 rows: 4 dateMatchingPrecision: Calendar.MatchYear gridModel: yearModel - - previousLabel: i18nd("libplasma5", "Previous Decade") - nextLabel: i18nd("libplasma5", "Next Decade") - - onPrevious: calendarBackend.previousDecade() - onNext: calendarBackend.nextDecade() onActivated: { - calendarBackend.goToYear(date.yearNumber) - stack.pop() + calendarBackend.goToYear(date.yearNumber); + swipeView.currentIndex = 1; } } }