diff --git a/views/calendar/main.qml b/views/calendar/main.qml index 8f7c66a6..1008422c 100644 --- a/views/calendar/main.qml +++ b/views/calendar/main.qml @@ -1,280 +1,280 @@ /* * Copyright (C) 2018 Christian Mollekopf, * * 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. */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.0 import org.kube.framework 1.0 as Kube import org.kube.test 1.0 import "qml" ApplicationWindow { id: app height: Screen.desktopAvailableHeight * 0.8 width: Screen.desktopAvailableWidth * 0.8 Component.onCompleted: { var initialState = { accounts: [ { id: "account1", name: "Test Account" }, { id: "account2", name: "Test Account2" }, ], identities: [{ account: "account1", name: "Test Identity", address: "identity@example.org" }], resources: [ { id: "caldavresource", account: "account1", type: "caldav", }, { id: "caldavresource2", account: "account2", type: "caldav", } ], calendars: [{ id: "calendar1", resource: "caldavresource", name: "Test Calendar", color: "#af1a6a", events: [ { resource: "caldavresource", summary: "Short event1!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Short event2!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Short event3!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Test Event1 with a waaaaaaaay to long summary. Why don't you just use the description you fool!", description: "This is test event #1", starts: "2018-04-10T14:03:00", ends: "2018-04-10T16:03:00", }, { resource: "caldavresource", summary: "Test Event2", description: "This is test event #2", starts: "2018-04-11T09:03:00", ends: "2018-04-11T14:03:00", }, { resource: "caldavresource", summary: "Test Event3", description: "This is test event #3", starts: "2018-04-11T10:00:00", ends: "2018-04-11T15:00:00", }, { resource: "caldavresource", summary: "Test Event4", description: "This is test event #4", starts: "2018-04-12T03:00:00", ends: "2018-04-14T22:00:00", }, { resource: "caldavresource", summary: "!!! Test Event5", description: "!!! This is test event #5", starts: "2018-04-22T22:00:00", ends: "2018-04-25T03:00:00", }, // Day-long events { resource: "caldavresource", summary: "Test day-long event1", description: "This is test day-long event #1", starts: "2018-04-10T00:00:00", ends: "2018-04-14T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event2", description: "This is test day-long event #2", starts: "2018-04-11", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event3", description: "This is test day-long event #3", starts: "2018-04-01T00:00:00", ends: "2018-04-13T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event4", description: "This is test day-long event #4", starts: "2018-04-01T00:00:00", ends: "2018-04-25T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "!!! Test day-long event5", description: "!!! This is test day-long event #5", starts: "2018-04-01T00:00:00", ends: "2018-04-05T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "!!! Test day-long event6", description: "!!! This is test day-long event #6", starts: "2018-04-23T00:00:00", ends: "2018-04-25T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event7", description: "This is test day-long event #7", starts: "2018-04-12", allDay: true, }, ], }, { id: "calendar2", resource: "caldavresource", name: "Test Calendar2", color: "#f67400" }, { id: "calendar3", resource: "caldavresource", name: "Test Calendar3 Loooooooooooooooooooooooooooooong Name", color: "#f67400" }, { id: "calendar4", resource: "caldavresource", name: "Test Calendar4", color: "#f67400" }, { id: "calendar5", resource: "caldavresource", name: "Test Calendar5", color: "#f67400" }, { id: "calendar6", resource: "caldavresource", name: "Test Calendar6", color: "#f67400" }, { id: "calendar7", resource: "caldavresource", name: "Test Calendar7", color: "#f67400" }, { id: "calendar8", resource: "caldavresource", name: "Test Calendar8", color: "#f67400" }, { id: "calendar9", resource: "caldavresource", name: "Test Calendar9", color: "#f67400" }, { id: "calendar10", resource: "caldavresource", name: "Test Calendar 10", color: "#f67400" }, { id: "calendar11", resource: "caldavresource", name: "Test Calendar11", color: "#f67400" }, { id: "calendar12", resource: "caldavresource", name: "Test Calendar12", color: "#f67400" }, { id: "calendar13", resource: "caldavresource", name: "Test Calendar13", color: "#f67400" }, { id: "calendar14", resource: "caldavresource", name: "Test Calendar14", color: "#f67400" }, { id: "calendar15", resource: "caldavresource", name: "Test Calendar15", color: "#f67400" }, { id: "calendar16", resource: "caldavresource", name: "Test Calendar16", color: "#f67400" }, { id: "account2calendar", resource: "caldavresource2", name: "Account2Calendar", color: "#f67400" }], } TestStore.setup(initialState) } View { anchors.fill: parent - currentDate: "2018-04-11T13:00:00" + currentDate: "2018-04-11T13:04:03" autoUpdateDate: false } } diff --git a/views/calendar/qml/DateTimeChooser.qml b/views/calendar/qml/DateTimeChooser.qml index 2eaf3db6..170689b9 100644 --- a/views/calendar/qml/DateTimeChooser.qml +++ b/views/calendar/qml/DateTimeChooser.qml @@ -1,129 +1,148 @@ /* * Copyright (C) 2017 Michael Bohlender, * Copyright (C) 2018 Christian Mollekopf, * * 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. */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import org.kube.framework 1.0 as Kube import Qt.labs.calendar 1.0 import "dateutils.js" as DateUtils RowLayout { id: root property bool enableTime: true property var notBefore: new Date(1980, 1, 1, 0, 0, 0) - property var notBeforeRounded: DateUtils.roundToMinutes(notBefore) + property var notBeforeRounded: DateUtils.roundToMinutes(notBefore, delta) property var initialValue: null property int delta: 15 property date dateTime: initialValue ? initialValue : new Date() spacing: Kube.Units.smallSpacing Component.onCompleted: { if (root.initialValue) { root.dateTime = root.initialValue } } + onNotBeforeRoundedChanged: { + timeEdit.setNotBefore(notBeforeRounded) + } + Kube.Button { id: button Layout.preferredWidth: implicitWidth text: selector.selectedDate.toLocaleDateString() onClicked: { popup.open() } Kube.Popup { id: popup x: button.x y: button.y + button.height width: selector.implicitWidth + Kube.Units.largeSpacing * 2 height: selector.implicitHeight + Kube.Units.largeSpacing * 2 modal: true focus: true DateSelector { id: selector anchors.fill: parent //TODO add earliest date, prevent selection before selectedDate: root.dateTime - onSelected: root.dateTime = date + onSelected: { + root.dateTime = date + } backgroundColor: Kube.Colors.backgroundColor textColor: Kube.Colors.textColor invertIcons: false onNext: selectedDate = DateUtils.nextMonth(selectedDate) onPrevious: selectedDate = DateUtils.previousMonth(selectedDate) } } } Kube.ComboBox { + id: timeEdit + visible: enableTime Layout.preferredWidth: Kube.Units.gridUnit * 4 function generateTimes(start, delta) { var d = new Date(2000, 1, 1, start.getHours(), start.getMinutes(), start.getSeconds()) var list = [] while (d.getDate() == 1) { list.push(dateToString(d)) d = DateUtils.addMinutesToDate(d, delta) } return list } + function setNotBefore(notBefore) { + availableTimes = generateTimes(DateUtils.sameDay(notBefore, root.dateTime) ? notBefore : new Date(2000, 1, 1, 0, 0, 0), root.delta) + model = availableTimes + currentIndex = findCurrentIndex(root.dateTime, root.delta) + if (currentIndex >= 0) { + setTimeFromIndex(currentIndex) + } else { + currentIndex = 0 + setTimeFromIndex(currentIndex) + } + } + property var availableTimes: null function dateToString(date) { return date.toLocaleTimeString(Qt.locale(), "hh:mm") } function findCurrentIndex(date, delta) { - return find(dateToString(DateUtils.roundToMinutes(date, delta))) + var s = dateToString(DateUtils.roundToMinutes(date, delta)) + //Find does not reliably work if we just reset the model + return availableTimes.indexOf(s) + // return find(s) } function setTimeFromIndex(index) { var date = Date.fromLocaleTimeString(Qt.locale(), availableTimes[index], "hh:mm") //Intermediate variable is necessary for binding to be updated var newDate = root.dateTime newDate.setHours(date.getHours(), date.getMinutes()) root.dateTime = newDate } Component.onCompleted: { - availableTimes = generateTimes(DateUtils.sameDay(root.notBeforeRounded, root.dateTime) ? root.notBeforeRounded : new Date(2000, 1, 1, 0, 0, 0), root.delta) - currentIndex = findCurrentIndex(root.initialValue, root.delta) - if (currentIndex >= 0) { - setTimeFromIndex(currentIndex) - } + setNotBefore(root.notBeforeRounded) } onActivated: { setTimeFromIndex(index) } model: availableTimes } } diff --git a/views/calendar/tests/tst_eventeditor.qml b/views/calendar/tests/tst_eventeditor.qml index 05b398f7..b2b620c5 100644 --- a/views/calendar/tests/tst_eventeditor.qml +++ b/views/calendar/tests/tst_eventeditor.qml @@ -1,87 +1,134 @@ /* * Copyright 2017 Christian Mollekopf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ import QtQuick 2.7 import QtTest 1.0 import org.kube.test 1.0 import org.kube.framework 1.0 as Kube import "../qml" ViewTestCase { id: testCase name: "EventEditor" Component { id: editorComponent EventEditor { focus: true } } Component { id: controllerComponent Kube.EventController { } } function test_1start() { var editor = createTemporaryObject(editorComponent, testCase, {}) verify(editor) } function test_2loadStartDate() { var start = new Date(2018, 1, 1, 11, 30, 0) var editor = createTemporaryObject(editorComponent, testCase, {editMode: false, start: start}) verify(editor) var startDate = findChild(editor, "startDate"); compare(startDate.dateTime, start) var endDate = findChild(editor, "endDate"); compare(endDate.dateTime, start) } function test_3loadControllerDates() { var start = new Date(2018, 1, 1, 11, 30, 0) var end = new Date(2018, 1, 1, 12, 0, 0) var controller = createTemporaryObject(controllerComponent, testCase, {start: start, end: end, allDay: false}) var editor = createTemporaryObject(editorComponent, testCase, {editMode: true, controller: controller}) verify(editor) var startDate = findChild(editor, "startDate"); compare(startDate.dateTime, start) var endDate = findChild(editor, "endDate"); compare(endDate.dateTime, end) } function test_4roundLoadedDates() { var start = new Date(2018, 1, 1, 11, 33, 0) var startRounded = new Date(2018, 1, 1, 11, 30, 0) var end = new Date(2018, 1, 1, 11, 58, 0) var endRounded = new Date(2018, 1, 1, 12, 0, 0) var controller = createTemporaryObject(controllerComponent, testCase, {start: start, end: end, allDay: false}) var editor = createTemporaryObject(editorComponent, testCase, {editMode: true, controller: controller}) verify(editor) var startDate = findChild(editor, "startDate"); compare(startDate.dateTime, startRounded) var endDate = findChild(editor, "endDate"); compare(endDate.dateTime, endRounded) } + + function test_5endFollowsStartDate() { + var start = new Date(2018, 1, 1, 11, 30, 0) + var editor = createTemporaryObject(editorComponent, testCase, {editMode: false, start: start}) + verify(editor) + var startDate = findChild(editor, "startDate"); + compare(startDate.dateTime, start) + + var endDate = findChild(editor, "endDate"); + compare(endDate.dateTime, start) + + //Follows forward + var newStart = new Date(2018, 1, 1, 12, 30, 0) + endDate.notBefore = newStart + compare(endDate.dateTime, newStart) + + //Doesn't follow back + endDate.notBefore = start + compare(endDate.dateTime, newStart) + } + + Component { + id: dateTimeChooserComponent + DateTimeChooser { + + } + } + + function test_6datePicker() { + var midnight = new Date(2018, 1, 1, 0, 0, 0) + var start = new Date(2018, 1, 1, 11, 33, 0) + var startRounded = new Date(2018, 1, 1, 11, 30, 0) + var end = new Date(2018, 1, 1, 11, 58, 0) + var endRounded = new Date(2018, 1, 1, 12, 0, 0) + + var chooser = createTemporaryObject(dateTimeChooserComponent, testCase, {notBefore: midnight, initialValue: start, enableTime: true}) + compare(chooser.dateTime, startRounded) + + //Follow notBefore + chooser.notBefore = end + compare(chooser.dateTime, endRounded) + + //Don't follow back + chooser.notBefore = start + compare(chooser.dateTime, endRounded) + } + }