Commit f9c0c21b authored by Claudio Cambra's avatar Claudio Cambra
Browse files

Stop the madness with infinitecalendarviewmodel, let views manage their own incidence models

For the negligible performance gains that letting the InfiniteCalendarViewModel manage incidence models provides, and the immense complexity it adds, it is better to use something simple and more maintainable. This MR makes the views manage their own incidence models
parent 9c4c427e
Pipeline #192011 passed with stage
in 5 minutes and 10 seconds
......@@ -17,7 +17,7 @@ Item {
id: root
property var openOccurrence
property var model
property var filter: ({})
property int daysToShow: daysPerRow * 6
property int daysPerRow: 7
......@@ -235,7 +235,15 @@ Item {
//Weeks
Repeater {
model: root.model
model: Kalendar.MultiDayIncidenceModel {
periodLength: 7
model: Kalendar.IncidenceOccurrenceModel {
start: root.startDate
length: root.daysToShow
calendar: Kalendar.CalendarManager.calendar
filter: root.filter
}
}
//One row => one week
Item {
width: parent.width
......
......@@ -16,7 +16,7 @@ Kirigami.Page {
id: root
property var openOccurrence: ({})
property var model
property var filter: ({})
property date selectedDate: new Date()
property date startDate: DateUtils.getFirstDayOfMonth(selectedDate)
......@@ -33,6 +33,7 @@ Kirigami.Page {
readonly property bool isDark: KalendarUiUtils.darkMode
property bool dragDropEnabled: true
property int periodLength: 15
property real scrollbarWidth: 0
readonly property real dayWidth: ((root.width - hourLabelWidth - leftPadding - scrollbarWidth) / daysToShow) - gridLineWidth
readonly property real incidenceSpacing: Kirigami.Units.smallSpacing / 2
......@@ -139,16 +140,51 @@ Kirigami.Page {
focus: true
interactive: Kirigami.Settings.tabletMode
pathItemCount: 3
path: Path {
startX: - pathView.width * pathView.count / 2 + pathView.width / 2
startX: - pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
startY: pathView.height / 2
PathLine {
x: pathView.width * pathView.count / 2 + pathView.width / 2
x: pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
y: pathView.height / 2
}
}
model: root.model
Component {
id: weekModel
Kalendar.InfiniteCalendarViewModel {
scale: Kalendar.InfiniteCalendarViewModel.WeekScale
}
}
Component {
id: threeDayModel
Kalendar.InfiniteCalendarViewModel {
scale: Kalendar.InfiniteCalendarViewModel.ThreeDayScale
}
}
Component {
id: dayModel
Kalendar.InfiniteCalendarViewModel {
scale: Kalendar.InfiniteCalendarViewModel.DayScale
}
}
Loader {
id: modelLoader
sourceComponent: switch(root.daysToShow) {
case 1:
return dayModel;
case 3:
return threeDayModel;
case 7:
default:
return weekModel;
}
}
model: modelLoader.item
property real scrollPosition
onMovementStarted: {
......@@ -160,10 +196,8 @@ Kirigami.Page {
}
property date dateToUse
property int startIndex: root.model.rowCount() / 2;
Component.onCompleted: {
currentIndex = startIndex;
}
property int startIndex: count / 2;
currentIndex: startIndex
onCurrentIndexChanged: if(currentItem) {
root.startDate = currentItem.startDate;
root.month = currentItem.month;
......@@ -226,15 +260,7 @@ Kirigami.Page {
Repeater {
id: dayHeadings
model: switch(root.daysToShow) {
case 1:
return dayViewModel.rowCount();
case 3:
return threeDayViewModel.rowCount();
case 7:
default:
return weekViewModel.rowCount();
}
model: root.daysToShow
delegate: Rectangle {
width: root.dayWidth
implicitHeight: dayHeading.implicitHeight
......@@ -394,31 +420,24 @@ Kirigami.Page {
anchors.fill: parent
anchors.leftMargin: root.hourLabelWidth
asynchronous: !viewLoader.isCurrentItem
active: switch(root.daysToShow) {
case 1:
return dayViewDayGridViewModel.incidenceCount > 0;
case 3:
return threeDayViewDayGridViewModel.incidenceCount > 0;
case 7:
default:
return weekViewDayGridViewModel.incidenceCount > 0;
}
sourceComponent: Item {
id: allDayViewItem
implicitHeight: allDayHeader.actualHeight
clip: true
Repeater {
// TODO: Clean this up
model: switch(root.daysToShow) {
case 1:
return dayViewDayGridViewModel;
case 3:
return threeDayViewDayGridViewModel;
case 7:
default:
return weekViewDayGridViewModel;
} // from root.model
model: Kalendar.MultiDayIncidenceModel {
periodLength: root.daysToShow
filters: Kalendar.MultiDayIncidenceModel.AllDayOnly | Kalendar.MultiDayIncidenceModel.MultiDayOnly
model: Kalendar.IncidenceOccurrenceModel {
start: viewLoader.startDate
length: root.daysToShow
calendar: Kalendar.CalendarManager.calendar
filter: root.filter
}
}
Layout.topMargin: Kirigami.Units.largeSpacing
//One row => one week
Item {
......@@ -592,17 +611,8 @@ Kirigami.Page {
z: -2
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
readonly property int periodLength: switch(root.daysToShow) {
case 1:
return dayViewModel.periodLength;
case 3:
return threeDayViewModel.periodLength;
case 7:
default:
return weekViewModel.periodLength;
}
readonly property real periodsPerHour: 60 / periodLength
readonly property real daySections: (60 * 24) / periodLength
readonly property real periodsPerHour: 60 / root.periodLength
readonly property real daySections: (60 * 24) / root.periodLength
readonly property real dayHeight: (daySections * root.periodHeight) + (root.gridLineWidth * 23)
readonly property real hourHeight: periodsPerHour * root.periodHeight
readonly property real minuteHeight: hourHeight / 60
......@@ -758,15 +768,16 @@ Kirigami.Page {
Repeater {
id: dayColumnRepeater
model: switch(root.daysToShow) {
case 1:
return dayViewModel;
case 3:
return threeDayViewModel;
case 7:
default:
return weekViewModel;
} // From root.model
model: Kalendar.HourlyIncidenceModel {
periodLength: root.periodLength
filters: Kalendar.MultiDayIncidenceModel.AllDayOnly | Kalendar.MultiDayIncidenceModel.MultiDayOnly
model: Kalendar.IncidenceOccurrenceModel {
start: viewLoader.startDate
length: root.daysToShow
calendar: Kalendar.CalendarManager.calendar
filter: root.filter
}
}
delegate: Item {
id: dayColumn
......
......@@ -15,7 +15,7 @@ Kirigami.Page {
id: monthPage
property var openOccurrence
property var model
property var filter: ({})
property date startDate
property date currentDate
property date firstDayOfMonth
......@@ -96,16 +96,19 @@ Kirigami.Page {
focus: true
interactive: Kirigami.Settings.tabletMode
pathItemCount: 3
path: Path {
startX: - pathView.width * pathView.count / 2 + pathView.width / 2
startX: - pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
startY: pathView.height / 2
PathLine {
x: pathView.width * pathView.count / 2 + pathView.width / 2
x: pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
y: pathView.height / 2
}
}
model: monthPage.model
model: Kalendar.InfiniteCalendarViewModel {
scale: Kalendar.InfiniteCalendarViewModel.MonthScale
}
property int startIndex
Component.onCompleted: {
......@@ -145,9 +148,9 @@ Kirigami.Page {
objectName: "monthView"
width: pathView.width
height: pathView.height
model: monthViewModel // from monthPage model
isCurrentView: viewLoader.isCurrentItem
dragDropEnabled: monthPage.dragDropEnabled
filter: root.filter
startDate: viewLoader.startDate
currentDate: monthPage.currentDate
......
......@@ -51,7 +51,7 @@ Kirigami.Page {
}
property var openOccurrence
property var model
property var filter: ({})
property date selectedDate: new Date()
property date startDate: DateUtils.getFirstDayOfMonth(selectedDate)
property int day: selectedDate.getDate()
......@@ -160,16 +160,19 @@ Kirigami.Page {
focus: true
interactive: Kirigami.Settings.tabletMode
pathItemCount: 3
path: Path {
startX: - pathView.width * pathView.count / 2 + pathView.width / 2
startX: - pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
startY: pathView.height / 2
PathLine {
x: pathView.width * pathView.count / 2 + pathView.width / 2
x: pathView.width * pathView.pathItemCount / 2 + pathView.width / 2
y: pathView.height / 2
}
}
model: root.model
model: Kalendar.InfiniteCalendarViewModel {
scale: Kalendar.InfiniteCalendarViewModel.MonthScale
}
property date dateToUse
property int startIndex
......@@ -195,6 +198,7 @@ Kirigami.Page {
property date startDate: model.startDate
property date firstDayOfMonth: model.firstDay
property int daysInMonth: new Date(firstDayOfMonth.getFullYear(), firstDayOfMonth.getMonth(), 0).getDate()
property int month: model.selectedMonth - 1 // Convert QDateTime month to JS month
property int year: model.selectedYear
......@@ -231,7 +235,15 @@ Kirigami.Page {
if(root.initialMonth) root.moveToSelected();
}
model: scheduleViewModel
model: Kalendar.MultiDayIncidenceModel {
periodLength: 1
model: Kalendar.IncidenceOccurrenceModel {
start: viewLoader.firstDayOfMonth
length: viewLoader.daysInMonth
calendar: Kalendar.CalendarManager.calendar
filter: root.filter
}
}
delegate: DayMouseArea {
id: dayMouseArea
......
......@@ -163,35 +163,30 @@ Kirigami.ApplicationWindow {
target: KalendarApplication
function onOpenMonthView() {
if(pageStack.currentItem.objectName !== "monthView" || root.ignoreCurrentPage) {
monthScaleModelLoader.active = true;
KalendarUiUtils.switchView(monthViewComponent);
}
}
function onOpenWeekView() {
if(pageStack.currentItem.objectName !== "weekView" || root.ignoreCurrentPage) {
weekScaleModelLoader.active = true;
KalendarUiUtils.switchView(hourlyViewComponent);
}
}
function onOpenThreeDayView() {
if(pageStack.currentItem.objectName !== "threeDayView" || root.ignoreCurrentPage) {
threeDayScaleModelLoader.active = true;
KalendarUiUtils.switchView(hourlyViewComponent, { daysToShow: 3 });
}
}
function onOpenDayView() {
if(pageStack.currentItem.objectName !== "dayView" || root.ignoreCurrentPage) {
dayScaleModelLoader.active = true;
KalendarUiUtils.switchView(hourlyViewComponent, { daysToShow: 1 });
}
}
function onOpenScheduleView() {
if(pageStack.currentItem.objectName !== "scheduleView" || root.ignoreCurrentPage) {
monthScaleModelLoader.active = true;
KalendarUiUtils.switchView(scheduleViewComponent);
}
}
......@@ -959,51 +954,6 @@ Kirigami.ApplicationWindow {
}
}
property alias monthScaleModelLoaderItem: monthScaleModelLoader
Loader {
id: monthScaleModelLoader
active: Config.lastOpenedView === Config.MonthView || Config.lastOpenedView === Config.ScheduleView
sourceComponent: InfiniteCalendarViewModel {
scale: InfiniteCalendarViewModel.MonthScale
calendar: CalendarManager.calendar
filter: root.filter
}
}
property alias weekScaleModelLoaderItem: weekScaleModelLoader
Loader {
id: weekScaleModelLoader
active: Config.lastOpenedView === Config.WeekView
sourceComponent: InfiniteCalendarViewModel {
maxLiveModels: 20
scale: InfiniteCalendarViewModel.WeekScale
calendar: CalendarManager.calendar
filter: root.filter
}
}
property alias threeDayScaleModelLoaderItem: threeDayScaleModelLoader
Loader {
id: threeDayScaleModelLoader
active: Config.lastOpenedView === Config.ThreeDayView
sourceComponent: InfiniteCalendarViewModel {
scale: InfiniteCalendarViewModel.ThreeDayScale
calendar: CalendarManager.calendar
filter: root.filter
}
}
property alias dayScaleModelLoaderItem: dayScaleModelLoader
Loader {
id: dayScaleModelLoader
active: Config.lastOpenedView === Config.DayView
sourceComponent: InfiniteCalendarViewModel {
scale: InfiniteCalendarViewModel.DayScale
calendar: CalendarManager.calendar
filter: root.filter
}
}
Component {
id: monthViewComponent
......@@ -1017,7 +967,7 @@ Kirigami.ApplicationWindow {
}
currentDate: root.currentDate
openOccurrence: root.openOccurrence
model: monthScaleModelLoader.item
filter: root.filter
onMonthChanged: if(month !== root.selectedDate.getMonth() && !initialMonth) root.selectedDate = new Date (year, month, 1)
onYearChanged: if(year !== root.selectedDate.getFullYear() && !initialMonth) root.selectedDate = new Date (year, month, 1)
......@@ -1039,7 +989,7 @@ Kirigami.ApplicationWindow {
}
selectedDate: root.selectedDate
openOccurrence: root.openOccurrence
model: monthScaleModelLoader.item
filter: root.filter
onDayChanged: if(day !== root.selectedDate.getDate() && !initialMonth) root.selectedDate = new Date (year, month, day)
onMonthChanged: if(month !== root.selectedDate.getMonth() && !initialMonth) root.selectedDate = new Date (year, month, day)
......@@ -1101,16 +1051,7 @@ Kirigami.ApplicationWindow {
selectedDate: root.selectedDate
currentDate: root.currentDate
openOccurrence: root.openOccurrence
model: switch(daysToShow) {
case 1:
return dayScaleModelLoader.item;
case 3:
return threeDayScaleModelLoader.item;
case 7:
default:
return weekScaleModelLoader.item;
}
onModelChanged: setToDate(root.selectedDate, true)
filter: root.filter
onDayChanged: if(day !== root.selectedDate.getDate() && !initialWeek) root.selectedDate = new Date (year, month, day)
onMonthChanged: if(month !== root.selectedDate.getMonth() && !initialWeek) root.selectedDate = new Date (year, month, day)
......
......@@ -13,18 +13,6 @@ InfiniteCalendarViewModel::InfiniteCalendarViewModel(QObject *parent)
: QAbstractListModel(parent)
{
setup();
ModelMetaData monthModel = {QVector<QDate>(), 42, TypeMonth, &m_monthViewModels, {}, &m_liveMonthViewModelKeys};
ModelMetaData scheduleModel = {QVector<QDate>(), 0, TypeSchedule, &m_scheduleViewModels, {}, &m_liveScheduleViewModelKeys};
ModelMetaData weekModel = {QVector<QDate>(), 7, TypeWeek, {}, &m_weekViewModels, &m_liveWeekViewModelKeys};
ModelMetaData weekMultiDayModel = {QVector<QDate>(), 7, TypeWeekMultiDay, &m_weekViewMultiDayModels, {}, &m_liveWeekViewMultiDayModelKeys};
ModelMetaData threeDayModel = {QVector<QDate>(), 7, TypeThreeDay, {}, &m_threeDayViewModels, &m_liveThreeDayViewModelKeys};
ModelMetaData threeDayMultiDayModel = {QVector<QDate>(), 7, TypeThreeDayMultiDay, &m_threeDayViewMultiDayModels, {}, &m_liveThreeDayViewMultiDayModelKeys};
ModelMetaData dayModel = {QVector<QDate>(), 7, TypeDay, {}, &m_dayViewModels, &m_liveDayViewModelKeys};
ModelMetaData dayMultiDayModel = {QVector<QDate>(), 7, TypeDayMultiDay, &m_dayViewMultiDayModels, {}, &m_liveDayViewMultiDayModelKeys};
m_models =
QVector<ModelMetaData>{monthModel, scheduleModel, weekModel, weekMultiDayModel, threeDayModel, threeDayMultiDayModel, dayModel, dayMultiDayModel};
}
void InfiniteCalendarViewModel::setup()
......@@ -96,81 +84,6 @@ QVariant InfiniteCalendarViewModel::data(const QModelIndex &idx, int role) const
const QDate startDate = m_startDates[idx.row()];
auto generateMultiDayIncidenceModel = [&](QDate start, int length, int periodLength) {
auto model = new MultiDayIncidenceModel;
model->setPeriodLength(periodLength);
model->setModel(new IncidenceOccurrenceModel);
model->model()->setStart(start);
model->model()->setLength(length);
model->model()->setFilter(mFilter);
model->model()->setCalendar(m_calendar);
return model;
};
auto generateHourlyIncidenceModel = [&](QDate start, int length, int periodLength) {
auto model = new HourlyIncidenceModel;
model->setPeriodLength(periodLength);
model->setFilters(HourlyIncidenceModel::NoAllDay | HourlyIncidenceModel::NoMultiDay);
model->setModel(new IncidenceOccurrenceModel);
model->model()->setStart(start);
model->model()->setLength(length);
model->model()->setFilter(mFilter);
model->model()->setCalendar(m_calendar);
return model;
};
auto cleanUpModels = [&, this]() {
int numLiveModels = m_liveMonthViewModelKeys.length() + m_liveScheduleViewModelKeys.length() + m_liveWeekViewModelKeys.length()
+ m_liveWeekViewMultiDayModelKeys.length() + m_liveThreeDayViewModelKeys.length() + m_liveThreeDayViewMultiDayModelKeys.length()
+ m_liveDayViewModelKeys.length() + m_liveDayViewMultiDayModelKeys.length(); // Find a more elegant way to do this
while (numLiveModels > m_maxLiveModels) {
for (int i = 0; i < m_models.length(); i++) {
if (m_models[i].liveKeysQueue->length() > m_maxLiveModels) {
while (m_models[i].liveKeysQueue->length() > m_maxLiveModels) {
auto firstKey = m_models[i].liveKeysQueue->dequeue();
if ((m_models[i].modelType == TypeWeek || m_models[i].modelType == TypeThreeDay || m_models[i].modelType == TypeDay)
&& m_models[i].hourlyModels->contains(firstKey)) {
delete m_models[i].hourlyModels->value(firstKey);
m_models[i].hourlyModels->remove(firstKey);
} else if (m_models[i].multiDayModels->contains(firstKey)) {
delete m_models[i].multiDayModels->value(firstKey);
m_models[i].multiDayModels->remove(firstKey);
}
}
} else if (m_models[i].liveKeysQueue->length() > 0) {
auto firstKey = m_models[i].liveKeysQueue->dequeue();
if ((m_models[i].modelType == TypeWeek || m_models[i].modelType == TypeThreeDay || m_models[i].modelType == TypeDay)
&& m_models[i].hourlyModels->contains(firstKey)) {
delete m_models[i].hourlyModels->value(firstKey);
m_models[i].hourlyModels->remove(firstKey);
} else if (m_models[i].multiDayModels->contains(firstKey)) {
delete m_models[i].multiDayModels->value(firstKey);
m_models[i].multiDayModels->remove(firstKey);
}
}
numLiveModels = m_liveMonthViewModelKeys.length() + m_liveScheduleViewModelKeys.length() + m_liveWeekViewModelKeys.length()
+ m_liveWeekViewMultiDayModelKeys.length() + m_liveThreeDayViewModelKeys.length() + m_liveThreeDayViewMultiDayModelKeys.length()
+ m_liveDayViewModelKeys.length() + m_liveDayViewMultiDayModelKeys.length();
}
}
};
auto requeue = [&](QQueue<QDate> &liveKeysQueue, const QDate &key) {
for (int i = 0; i < liveKeysQueue.length(); i++) {
if (liveKeysQueue[i] == key) {
liveKeysQueue.move(i, liveKeysQueue.length() - 1);
break;
}
}
};
if (m_scale == MonthScale && role != StartDateRole) {
const QDate firstDay = m_firstDayOfMonthDates[idx.row()];
......@@ -181,43 +94,6 @@ QVariant InfiniteCalendarViewModel::data(const QModelIndex &idx, int role) const
return firstDay.month();
case SelectedYearRole:
return firstDay.year();
case MonthViewModelRole: {
m_lastAccessedModelType = TypeMonth;
if (m_datesToAdd > 5 && idx.row() < 2 && m_monthViewModels.count() < 3) {
return {}; // HACK: Prevent creating the models for the default index 1 date
// Unfortunately this gets called by the pathviews no matter what the currentIndex
// value is set to.
}
if (!m_liveMonthViewModelKeys.contains(startDate)) {
m_monthViewModels[startDate] = generateMultiDayIncidenceModel(startDate, 42, 7);
m_liveMonthViewModelKeys.enqueue(startDate);
cleanUpModels();
} else {
requeue(m_liveMonthViewModelKeys, startDate);
}
return QVariant::fromValue(m_monthViewModels[startDate]);
}
case ScheduleViewModelRole: {
m_lastAccessedModelType = TypeSchedule;
if (m_datesToAdd > 5 && idx.row() < 2 && m_scheduleViewModels.count() < 3) {
return {};
}
if (!m_scheduleViewModels.contains(firstDay)) {
m_scheduleViewModels[firstDay] = generateMultiDayIncidenceModel(firstDay, firstDay.daysInMonth(), 1);
m_liveScheduleViewModelKeys.enqueue(startDate);
cleanUpModels();
} else {
requeue(m_liveScheduleViewModelKeys, startDate);
}
return QVariant::fromValue(m_scheduleViewModels[firstDay]);
}
default:
qCWarning(KALENDAR_LOG) << "Unknown role for startdate:" << QMetaEnum::fromType<Roles>().valueToKey(role);
return {};
......@@ -231,117 +107,6 @@ QVariant InfiniteCalendarViewModel::data(const QModelIndex &idx, int role) const
return startDate.month();
case SelectedYearRole:
return startDate.year();
case WeekViewModelRole: {
m_lastAccessedModelType = TypeWeek;
if (m_datesToAdd > 5 && idx.row() < 2 && m_weekViewModels.count() < 3) {
return {};
}
if (!m_weekViewModels.contains(startDate)) {
m_weekViewModels[startDate] = generateHourlyIncidenceModel(startDate, 7, 15);
m_liveWeekViewModelKeys.enqueue(<