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:
parent
80940951e0
commit
11e5ff10ae
@ -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()));
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -4,9 +4,7 @@
|
||||
class DayData
|
||||
{
|
||||
public:
|
||||
bool isPreviousMonth;
|
||||
bool isCurrentMonth;
|
||||
bool isNextMonth;
|
||||
bool isCurrent;
|
||||
// bool containsHolidayItems;
|
||||
// bool containsEventItems;
|
||||
// bool containsTodoItems;
|
||||
|
@ -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 ¤tData = 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:
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user