mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 08:08:42 +00:00
Calendar: Agenda View (#4727)
* Read-only view to show
* missed planned activities (configurable number of days to look back)
* todays planned activities, phases and events
* upcoming planned activities, phases and events (configurable number
of days to look ahead)
* Color of planned activities is now user configurable (coming from
global color theme)
* Added a hint to show whether a filter is active (all calendar views)
* Configuration: Reorganized in tabs
* Refactoring: Created enums for all user roles used in the calendar
(all views, all delegates) to improve readability; removed data that
was only written but never read
This commit is contained in:
committed by
GitHub
parent
d12f14f4a4
commit
84d7272f6d
@@ -44,6 +44,8 @@ PlanningCalendarWindow::PlanningCalendarWindow(Context *context)
|
||||
|
||||
setStartHour(8);
|
||||
setEndHour(21);
|
||||
setAgendaPastDays(7);
|
||||
setAgendaFutureDays(7);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
setChartLayout(mainLayout);
|
||||
@@ -236,6 +238,46 @@ PlanningCalendarWindow::setEndHour
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
PlanningCalendarWindow::getAgendaPastDays
|
||||
() const
|
||||
{
|
||||
return agendaPastDaysSpin->value();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlanningCalendarWindow::setAgendaPastDays
|
||||
(int days)
|
||||
{
|
||||
agendaPastDaysSpin->setValue(days);
|
||||
if (calendar != nullptr) {
|
||||
calendar->setAgendaPastDays(days);
|
||||
updateActivities();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
PlanningCalendarWindow::getAgendaFutureDays
|
||||
() const
|
||||
{
|
||||
return agendaFutureDaysSpin->value();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlanningCalendarWindow::setAgendaFutureDays
|
||||
(int days)
|
||||
{
|
||||
agendaFutureDaysSpin->setValue(days);
|
||||
if (calendar != nullptr) {
|
||||
calendar->setAgendaFutureDays(days);
|
||||
updateActivities();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PlanningCalendarWindow::isSummaryVisibleDay
|
||||
() const
|
||||
@@ -473,9 +515,10 @@ PlanningCalendarWindow::mkControls
|
||||
{
|
||||
QLocale locale;
|
||||
defaultViewCombo = new QComboBox();
|
||||
defaultViewCombo->addItem(tr("Day View"));
|
||||
defaultViewCombo->addItem(tr("Week View"));
|
||||
defaultViewCombo->addItem(tr("Month View"));
|
||||
defaultViewCombo->addItem(tr("Day"));
|
||||
defaultViewCombo->addItem(tr("Week"));
|
||||
defaultViewCombo->addItem(tr("Month"));
|
||||
defaultViewCombo->addItem(tr("Agenda"));
|
||||
defaultViewCombo->setCurrentIndex(static_cast<int>(CalendarView::Month));
|
||||
firstDayOfWeekCombo = new QComboBox();
|
||||
for (int i = Qt::Monday; i <= Qt::Sunday; ++i) {
|
||||
@@ -488,6 +531,12 @@ PlanningCalendarWindow::mkControls
|
||||
endHourSpin = new QSpinBox();
|
||||
endHourSpin->setSuffix(":00");
|
||||
endHourSpin->setMaximum(24);
|
||||
agendaPastDaysSpin = new QSpinBox();
|
||||
agendaPastDaysSpin->setMaximum(31);
|
||||
agendaPastDaysSpin->setSuffix(" " + tr("day(s)"));
|
||||
agendaFutureDaysSpin = new QSpinBox();
|
||||
agendaFutureDaysSpin->setMaximum(31);
|
||||
agendaFutureDaysSpin->setSuffix(" " + tr("day(s)"));
|
||||
summaryDayCheck = new QCheckBox(tr("Day View"));
|
||||
summaryDayCheck->setChecked(true);
|
||||
summaryWeekCheck = new QCheckBox(tr("Week View"));
|
||||
@@ -510,37 +559,38 @@ PlanningCalendarWindow::mkControls
|
||||
tertiaryCombo->setCurrentText("Notes");
|
||||
QStringList summaryMetrics { "ride_count", "total_distance", "coggan_tss", "workout_time" };
|
||||
multiMetricSelector = new MultiMetricSelector(tr("Available Metrics"), tr("Selected Metrics"), summaryMetrics);
|
||||
multiMetricSelector->setContentsMargins(10 * dpiXFactor, 10 * dpiYFactor, 10 * dpiXFactor, 10 * dpiYFactor);
|
||||
multiMetricSelector->setMinimumHeight(300 * dpiYFactor);
|
||||
|
||||
QFormLayout *formLayout = newQFormLayout();
|
||||
formLayout->addRow(tr("Default View on Startup"), defaultViewCombo);
|
||||
formLayout->addRow(tr("First Day of Week"), firstDayOfWeekCombo);
|
||||
formLayout->addRow(tr("Default Start Time"), startHourSpin);
|
||||
formLayout->addRow(tr("Default End Time"), endHourSpin);
|
||||
formLayout->addRow(tr("Show Summary In"), summaryDayCheck);
|
||||
formLayout->addRow("", summaryWeekCheck);
|
||||
formLayout->addRow("", summaryMonthCheck);
|
||||
formLayout->addItem(new QSpacerItem(0, 10 * dpiYFactor, QSizePolicy::Minimum, QSizePolicy::Fixed));
|
||||
formLayout->addRow(new QLabel(HLO + tr("Calendar Entries") + HLC));
|
||||
formLayout->addRow(tr("Field for Primary Line"), primaryMainCombo);
|
||||
formLayout->addRow(tr("Fallback Field for Primary Line"), primaryFallbackCombo);
|
||||
formLayout->addRow(tr("Metric for Secondary Line"), secondaryCombo);
|
||||
formLayout->addRow(tr("Field for Tertiary Line (Day and Week View)"), tertiaryCombo);
|
||||
formLayout->addItem(new QSpacerItem(0, 10 * dpiYFactor, QSizePolicy::Minimum, QSizePolicy::Fixed));
|
||||
formLayout->addRow(new QLabel(HLO + tr("Summary") + HLC));
|
||||
QFormLayout *generalForm = newQFormLayout();
|
||||
generalForm->setContentsMargins(0, 10 * dpiYFactor, 0, 10 * dpiYFactor);
|
||||
generalForm->addRow(tr("Startup View"), defaultViewCombo);
|
||||
generalForm->addRow(tr("First Day of Week"), firstDayOfWeekCombo);
|
||||
generalForm->addRow(tr("Default Start Time"), startHourSpin);
|
||||
generalForm->addRow(tr("Default End Time"), endHourSpin);
|
||||
generalForm->addRow(tr("Agenda: Look Back"), agendaPastDaysSpin);
|
||||
generalForm->addRow(tr("Agenda: Look Forward"), agendaFutureDaysSpin);
|
||||
generalForm->addRow(tr("Show Summary In"), summaryDayCheck);
|
||||
generalForm->addRow("", summaryWeekCheck);
|
||||
generalForm->addRow("", summaryMonthCheck);
|
||||
|
||||
QWidget *controlsWidget = new QWidget();
|
||||
QVBoxLayout *controlsLayout = new QVBoxLayout(controlsWidget);
|
||||
controlsLayout->addWidget(centerLayoutInWidget(formLayout, false));
|
||||
controlsLayout->addWidget(multiMetricSelector);
|
||||
QFormLayout *entriesForm = newQFormLayout();
|
||||
entriesForm->setContentsMargins(0, 10 * dpiYFactor, 0, 10 * dpiYFactor);
|
||||
entriesForm->addRow(tr("Field for Primary Line"), primaryMainCombo);
|
||||
entriesForm->addRow(tr("Fallback Field for Primary Line"), primaryFallbackCombo);
|
||||
entriesForm->addRow(tr("Metric for Secondary Line"), secondaryCombo);
|
||||
entriesForm->addRow(tr("Field for Tertiary Line (Day and Week View)"), tertiaryCombo);
|
||||
|
||||
QScrollArea *controlsScroller = new QScrollArea();
|
||||
controlsScroller->setWidgetResizable(true);
|
||||
controlsScroller->setWidget(controlsWidget);
|
||||
QTabWidget *controlsTabs = new QTabWidget();
|
||||
controlsTabs->addTab(centerLayoutInWidget(generalForm, false), tr("General"));
|
||||
controlsTabs->addTab(centerLayoutInWidget(entriesForm, false), tr("Calendar Entries"));
|
||||
controlsTabs->addTab(multiMetricSelector, tr("Summary"));
|
||||
|
||||
#if QT_VERSION < 0x060000
|
||||
connect(startHourSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &PlanningCalendarWindow::setStartHour);
|
||||
connect(endHourSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &PlanningCalendarWindow::setEndHour);
|
||||
connect(agendaPastDaysSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &PlanningCalendarWindow::setAgendaPastDays);
|
||||
connect(agendaFutureDaysSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &PlanningCalendarWindow::setAgendaFutureDays);
|
||||
connect(defaultViewCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PlanningCalendarWindow::setDefaultView);
|
||||
connect(firstDayOfWeekCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int idx) { setFirstDayOfWeek(idx + 1); });
|
||||
connect(primaryMainCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PlanningCalendarWindow::updateActivities);
|
||||
@@ -550,6 +600,8 @@ PlanningCalendarWindow::mkControls
|
||||
#else
|
||||
connect(startHourSpin, &QSpinBox::valueChanged, this, &PlanningCalendarWindow::setStartHour);
|
||||
connect(endHourSpin, &QSpinBox::valueChanged, this, &PlanningCalendarWindow::setEndHour);
|
||||
connect(agendaPastDaysSpin, &QSpinBox::valueChanged, this, &PlanningCalendarWindow::setAgendaPastDays);
|
||||
connect(agendaFutureDaysSpin, &QSpinBox::valueChanged, this, &PlanningCalendarWindow::setAgendaFutureDays);
|
||||
connect(defaultViewCombo, &QComboBox::currentIndexChanged, this, &PlanningCalendarWindow::setDefaultView);
|
||||
connect(firstDayOfWeekCombo, &QComboBox::currentIndexChanged, [=](int idx) { setFirstDayOfWeek(idx + 1); });
|
||||
connect(primaryMainCombo, &QComboBox::currentIndexChanged, this, &PlanningCalendarWindow::updateActivities);
|
||||
@@ -562,7 +614,7 @@ PlanningCalendarWindow::mkControls
|
||||
connect(summaryMonthCheck, &QCheckBox::toggled, this, &PlanningCalendarWindow::setSummaryVisibleMonth);
|
||||
connect(multiMetricSelector, &MultiMetricSelector::selectedChanged, this, &PlanningCalendarWindow::updateActivities);
|
||||
|
||||
setControls(controlsScroller);
|
||||
setControls(controlsTabs);
|
||||
}
|
||||
|
||||
|
||||
@@ -694,7 +746,7 @@ PlanningCalendarWindow::getActivities
|
||||
|
||||
activity.iconFile = IconManager::instance().getFilepath(rideItem);
|
||||
if (rideItem->color.alpha() < 255 || rideItem->planned) {
|
||||
activity.color = QColor("#F79130");
|
||||
activity.color = GColor(CCALPLANNED);
|
||||
} else {
|
||||
activity.color = rideItem->color;
|
||||
}
|
||||
@@ -770,33 +822,6 @@ PlanningCalendarWindow::getPhasesEvents
|
||||
(const Season &season, const QDate &firstDay, const QDate &lastDay) const
|
||||
{
|
||||
QHash<QDate, QList<CalendarEntry>> phasesEvents;
|
||||
for (const Phase &phase : season.phases) {
|
||||
if (phase.getAbsoluteStart().isValid() && phase.getAbsoluteEnd().isValid()) {
|
||||
int duration = std::max(qint64(1), phase.getAbsoluteStart().daysTo(phase.getAbsoluteEnd()));
|
||||
for (QDate date = phase.getAbsoluteStart(); date <= phase.getAbsoluteEnd(); date = date.addDays(1)) {
|
||||
if ( ( ( firstDay.isValid()
|
||||
&& date >= firstDay)
|
||||
|| ! firstDay.isValid())
|
||||
&& ( ( lastDay.isValid()
|
||||
&& date <= lastDay)
|
||||
|| ! lastDay.isValid())) {
|
||||
int progress = int(phase.getAbsoluteStart().daysTo(date) / double(duration) * 5.0) * 20;
|
||||
CalendarEntry entry;
|
||||
entry.primary = phase.getName();
|
||||
entry.secondary = "";
|
||||
entry.iconFile = QString(":images/breeze/network-mobile-%1.svg").arg(progress);
|
||||
entry.color = Qt::red;
|
||||
entry.reference = phase.id().toString();
|
||||
entry.start = QTime(0, 0, 1);
|
||||
entry.type = ENTRY_TYPE_PHASE;
|
||||
entry.isRelocatable = false;
|
||||
entry.spanStart = phase.getStart();
|
||||
entry.spanEnd = phase.getEnd();
|
||||
phasesEvents[date] << entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QList<Season> tmpSeasons = context->athlete->seasons->seasons;
|
||||
std::sort(tmpSeasons.begin(), tmpSeasons.end(), Season::LessThanForStarts);
|
||||
for (const Season &s : tmpSeasons) {
|
||||
@@ -821,7 +846,7 @@ PlanningCalendarWindow::getPhasesEvents
|
||||
} else {
|
||||
entry.iconFile = ":images/breeze/task-process-0.svg";
|
||||
}
|
||||
entry.color = Qt::yellow;
|
||||
entry.color = Qt::yellow; // TODO: Use color from GC-theme instead
|
||||
entry.reference = event.id;
|
||||
entry.start = QTime(0, 0, 0);
|
||||
entry.durationSecs = 0;
|
||||
@@ -831,6 +856,33 @@ PlanningCalendarWindow::getPhasesEvents
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const Phase &phase : season.phases) {
|
||||
if (phase.getAbsoluteStart().isValid() && phase.getAbsoluteEnd().isValid()) {
|
||||
int duration = std::max(qint64(1), phase.getAbsoluteStart().daysTo(phase.getAbsoluteEnd()));
|
||||
for (QDate date = phase.getAbsoluteStart(); date <= phase.getAbsoluteEnd(); date = date.addDays(1)) {
|
||||
if ( ( ( firstDay.isValid()
|
||||
&& date >= firstDay)
|
||||
|| ! firstDay.isValid())
|
||||
&& ( ( lastDay.isValid()
|
||||
&& date <= lastDay)
|
||||
|| ! lastDay.isValid())) {
|
||||
int progress = int(phase.getAbsoluteStart().daysTo(date) / double(duration) * 5.0) * 20;
|
||||
CalendarEntry entry;
|
||||
entry.primary = phase.getName();
|
||||
entry.secondary = "";
|
||||
entry.iconFile = QString(":images/breeze/network-mobile-%1.svg").arg(progress);
|
||||
entry.color = Qt::red; // TODO: Use color from GC-theme instead
|
||||
entry.reference = phase.id().toString();
|
||||
entry.start = QTime(0, 0, 1);
|
||||
entry.type = ENTRY_TYPE_PHASE;
|
||||
entry.isRelocatable = false;
|
||||
entry.spanStart = phase.getStart();
|
||||
entry.spanEnd = phase.getEnd();
|
||||
phasesEvents[date] << entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return phasesEvents;
|
||||
}
|
||||
@@ -854,7 +906,7 @@ PlanningCalendarWindow::updateActivities
|
||||
} else {
|
||||
summaries = getSummaries(calendar->firstVisibleDay(), calendar->lastVisibleDay(), 7);
|
||||
}
|
||||
calendar->fillEntries(activities, summaries, phasesEvents);
|
||||
calendar->fillEntries(activities, summaries, phasesEvents, isFiltered());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ class PlanningCalendarWindow : public GcChartWindow
|
||||
Q_PROPERTY(int firstDayOfWeek READ getFirstDayOfWeek WRITE setFirstDayOfWeek USER true)
|
||||
Q_PROPERTY(int startHour READ getStartHour WRITE setStartHour USER true)
|
||||
Q_PROPERTY(int endHour READ getEndHour WRITE setEndHour USER true)
|
||||
Q_PROPERTY(int agendaPastDays READ getAgendaPastDays WRITE setAgendaPastDays USER true)
|
||||
Q_PROPERTY(int agendaFutureDays READ getAgendaFutureDays WRITE setAgendaFutureDays USER true)
|
||||
Q_PROPERTY(bool summaryVisibleDay READ isSummaryVisibleDay WRITE setSummaryVisibleDay USER true)
|
||||
Q_PROPERTY(bool summaryVisibleWeek READ isSummaryVisibleWeek WRITE setSummaryVisibleWeek USER true)
|
||||
Q_PROPERTY(bool summaryVisibleMonth READ isSummaryVisibleMonth WRITE setSummaryVisibleMonth USER true)
|
||||
@@ -58,6 +60,8 @@ class PlanningCalendarWindow : public GcChartWindow
|
||||
int getFirstDayOfWeek() const;
|
||||
int getStartHour() const;
|
||||
int getEndHour() const;
|
||||
int getAgendaPastDays() const;
|
||||
int getAgendaFutureDays() const;
|
||||
bool isSummaryVisibleDay() const;
|
||||
bool isSummaryVisibleWeek() const;
|
||||
bool isSummaryVisibleMonth() const;
|
||||
@@ -76,6 +80,8 @@ class PlanningCalendarWindow : public GcChartWindow
|
||||
void setFirstDayOfWeek(int fdw);
|
||||
void setStartHour(int hour);
|
||||
void setEndHour(int hour);
|
||||
void setAgendaPastDays(int days);
|
||||
void setAgendaFutureDays(int days);
|
||||
void setSummaryVisibleDay(bool visible);
|
||||
void setSummaryVisibleWeek(bool visible);
|
||||
void setSummaryVisibleMonth(bool svm);
|
||||
@@ -95,6 +101,8 @@ class PlanningCalendarWindow : public GcChartWindow
|
||||
QComboBox *firstDayOfWeekCombo;
|
||||
QSpinBox *startHourSpin;
|
||||
QSpinBox *endHourSpin;
|
||||
QSpinBox *agendaPastDaysSpin;
|
||||
QSpinBox *agendaFutureDaysSpin;
|
||||
QCheckBox *summaryDayCheck;
|
||||
QCheckBox *summaryWeekCheck;
|
||||
QCheckBox *summaryMonthCheck;
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "RemoteControl.h"
|
||||
#include "Measures.h"
|
||||
#include "StyledItemDelegates.h"
|
||||
#include "Qt5Compatibility.h"
|
||||
|
||||
class MeasuresPage : public QWidget
|
||||
{
|
||||
@@ -245,31 +246,6 @@ class SchemePage : public QWidget
|
||||
};
|
||||
|
||||
|
||||
// Compatibility helper for Qt5
|
||||
// exposes methods that turned public in Qt6 from protected in Qt5
|
||||
#if QT_VERSION < 0x060000
|
||||
class TreeWidget6 : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TreeWidget6(QWidget *parent = nullptr): QTreeWidget(parent) {
|
||||
}
|
||||
|
||||
QModelIndex indexFromItem(const QTreeWidgetItem *item, int column = 0) const {
|
||||
return QTreeWidget::indexFromItem(item, column);
|
||||
}
|
||||
|
||||
QTreeWidgetItem* itemFromIndex(const QModelIndex &index) const {
|
||||
return QTreeWidget::itemFromIndex(index);
|
||||
}
|
||||
|
||||
};
|
||||
#else
|
||||
typedef QTreeWidget TreeWidget6;
|
||||
#endif
|
||||
|
||||
|
||||
class CPPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QCalendarWidget>
|
||||
#include <QPushButton>
|
||||
#include <QToolBar>
|
||||
#include <QMenu>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "CalendarData.h"
|
||||
#include "TimeUtils.h"
|
||||
#include "Measures.h"
|
||||
#include "Qt5Compatibility.h"
|
||||
|
||||
|
||||
class CalendarOverview : public QCalendarWidget {
|
||||
@@ -140,6 +141,10 @@ class CalendarMonthTable : public QTableWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum CalendarDayTableRoles {
|
||||
DateRole = Qt::UserRole + 1000 // [QDate] Date of cell
|
||||
};
|
||||
|
||||
explicit CalendarMonthTable(Qt::DayOfWeek firstDayOfWeek = Qt::Monday, QWidget *parent = nullptr);
|
||||
explicit CalendarMonthTable(const QDate &dateInMonth, Qt::DayOfWeek firstDayOfWeek = Qt::Monday, QWidget *parent = nullptr);
|
||||
|
||||
@@ -212,7 +217,8 @@ private:
|
||||
enum class CalendarView {
|
||||
Day = 0,
|
||||
Week = 1,
|
||||
Month = 2
|
||||
Month = 2,
|
||||
Agenda = 3
|
||||
};
|
||||
|
||||
|
||||
@@ -285,6 +291,65 @@ private:
|
||||
};
|
||||
|
||||
|
||||
struct CalendarAgendaStyles {
|
||||
QFont defaultFont;
|
||||
QFont relativeFont;
|
||||
QFont hoverFont;
|
||||
QFont headlineDefaultFont;
|
||||
QFont headlineTodayFont;
|
||||
QFont headlineEmptyFont;
|
||||
QFont headlineSmallEmptyFont;
|
||||
|
||||
int sectionSpacerHeight;
|
||||
int sectionEntrySpacerHeight;
|
||||
int entrySpacerHeight;
|
||||
int daySpacerHeight;
|
||||
};
|
||||
|
||||
class CalendarAgendaView : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CalendarAgendaView(QWidget *parent = nullptr);
|
||||
|
||||
void updateDate();
|
||||
void setDateRange(const DateRange &dateRange);
|
||||
void setPastDays(int days);
|
||||
void setFutureDays(int days);
|
||||
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries);
|
||||
QDate firstVisibleDay() const;
|
||||
QDate lastVisibleDay() const;
|
||||
QDate selectedDate() const;
|
||||
|
||||
signals:
|
||||
void showInTrainMode(const CalendarEntry &activity);
|
||||
void showInMonthView(const QDate &date);
|
||||
void viewActivity(const CalendarEntry &activity);
|
||||
void dayChanged(const QDate &date);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void changeEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
QDate currentDate;
|
||||
DateRange dateRange;
|
||||
int pastDays = 7;
|
||||
int futureDays = 7;
|
||||
TreeWidget6 *agendaTree;
|
||||
QTreeWidgetItem *lastHoveredItem = nullptr;
|
||||
int lastHoveredColumn = -1;
|
||||
void clearHover();
|
||||
void addEntries(const QDate &today, const QDate &date, const QList<CalendarEntry> &entries, QTreeWidgetItem *parent, const CalendarAgendaStyles &cas);
|
||||
void addSpacer(QTreeWidgetItem *parent, int height);
|
||||
void addSeparator(QTreeWidgetItem *parent, int top, int bottom);
|
||||
void fillStyles(CalendarAgendaStyles &cas) const;
|
||||
|
||||
private slots:
|
||||
void showContextMenu(const QPoint &pos);
|
||||
};
|
||||
|
||||
|
||||
class Calendar : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -292,7 +357,7 @@ public:
|
||||
explicit Calendar(const QDate &dateInMonth, Qt::DayOfWeek firstDayOfWeek = Qt::Monday, Measures * const athleteMeasures = nullptr, QWidget *parent = nullptr);
|
||||
|
||||
void setDate(const QDate &dateInMonth, bool allowKeepMonth = false);
|
||||
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries);
|
||||
void fillEntries(const QHash<QDate, QList<CalendarEntry>> &activityEntries, const QList<CalendarSummary> &summaries, const QHash<QDate, QList<CalendarEntry>> &headlineEntries, bool isFiltered);
|
||||
QDate firstOfCurrentMonth() const;
|
||||
QDate firstVisibleDay() const;
|
||||
QDate lastVisibleDay() const;
|
||||
@@ -311,6 +376,8 @@ public slots:
|
||||
void setFirstDayOfWeek(Qt::DayOfWeek firstDayOfWeek);
|
||||
void setStartHour(int hour);
|
||||
void setEndHour(int hour);
|
||||
void setAgendaPastDays(int days);
|
||||
void setAgendaFutureDays(int days);
|
||||
void setSummaryDayVisible(bool visible);
|
||||
void setSummaryWeekVisible(bool visible);
|
||||
void setSummaryMonthVisible(bool visible);
|
||||
@@ -335,23 +402,34 @@ signals:
|
||||
void delRestday(const QDate &day);
|
||||
|
||||
private:
|
||||
QToolBar *toolbar;
|
||||
QAction *prevAction;
|
||||
QAction *nextAction;
|
||||
QAction *todayAction;
|
||||
QAction *separator;
|
||||
QAction *dayAction;
|
||||
QAction *weekAction;
|
||||
QAction *monthAction;
|
||||
QPushButton *dateNavigator;
|
||||
QAction *agendaAction;
|
||||
QToolButton *dateNavigator;
|
||||
QAction *dateNavigatorAction;
|
||||
QMenu *dateMenu;
|
||||
QLabel *seasonLabel;
|
||||
QAction *seasonLabelAction;
|
||||
QAction *filterSpacerAction;
|
||||
QLabel *filterLabel;
|
||||
QAction *filterLabelAction;
|
||||
QStackedWidget *viewStack;
|
||||
CalendarDayView *dayView;
|
||||
CalendarWeekView *weekView;
|
||||
CalendarMonthTable *monthView;
|
||||
CalendarAgendaView *agendaView;
|
||||
DateRange dateRange;
|
||||
Qt::DayOfWeek firstDayOfWeek = Qt::Monday;
|
||||
QDate lastNonAgendaDate;
|
||||
|
||||
void setNavButtonState();
|
||||
void updateDateLabel();
|
||||
void updateHeader();
|
||||
|
||||
private slots:
|
||||
void populateDateMenu();
|
||||
|
||||
@@ -351,12 +351,12 @@ CalendarDetailedDayDelegate::paint(QPainter *painter, const QStyleOptionViewItem
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
bool ok;
|
||||
int pressedEntryIdx = index.data(Qt::UserRole + 2).toInt(&ok);
|
||||
int pressedEntryIdx = index.data(PressedEntryRole).toInt(&ok);
|
||||
if (! ok) {
|
||||
pressedEntryIdx = -2;
|
||||
}
|
||||
CalendarDay calendarDay = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
QList<CalendarEntryLayout> layout = index.data(Qt::UserRole + 3).value<QList<CalendarEntryLayout>>();
|
||||
CalendarDay calendarDay = index.data(DayRole).value<CalendarDay>();
|
||||
QList<CalendarEntryLayout> layout = index.data(LayoutRole).value<QList<CalendarEntryLayout>>();
|
||||
entryTester.resize(index, layout.count());
|
||||
|
||||
// Background
|
||||
@@ -494,7 +494,7 @@ CalendarDetailedDayDelegate::helpEvent
|
||||
if (! event || ! view) {
|
||||
return false;
|
||||
}
|
||||
CalendarDay day = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
CalendarDay day = index.data(DayRole).value<CalendarDay>();
|
||||
if (toolTipDayEntry(event->globalPos(), view, day, entryTester.hitTest(index, event->pos()))) {
|
||||
return true;
|
||||
}
|
||||
@@ -622,7 +622,7 @@ CalendarHeadlineDelegate::helpEvent
|
||||
if (! event || ! view) {
|
||||
return false;
|
||||
}
|
||||
CalendarDay day = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
CalendarDay day = index.data(DayRole).value<CalendarDay>();
|
||||
if (toolTipHeadlineEntry(event->globalPos(), view, day, headlineTester.hitTest(index, event->pos()))) {
|
||||
return true;
|
||||
}
|
||||
@@ -704,7 +704,7 @@ CalendarTimeScaleDelegate::paint
|
||||
}
|
||||
int blockY = -1;
|
||||
QModelIndex scaleIndex = index.siblingAtColumn(0);
|
||||
BlockIndicator blockIndicator = static_cast<BlockIndicator>(scaleIndex.data(Qt::UserRole + 1).toInt());
|
||||
BlockIndicator blockIndicator = static_cast<BlockIndicator>(scaleIndex.data(BlockRole).toInt());
|
||||
if (blockIndicator == BlockIndicator::AllBlock) {
|
||||
blockY = option.rect.height();
|
||||
} else if (blockIndicator == BlockIndicator::BlockBeforeNow) {
|
||||
@@ -717,7 +717,7 @@ CalendarTimeScaleDelegate::paint
|
||||
blockColor.setAlpha(180);
|
||||
painter->fillRect(rect, blockColor);
|
||||
}
|
||||
int currentY = scaleIndex.data(Qt::UserRole).toInt();
|
||||
int currentY = scaleIndex.data(CurrentYRole).toInt();
|
||||
if (currentY - option.rect.y() > blockY) {
|
||||
painter->save();
|
||||
painter->setPen(textPen);
|
||||
@@ -779,14 +779,14 @@ CalendarCompactDayDelegate::paint
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
CalendarDay calendarDay = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
CalendarDay calendarDay = index.data(DayRole).value<CalendarDay>();
|
||||
QColor bgColor;
|
||||
QColor selColor = opt.palette.highlight().color();
|
||||
QColor dayColor;
|
||||
QColor entryColor;
|
||||
|
||||
bool ok;
|
||||
int pressedEntryIdx = index.data(Qt::UserRole + 2).toInt(&ok);
|
||||
int pressedEntryIdx = index.data(PressedEntryRole).toInt(&ok);
|
||||
if (! ok) {
|
||||
pressedEntryIdx = -2;
|
||||
}
|
||||
@@ -914,7 +914,7 @@ CalendarCompactDayDelegate::helpEvent
|
||||
if (! event || ! view) {
|
||||
return false;
|
||||
}
|
||||
CalendarDay day = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
CalendarDay day = index.data(DayRole).value<CalendarDay>();
|
||||
if ( (moreTester.hitTest(index, event->pos()) != -1 && toolTipMore(event->globalPos(), view, day))
|
||||
|| (toolTipDayEntry(event->globalPos(), view, day, entryTester.hitTest(index, event->pos())))
|
||||
|| (toolTipHeadlineEntry(event->globalPos(), view, day, headlineTester.hitTest(index, event->pos())))) {
|
||||
@@ -941,7 +941,7 @@ CalendarSummaryDelegate::paint
|
||||
bool hasToolTip = false;
|
||||
const QColor bgColor = option.palette.color(QPalette::Active, QPalette::AlternateBase);
|
||||
const QColor fgColor = option.palette.color(QPalette::Active, QPalette::Text);
|
||||
const CalendarSummary summary = index.data(Qt::UserRole).value<CalendarSummary>();
|
||||
const CalendarSummary summary = index.data(SummaryRole).value<CalendarSummary>();
|
||||
QFont valueFont(painter->font());
|
||||
valueFont.setWeight(QFont::Normal);
|
||||
valueFont.setPointSize(valueFont.pointSize() * 0.95);
|
||||
@@ -1018,7 +1018,7 @@ CalendarSummaryDelegate::helpEvent
|
||||
return false;
|
||||
}
|
||||
|
||||
CalendarSummary summary = index.data(Qt::UserRole).value<CalendarSummary>();
|
||||
CalendarSummary summary = index.data(SummaryRole).value<CalendarSummary>();
|
||||
QString tooltip = "<table>";
|
||||
for (const std::pair<QString, QString> &p : summary.keyValues) {
|
||||
tooltip += QString("<tr><td><b>%1</b></td><td> </td><td align='right'>%2</td></tr>").arg(p.first).arg(p.second);
|
||||
@@ -1035,7 +1035,7 @@ CalendarSummaryDelegate::sizeHint
|
||||
{
|
||||
Q_UNUSED(option)
|
||||
|
||||
const CalendarSummary summary = index.data(Qt::UserRole).value<CalendarSummary>();
|
||||
const CalendarSummary summary = index.data(SummaryRole).value<CalendarSummary>();
|
||||
QFont font;
|
||||
font.setWeight(QFont::DemiBold);
|
||||
const QFontMetrics fontMetrics(font);
|
||||
@@ -1045,6 +1045,228 @@ CalendarSummaryDelegate::sizeHint
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// AgendaMultiDelegate
|
||||
|
||||
AgendaMultiDelegate::AgendaMultiDelegate
|
||||
(QObject *parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AgendaMultiDelegate::paint
|
||||
(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
opt.state &= ~QStyle::State_Selected;
|
||||
opt.state &= ~QStyle::State_HasFocus;
|
||||
opt.state &= ~QStyle::State_MouseOver;
|
||||
|
||||
int type = index.data(TypeRole).toInt();
|
||||
if (type == 0) {
|
||||
opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop;
|
||||
|
||||
const bool hovered = index.data(HoverFlagRole).toBool();
|
||||
const QString hoverText = index.data(HoverTextRole).toString();
|
||||
const QFont hoverFont = index.data(HoverFontRole).value<QFont>();
|
||||
|
||||
if (hovered) {
|
||||
if (! hoverText.isEmpty()) {
|
||||
opt.text = hoverText;
|
||||
}
|
||||
if (hoverFont != QFont()) {
|
||||
opt.font = hoverFont;
|
||||
}
|
||||
}
|
||||
|
||||
const QWidget *widget = opt.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
|
||||
} else if (type == 1) {
|
||||
const QWidget *widget = opt.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
|
||||
} else if (type == 2) {
|
||||
const int horPadding = 4 * dpiXFactor;
|
||||
const QWidget *widget = option.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
|
||||
int topMargin = index.data(MarginTopRole).toInt();
|
||||
QStyleOption sepOpt;
|
||||
sepOpt.rect = QRect(opt.rect.x() + horPadding + 1, opt.rect.y() + topMargin + 1, opt.rect.width() - 2 * horPadding - 2, 1);
|
||||
sepOpt.palette = opt.palette;
|
||||
sepOpt.state = QStyle::State_Enabled;
|
||||
style->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &sepOpt, painter, widget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QSize
|
||||
AgendaMultiDelegate::sizeHint
|
||||
(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
int type = index.data(TypeRole).toInt();
|
||||
if (type == 0) {
|
||||
QString text = index.data(Qt::DisplayRole).toString();
|
||||
QFont normalFont = index.data(Qt::FontRole).value<QFont>();
|
||||
if (! normalFont.family().isEmpty()) {
|
||||
opt.font = normalFont;
|
||||
}
|
||||
|
||||
QFontMetrics normalFM(opt.font);
|
||||
QSize normalSize = normalFM.size(Qt::TextSingleLine, text);
|
||||
|
||||
QString hoverText = index.data(HoverTextRole).toString();
|
||||
QFont hoverFont = index.data(HoverFontRole).value<QFont>();
|
||||
|
||||
QSize hoverSize = QSize(0, 0);
|
||||
if (! hoverText.isEmpty() || hoverFont != QFont()) {
|
||||
QFontMetrics hoverFM(hoverFont != QFont() ? hoverFont : opt.font);
|
||||
hoverSize = hoverFM.size(Qt::TextSingleLine, hoverText.isEmpty() ? text : hoverText);
|
||||
}
|
||||
|
||||
QSize maxSize(std::max(normalSize.width(), hoverSize.width()), std::max(normalSize.height(), hoverSize.height()));
|
||||
maxSize += QSize(8, 4);
|
||||
|
||||
return maxSize;
|
||||
} else if (type == 1) {
|
||||
return QSize(opt.rect.width(), index.data(MarginTopRole).toInt());
|
||||
} else if (type == 2) {
|
||||
int topMargin = index.data(MarginTopRole).toInt();
|
||||
int bottomMargin = index.data(MarginBottomRole).toInt();
|
||||
const QWidget *widget = opt.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
int sepHeight = style->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, widget);
|
||||
return QSize(option.rect.width(), sepHeight + topMargin + bottomMargin);
|
||||
}
|
||||
return QStyledItemDelegate::sizeHint(option, index);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// CalendarSingleActivityDelegate
|
||||
|
||||
CalendarSingleActivityDelegate::CalendarSingleActivityDelegate
|
||||
(QObject *parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CalendarSingleActivityDelegate::paint
|
||||
(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
CalendarEntry entry = index.data(EntryRole).value<CalendarEntry>();
|
||||
bool hover = index.data(HoverFlagRole).toBool();
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
opt.state &= ~QStyle::State_Selected;
|
||||
opt.state &= ~QStyle::State_HasFocus;
|
||||
opt.state &= ~QStyle::State_MouseOver;
|
||||
opt.text.clear();
|
||||
|
||||
const QWidget *widget = opt.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
|
||||
|
||||
QFontMetrics entryFM(painter->font());
|
||||
const int horPadding = 4 * dpiXFactor;
|
||||
const int lineSpacing = 2 * dpiYFactor;
|
||||
const int lineHeight = entryFM.height();
|
||||
const int radius = 4 * dpiXFactor;
|
||||
const int iconSpacing = 4 * dpiXFactor;
|
||||
const int iconWidth = 2 * lineHeight + lineSpacing;
|
||||
const QSize pixmapSize(iconWidth, iconWidth);
|
||||
QColor textColor = opt.palette.color(QPalette::Active, QPalette::Text);
|
||||
QColor iconColor = entry.color;
|
||||
|
||||
if (hover) {
|
||||
painter->save();
|
||||
QRect hoverRect;
|
||||
if (entry.type == ENTRY_TYPE_PLANNED_ACTIVITY) {
|
||||
hoverRect = QRect(opt.rect.x() + horPadding + 1, opt.rect.y() + 1, opt.rect.width() - 2 * horPadding - 2, iconWidth - 2);
|
||||
} else {
|
||||
hoverRect = QRect(opt.rect.x() + horPadding + 1, opt.rect.y() + 1, opt.rect.width() - 2 * horPadding - 2, lineHeight - 2);
|
||||
}
|
||||
QColor bgColor = opt.palette.color(QPalette::Active, QPalette::Highlight);
|
||||
bgColor.setAlphaF(0.2);
|
||||
QColor bgBorder = bgColor;
|
||||
bgBorder.setAlphaF(0.6);
|
||||
iconColor = GCColor::invertColor(GCColor::blendedColor(bgColor, opt.palette.color(QPalette::Active, QPalette::AlternateBase)));
|
||||
textColor = iconColor;
|
||||
|
||||
painter->setPen(bgBorder);
|
||||
painter->setBrush(bgColor);
|
||||
painter->drawRoundedRect(hoverRect, radius, radius);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
if (entry.type == ENTRY_TYPE_PLANNED_ACTIVITY) {
|
||||
QPixmap pixmap = svgAsColoredPixmap(entry.iconFile, pixmapSize, iconSpacing, iconColor);
|
||||
painter->drawPixmap(opt.rect.x() + horPadding, opt.rect.y(), pixmap);
|
||||
} else {
|
||||
QPixmap pixmap = svgOnBackground(entry.iconFile, QSize(lineHeight, lineHeight), iconSpacing, entry.color, radius);
|
||||
painter->drawPixmap(opt.rect.x() + horPadding + lineHeight / 2, opt.rect.y(), pixmap);
|
||||
}
|
||||
|
||||
painter->setPen(textColor);
|
||||
QRect textRect(opt.rect.x() + iconWidth + iconSpacing + horPadding, opt.rect.y(), opt.rect.width() - iconWidth - iconSpacing - 2 * horPadding, lineHeight);
|
||||
|
||||
const QFontMetrics fm(painter->font());
|
||||
QString primary(entry.primary);
|
||||
QRect boundingRect = fm.boundingRect(primary);
|
||||
if (boundingRect.width() > textRect.width()) {
|
||||
primary = fm.elidedText(primary, Qt::ElideRight, textRect.width());
|
||||
}
|
||||
|
||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip, primary);
|
||||
if (! entry.secondary.isEmpty()) {
|
||||
textRect.translate(0, lineHeight + lineSpacing);
|
||||
QString secondary = entry.secondary;
|
||||
if (! entry.secondaryMetric.isEmpty()) {
|
||||
secondary += " (" + entry.secondaryMetric + ")";
|
||||
}
|
||||
if (fm.boundingRect(secondary).width() > textRect.width()) {
|
||||
secondary = fm.elidedText(secondary, Qt::ElideRight, textRect.width());
|
||||
}
|
||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip, secondary);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
|
||||
QSize
|
||||
CalendarSingleActivityDelegate::sizeHint
|
||||
(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QVariant data = index.data(EntryRole);
|
||||
if (! data.isNull()) {
|
||||
QFontMetrics entryFM(option.font);
|
||||
const int lineSpacing = 2 * dpiYFactor;
|
||||
const int lineHeight = entryFM.height();
|
||||
CalendarEntry entry = data.value<CalendarEntry>();
|
||||
if (entry.type == ENTRY_TYPE_PLANNED_ACTIVITY) {
|
||||
return QSize(100, 2 * lineHeight + lineSpacing);
|
||||
} else {
|
||||
return QSize(100, lineHeight);
|
||||
}
|
||||
} else {
|
||||
return QSize(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Local helpers
|
||||
|
||||
@@ -1138,7 +1360,7 @@ static QRect
|
||||
paintHeadline
|
||||
(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index, HitTester &headlineTester, const QString &dateFormat, int pressedEntryIdx, int leftMargin, int rightMargin, int topMargin, int lineSpacing, int radius)
|
||||
{
|
||||
CalendarDay calendarDay = index.data(Qt::UserRole + 1).value<CalendarDay>();
|
||||
CalendarDay calendarDay = index.data(CalendarHeadlineDelegate::DayRole).value<CalendarDay>();
|
||||
QColor dayColor;
|
||||
bool isToday = (calendarDay.date == QDate::currentDate());
|
||||
QLocale locale;
|
||||
|
||||
@@ -94,6 +94,12 @@ private:
|
||||
|
||||
class CalendarDetailedDayDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
DayRole = Qt::UserRole + 1, // [CalendarDay] Calendar day
|
||||
PressedEntryRole, // [int] Index of the currently pressed CalendarDay (see DayRole >> entries)
|
||||
LayoutRole // [QList<CalendarEntryLayout>] Layout of the activities in one column
|
||||
};
|
||||
|
||||
explicit CalendarDetailedDayDelegate(TimeScaleData const * const timeScaleData, QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
@@ -109,6 +115,10 @@ private:
|
||||
|
||||
class CalendarHeadlineDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
DayRole = Qt::UserRole + 1 // [CalendarDay] Calendar day
|
||||
};
|
||||
|
||||
explicit CalendarHeadlineDelegate(QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
@@ -124,6 +134,11 @@ private:
|
||||
|
||||
class CalendarTimeScaleDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
CurrentYRole = Qt::UserRole, // [int] Current Y value of the mousepointer
|
||||
BlockRole // [int / BlockIndicator] How the timescale should be marked as blocked
|
||||
};
|
||||
|
||||
explicit CalendarTimeScaleDelegate(TimeScaleData *timeScaleData, QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
@@ -138,6 +153,11 @@ private:
|
||||
|
||||
class CalendarCompactDayDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
DayRole = Qt::UserRole + 1, // [CalendarDay] Calendar day
|
||||
PressedEntryRole // [int] Index of the currently pressed CalendarDay (see DayRole >> entries)
|
||||
};
|
||||
|
||||
explicit CalendarCompactDayDelegate(QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
@@ -151,6 +171,10 @@ public:
|
||||
|
||||
class CalendarSummaryDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
SummaryRole = Qt::UserRole // [CalendarSummary] Summary data
|
||||
};
|
||||
|
||||
explicit CalendarSummaryDelegate(int horMargin = 4, QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
@@ -164,4 +188,41 @@ private:
|
||||
const int lineSpacing = 2;
|
||||
};
|
||||
|
||||
|
||||
class AgendaMultiDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
// Qt::DisplayRole // [QString] The default text to display
|
||||
// Qt::FontRole // [QFont] The default font to use for text
|
||||
HoverFlagRole = Qt::UserRole, // [bool] Hover flag. True if the item is hovered. If not set or invalid, treated as false
|
||||
HoverTextRole, // [QString] Hover text to display when the hover flag is true. If empty, the normal DisplayRole text is used
|
||||
HoverFontRole, // [QFont] Hover font to use when the hover flag is true. If default-constructed, the normal font is used
|
||||
TypeRole, // [int] 0: Text
|
||||
// 1: Spacer (Qt::UserRole + 4: Height)
|
||||
// 2: Separator
|
||||
MarginTopRole, // [int] Margin above (Type Spacer + Separator only)
|
||||
MarginBottomRole // [int] Margin below (Type Separator only)
|
||||
};
|
||||
|
||||
explicit AgendaMultiDelegate(QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
|
||||
class CalendarSingleActivityDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
enum Roles {
|
||||
EntryRole = Qt::UserRole, // [CalendarEntry] Entry to be displayed
|
||||
HoverFlagRole, // [bool] Hover flag. True if the item is hovered. If not set or invalid, treated as false
|
||||
EntryDateRole // [bool] Date of the CalendarEntry
|
||||
};
|
||||
|
||||
explicit CalendarSingleActivityDelegate(QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1281,6 +1281,21 @@ newQFormLayout
|
||||
}
|
||||
|
||||
|
||||
extern QLayout*
|
||||
centerWidgetInLayout
|
||||
(QWidget *widget, bool margins)
|
||||
{
|
||||
QHBoxLayout *centerLayout = new QHBoxLayout();
|
||||
if (! margins) {
|
||||
centerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
centerLayout->addStretch(1);
|
||||
centerLayout->addWidget(widget, 3);
|
||||
centerLayout->addStretch(1);
|
||||
return centerLayout;
|
||||
}
|
||||
|
||||
|
||||
extern QLayout*
|
||||
centerLayout
|
||||
(QLayout *layout, bool margins)
|
||||
|
||||
@@ -43,6 +43,7 @@ extern QFont baseFont;
|
||||
// layout and widget styling
|
||||
extern void basicTreeWidgetStyle(QTreeWidget *tree, bool editable = true);
|
||||
extern QFormLayout *newQFormLayout(QWidget *parent = nullptr);
|
||||
extern QLayout *centerWidgetInLayout(QWidget *widget, bool margins = true);
|
||||
extern QLayout *centerLayout(QLayout *layout, bool margins = true);
|
||||
extern QWidget *centerLayoutInWidget(QLayout *layout, bool margins = true);
|
||||
|
||||
|
||||
50
src/Gui/Qt5Compatibility.h
Normal file
50
src/Gui/Qt5Compatibility.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Joachim Kohlhammer (joachim.kohlhammer@gmx.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef QT5COMPATIBILITY__H
|
||||
#define QT5COMPATIBILITY__H
|
||||
|
||||
#include <QTreeWidget>
|
||||
|
||||
|
||||
// Compatibility helper for Qt5
|
||||
// exposes methods that turned public in Qt6 from protected in Qt5
|
||||
#if QT_VERSION < 0x060000
|
||||
class TreeWidget6 : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TreeWidget6(QWidget *parent = nullptr): QTreeWidget(parent) {
|
||||
}
|
||||
|
||||
QModelIndex indexFromItem(const QTreeWidgetItem *item, int column = 0) const {
|
||||
return QTreeWidget::indexFromItem(item, column);
|
||||
}
|
||||
|
||||
QTreeWidgetItem* itemFromIndex(const QModelIndex &index) const {
|
||||
return QTreeWidget::itemFromIndex(index);
|
||||
}
|
||||
|
||||
};
|
||||
#else
|
||||
typedef QTreeWidget TreeWidget6;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -652,7 +652,7 @@ HEADERS += Gui/AboutDialog.h Gui/AddIntervalDialog.h Gui/AnalysisSidebar.h Gui/C
|
||||
Gui/PerspectiveDialog.h Gui/SplashScreen.h Gui/StyledItemDelegates.h Gui/MetadataDialog.h Gui/ActionButtonBox.h \
|
||||
Gui/MetricOverrideDialog.h Gui/RepeatScheduleWizard.h \
|
||||
Gui/Calendar.h Gui/CalendarData.h Gui/CalendarItemDelegates.h \
|
||||
Gui/IconManager.h
|
||||
Gui/IconManager.h Gui/Qt5Compatibility.h
|
||||
|
||||
# metrics and models
|
||||
HEADERS += Metrics/Banister.h Metrics/CPSolver.h Metrics/Estimator.h Metrics/ExtendedCriticalPower.h Metrics/HrZones.h Metrics/PaceZones.h \
|
||||
|
||||
Reference in New Issue
Block a user