Add up to 5 event indicators to the DayDelegate

This commit replaces the existing triangular event indicator on calendar
days with colored dots; one per event, up to five events per day. The dots
will use the color of the event, if it has one.
This commit is contained in:
Carl Schwan 2021-04-29 21:38:46 +00:00 committed by Nate Graham
parent 629f4421a7
commit b3f8e96517
6 changed files with 146 additions and 34 deletions

View File

@ -199,7 +199,7 @@ int Calendar::year() const
return m_displayedDate.year();
}
QAbstractListModel *Calendar::daysModel() const
QAbstractItemModel *Calendar::daysModel() const
{
return m_daysModel;
}

View File

@ -8,7 +8,6 @@
#ifndef CALENDAR_H
#define CALENDAR_H
#include <QAbstractListModel>
#include <QDate>
#include <QJsonArray>
#include <QObject>
@ -16,6 +15,8 @@
#include "daydata.h"
#include "daysmodel.h"
class QAbstractItemModel;
class Calendar : public QObject
{
Q_OBJECT
@ -110,7 +111,7 @@ class Calendar : public QObject
* metadata about the current day. The exact metadata can be found in "daysmodel.cpp"
* where the exact names usable in QML are being set.
*/
Q_PROPERTY(QAbstractListModel *daysModel READ daysModel CONSTANT)
Q_PROPERTY(QAbstractItemModel *daysModel READ daysModel CONSTANT)
public:
enum Type {
@ -164,7 +165,7 @@ public:
int year() const;
// Models
QAbstractListModel *daysModel() const;
QAbstractItemModel *daysModel() const;
QJsonArray weeksModel() const;
// QML invokables

View File

@ -1,6 +1,7 @@
/*
SPDX-FileCopyrightText: 2013 Mark Gaiser <markg85@gmail.com>
SPDX-FileCopyrightText: 2016 Martin Klapetek <mklapetek@kde.org>
SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -15,7 +16,7 @@
#include <QMetaObject>
DaysModel::DaysModel(QObject *parent)
: QAbstractListModel(parent)
: QAbstractItemModel(parent)
, m_pluginsManager(nullptr)
, m_lastRequestedEventsStartDate(QDate())
, m_agendaNeedsUpdate(false)
@ -38,18 +39,38 @@ void DaysModel::setSourceData(QList<DayData> *data)
int DaysModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if (m_data->size() <= 0) {
return 0;
if (!parent.isValid()) {
// day count
if (m_data->size() <= 0) {
return 0;
} else {
return m_data->size();
}
} else {
return m_data->size();
// event count
const auto &eventDatas = data(parent, Roles::Events).value<QList<CalendarEvents::EventData>>();
Q_ASSERT(eventDatas.count() <= 5);
return eventDatas.count();
}
}
int DaysModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant DaysModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
const DayData &currentData = m_data->at(index.row());
if (!index.isValid()) {
return {};
}
const int row = index.row();
if (!index.parent().isValid()) {
// Fetch days in month
const DayData &currentData = m_data->at(row);
const QDate currentDate(currentData.yearNumber, currentData.monthNumber, currentData.dayNumber);
switch (role) {
@ -57,6 +78,10 @@ QVariant DaysModel::data(const QModelIndex &index, int role) const
return currentData.isCurrent;
case containsEventItems:
return m_eventsData.contains(currentDate);
case Events:
return QVariant::fromValue(m_eventsData.values(currentDate));
case EventCount:
return m_eventsData.values(currentDate).count();
case containsMajorEventItems:
return hasMajorEventAtDate(currentDate);
case containsMinorEventItems:
@ -68,8 +93,20 @@ QVariant DaysModel::data(const QModelIndex &index, int role) const
case yearNumber:
return currentData.yearNumber;
}
} else {
// Fetch event in day
const auto &eventDatas = data(index.parent(), Roles::Events).value<QList<CalendarEvents::EventData>>();
if (eventDatas.count() < row) {
return {};
}
const auto &eventData = eventDatas[row];
switch (role) {
case EventColor:
return eventData.eventColor();
}
}
return QVariant();
return {};
}
void DaysModel::update()
@ -78,7 +115,12 @@ void DaysModel::update()
return;
}
// We need to reset the model since m_data has already been changed here
// and we can't remove the events manually with beginRemoveRows() since
// we don't know where the old events were located.
beginResetModel();
m_eventsData.clear();
endResetModel();
const QDate modelFirstDay(m_data->at(0).yearNumber, m_data->at(0).monthNumber, m_data->at(0).dayNumber);
@ -96,14 +138,44 @@ void DaysModel::update()
void DaysModel::onDataReady(const QMultiHash<QDate, CalendarEvents::EventData> &data)
{
m_eventsData.reserve(m_eventsData.size() + data.size());
m_eventsData += data;
for (int i = 0; i < m_data->count(); i++) {
const DayData &currentData = m_data->at(i);
const QDate currentDate(currentData.yearNumber, currentData.monthNumber, currentData.dayNumber);
if (!data.values(currentDate).isEmpty()) {
// Make sure we don't display more than 5 events
const int currentCount = m_eventsData.values(currentDate).count();
if (currentCount > 5) {
break;
}
const int additionalCount = data.values(currentDate).count();
int nextIndex = data.values(currentDate).count() - 1;
if (currentCount + additionalCount > 5) {
nextIndex = 5 - currentCount - 1;
}
// Add event
beginInsertRows(index(i, 0), 0, nextIndex);
int stopCounter = 0;
for (const auto &dataDay : data.values(currentDate)) {
if (stopCounter > nextIndex) {
break;
}
stopCounter++;
m_eventsData.insert(currentDate, dataDay);
}
endInsertRows();
}
}
if (data.contains(QDate::currentDate())) {
m_agendaNeedsUpdate = true;
}
// only the containsEventItems roles may have changed
Q_EMIT dataChanged(index(0, 0), index(m_data->count() - 1, 0), {containsEventItems, containsMajorEventItems, containsMinorEventItems});
Q_EMIT dataChanged(index(0, 0), index(m_data->count() - 1, 0), {containsEventItems, containsMajorEventItems, containsMinorEventItems, Events, EventCount});
Q_EMIT agendaUpdated(QDate::currentDate());
}
@ -128,7 +200,7 @@ void DaysModel::onEventModified(const CalendarEvents::EventData &data)
for (const QDate date : qAsConst(updatesList)) {
const QModelIndex changedIndex = indexForDate(date);
if (changedIndex.isValid()) {
Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems, containsMajorEventItems, containsMinorEventItems});
Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems, containsMajorEventItems, containsMinorEventItems, EventColor});
}
Q_EMIT agendaUpdated(date);
}
@ -136,6 +208,12 @@ void DaysModel::onEventModified(const CalendarEvents::EventData &data)
void DaysModel::onEventRemoved(const QString &uid)
{
// HACK We should update the model with beginRemoveRows instead of
// using beginResetModel() since this creates a small visual glitches
// if an event is removed in Korganizer and the calendar is open.
// Using beginRemoveRows instead we make the code a lot more complex
// and if not done correcly will introduce bugs.
beginResetModel();
QList<QDate> updatesList;
auto i = m_eventsData.begin();
while (i != m_eventsData.end()) {
@ -156,8 +234,10 @@ void DaysModel::onEventRemoved(const QString &uid)
if (changedIndex.isValid()) {
Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems, containsMajorEventItems, containsMinorEventItems});
}
Q_EMIT agendaUpdated(date);
}
endResetModel();
}
QList<QObject *> DaysModel::eventsForDate(const QDate &date)
@ -255,5 +335,26 @@ QHash<int, QByteArray> DaysModel::roleNames() const
{containsMinorEventItems, "containsMinorEventItems"},
{dayNumber, "dayNumber"},
{monthNumber, "monthNumber"},
{yearNumber, "yearNumber"}};
{yearNumber, "yearNumber"},
{EventColor, "eventColor"},
{EventCount, "eventCount"},
{Events, "events"}};
}
QModelIndex DaysModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid()) {
return createIndex(row, column, (intptr_t)parent.row());
}
return createIndex(row, column, nullptr);
}
QModelIndex DaysModel::parent(const QModelIndex &child) const
{
if (child.internalId()) {
return createIndex(child.internalId(), 0, nullptr);
}
return QModelIndex();
}
Q_DECLARE_METATYPE(CalendarEvents::EventData)

View File

@ -8,14 +8,14 @@
#ifndef DAYSMODEL_H
#define DAYSMODEL_H
#include <QAbstractListModel>
#include <QAbstractItemModel>
#include "daydata.h"
#include <CalendarEvents/CalendarEventsPlugin>
class EventPluginsManager;
class DaysModel : public QAbstractListModel
class DaysModel : public QAbstractItemModel
{
Q_OBJECT
@ -31,12 +31,19 @@ public:
dayNumber,
monthNumber,
yearNumber,
Events,
EventColor,
EventCount,
};
explicit DaysModel(QObject *parent = nullptr);
virtual ~DaysModel();
void setSourceData(QList<DayData> *data);
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
Q_INVOKABLE void setPluginsManager(QObject *manager);

View File

@ -13,6 +13,7 @@ import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents2 // For Highlight
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import QtQml.Models 2.15
import org.kde.plasma.calendar 2.0
@ -20,6 +21,7 @@ PlasmaComponents3.AbstractButton {
id: dayStyle
hoverEnabled: true
property var dayModel: null
signal activated
@ -68,13 +70,23 @@ PlasmaComponents3.AbstractButton {
}
}
Loader {
active: model.containsMajorEventItems !== undefined && model.containsMajorEventItems
Row {
spacing: PlasmaCore.Units.smallSpacing
anchors.bottom: parent.bottom
anchors.right: parent.right
height: parent.height / 3
width: height
sourceComponent: eventsMarkerComponent
anchors.bottomMargin: PlasmaCore.Units.smallSpacing
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
model: DelegateModel {
model: dayStyle.dayModel
rootIndex: modelIndex(index)
delegate: Rectangle {
width: PlasmaCore.Units.smallSpacing * 1.5
height: width
radius: width / 2
color: eventColor || PlasmaCore.Theme.highlightColor
}
}
}
}
contentItem: PlasmaExtras.Heading {

View File

@ -48,16 +48,6 @@ Item {
imagePath: "widgets/calendar"
}
Component {
id: eventsMarkerComponent
PlasmaCore.SvgItem {
id: eventsMarker
svg: calendarSvg
elementId: "event"
}
}
Column {
id: weeksColumn
visible: showWeekNumbers
@ -129,6 +119,7 @@ Item {
id: delegate
width: daysCalendar.cellWidth
height: daysCalendar.cellHeight
dayModel: repeater.model
onClicked: daysCalendar.activated(index, model, delegate)