Commit a0a30549 authored by Laurent Montel's avatar Laurent Montel 😁 Committed by Glen Ditchfield
Browse files

Avoid vertical gaps around multi-day events in the month view

Given some single-day events and a multi-day event that ends on that day,
the layout heuristic would place the single-day events first, leaving a
gap above the multi-day event.  Placing longer events first tends to
produce more compact layouts.

BUG: 435667
parent fbc0a594
Pipeline #79721 failed with stage
in 2 minutes and 4 seconds
......@@ -240,3 +240,7 @@ install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
ecm_qt_install_logging_categories(EXPORT EVENTVIEWS FILE eventviews.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
install(FILES agenda/calendardecoration.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR})
if(BUILD_TESTING)
add_subdirectory(month/autotests)
endif()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
ecm_add_test(monthitemordertest.cpp LINK_LIBRARIES Qt::Test KF5::EventViews)
/*
* SPDX-FileCopyrightText: 2021 Glen Ditchfield <GJDitchfield@acm.org>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QTest>
#include "../monthitem.h"
using namespace EventViews;
class MonthItemOrderTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void longerInstancesFirst();
void holidaysFirst();
void stableOrder();
public:
IncidenceMonthItem *eventItem(QDate start, QDate end);
};
/**
* Longer instances are placed before shorter ones, regardless of their
* relative dates.
*/
void MonthItemOrderTest::longerInstancesFirst()
{
QDate startDate(2000, 01, 01);
IncidenceMonthItem *longEvent = eventItem(startDate, startDate.addDays(1));
for (int offset = -1; offset < 3; offset++) {
QDate d = startDate.addDays(offset);
IncidenceMonthItem *shortEvent = eventItem(d, d);
QVERIFY( MonthItem::greaterThan(longEvent, shortEvent));
QVERIFY( ! MonthItem::greaterThan(shortEvent, longEvent));
HolidayMonthItem *shortHoliday = new HolidayMonthItem(nullptr, d, QStringLiteral(""));
QVERIFY( MonthItem::greaterThan(longEvent, shortHoliday));
QVERIFY( ! MonthItem::greaterThan(shortHoliday, longEvent));
}
}
/**
* Holidays are placed before events with the same length and day.
*/
void MonthItemOrderTest::holidaysFirst()
{
QDate startDate(2000, 01, 01);
IncidenceMonthItem *event = eventItem(startDate, startDate);
HolidayMonthItem *holiday = new HolidayMonthItem(nullptr, startDate, QStringLiteral(""));
QVERIFY( ! MonthItem::greaterThan(event, holiday));
QVERIFY( MonthItem::greaterThan(holiday, event));
}
/**
* If two holidays are on the same day, they do not both come before the other.
* Similarly for two events with the same length and start day.
*/
void MonthItemOrderTest::stableOrder()
{
QDate startDate(2000, 01, 01);
HolidayMonthItem *holiday = new HolidayMonthItem(nullptr, startDate, QStringLiteral(""));
HolidayMonthItem *otherHoliday = new HolidayMonthItem(nullptr, startDate, QStringLiteral(""));
QVERIFY( ! (MonthItem::greaterThan(otherHoliday, holiday) && MonthItem::greaterThan(holiday, otherHoliday)));
IncidenceMonthItem *event = eventItem(startDate, startDate);
IncidenceMonthItem *otherEvent = eventItem(startDate, startDate);
QVERIFY( ! (MonthItem::greaterThan(otherEvent, event) && MonthItem::greaterThan(event, otherEvent)));
}
IncidenceMonthItem *MonthItemOrderTest::eventItem(QDate start, QDate end)
{
KCalendarCore::Event *e = new KCalendarCore::Event;
e->setDtStart(QDateTime(start, QTime(00, 00, 00)));
e->setDtEnd(QDateTime(end, QTime(00, 00, 00)));
e->setAllDay(true);
return new IncidenceMonthItem(nullptr, nullptr, Akonadi::Item(), KCalendarCore::Event::Ptr(e), start);
}
QTEST_MAIN(MonthItemOrderTest)
#include "monthitemordertest.moc"
......@@ -205,17 +205,15 @@ int MonthItem::daySpan() const
bool MonthItem::greaterThan(const MonthItem *e1, const MonthItem *e2)
{
const QDate leftStartDate = e1->startDate();
const QDate rightStartDate = e2->startDate();
if (!leftStartDate.isValid() || !rightStartDate.isValid()) {
return false;
}
if (leftStartDate == rightStartDate) {
const int leftDaySpan = e1->daySpan();
const int rightDaySpan = e2->daySpan();
if (leftDaySpan == rightDaySpan) {
const int leftDaySpan = e1->daySpan();
const int rightDaySpan = e2->daySpan();
if (leftDaySpan == rightDaySpan) {
const QDate leftStartDate = e1->startDate();
const QDate rightStartDate = e2->startDate();
if (!leftStartDate.isValid() || !rightStartDate.isValid()) {
return false;
}
if (leftStartDate == rightStartDate) {
if (e1->allDay() && !e2->allDay()) {
return true;
}
......@@ -224,11 +222,10 @@ bool MonthItem::greaterThan(const MonthItem *e1, const MonthItem *e2)
}
return e1->greaterThanFallback(e2);
} else {
return leftDaySpan > rightDaySpan;
return leftStartDate > rightStartDate;
}
}
return leftStartDate > rightStartDate;
return leftDaySpan > rightDaySpan;
}
bool MonthItem::greaterThanFallback(const MonthItem *other) const
......@@ -683,12 +680,8 @@ HolidayMonthItem::~HolidayMonthItem()
bool HolidayMonthItem::greaterThanFallback(const MonthItem *other) const
{
const auto o = qobject_cast<const HolidayMonthItem *>(other);
if (o) {
return MonthItem::greaterThanFallback(other);
}
// always put holidays on top
return false;
return !o;
}
void HolidayMonthItem::finalizeMove(const QDate &newStartDate)
......
......@@ -34,14 +34,16 @@ public:
QWidget *parentWidget() const;
/**
Compares two events
Compares two items to decide which to place in the view first.
The month view displays a list of items. When loading (which occurs each
time there is a change), the items are sorted :
- smallest date
- bigger span
- floating
time there is a change), the items are sorted in an order intended to
avoid unsightly gaps:
- biggest durations first
- earliest date
- finally, time in the day
Holidays are sorted before events with the same start date and length,
so they appear at the top of the day's box.
*/
static bool greaterThan(const MonthItem *e1, const MonthItem *e2);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment