From 66e12d50192a4927132facc384049d7463dc2f82 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Mon, 13 Jan 2025 18:32:02 +0100 Subject: [PATCH] Add TimelineMonthRow and TimelineMonthModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are a lot like TimelineDayRow and TimelineDayModel, and are supposed to go in the timeline’s header too, but instead of showing the day number, they show the month name. I still need to know the with of each day, because the month must be as wide as all shown days, that may not be all the days in that month, in the first and last months, at least. Usually i will show two or three months, so i thing is is fair to keep a list of all the months and their days, rather than compute them each time the name or the day count, two times per month, are requested. --- src/CMakeLists.txt | 2 + src/ReservationsPage.qml | 17 +++-- src/TimelineMonthRow.qml | 38 ++++++++++ src/timelinemonthmodel.cpp | 141 +++++++++++++++++++++++++++++++++++++ src/timelinemonthmodel.h | 60 ++++++++++++++++ 5 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 src/TimelineMonthRow.qml create mode 100644 src/timelinemonthmodel.cpp create mode 100644 src/timelinemonthmodel.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f69465f..e308071 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ qt_add_qml_module(${PROJECT_NAME} timelinedaymodel.cpp timelinedaymodel.h timelinelistmodel.cpp timelinelistmodel.h timelinemodel.cpp timelinemodel.h + timelinemonthmodel.cpp timelinemonthmodel.h timelineview.cpp timelineview.h QML_FILES ErrorNotification.qml @@ -25,6 +26,7 @@ qt_add_qml_module(${PROJECT_NAME} ReservationsPage.qml SelectableLabel.qml TimelineDayRow.qml + TimelineMonthRow.qml ) set_target_properties(${PROJECT_NAME} PROPERTIES diff --git a/src/ReservationsPage.qml b/src/ReservationsPage.qml index c2903a0..771c471 100644 --- a/src/ReservationsPage.qml +++ b/src/ReservationsPage.qml @@ -40,6 +40,7 @@ Page { text: name } header: Pane { + height: timelineList.headerItem.height z: 2 Label { @@ -89,10 +90,18 @@ Page { rightPadding: 0 z: 2 - TimelineDayRow { - dayWidth: page.dayWidth - fromDate: page.fromDate - toDate: page.toDate + Column { + TimelineMonthRow { + dayWidth: page.dayWidth + fromDate: page.fromDate + toDate: page.toDate + } + + TimelineDayRow { + dayWidth: page.dayWidth + fromDate: page.fromDate + toDate: page.toDate + } } } } diff --git a/src/TimelineMonthRow.qml b/src/TimelineMonthRow.qml new file mode 100644 index 0000000..9310529 --- /dev/null +++ b/src/TimelineMonthRow.qml @@ -0,0 +1,38 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls + +Control { + id: control + + property real dayWidth: 24 + property alias fromDate: model.fromDate + property alias toDate: model.toDate + + contentItem: Row { + Repeater { + model: model + + delegate: Label { + required property int dayCount + required property string display + + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + text: display + width: control.dayWidth * dayCount + + background: Rectangle { + border.color: "red" + border.width: 1 + color: "lightgreen" + } + } + } + } + + TimelineMonthModel { + id: model + + } +} diff --git a/src/timelinemonthmodel.cpp b/src/timelinemonthmodel.cpp new file mode 100644 index 0000000..0e1e859 --- /dev/null +++ b/src/timelinemonthmodel.cpp @@ -0,0 +1,141 @@ +#include "timelinemonthmodel.h" +#include +#include + +using namespace Qt::Literals::StringLiterals; + +namespace { + +void advanceMonth(QCalendar::YearMonthDay &ymd) +{ + if (ymd.month == 12) { + ymd.month = 1; + ymd.year++; + } else { + ymd.month++; + } +} + +} // namespace + +TimelineMonthModel::TimelineMonthModel(QObject *parent) + : QAbstractListModel(parent) + , m_fromDate() + , m_toDate() + , m_locale() + , m_months() +{} + +int TimelineMonthModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + return m_months.length(); +} + +QVariant TimelineMonthModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= rowCount()) { + return {}; + } + + Month month = m_months.at(index.row()); + switch (role) { + case Qt::DisplayRole: + return m_locale.standaloneMonthName(month.number); + case DayCount: + return month.days; + } + + return {}; +} + +QHash TimelineMonthModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles[DayCount] = "dayCount"_ba; + return roles; +} + +QDate TimelineMonthModel::fromDate() const +{ + return m_fromDate; +} + +void TimelineMonthModel::setFromDate(QDate date) +{ + if (date == m_fromDate) { + return; + } + m_fromDate = date; + emit fromDateChanged(m_fromDate); + updateMonths(); +} + +QDate TimelineMonthModel::toDate() const +{ + return m_toDate; +} + +void TimelineMonthModel::setToDate(QDate date) +{ + if (date == m_toDate) { + return; + } + m_toDate = date; + emit toDateChanged(m_toDate); + updateMonths(); +} + +QLocale TimelineMonthModel::locale() const +{ + return m_locale; +} + +void TimelineMonthModel::setLocale(const QLocale &locale) +{ + if (locale == m_locale) { + return; + } + m_locale = locale; + emit localeChanged(m_locale); + if (m_months.isEmpty()) { + return; + } + emit dataChanged(index(0), index(rowCount() - 1), {Qt::DisplayRole}); +} + +void TimelineMonthModel::updateMonths() +{ + QCalendar gregorian; + QCalendar::YearMonthDay fromYMD = gregorian.partsFromDate(std::min(m_fromDate, m_toDate)); + QCalendar::YearMonthDay toYMD = gregorian.partsFromDate(std::max(m_fromDate, m_toDate)); + + if (!fromYMD.isValid() || !toYMD.isValid()) { + if (m_months.isEmpty()) { + return; + } + beginResetModel(); + m_months.clear(); + endResetModel(); + return; + } + + beginResetModel(); + m_months.clear(); + if (fromYMD.year == toYMD.year && fromYMD.month == toYMD.month) { + m_months.push_back({fromYMD.month, toYMD.day - fromYMD.day + 1}); + } else { + m_months.push_back( + {fromYMD.month, gregorian.daysInMonth(fromYMD.month, fromYMD.year) - fromYMD.day + 1}); + advanceMonth(fromYMD); + for (; fromYMD.month != toYMD.month || fromYMD.year != toYMD.year; advanceMonth(fromYMD)) { + m_months.push_back({fromYMD.month, gregorian.daysInMonth(fromYMD.month, fromYMD.year)}); + } + m_months.push_back({toYMD.month, toYMD.day}); + } + endResetModel(); +} + +#include "moc_timelinemonthmodel.cpp" diff --git a/src/timelinemonthmodel.h b/src/timelinemonthmodel.h new file mode 100644 index 0000000..431d171 --- /dev/null +++ b/src/timelinemonthmodel.h @@ -0,0 +1,60 @@ +#ifndef TIMELINEMONTHMODEL_H +#define TIMELINEMONTHMODEL_H + +#include +#include +#include +#include + +class TimelineMonthModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QDate fromDate READ fromDate WRITE setFromDate NOTIFY fromDateChanged) + Q_PROPERTY(QDate toDate READ toDate WRITE setToDate NOTIFY toDateChanged) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) + +public: + enum { + DayCount = Qt::UserRole, + }; + + explicit TimelineMonthModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + QDate fromDate() const; + void setFromDate(QDate date); + + QDate toDate() const; + void setToDate(QDate date); + + QLocale locale() const; + void setLocale(const QLocale &locale); + +signals: + void fromDateChanged(QDate date); + void toDateChanged(QDate date); + void localeChanged(const QLocale &locale); + +private: + Q_DISABLE_COPY_MOVE(TimelineMonthModel) + + struct Month + { + int number; + int days; + }; + + void updateMonths(); + + QDate m_fromDate; + QDate m_toDate; + QLocale m_locale; + QList m_months; +}; + +#endif // TIMELINEMONTHMODEL_H