Improve calendar navigation

This adds a "Year overview" showing all 12 months in a grid, and a "Decade overview"
showing the current decade.

CHANGELOG: Calendar navigation has been significantly improved, providing a year and decade overview
REVIEW: 122488
This commit is contained in:
Kai Uwe Broulik 2015-07-27 11:41:35 +02:00
parent 80940951e0
commit 11e5ff10ae
8 changed files with 433 additions and 252 deletions

View File

@ -48,10 +48,22 @@ void Calendar::setDisplayedDate(const QDate &dateTime)
if (m_displayedDate == dateTime) {
return;
}
const int oldMonth = m_displayedDate.month();
const int oldYear = m_displayedDate.year();
m_displayedDate = dateTime;
// m_dayHelper->setDate(m_displayedDate.year(), m_displayedDate.month());
updateData();
emit displayedDateChanged();
if (oldMonth != m_displayedDate.month()) {
emit monthNameChanged();
}
if (oldYear != m_displayedDate.year()) {
emit yearChanged();
}
}
QDate Calendar::today() const
@ -222,9 +234,7 @@ void Calendar::updateData()
//QDate previousMonth(m_displayedDate.year(), m_displayedDate.month() - 1, 1);
for (int i = 0; i < daysBeforeCurrentMonth; i++) {
DayData day;
day.isCurrentMonth = false;
day.isNextMonth = false;
day.isPreviousMonth = true;
day.isCurrent = false;
day.dayNumber = previousMonth.daysInMonth() - (daysBeforeCurrentMonth - (i + 1));
day.monthNumber = previousMonth.month();
day.yearNumber = previousMonth.year();
@ -235,9 +245,7 @@ void Calendar::updateData()
for (int i = 0; i < m_displayedDate.daysInMonth(); i++) {
DayData day;
day.isCurrentMonth = true;
day.isNextMonth = false;
day.isPreviousMonth = false;
day.isCurrent = true;
day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates
// day.containsEventItems = m_dayHelper->containsEventItems(i + 1);
day.monthNumber = m_displayedDate.month();
@ -249,9 +257,7 @@ void Calendar::updateData()
if (daysAfterCurrentMonth > 0) {
for (int i = 0; i < daysAfterCurrentMonth; i++) {
DayData day;
day.isCurrentMonth = false;
day.isNextMonth = true;
day.isPreviousMonth = false;
day.isCurrent = false;
day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates
// day.containsEventItems = false;
day.monthNumber = m_displayedDate.addMonths(1).month();
@ -292,36 +298,43 @@ void Calendar::updateData()
// qDebug() << "m_dayList size: " << m_dayList.count();
// qDebug() << "---------------------------------------------------------------";
}
void Calendar::nextDecade()
{
setDisplayedDate(m_displayedDate.addYears(10));
}
void Calendar::previousDecade()
{
setDisplayedDate(m_displayedDate.addYears(-10));
}
void Calendar::nextYear()
{
m_displayedDate = m_displayedDate.addYears(1);
updateData();
emit displayedDateChanged();
emit yearChanged();
setDisplayedDate(m_displayedDate.addYears(1));
}
void Calendar::previousYear()
{
m_displayedDate = m_displayedDate.addYears(-1);
updateData();
emit displayedDateChanged();
emit yearChanged();
setDisplayedDate(m_displayedDate.addYears(-1));
}
void Calendar::nextMonth()
{
m_displayedDate = m_displayedDate.addMonths(1);
updateData();
emit displayedDateChanged();
emit monthNameChanged();
emit yearChanged();
setDisplayedDate(m_displayedDate.addMonths(1));
}
void Calendar::previousMonth()
{
m_displayedDate = m_displayedDate.addMonths(-1);
updateData();
emit displayedDateChanged();
emit monthNameChanged();
emit yearChanged();
setDisplayedDate(m_displayedDate.addMonths(-1));
}
void Calendar::goToMonth(int month)
{
setDisplayedDate(QDate(m_displayedDate.year(), month, m_displayedDate.day()));
}
void Calendar::goToYear(int year)
{
setDisplayedDate(QDate(year, m_displayedDate.month(), m_displayedDate.day()));
}

View File

@ -118,7 +118,7 @@ class Calendar : public QObject
*/
Q_PROPERTY(QAbstractListModel *daysModel READ daysModel CONSTANT)
Q_ENUMS(Type)
Q_ENUMS(Type DateMatchingPrecision)
public:
enum Type {
@ -129,6 +129,12 @@ public:
};
Q_DECLARE_FLAGS(Types, Type)
enum DateMatchingPrecision {
MatchYear,
MatchYearAndMonth,
MatchYearMonthAndDay
};
explicit Calendar(QObject *parent = 0);
// Displayed date
@ -171,9 +177,13 @@ public:
Q_INVOKABLE void previousMonth();
Q_INVOKABLE void nextYear();
Q_INVOKABLE void previousYear();
Q_INVOKABLE void nextDecade();
Q_INVOKABLE void previousDecade();
Q_INVOKABLE QString dayName(int weekday) const;
Q_INVOKABLE int currentWeek() const;
Q_INVOKABLE void resetToToday();
Q_INVOKABLE void goToMonth(int month);
Q_INVOKABLE void goToYear(int year);
Q_SIGNALS:
void displayedDateChanged();

View File

@ -4,9 +4,7 @@
class DayData
{
public:
bool isPreviousMonth;
bool isCurrentMonth;
bool isNextMonth;
bool isCurrent;
// bool containsHolidayItems;
// bool containsEventItems;
// bool containsTodoItems;

View File

@ -26,9 +26,7 @@ DaysModel::DaysModel(QObject *parent) :
{
QHash<int, QByteArray> roleNames;
roleNames.insert(isPreviousMonth, "isPreviousMonth");
roleNames.insert(isCurrentMonth, "isCurrentMonth");
roleNames.insert(isNextMonth, "isNextMonth");
roleNames.insert(isCurrent, "isCurrent");
//roleNames.insert(containsHolidayItems, "containsHolidayItems");
//roleNames.insert(containsEventItems, "containsEventItems");
// roleNames.insert(containsTodoItems, "containsTodoItems");
@ -63,13 +61,11 @@ QVariant DaysModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
DayData currentData = m_data->at(index.row());
const DayData &currentData = m_data->at(index.row());
switch (role) {
case isPreviousMonth:
return currentData.isPreviousMonth;
case isNextMonth:
return currentData.isNextMonth;
case isCurrent:
return currentData.isCurrent;
// case containsHolidayItems:
// return currentData.containsHolidayItems;
/* case containsEventItems:

View File

@ -28,9 +28,7 @@ class DaysModel : public QAbstractListModel
Q_OBJECT
public:
enum Roles {
isPreviousMonth = Qt::UserRole + 1,
isCurrentMonth,
isNextMonth,
isCurrent = Qt::UserRole + 1,
//containsHolidayItems,
//containsEventItems,
//containsTodoItems,

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 Heena Mahour <heena393@gmail.com>
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
* Copyright 2015 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -20,26 +21,55 @@ import org.kde.plasma.calendar 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as Components
import org.kde.plasma.calendar 2.0
Item {
id: dayStyle
width: root.cellWidth
height: root.cellHeight
property bool today: root.today.toDateString() == new Date(yearNumber, monthNumber - 1, dayNumber).toDateString() // for some reason the comparison doesn't work without toDateString()
signal activated
readonly property date thisDate: new Date(yearNumber, typeof monthNumber !== "undefined" ? monthNumber - 1 : 0, typeof dayNumber !== "undefined" ? dayNumber : 1)
readonly property bool today: {
var today = root.today;
var result = true;
if (dateMatchingPrecision >= Calendar.MatchYear) {
result = result && today.getFullYear() === thisDate.getFullYear()
}
if (dateMatchingPrecision >= Calendar.MatchYearAndMonth) {
result = result && today.getMonth() === thisDate.getMonth()
}
if (dateMatchingPrecision >= Calendar.MatchYearMonthAndDay) {
result = result && today.getDate() === thisDate.getDate()
}
return result
}
readonly property bool selected: {
var current = root.currentDate
var result = true
if (dateMatchingPrecision >= Calendar.MatchYear) {
result = result && current.getFullYear() === thisDate.getFullYear()
}
if (dateMatchingPrecision >= Calendar.MatchYearAndMonth) {
result = result && current.getMonth() === thisDate.getMonth()
}
if (dateMatchingPrecision >= Calendar.MatchYearMonthAndDay) {
result = result && current.getDate() === thisDate.getDate()
}
return result
}
onHeightChanged: {
// this is needed here as the text is first rendered, counting with the default root.cellHeight
// then root.cellHeight actually changes to whatever it should be, but the Label does not pick
// it up after that, so we need to change it explicitly after the cell size changes
label.font.pixelSize = Math.max(theme.smallestFont.pixelSize, Math.floor(root.cellHeight / 3))
label.font.pixelSize = Math.max(theme.smallestFont.pixelSize, Math.floor(daysCalendar.cellHeight / 3))
}
Rectangle {
id: todayRect
anchors.fill: parent
opacity: {
if (calendarGrid.selectedItem == dayStyle && today) {
if (selected && today) {
0.6
} else if (today) {
0.4
@ -55,7 +85,7 @@ Item {
id: highlightDate
anchors.fill: todayRect
opacity: {
if (calendarGrid.selectedItem == dayStyle) {
if (selected) {
0.6
} else if (dateMouse.containsMouse) {
0.4
@ -71,10 +101,17 @@ Item {
Components.Label {
id: label
anchors.centerIn: parent
text: dayNumber
opacity: (isPreviousMonth || isNextMonth) ? 0.5: 1.0
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: model.label || dayNumber
opacity: isCurrent ? 1.0 : 0.5
wrapMode: Text.NoWrap
elide: Text.ElideRight
color: today ? theme.backgroundColor : theme.textColor
Behavior on color {
ColorAnimation { duration: units.shortDuration * 2 }
}
}
MouseArea {
@ -82,17 +119,12 @@ Item {
anchors.fill: parent
//z: label.z + 1
hoverEnabled: true
onClicked: {
var rowNumber = Math.floor(index / 7);
week = 1+calendarBackend.weeksModel[rowNumber];
root.date = model;
calendarGrid.selectedItem = dayStyle;
}
onClicked: dayStyle.activated()
}
Component.onCompleted: {
if (today) {
root.date = model;
if (stack.depth === 1 && today) {
root.date = model
}
}
}

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 Heena Mahour <heena393@gmail.com>
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
* Copyright 2015 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -16,22 +17,146 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import org.kde.plasma.calendar 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as Components
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
id: daysCalendar
readonly property int gridColumns: root.showWeekNumbers ? calendarGrid.columns + 1 : calendarGrid.columns
signal headerClicked
signal previous
signal next
signal activated(int index, var date, var item)
readonly property int gridColumns: showWeekNumbers ? calendarGrid.columns + 1 : calendarGrid.columns
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)))
// 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 - (daysCalendar.rows + 1) * root.borderWidth) / (daysCalendar.rows + 1))
PlasmaExtras.Heading {
id: heading
anchors {
top: parent.top
left: parent.left
right: parent.right
}
level: 1
elide: Text.ElideRight
font.capitalization: Font.Capitalize
MouseArea {
id: monthMouse
property int previousPixelDelta
width: heading.paintedWidth
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
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.Label {
anchors {
top: heading.bottom
left: parent.left
leftMargin: Math.floor(units.largeSpacing / 2)
}
text: "◀"
opacity: leftmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
MouseArea {
id: leftmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: daysCalendar.previous()
}
}
Components.Label {
anchors {
top: heading.bottom
right: parent.right
rightMargin: Math.floor(units.largeSpacing / 2)
}
text: "▶"
opacity: rightmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
MouseArea {
id: rightmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: daysCalendar.next()
}
}
// Paints the inner grid and the outer frame
Canvas {
id: canvas
width: (root.cellWidth + root.borderWidth) * gridColumns + root.borderWidth
height: (root.cellHeight + root.borderWidth) * calendarGrid.rows + root.borderWidth
anchors.bottom: parent.bottom
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
@ -58,26 +183,26 @@ Item {
// horizontal lines
for (var i = 0; i < calendarGrid.rows + 1; i++) {
var lineY = lineBasePoint + (root.cellHeight + root.borderWidth) * (i);
var lineY = lineBasePoint + (daysCalendar.cellHeight + root.borderWidth) * (i);
if (i == 0 || i == calendarGrid.rows) {
ctx.moveTo(0, lineY);
} else {
ctx.moveTo(root.showWeekNumbers ? root.cellWidth + root.borderWidth : root.borderWidth, lineY);
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 + (root.cellWidth + root.borderWidth) * (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) {
ctx.moveTo(lineX, 0);
} else {
ctx.moveTo(lineX, root.borderWidth + root.cellHeight);
ctx.moveTo(lineX, root.borderWidth + daysCalendar.cellHeight);
}
ctx.lineTo(lineX, height);
}
@ -88,13 +213,6 @@ Item {
}
}
Connections {
target: root
onShowWeekNumbersChanged: {
canvas.requestPaint();
}
}
Connections {
target: theme
onTextColorChanged: {
@ -104,7 +222,7 @@ Item {
Column {
id: weeksColumn
visible: root.showWeekNumbers
visible: showWeekNumbers
anchors {
top: canvas.top
left: parent.left
@ -112,40 +230,38 @@ Item {
// 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
topMargin: root.cellHeight + root.borderWidth + root.borderWidth
topMargin: daysCalendar.cellHeight + root.borderWidth + root.borderWidth
}
spacing: root.borderWidth
Repeater {
model: root.showWeekNumbers ? calendarBackend.weeksModel : []
model: showWeekNumbers ? calendarBackend.weeksModel : []
Components.Label {
height: root.cellHeight
width: root.cellWidth
height: daysCalendar.cellHeight
width: daysCalendar.cellWidth
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 0.4
text: modelData
font.pixelSize: Math.max(theme.smallestFont.pixelSize, root.cellHeight / 6)
font.pixelSize: Math.max(theme.smallestFont.pixelSize, daysCalendar.cellHeight / 6)
}
}
}
Grid {
id: calendarGrid
// Pad the grid to not overlap with the top and left frame
// When week numbers are shown, the border needs to be counted twice
// because there's one more cell to count with and therefore also
// another border to add
x: root.showWeekNumbers ? 2 * root.borderWidth + root.cellWidth: root.borderWidth
anchors {
right: canvas.right
rightMargin: root.borderWidth
bottom: parent.bottom
bottomMargin: root.borderWidth
}
columns: calendarBackend.days
rows: calendarBackend.weeks + 1
columns: daysCalendar.columns
rows: daysCalendar.rows + 1
spacing: root.borderWidth
property Item selectedItem
property bool containsEventItems: false // FIXME
@ -159,17 +275,16 @@ Item {
}
}
Repeater {
id: days
model: calendarBackend.days
Item {
width: root.cellWidth
height: root.cellHeight
width: daysCalendar.cellWidth
height: daysCalendar.cellHeight
Components.Label {
text: Qt.locale().dayName(calendarBackend.firstDayOfWeek + index, Locale.ShortFormat)
font.pixelSize: Math.max(theme.smallestFont.pixelSize, root.cellHeight / 6)
font.pixelSize: Math.max(theme.smallestFont.pixelSize, daysCalendar.cellHeight / 6)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignBottom
anchors.horizontalCenter: parent.horizontalCenter
@ -181,9 +296,14 @@ Item {
Repeater {
id: repeater
model: calendarBackend.daysModel
DayDelegate {}
DayDelegate {
id: delegate
width: daysCalendar.cellWidth
height: daysCalendar.cellHeight
onActivated: daysCalendar.activated(index, model, delegate)
}
}
}
}

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 Heena Mahour <heena393@gmail.com>
* Copyright 2013 Sebastian Kügler <sebas@kde.org>
* Copyright 2015 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -16,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 1.1
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
@ -27,31 +30,26 @@ Item {
anchors.fill: parent
property QtObject date
property date showDate: new Date()
property alias selectedMonth: calendarBackend.monthName
property alias selectedYear: calendarBackend.year
property QtObject date
property date currentDate
property date showDate: new Date()
property int borderWidth: 1
property real borderOpacity: 0.4
property int columns: calendarBackend.days
property int rows: calendarBackend.weeks
// Take the calendar width, subtract the inner and outer spacings and divide by number of columns (==days in week)
property int cellWidth: Math.floor((calendar.width - (root.columns + 1) * borderWidth) / (root.columns + (root.showWeekNumbers ? 1 : 0))) //prefCellWidth()
// Take the calendar height, subtract the inner spacings and divide by number of rows (root.weeks + one row for day names)
property int cellHeight: Math.floor((calendar.height - (root.rows + 1) * borderWidth) / (root.rows + 1)) //prefCellHeight()
property Item selectedItem
property int week;
property int firstDay: new Date(showDate.getFullYear(), showDate.getMonth(), 1).getDay()
property date today
property bool showWeekNumbers: false
function isToday(date) {
if (date.toDateString() == new Date().toDateString()) {
return true;
@ -67,6 +65,31 @@ Item {
function resetToToday() {
calendarBackend.resetToToday();
stack.pop(null);
}
function updateYearOverview() {
var date = calendarBackend.displayedDate;
var day = date.getDate();
var year = date.getFullYear();
for (var 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;
for (var i = 0, j = yearModel.count; i < j; ++i) {
var label = decade - 1 + i;
yearModel.setProperty(i, "yearNumber", label);
yearModel.setProperty(i, "label", label);
}
}
Calendar {
@ -76,176 +99,167 @@ Item {
weeks: 6
firstDayOfWeek: Qt.locale().firstDayOfWeek
today: root.today
onYearChanged: {
updateYearOverview()
updateDecadeOverview()
}
}
ColumnLayout {
// This is to ensure that the inner grid.width is always aligned to be divisible by 7,
// fixes wrong side margins because of the rounding of cell size
// (consider the parent.width to be 404, the cell width would be 56,
// but 56*7 + 6 (the inner spacing) is 398, so we split the remaining 6 to avoid
// wrong alignment)
anchors {
fill: parent
leftMargin: Math.floor(((parent.width - (calendar.gridColumns + 1) * borderWidth) % calendar.gridColumns) / 2)
rightMargin: anchors.leftMargin
bottomMargin: anchors.leftMargin
}
ListModel {
id: monthModel
PlasmaExtras.Heading {
id: monthHeading
level: 1
text: calendarBackend.displayedDate.getFullYear() == new Date().getFullYear() ? root.selectedMonth : root.selectedMonth + ", " + root.selectedYear
elide: Text.ElideRight
font.capitalization: Font.Capitalize
Loader {
id: menuLoader
property QtObject calendarBackend: calendarBackend
Component.onCompleted: {
for (var i = 0; i < 12; ++i) {
append({
label: Qt.locale().standaloneMonthName(i, Locale.LongFormat),
monthNumber: i + 1,
isCurrent: true
})
}
MouseArea {
id: monthMouse
property int previousPixelDelta
updateYearOverview()
}
}
width: monthHeading.paintedWidth
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
ListModel {
id: yearModel
Component.onCompleted: {
for (var i = 0; i < 12; ++i) {
append({
isCurrent: (i > 0 && i < 11) // first and last year are outside the decade
})
}
updateDecadeOverview()
}
}
StackView {
id: stack
anchors.fill: parent
delegate: StackViewDelegate {
pushTransition: StackViewTransition {
NumberAnimation {
target: exitItem
duration: units.longDuration
property: "opacity"
from: 1
to: 0
}
onClicked: {
if (menuLoader.source == "") {
menuLoader.source = "MonthMenu.qml"
}
menuLoader.item.year = selectedYear
menuLoader.item.open(0, height);
NumberAnimation {
target: enterItem
duration: units.longDuration
property: "opacity"
from: 0
to: 1
}
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) {
calendarBackend.previousMonth()
} else if (delta <= -15) {
calendarBackend.nextMonth()
}
previousPixelDelta = 0
NumberAnimation {
target: enterItem
duration: units.longDuration
property: "scale"
from: 1.5
to: 1
}
}
popTransition: StackViewTransition {
NumberAnimation {
target: exitItem
duration: units.longDuration
property: "opacity"
from: 1
to: 0
}
NumberAnimation {
target: exitItem
duration: units.longDuration
property: "scale"
from: 1
to: 1.5
}
NumberAnimation {
target: enterItem
duration: units.longDuration
property: "opacity"
from: 0
to: 1
}
}
}
initialItem: DaysCalendar {
title: calendarBackend.displayedDate.getFullYear() == new Date().getFullYear() ? root.selectedMonth : root.selectedMonth + ", " + root.selectedYear
columns: calendarBackend.days
rows: calendarBackend.weeks
showWeekNumbers: root.showWeekNumbers
headerModel: calendarBackend.days
gridModel: calendarBackend.daysModel
dateMatchingPrecision: Calendar.MatchYearMonthAndDay
onPrevious: calendarBackend.previousMonth()
onNext: calendarBackend.nextMonth()
onHeaderClicked: {
stack.push(yearOverview)
}
onActivated: {
var rowNumber = Math.floor(index / 7);
week = 1 + calendarBackend.weeksModel[rowNumber];
root.date = date
root.currentDate = new Date(date.yearNumber, date.monthNumber - 1, date.dayNumber)
}
}
}
Component {
id: yearOverview
DaysCalendar {
id: calendar
title: calendarBackend.displayedDate.getFullYear()
columns: 3
rows: 4
Layout.fillWidth: true
Layout.fillHeight: true
dateMatchingPrecision: Calendar.MatchYearAndMonth
PlasmaComponents.Label {
text: "◀"
opacity: leftmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
font.pixelSize: Math.max(theme.smallestFont.pixelSize, Math.floor(root.cellHeight / 3))
anchors {
top: parent.top
left: parent.left
leftMargin: Math.floor(units.largeSpacing / 2) + root.borderWidth
topMargin: anchors.leftMargin
}
MouseArea {
id: leftmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: {
calendarBackend.previousMonth()
}
}
}
PlasmaComponents.Label {
text: "▶"
opacity: rightmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
font.pixelSize: Math.max(theme.smallestFont.pixelSize, Math.floor(root.cellHeight / 3))
anchors {
top: parent.top
right: parent.right
rightMargin: Math.floor(units.largeSpacing / 2) + root.borderWidth
topMargin: anchors.rightMargin
}
MouseArea {
id: rightmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: {
calendarBackend.nextMonth()
}
}
gridModel: monthModel
onPrevious: calendarBackend.previousYear()
onNext: calendarBackend.nextYear()
onHeaderClicked: stack.push(decadeOverview)
onActivated: {
calendarBackend.goToMonth(date.monthNumber)
stack.pop()
}
}
}
Component {
id: decadeOverview
/*
Item {
id: calendarToolbar
visible: false
anchors {
left: parent.left
right: parent.right
bottomMargin: 20
bottom: parent.bottom
}
DaysCalendar {
readonly property int decade: {
var year = calendarBackend.displayedDate.getFullYear()
return year - year % 10
}
PlasmaComponents.ToolButton {
id: currentDate
iconSource: "view-pim-calendar"
width: height
onClicked: {
calendarBackend.startDate = today();
}
PlasmaCore.ToolTipArea {
id: tool
anchors.fill: currentDate
mainText: "Select Today"
}
anchors {
left: parent.left
}
}
title: decade + " " + (decade + 9)
columns: 3
rows: 4
PlasmaComponents.TextField {
id: dateField
text: date == "" ? Qt.formatDateTime ( new Date(), "d/M/yyyy" ): date
width: calendarOperations.width/3
anchors {
leftMargin: 20
rightMargin: 30
left: currentDate.right
right: weekField.left
}
}
dateMatchingPrecision: Calendar.MatchYear
PlasmaComponents.TextField {
id: weekField
text: week == 0 ? calendarBackend.currentWeek(): week
width: calendarOperations.width/10
anchors {
right: parent.right
gridModel: yearModel
onPrevious: calendarBackend.previousDecade()
onNext: calendarBackend.nextDecade()
onActivated: {
calendarBackend.goToYear(date.yearNumber)
stack.pop()
}
}
}
*/
}