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