From 7b8fb44d9a0339baf045b1de17ebb39f76fbac41 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 16:47:58 +0800 Subject: [PATCH 01/26] Eliminate wrapper SpinBox for expire date field Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 72 +++++++++--------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index 527f48de8402e..09c3855d12ea1 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -734,11 +734,11 @@ Page { // QML dates are essentially JavaScript dates, which makes them very finicky and unreliable. // Instead, we exclusively deal with msecs from epoch time to make things less painful when editing. // We only use the QML Date when showing the nice string to the user. - SpinBox { - id: expireDateSpinBox + NCInputTextField { + id: expireDateField function updateText() { - expireDateSpinBoxTextField.text = textFromValue(value, locale); + text = textFromValue(value, locale); } // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch @@ -765,6 +765,10 @@ Page { return Math.floor(minDateUTC / dayInMSecs) // Start of day at 00:00:0000 UTC } + readonly property var from: minimumExpireDateReduced + readonly property var to: maximumExpireDateReduced + property var value: expireDateReduced + // Taken from Kalendar 22.08 // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js function parseDateString(dateString) { @@ -820,63 +824,39 @@ Page { return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum)); } - Layout.fillWidth: true - height: visible ? implicitHeight : 0 - - // We want all the internal benefits of the spinbox but don't actually want the - // buttons, so set an empty item as a dummy - up.indicator: Item {} - down.indicator: Item {} - - padding: 0 - background: null - contentItem: NCInputTextField { - id: expireDateSpinBoxTextField - - validInput: { - const value = expireDateSpinBox.valueFromText(text); - return value >= expireDateSpinBox.from && value <= expireDateSpinBox.to; - } - - text: expireDateSpinBox.textFromValue(expireDateSpinBox.value, expireDateSpinBox.locale) - readOnly: !expireDateSpinBox.editable - validator: expireDateSpinBox.validator - inputMethodHints: Qt.ImhFormattedNumbersOnly - onAccepted: { - expireDateSpinBox.value = expireDateSpinBox.valueFromText(text, expireDateSpinBox.locale); - expireDateSpinBox.valueModified(); - } - } - - value: expireDateReduced - from: minimumExpireDateReduced - to: maximumExpireDateReduced - - textFromValue: (value, locale) => { + function textFromValue(value, locale) { const dateFromValue = new Date(value * dayInMSecs); return dateFromValue.toLocaleDateString(Qt.locale(), Locale.NarrowFormat); } - valueFromText: (text, locale) => { + + function valueFromText(text, locale) { const dateFromText = parseDateString(text); return Math.floor(dateFromText.getTime() / dayInMSecs); } - editable: true - inputMethodHints: Qt.ImhDate | Qt.ImhFormattedNumbersOnly + Layout.fillWidth: true + height: visible ? implicitHeight : 0 - enabled: root.expireDateEnabled && - !root.waitingForExpireDateChange && - !root.waitingForExpireDateEnabledChange + validInput: { + const value = valueFromText(text); + return value >= from && value <= to; + } - onValueModified: { - if (!enabled || !activeFocus) { - return; - } + text: textFromValue(expireDateSpinBox.value, expireDateSpinBox.locale) + inputMethodHints: expireDateSpinBox.inputMethodHints + onAccepted: { + const value = valueFromText(text, expireDateSpinBox.locale); root.setExpireDate(value * dayInMSecs); root.waitingForExpireDateChange = true; } + inputMethodHints: Qt.ImhDate + + enabled: root.expireDateEnabled && + !root.waitingForExpireDateChange && + !root.waitingForExpireDateEnabledChange + NCBusyIndicator { anchors.fill: parent visible: root.waitingForExpireDateEnabledChange || From 15c7e21ac8565310ae67a36fd2372d841d3c0867 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 18:19:39 +0800 Subject: [PATCH 02/26] Extract date field into a separate file Signed-off-by: Claudio Cambra --- resources.qrc | 1 + src/gui/filedetails/NCInputDateField.qml | 120 ++++++++++++++++++++++ src/gui/filedetails/ShareDetailsPage.qml | 125 ++--------------------- 3 files changed, 132 insertions(+), 114 deletions(-) create mode 100644 src/gui/filedetails/NCInputDateField.qml diff --git a/resources.qrc b/resources.qrc index fdd4bcf570e63..7a53e12e65106 100644 --- a/resources.qrc +++ b/resources.qrc @@ -59,5 +59,6 @@ src/gui/ResolveConflictsDialog.qml src/gui/ConflictDelegate.qml src/gui/ConflictItemFileInfo.qml + src/gui/filedetails/NCInputDateField.qml diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml new file mode 100644 index 0000000000000..bba717e83340c --- /dev/null +++ b/src/gui/filedetails/NCInputDateField.qml @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * 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. + */ + +// QML dates are essentially JavaScript dates, which makes them very finicky and unreliable. +// Instead, we exclusively deal with msecs from epoch time to make things less painful when editing. +// We only use the QML Date when showing the nice string to the user. +NCInputTextField { + id: root + + function updateText() { + text = _textFromValue(_value, root.locale); + } + + // Taken from Kalendar 22.08 + // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js + function _parseDateString(dateString) { + function defaultParse() { + const defaultParsedDate = Date.fromLocaleDateString(root.locale, dateString, Locale.NarrowFormat); + // JS always generates date in system locale, eliminate timezone difference to UTC + const msecsSinceEpoch = defaultParsedDate.getTime() - (defaultParsedDate.getTimezoneOffset() * 60 * 1000); + return new Date(msecsSinceEpoch); + } + + const dateStringDelimiterMatches = dateString.match(/\D/); + if(dateStringDelimiterMatches.length === 0) { + // Let the date method figure out this weirdness + return defaultParse(); + } + + const dateStringDelimiter = dateStringDelimiterMatches[0]; + + const localisedDateFormatSplit = root.locale.dateFormat(Locale.NarrowFormat).split(dateStringDelimiter); + const localisedDateDayPosition = localisedDateFormatSplit.findIndex((x) => /d/gi.test(x)); + const localisedDateMonthPosition = localisedDateFormatSplit.findIndex((x) => /m/gi.test(x)); + const localisedDateYearPosition = localisedDateFormatSplit.findIndex((x) => /y/gi.test(x)); + + let splitDateString = dateString.split(dateStringDelimiter); + let userProvidedYear = splitDateString[localisedDateYearPosition] + + const dateNow = new Date(); + const stringifiedCurrentYear = dateNow.getFullYear().toString(); + + // If we have any input weirdness, or if we have a fully-written year + // (e.g. 2022 instead of 22) then use default parse + if(splitDateString.length === 0 || + splitDateString.length > 3 || + userProvidedYear.length >= stringifiedCurrentYear.length) { + + return defaultParse(); + } + + let fullyWrittenYear = userProvidedYear.split(""); + const digitsToAdd = stringifiedCurrentYear.length - fullyWrittenYear.length; + for(let i = 0; i < digitsToAdd; i++) { + fullyWrittenYear.splice(i, 0, stringifiedCurrentYear[i]) + } + fullyWrittenYear = fullyWrittenYear.join(""); + + const fixedYearNum = Number(fullyWrittenYear); + const monthIndexNum = Number(splitDateString[localisedDateMonthPosition]) - 1; + const dayNum = Number(splitDateString[localisedDateDayPosition]); + + console.log(dayNum, monthIndexNum, fixedYearNum); + + // Modification: return date in UTC + return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum)); + } + + function _textFromValue(value, locale) { + const dateFromValue = new Date(value * dayInMSecs); + return dateFromValue.toLocaleDateString(root.locale, Locale.NarrowFormat); + } + + function _valueFromText(text, locale) { + const dateFromText = _parseDateString(text); + return Math.floor(dateFromText.getTime() / dayInMSecs); + } + + property var date: new Date().getTime() * 1000 // QDateTime msecsFromEpoch + property var minimumDate: 0 + property var maximumDate: Number.MAX_SAFE_INTEGER + + // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch + // Instead, we handle everything as days since epoch + readonly property int _dayInMSecs: 24 * 60 * 60 * 1000 + readonly property int _expireDateReduced: Math.floor(root.date / dayInMSecs) + // Reset the model data after binding broken on user interact + onExpireDateReducedChanged: { + value = expireDateReduced; + updateText(); + } + + readonly property int _maximumExpireDateReduced: Math.floor(maximumDate / dayInMSecs) + readonly property int _minimumExpireDateReduced: Math.floor(minimumDate / dayInMSecs) + + readonly property var _from: minimumExpireDateReduced + readonly property var _to: maximumExpireDateReduced + readonly property var _value: expireDateReduced + + validInput: { + const value = valueFromText(text); + return value >= _from && value <= _to; + } + + text: _textFromValue(_value, locale) + inputMethodHints: Qt.ImhDate + + onAccepted: root.date = _valueFromText(text, locale); +} \ No newline at end of file diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index 09c3855d12ea1..380fd114262a8 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -731,127 +731,24 @@ Page { sourceSize.height: scrollContentsColumn.rowIconWidth } - // QML dates are essentially JavaScript dates, which makes them very finicky and unreliable. - // Instead, we exclusively deal with msecs from epoch time to make things less painful when editing. - // We only use the QML Date when showing the nice string to the user. - NCInputTextField { + NCInputDateField { id: expireDateField - function updateText() { - text = textFromValue(value, locale); - } - - // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch - // Instead, we handle everything as days since epoch - readonly property int dayInMSecs: 24 * 60 * 60 * 1000 - readonly property int expireDateReduced: Math.floor(root.expireDate / dayInMSecs) - // Reset the model data after binding broken on user interact - onExpireDateReducedChanged: { - value = expireDateReduced; - updateText(); - } - - // We can't use JS's convenient Infinity or Number.MAX_VALUE as - // JS Number type is 64 bits, whereas QML's int type is only 32 bits - readonly property IntValidator intValidator: IntValidator {} - readonly property int maximumExpireDateReduced: root.expireDateEnforced ? - Math.floor(root.maximumExpireDate / dayInMSecs) : - intValidator.top - readonly property int minimumExpireDateReduced: { - const currentDate = new Date(); - const minDateUTC = new Date(Date.UTC(currentDate.getFullYear(), - currentDate.getMonth(), - currentDate.getDate() + 1)); - return Math.floor(minDateUTC / dayInMSecs) // Start of day at 00:00:0000 UTC - } - - readonly property var from: minimumExpireDateReduced - readonly property var to: maximumExpireDateReduced - property var value: expireDateReduced - - // Taken from Kalendar 22.08 - // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js - function parseDateString(dateString) { - function defaultParse() { - const defaultParsedDate = Date.fromLocaleDateString(Qt.locale(), dateString, Locale.NarrowFormat); - // JS always generates date in system locale, eliminate timezone difference to UTC - const msecsSinceEpoch = defaultParsedDate.getTime() - (defaultParsedDate.getTimezoneOffset() * 60 * 1000); - return new Date(msecsSinceEpoch); - } - - const dateStringDelimiterMatches = dateString.match(/\D/); - if(dateStringDelimiterMatches.length === 0) { - // Let the date method figure out this weirdness - return defaultParse(); - } - - const dateStringDelimiter = dateStringDelimiterMatches[0]; - - const localisedDateFormatSplit = Qt.locale().dateFormat(Locale.NarrowFormat).split(dateStringDelimiter); - const localisedDateDayPosition = localisedDateFormatSplit.findIndex((x) => /d/gi.test(x)); - const localisedDateMonthPosition = localisedDateFormatSplit.findIndex((x) => /m/gi.test(x)); - const localisedDateYearPosition = localisedDateFormatSplit.findIndex((x) => /y/gi.test(x)); - - let splitDateString = dateString.split(dateStringDelimiter); - let userProvidedYear = splitDateString[localisedDateYearPosition] - - const dateNow = new Date(); - const stringifiedCurrentYear = dateNow.getFullYear().toString(); - - // If we have any input weirdness, or if we have a fully-written year - // (e.g. 2022 instead of 22) then use default parse - if(splitDateString.length === 0 || - splitDateString.length > 3 || - userProvidedYear.length >= stringifiedCurrentYear.length) { - - return defaultParse(); - } - - let fullyWrittenYear = userProvidedYear.split(""); - const digitsToAdd = stringifiedCurrentYear.length - fullyWrittenYear.length; - for(let i = 0; i < digitsToAdd; i++) { - fullyWrittenYear.splice(i, 0, stringifiedCurrentYear[i]) - } - fullyWrittenYear = fullyWrittenYear.join(""); - - const fixedYearNum = Number(fullyWrittenYear); - const monthIndexNum = Number(splitDateString[localisedDateMonthPosition]) - 1; - const dayNum = Number(splitDateString[localisedDateDayPosition]); - - console.log(dayNum, monthIndexNum, fixedYearNum); - - // Modification: return date in UTC - return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum)); - } - - function textFromValue(value, locale) { - const dateFromValue = new Date(value * dayInMSecs); - return dateFromValue.toLocaleDateString(Qt.locale(), Locale.NarrowFormat); - } - - function valueFromText(text, locale) { - const dateFromText = parseDateString(text); - return Math.floor(dateFromText.getTime() / dayInMSecs); - } - - Layout.fillWidth: true - height: visible ? implicitHeight : 0 - - validInput: { - const value = valueFromText(text); - return value >= from && value <= to; - } - - text: textFromValue(expireDateSpinBox.value, expireDateSpinBox.locale) - inputMethodHints: expireDateSpinBox.inputMethodHints - - onAccepted: { + date: root.expireDate + onDateChanged: { const value = valueFromText(text, expireDateSpinBox.locale); root.setExpireDate(value * dayInMSecs); root.waitingForExpireDateChange = true; } - inputMethodHints: Qt.ImhDate + maximumDate: root.maximumExpireDate + minimumDate: { + const currentDate = new Date(); + // Start of day at 00:00:0000 UTC + return new Date(Date.UTC(currentDate.getFullYear(), + currentDate.getMonth(), + currentDate.getDate() + 1)); + } enabled: root.expireDateEnabled && !root.waitingForExpireDateChange && From e7343a17ea8a56435553567f2abaf1b6aeb644d4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 19:41:24 +0800 Subject: [PATCH 03/26] Stop reducing date numbers Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index bba717e83340c..73a0a369d5182 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -12,9 +12,6 @@ * for more details. */ -// QML dates are essentially JavaScript dates, which makes them very finicky and unreliable. -// Instead, we exclusively deal with msecs from epoch time to make things less painful when editing. -// We only use the QML Date when showing the nice string to the user. NCInputTextField { id: root @@ -88,29 +85,14 @@ NCInputTextField { } property var date: new Date().getTime() * 1000 // QDateTime msecsFromEpoch + onDateChanged: updateText() + property var minimumDate: 0 property var maximumDate: Number.MAX_SAFE_INTEGER - // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch - // Instead, we handle everything as days since epoch - readonly property int _dayInMSecs: 24 * 60 * 60 * 1000 - readonly property int _expireDateReduced: Math.floor(root.date / dayInMSecs) - // Reset the model data after binding broken on user interact - onExpireDateReducedChanged: { - value = expireDateReduced; - updateText(); - } - - readonly property int _maximumExpireDateReduced: Math.floor(maximumDate / dayInMSecs) - readonly property int _minimumExpireDateReduced: Math.floor(minimumDate / dayInMSecs) - - readonly property var _from: minimumExpireDateReduced - readonly property var _to: maximumExpireDateReduced - readonly property var _value: expireDateReduced - validInput: { const value = valueFromText(text); - return value >= _from && value <= _to; + return value >= minimumDate && value <= maximumDate; } text: _textFromValue(_value, locale) From dc23615d1d16506af3decde0585a14cea57b9e96 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 20:17:00 +0800 Subject: [PATCH 04/26] Add starter datefieldbackend class Signed-off-by: Claudio Cambra --- src/gui/CMakeLists.txt | 2 ++ src/gui/filedetails/datefieldbackend.cpp | 23 +++++++++++++++++ src/gui/filedetails/datefieldbackend.h | 33 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/gui/filedetails/datefieldbackend.cpp create mode 100644 src/gui/filedetails/datefieldbackend.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 0a89c6d307afc..fdcde5d6f32bd 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -190,6 +190,8 @@ set(client_SRCS syncconflictsmodel.cpp fileactivitylistmodel.h fileactivitylistmodel.cpp + filedetails/datefieldbackend.h + filedetails/datefieldbackend.cpp filedetails/filedetails.h filedetails/filedetails.cpp filedetails/sharemodel.h diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp new file mode 100644 index 0000000000000..7bc585650a967 --- /dev/null +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 by Claudio Cambra + * + * 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. + */ + +#include "datefieldbackend.h" + +namespace OCC +{ +namespace Quick +{ + +} +} \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h new file mode 100644 index 0000000000000..96d92784f5e1e --- /dev/null +++ b/src/gui/filedetails/datefieldbackend.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 by Claudio Cambra + * + * 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. + */ + +#pragma once + +#include + +namespace OCC +{ +namespace Quick +{ + +class DateFieldBackend : public QObject +{ + Q_OBJECT + +public: + explicit DateFieldBackend() = default; +}; + +} // namespace Quick +} // namespace OCC \ No newline at end of file From 429aa89761c0c40aec784cc23429dbf8a1d68c73 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 20:21:22 +0800 Subject: [PATCH 05/26] Add dateTime property to datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 14 ++++++++++++++ src/gui/filedetails/datefieldbackend.h | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 7bc585650a967..218f6346c0321 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -19,5 +19,19 @@ namespace OCC namespace Quick { +QDateTime DateFieldBackend::dateTime() const +{ + return m_dateTime; +} + +void DateFieldBackend::setDateTime(const QDateTime &dateTime) +{ + if (m_dateTime == dateTime) { + return; + } + + m_dateTime = dateTime; + Q_EMIT dateTimeChanged(); +} } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index 96d92784f5e1e..1311e7bba0199 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -14,6 +14,7 @@ #pragma once +#include #include namespace OCC @@ -25,8 +26,21 @@ class DateFieldBackend : public QObject { Q_OBJECT + Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) + public: explicit DateFieldBackend() = default; + + [[nodiscard]] QDateTime dateTime() const; + +public slots: + void setDateTime(const QDateTime &dateTime); + +signals: + void dateTimeChanged(); + +private: + QDateTime m_dateTime; }; } // namespace Quick From f895173ea4cf627ee310ef61691cd12f835c01d2 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 22:38:08 +0800 Subject: [PATCH 06/26] Add datetime msecs property to datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 16 ++++++++++++++++ src/gui/filedetails/datefieldbackend.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 218f6346c0321..e2dc679f18c72 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -33,5 +33,21 @@ void DateFieldBackend::setDateTime(const QDateTime &dateTime) m_dateTime = dateTime; Q_EMIT dateTimeChanged(); } + +qint64 DateFieldBackend::dateTimeMsecs() const +{ + return m_dateTime.toMSecsSinceEpoch(); +} + +void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) +{ + if (m_dateTime.toMSecsSinceEpoch() == dateTimeMsecs) { + return; + } + + const auto dt = QDateTime::fromMSecsSinceEpoch(dateTimeMsecs); + setDateTime(dt); +} + } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index 1311e7bba0199..92e5508c87bd7 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -27,14 +27,17 @@ class DateFieldBackend : public QObject Q_OBJECT Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) + Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeChanged) public: explicit DateFieldBackend() = default; [[nodiscard]] QDateTime dateTime() const; + [[nodiscard]] qint64 dateTimeMsecs() const; public slots: void setDateTime(const QDateTime &dateTime); + void setDateTimeMsecs(const qint64 dateTimeMsecs); signals: void dateTimeChanged(); From e2a99c6ee8910d1b9e5305ad35a9397a78fe7dc5 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 23:00:13 +0800 Subject: [PATCH 07/26] Add strig based representation and setting for date in datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 15 +++++++++++++++ src/gui/filedetails/datefieldbackend.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index e2dc679f18c72..5fadabe0cb478 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -14,6 +14,8 @@ #include "datefieldbackend.h" +#include + namespace OCC { namespace Quick @@ -49,5 +51,18 @@ void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) setDateTime(dt); } +QString DateFieldBackend::dateTimeString() const +{ + const auto locale = QLocale::system(); + return m_dateTime.toString(locale.dateTimeFormat(QLocale::ShortFormat)); +} + +void DateFieldBackend::setDateTimeString(const QString &dateTimeString) +{ + const auto locale = QLocale::system(); + const auto dt = locale.toDateTime(dateTimeString, locale.dateTimeFormat(QLocale::ShortFormat)); + setDateTime(dt); +} + } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index 92e5508c87bd7..b20e17dca9303 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -28,16 +28,19 @@ class DateFieldBackend : public QObject Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeChanged) + Q_PROPERTY(QString dateTimeString READ dateTimeString WRITE setDateTimeString NOTIFY dateTimeChanged) public: explicit DateFieldBackend() = default; [[nodiscard]] QDateTime dateTime() const; [[nodiscard]] qint64 dateTimeMsecs() const; + [[nodiscard]] QString dateTimeString() const; public slots: void setDateTime(const QDateTime &dateTime); void setDateTimeMsecs(const qint64 dateTimeMsecs); + void setDateTimeString(const QString &dateTimeString); signals: void dateTimeChanged(); From 8cbf8ef34b756bec525518a77b484dc38a0da0d7 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 23:11:53 +0800 Subject: [PATCH 08/26] Add minimum date properties to datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 41 ++++++++++++++++++++---- src/gui/filedetails/datefieldbackend.h | 12 ++++++- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 5fadabe0cb478..7616fac388123 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -23,27 +23,27 @@ namespace Quick QDateTime DateFieldBackend::dateTime() const { - return m_dateTime; + return _dateTime; } void DateFieldBackend::setDateTime(const QDateTime &dateTime) { - if (m_dateTime == dateTime) { + if (_dateTime == dateTime) { return; } - m_dateTime = dateTime; + _dateTime = dateTime; Q_EMIT dateTimeChanged(); } qint64 DateFieldBackend::dateTimeMsecs() const { - return m_dateTime.toMSecsSinceEpoch(); + return _dateTime.toMSecsSinceEpoch(); } void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) { - if (m_dateTime.toMSecsSinceEpoch() == dateTimeMsecs) { + if (_dateTime.toMSecsSinceEpoch() == dateTimeMsecs) { return; } @@ -54,7 +54,7 @@ void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) QString DateFieldBackend::dateTimeString() const { const auto locale = QLocale::system(); - return m_dateTime.toString(locale.dateTimeFormat(QLocale::ShortFormat)); + return _dateTime.toString(locale.dateTimeFormat(QLocale::ShortFormat)); } void DateFieldBackend::setDateTimeString(const QString &dateTimeString) @@ -64,5 +64,34 @@ void DateFieldBackend::setDateTimeString(const QString &dateTimeString) setDateTime(dt); } +QDateTime DateFieldBackend::minimumDateTime() const +{ + return _minimumDateTime; +} + +void DateFieldBackend::setMinimumDateTime(const QDateTime &minimumDateTime) +{ + if (_minimumDateTime == minimumDateTime) { + return; + } + + _minimumDateTime = minimumDateTime; + Q_EMIT minimumDateTimeChanged(); +} + +qint64 DateFieldBackend::minimumDateTimeMsecs() const +{ + return _minimumDateTime.toMSecsSinceEpoch(); +} + +void DateFieldBackend::setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs) +{ + if (_minimumDateTime.toMSecsSinceEpoch() == minimumDateTimeMsecs) { + return; + } + + const auto dt = QDateTime::fromMSecsSinceEpoch(minimumDateTimeMsecs); + setMinimumDateTime(dt); +} } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index b20e17dca9303..fe453a00f60b3 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -30,6 +30,8 @@ class DateFieldBackend : public QObject Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeChanged) Q_PROPERTY(QString dateTimeString READ dateTimeString WRITE setDateTimeString NOTIFY dateTimeChanged) + Q_PROPERTY(QDateTime minimumDateTime READ minimumDateTime WRITE setMinimumDateTime NOTIFY minimumDateTimeChanged) + Q_PROPERTY(qint64 minimumDateTimeMsecs READ minimumDateTimeMsecs WRITE setMinimumDateTimeMsecs NOTIFY minimumDateTimeChanged) public: explicit DateFieldBackend() = default; @@ -37,16 +39,24 @@ class DateFieldBackend : public QObject [[nodiscard]] qint64 dateTimeMsecs() const; [[nodiscard]] QString dateTimeString() const; + [[nodiscard]] QDateTime minimumDateTime() const; + [[nodiscard]] qint64 minimumDateTimeMsecs() const; + public slots: void setDateTime(const QDateTime &dateTime); void setDateTimeMsecs(const qint64 dateTimeMsecs); void setDateTimeString(const QString &dateTimeString); + void setMinimumDateTime(const QDateTime &minimumDateTime); + void setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs); + signals: void dateTimeChanged(); + void minimumDateTimeChanged(); private: - QDateTime m_dateTime; + QDateTime _dateTime; + QDateTime _minimumDateTime; }; } // namespace Quick From 37615694cafe5372b0c08ca4856706428bb76814 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 7 Aug 2023 23:14:34 +0800 Subject: [PATCH 09/26] Add maximum date properties to datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 30 ++++++++++++++++++++++++ src/gui/filedetails/datefieldbackend.h | 12 ++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 7616fac388123..22948c12f285c 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -93,5 +93,35 @@ void DateFieldBackend::setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs const auto dt = QDateTime::fromMSecsSinceEpoch(minimumDateTimeMsecs); setMinimumDateTime(dt); } + +QDateTime DateFieldBackend::maximumDateTime() const +{ + return _maximumDateTime; +} + +void DateFieldBackend::setMaximumDateTime(const QDateTime &maximumDateTime) +{ + if (_maximumDateTime == maximumDateTime) { + return; + } + + _maximumDateTime = maximumDateTime; + Q_EMIT maximumDateTimeChanged(); +} + +qint64 DateFieldBackend::maximumDateTimeMsecs() const +{ + return _maximumDateTime.toMSecsSinceEpoch(); +} + +void DateFieldBackend::setMaximumDateTimeMsecs(const qint64 maximumDateTimeMsecs) +{ + if (_maximumDateTime.toMSecsSinceEpoch() == maximumDateTimeMsecs) { + return; + } + + const auto dt = QDateTime::fromMSecsSinceEpoch(maximumDateTimeMsecs); + setMaximumDateTime(dt); +} } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index fe453a00f60b3..970b00f6820da 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -32,6 +32,10 @@ class DateFieldBackend : public QObject Q_PROPERTY(QDateTime minimumDateTime READ minimumDateTime WRITE setMinimumDateTime NOTIFY minimumDateTimeChanged) Q_PROPERTY(qint64 minimumDateTimeMsecs READ minimumDateTimeMsecs WRITE setMinimumDateTimeMsecs NOTIFY minimumDateTimeChanged) + + Q_PROPERTY(QDateTime maximumDateTime READ maximumDateTime WRITE setMaximumDateTime NOTIFY maximumDateTimeChanged) + Q_PROPERTY(qint64 maximumDateTimeMsecs READ maximumDateTimeMsecs WRITE setMaximumDateTimeMsecs NOTIFY maximumDateTimeChanged) + public: explicit DateFieldBackend() = default; @@ -42,6 +46,9 @@ class DateFieldBackend : public QObject [[nodiscard]] QDateTime minimumDateTime() const; [[nodiscard]] qint64 minimumDateTimeMsecs() const; + [[nodiscard]] QDateTime maximumDateTime() const; + [[nodiscard]] qint64 maximumDateTimeMsecs() const; + public slots: void setDateTime(const QDateTime &dateTime); void setDateTimeMsecs(const qint64 dateTimeMsecs); @@ -50,13 +57,18 @@ public slots: void setMinimumDateTime(const QDateTime &minimumDateTime); void setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs); + void setMaximumDateTime(const QDateTime &maximumDateTime); + void setMaximumDateTimeMsecs(const qint64 maximumDateTimeMsecs); + signals: void dateTimeChanged(); void minimumDateTimeChanged(); + void maximumDateTimeChanged(); private: QDateTime _dateTime; QDateTime _minimumDateTime; + QDateTime _maximumDateTime; }; } // namespace Quick From df744cc47a5d5d9633e9ccff70a964c796c87dd2 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 15:06:52 +0800 Subject: [PATCH 10/26] Add validDateTime property to datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 19 +++++++++++++++++-- src/gui/filedetails/datefieldbackend.h | 6 +++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 22948c12f285c..0764ac2e1072d 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -54,13 +54,13 @@ void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) QString DateFieldBackend::dateTimeString() const { const auto locale = QLocale::system(); - return _dateTime.toString(locale.dateTimeFormat(QLocale::ShortFormat)); + return _dateTime.toString(locale.dateFormat(QLocale::ShortFormat)); } void DateFieldBackend::setDateTimeString(const QString &dateTimeString) { const auto locale = QLocale::system(); - const auto dt = locale.toDateTime(dateTimeString, locale.dateTimeFormat(QLocale::ShortFormat)); + const auto dt = locale.toDateTime(dateTimeString, locale.dateFormat(QLocale::ShortFormat)); setDateTime(dt); } @@ -123,5 +123,20 @@ void DateFieldBackend::setMaximumDateTimeMsecs(const qint64 maximumDateTimeMsecs const auto dt = QDateTime::fromMSecsSinceEpoch(maximumDateTimeMsecs); setMaximumDateTime(dt); } + +bool DateFieldBackend::validDateTime() const +{ + auto valid = _dateTime.isValid(); + + if (_minimumDateTime.isValid()) { + valid &= _dateTime >= _minimumDateTime; + } + + if (_maximumDateTime.isValid()) { + valid &= _dateTime <= _maximumDateTime; + } + + return valid; +} } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index 970b00f6820da..dde64187fb2a4 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -36,6 +36,8 @@ class DateFieldBackend : public QObject Q_PROPERTY(QDateTime maximumDateTime READ maximumDateTime WRITE setMaximumDateTime NOTIFY maximumDateTimeChanged) Q_PROPERTY(qint64 maximumDateTimeMsecs READ maximumDateTimeMsecs WRITE setMaximumDateTimeMsecs NOTIFY maximumDateTimeChanged) + Q_PROPERTY(bool validDateTime READ validDateTime NOTIFY dateTimeChanged NOTIFY minimumDateTimeChanged NOTIFY maximumDateTimeChanged) + public: explicit DateFieldBackend() = default; @@ -49,6 +51,8 @@ class DateFieldBackend : public QObject [[nodiscard]] QDateTime maximumDateTime() const; [[nodiscard]] qint64 maximumDateTimeMsecs() const; + [[nodiscard]] bool validDateTime() const; + public slots: void setDateTime(const QDateTime &dateTime); void setDateTimeMsecs(const qint64 dateTimeMsecs); @@ -66,7 +70,7 @@ public slots: void maximumDateTimeChanged(); private: - QDateTime _dateTime; + QDateTime _dateTime = QDateTime::currentDateTimeUtc(); QDateTime _minimumDateTime; QDateTime _maximumDateTime; }; From 183df8963a7e8f7246088d322cb8cb2e654107a8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 17:30:11 +0800 Subject: [PATCH 11/26] Register DateFieldBackend in QML engine Signed-off-by: Claudio Cambra --- src/gui/owncloudgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 9d1d5856f3405..6749df8a6e91b 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -32,6 +32,7 @@ #include "theme.h" #include "wheelhandler.h" #include "syncconflictsmodel.h" +#include "filedetails/datefieldbackend.h" #include "filedetails/filedetails.h" #include "filedetails/shareemodel.h" #include "filedetails/sharemodel.h" @@ -118,6 +119,7 @@ ownCloudGui::ownCloudGui(Application *parent) qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "SortedActivityListModel"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "WheelHandler"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "CallStateChecker"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "DateFieldBackend"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "FileDetails"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "ShareModel"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "ShareeModel"); From 29b568a63f21a89c5c17e88cc7d0427f40b8a36e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 15:07:42 +0800 Subject: [PATCH 12/26] Vastly simplify NCInputDateField by using the DateFieldBackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 98 ++++++------------------ 1 file changed, 22 insertions(+), 76 deletions(-) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index 73a0a369d5182..63d979e35442e 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -12,91 +12,37 @@ * for more details. */ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import com.nextcloud.desktopclient 1.0 + NCInputTextField { id: root - function updateText() { - text = _textFromValue(_value, root.locale); - } - - // Taken from Kalendar 22.08 - // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js - function _parseDateString(dateString) { - function defaultParse() { - const defaultParsedDate = Date.fromLocaleDateString(root.locale, dateString, Locale.NarrowFormat); - // JS always generates date in system locale, eliminate timezone difference to UTC - const msecsSinceEpoch = defaultParsedDate.getTime() - (defaultParsedDate.getTimezoneOffset() * 60 * 1000); - return new Date(msecsSinceEpoch); - } - - const dateStringDelimiterMatches = dateString.match(/\D/); - if(dateStringDelimiterMatches.length === 0) { - // Let the date method figure out this weirdness - return defaultParse(); - } - - const dateStringDelimiter = dateStringDelimiterMatches[0]; - - const localisedDateFormatSplit = root.locale.dateFormat(Locale.NarrowFormat).split(dateStringDelimiter); - const localisedDateDayPosition = localisedDateFormatSplit.findIndex((x) => /d/gi.test(x)); - const localisedDateMonthPosition = localisedDateFormatSplit.findIndex((x) => /m/gi.test(x)); - const localisedDateYearPosition = localisedDateFormatSplit.findIndex((x) => /y/gi.test(x)); - - let splitDateString = dateString.split(dateStringDelimiter); - let userProvidedYear = splitDateString[localisedDateYearPosition] - - const dateNow = new Date(); - const stringifiedCurrentYear = dateNow.getFullYear().toString(); - - // If we have any input weirdness, or if we have a fully-written year - // (e.g. 2022 instead of 22) then use default parse - if(splitDateString.length === 0 || - splitDateString.length > 3 || - userProvidedYear.length >= stringifiedCurrentYear.length) { + signal userAcceptedDate - return defaultParse(); - } - - let fullyWrittenYear = userProvidedYear.split(""); - const digitsToAdd = stringifiedCurrentYear.length - fullyWrittenYear.length; - for(let i = 0; i < digitsToAdd; i++) { - fullyWrittenYear.splice(i, 0, stringifiedCurrentYear[i]) - } - fullyWrittenYear = fullyWrittenYear.join(""); - - const fixedYearNum = Number(fullyWrittenYear); - const monthIndexNum = Number(splitDateString[localisedDateMonthPosition]) - 1; - const dayNum = Number(splitDateString[localisedDateDayPosition]); - - console.log(dayNum, monthIndexNum, fixedYearNum); - - // Modification: return date in UTC - return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum)); - } - - function _textFromValue(value, locale) { - const dateFromValue = new Date(value * dayInMSecs); - return dateFromValue.toLocaleDateString(root.locale, Locale.NarrowFormat); + function updateText() { + text = backend.dateTimeString; } - function _valueFromText(text, locale) { - const dateFromText = _parseDateString(text); - return Math.floor(dateFromText.getTime() / dayInMSecs); + property DateFieldBackend backend: DateFieldBackend { + id: backend + onDateTimeChanged: if (!root.activeFocus) root.updateText() } - property var date: new Date().getTime() * 1000 // QDateTime msecsFromEpoch - onDateChanged: updateText() + property alias date: backend.dateTime + property alias dateInMs: backend.dateTimeMsecs + property alias minimumDate: backend.minimumDateTime + property alias minimumDateMs: backend.minimumDateTimeMsecs + property alias maximumDate: backend.maximumDateTime + property alias maximumDateMs: backend.maximumDateTimeMsecs - property var minimumDate: 0 - property var maximumDate: Number.MAX_SAFE_INTEGER + validInput: backend.validDateTime + text: backend.dateTimeString + onTextChanged: backend.dateTimeString = text - validInput: { - const value = valueFromText(text); - return value >= minimumDate && value <= maximumDate; + onAccepted: { + backend.dateTimeString = text; + root.userAcceptedDate(); } - - text: _textFromValue(_value, locale) - inputMethodHints: Qt.ImhDate - - onAccepted: root.date = _valueFromText(text, locale); } \ No newline at end of file From eb76c0b0a46d9fa85312d73d6fbee2c2dec90c06 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 15:08:12 +0800 Subject: [PATCH 13/26] Use NCInputDateField in ShareDetailsPage Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 29 +++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index 380fd114262a8..c09f42abd6dc2 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -64,7 +64,7 @@ Page { readonly property string passwordPlaceholder: "●●●●●●●●●●" readonly property var expireDate: shareModelData.expireDate // Don't use int as we are limited - readonly property var maximumExpireDate: shareModelData.enforcedMaximumExpireDate + readonly property var maximumExpireDate: shareModelData.enforcedMaximumExpireDate // msecs epoch readonly property string linkShareLabel: shareModelData.linkShareLabel ?? "" @@ -127,7 +127,7 @@ Page { // // So to ensure that the text of the spin box is correctly updated, force an update of the // contents of the expire date text field. - expireDateSpinBox.updateText(); + expireDateField.updateText(); waitingForExpireDateChange = false; } @@ -734,26 +734,29 @@ Page { NCInputDateField { id: expireDateField - date: root.expireDate - onDateChanged: { - const value = valueFromText(text, expireDateSpinBox.locale); - root.setExpireDate(value * dayInMSecs); - root.waitingForExpireDateChange = true; - } + Layout.fillWidth: true + height: visible ? implicitHeight : 0 - maximumDate: root.maximumExpireDate - minimumDate: { + dateInMs: root.expireDate + maximumDateMs: root.maximumExpireDate + minimumDateMs: { const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + const currentMonth = currentDate.getMonth(); + const currentMonthDay = currentDate.getDate(); // Start of day at 00:00:0000 UTC - return new Date(Date.UTC(currentDate.getFullYear(), - currentDate.getMonth(), - currentDate.getDate() + 1)); + return Date.UTC(currentYear, currentMonth, currentMonthDay + 1); } enabled: root.expireDateEnabled && !root.waitingForExpireDateChange && !root.waitingForExpireDateEnabledChange + onUserAcceptedDate: { + root.setExpireDate(dateInMs); + root.waitingForExpireDateChange = true; + } + NCBusyIndicator { anchors.fill: parent visible: root.waitingForExpireDateEnabledChange || From 48c49fca60da46c8066e7e8dc8e202b055ff665b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 17:27:11 +0800 Subject: [PATCH 14/26] Customise locale default date format to ensure we eliminate issues parsing two-digit year strings Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 21 ++++++++++++++++++--- src/gui/filedetails/datefieldbackend.h | 4 +++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 0764ac2e1072d..8377afad93749 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -15,12 +15,28 @@ #include "datefieldbackend.h" #include +#include namespace OCC { namespace Quick { +DateFieldBackend::DateFieldBackend(QObject *const parent) + : QObject(parent) +{ + // Ensure the date format is for a full year. QLocale::ShortFormat often + // provides a short year format that is only two years, which is an absolute + // pain to work with -- ensure instead we have the full, unambiguous year + _dateFormat = QLocale::system().dateFormat(QLocale::ShortFormat); + // Check for specifically two y's, no more and no fewer, within format date + const QRegularExpression re("(? Date: Tue, 8 Aug 2023 17:29:45 +0800 Subject: [PATCH 15/26] Give each property unique signals, fixing QML bugs Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 2 +- src/gui/filedetails/datefieldbackend.cpp | 7 +++++++ src/gui/filedetails/datefieldbackend.h | 18 +++++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index 63d979e35442e..5a5ccf4f25de6 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -27,7 +27,7 @@ NCInputTextField { property DateFieldBackend backend: DateFieldBackend { id: backend - onDateTimeChanged: if (!root.activeFocus) root.updateText() + onDateTimeStringChanged: if (!root.activeFocus) root.updateText() } property alias date: backend.dateTime diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 8377afad93749..23410eabd83bd 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -50,6 +50,9 @@ void DateFieldBackend::setDateTime(const QDateTime &dateTime) _dateTime = dateTime; Q_EMIT dateTimeChanged(); + Q_EMIT dateTimeMsecsChanged(); + Q_EMIT dateTimeStringChanged(); + Q_EMIT validDateTimeChanged(); } qint64 DateFieldBackend::dateTimeMsecs() const @@ -92,6 +95,8 @@ void DateFieldBackend::setMinimumDateTime(const QDateTime &minimumDateTime) _minimumDateTime = minimumDateTime; Q_EMIT minimumDateTimeChanged(); + Q_EMIT minimumDateTimeMsecsChanged(); + Q_EMIT validDateTimeChanged(); } qint64 DateFieldBackend::minimumDateTimeMsecs() const @@ -122,6 +127,8 @@ void DateFieldBackend::setMaximumDateTime(const QDateTime &maximumDateTime) _maximumDateTime = maximumDateTime; Q_EMIT maximumDateTimeChanged(); + Q_EMIT maximumDateTimeMsecsChanged(); + Q_EMIT validDateTimeChanged(); } qint64 DateFieldBackend::maximumDateTimeMsecs() const diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index d0df944ef5dfb..caea5ea2f4c5d 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -27,16 +27,16 @@ class DateFieldBackend : public QObject Q_OBJECT Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) - Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeChanged) - Q_PROPERTY(QString dateTimeString READ dateTimeString WRITE setDateTimeString NOTIFY dateTimeChanged) + Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeMsecsChanged) + Q_PROPERTY(QString dateTimeString READ dateTimeString WRITE setDateTimeString NOTIFY dateTimeStringChanged) Q_PROPERTY(QDateTime minimumDateTime READ minimumDateTime WRITE setMinimumDateTime NOTIFY minimumDateTimeChanged) - Q_PROPERTY(qint64 minimumDateTimeMsecs READ minimumDateTimeMsecs WRITE setMinimumDateTimeMsecs NOTIFY minimumDateTimeChanged) + Q_PROPERTY(qint64 minimumDateTimeMsecs READ minimumDateTimeMsecs WRITE setMinimumDateTimeMsecs NOTIFY minimumDateTimeMsecsChanged) Q_PROPERTY(QDateTime maximumDateTime READ maximumDateTime WRITE setMaximumDateTime NOTIFY maximumDateTimeChanged) - Q_PROPERTY(qint64 maximumDateTimeMsecs READ maximumDateTimeMsecs WRITE setMaximumDateTimeMsecs NOTIFY maximumDateTimeChanged) + Q_PROPERTY(qint64 maximumDateTimeMsecs READ maximumDateTimeMsecs WRITE setMaximumDateTimeMsecs NOTIFY maximumDateTimeMsecsChanged) - Q_PROPERTY(bool validDateTime READ validDateTime NOTIFY dateTimeChanged NOTIFY minimumDateTimeChanged NOTIFY maximumDateTimeChanged) + Q_PROPERTY(bool validDateTime READ validDateTime NOTIFY validDateTimeChanged) public: explicit DateFieldBackend(QObject *const parent = nullptr); @@ -66,8 +66,16 @@ public slots: signals: void dateTimeChanged(); + void dateTimeMsecsChanged(); + void dateTimeStringChanged(); + void minimumDateTimeChanged(); + void minimumDateTimeMsecsChanged(); + void maximumDateTimeChanged(); + void maximumDateTimeMsecsChanged(); + + void validDateTimeChanged(); private: QDateTime _dateTime = QDateTime::currentDateTimeUtc(); From c57852e9e36f8587da1577fd2bf8c15c8868f6f4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 18:03:35 +0800 Subject: [PATCH 16/26] Do not use QDateTime, use only QDate for datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 24 ++--- src/gui/filedetails/datefieldbackend.cpp | 109 ++++++++++++----------- src/gui/filedetails/datefieldbackend.h | 70 +++++++-------- 3 files changed, 102 insertions(+), 101 deletions(-) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index 5a5ccf4f25de6..ea3f0b6e8f1ae 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -22,27 +22,27 @@ NCInputTextField { signal userAcceptedDate function updateText() { - text = backend.dateTimeString; + text = backend.dateString; } property DateFieldBackend backend: DateFieldBackend { id: backend - onDateTimeStringChanged: if (!root.activeFocus) root.updateText() + onDateStringChanged: if (!root.activeFocus) root.updateText() } - property alias date: backend.dateTime - property alias dateInMs: backend.dateTimeMsecs - property alias minimumDate: backend.minimumDateTime - property alias minimumDateMs: backend.minimumDateTimeMsecs - property alias maximumDate: backend.maximumDateTime - property alias maximumDateMs: backend.maximumDateTimeMsecs + property alias date: backend.date + property alias dateInMs: backend.dateMsecs + property alias minimumDate: backend.minimumDate + property alias minimumDateMs: backend.minimumDateMsecs + property alias maximumDate: backend.maximumDate + property alias maximumDateMs: backend.maximumDateMsecs - validInput: backend.validDateTime - text: backend.dateTimeString - onTextChanged: backend.dateTimeString = text + validInput: backend.validDate + text: backend.dateString + onTextChanged: backend.dateString = text onAccepted: { - backend.dateTimeString = text; + backend.dateString = text; root.userAcceptedDate(); } } \ No newline at end of file diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 23410eabd83bd..6c043697809fb 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -37,125 +37,126 @@ DateFieldBackend::DateFieldBackend(QObject *const parent) } } -QDateTime DateFieldBackend::dateTime() const +QDate DateFieldBackend::date() const { - return _dateTime; + return _date; } -void DateFieldBackend::setDateTime(const QDateTime &dateTime) +void DateFieldBackend::setDate(const QDate &date) { - if (_dateTime == dateTime) { + if (_date == date) { return; } - _dateTime = dateTime; - Q_EMIT dateTimeChanged(); - Q_EMIT dateTimeMsecsChanged(); - Q_EMIT dateTimeStringChanged(); - Q_EMIT validDateTimeChanged(); + _date = date; + + Q_EMIT dateChanged(); + Q_EMIT dateMsecsChanged(); + Q_EMIT dateStringChanged(); + Q_EMIT validDateChanged(); } -qint64 DateFieldBackend::dateTimeMsecs() const +qint64 DateFieldBackend::dateMsecs() const { - return _dateTime.toMSecsSinceEpoch(); + return _date.startOfDay().toMSecsSinceEpoch(); } -void DateFieldBackend::setDateTimeMsecs(const qint64 dateTimeMsecs) +void DateFieldBackend::setDateMsecs(const qint64 dateMsecs) { - if (_dateTime.toMSecsSinceEpoch() == dateTimeMsecs) { + if (_date.startOfDay().toMSecsSinceEpoch() == dateMsecs) { return; } - const auto dt = QDateTime::fromMSecsSinceEpoch(dateTimeMsecs); - setDateTime(dt); + const auto dt = QDateTime::fromMSecsSinceEpoch(dateMsecs); + setDate(dt.date()); } -QString DateFieldBackend::dateTimeString() const +QString DateFieldBackend::dateString() const { - return _dateTime.toString(_dateFormat); + return _date.toString(_dateFormat); } -void DateFieldBackend::setDateTimeString(const QString &dateTimeString) +void DateFieldBackend::setDateString(const QString &dateString) { const auto locale = QLocale::system(); - const auto dt = locale.toDateTime(dateTimeString, _dateFormat); - setDateTime(dt); + const auto date = locale.toDate(dateString, _dateFormat); + setDate(date); } -QDateTime DateFieldBackend::minimumDateTime() const +QDate DateFieldBackend::minimumDate() const { - return _minimumDateTime; + return _minimumDate; } -void DateFieldBackend::setMinimumDateTime(const QDateTime &minimumDateTime) +void DateFieldBackend::setMinimumDate(const QDate &minimumDate) { - if (_minimumDateTime == minimumDateTime) { + if (_minimumDate == minimumDate) { return; } - _minimumDateTime = minimumDateTime; - Q_EMIT minimumDateTimeChanged(); - Q_EMIT minimumDateTimeMsecsChanged(); - Q_EMIT validDateTimeChanged(); + _minimumDate = minimumDate; + Q_EMIT minimumDateChanged(); + Q_EMIT minimumDateMsecsChanged(); + Q_EMIT validDateChanged(); } -qint64 DateFieldBackend::minimumDateTimeMsecs() const +qint64 DateFieldBackend::minimumDateMsecs() const { - return _minimumDateTime.toMSecsSinceEpoch(); + return _minimumDate.startOfDay().toMSecsSinceEpoch(); } -void DateFieldBackend::setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs) +void DateFieldBackend::setMinimumDateMsecs(const qint64 minimumDateMsecs) { - if (_minimumDateTime.toMSecsSinceEpoch() == minimumDateTimeMsecs) { + if (_minimumDate.startOfDay().toMSecsSinceEpoch() == minimumDateMsecs) { return; } - const auto dt = QDateTime::fromMSecsSinceEpoch(minimumDateTimeMsecs); - setMinimumDateTime(dt); + const auto dt = QDateTime::fromMSecsSinceEpoch(minimumDateMsecs); + setMinimumDate(dt.date()); } -QDateTime DateFieldBackend::maximumDateTime() const +QDate DateFieldBackend::maximumDate() const { - return _maximumDateTime; + return _maximumDate; } -void DateFieldBackend::setMaximumDateTime(const QDateTime &maximumDateTime) +void DateFieldBackend::setMaximumDate(const QDate &maximumDate) { - if (_maximumDateTime == maximumDateTime) { + if (_maximumDate == maximumDate) { return; } - _maximumDateTime = maximumDateTime; - Q_EMIT maximumDateTimeChanged(); - Q_EMIT maximumDateTimeMsecsChanged(); - Q_EMIT validDateTimeChanged(); + _maximumDate = maximumDate; + Q_EMIT maximumDateChanged(); + Q_EMIT maximumDateMsecsChanged(); + Q_EMIT validDateChanged(); } -qint64 DateFieldBackend::maximumDateTimeMsecs() const +qint64 DateFieldBackend::maximumDateMsecs() const { - return _maximumDateTime.toMSecsSinceEpoch(); + return _maximumDate.startOfDay().toMSecsSinceEpoch(); } -void DateFieldBackend::setMaximumDateTimeMsecs(const qint64 maximumDateTimeMsecs) +void DateFieldBackend::setMaximumDateMsecs(const qint64 maximumDateMsecs) { - if (_maximumDateTime.toMSecsSinceEpoch() == maximumDateTimeMsecs) { + if (_maximumDate.startOfDay().toMSecsSinceEpoch() == maximumDateMsecs) { return; } - const auto dt = QDateTime::fromMSecsSinceEpoch(maximumDateTimeMsecs); - setMaximumDateTime(dt); + const auto dt = QDateTime::fromMSecsSinceEpoch(maximumDateMsecs); + setMaximumDate(dt.date()); } -bool DateFieldBackend::validDateTime() const +bool DateFieldBackend::validDate() const { - auto valid = _dateTime.isValid(); + auto valid = _date.isValid(); - if (_minimumDateTime.isValid()) { - valid &= _dateTime >= _minimumDateTime; + if (_minimumDate.isValid()) { + valid &= _date >= _minimumDate; } - if (_maximumDateTime.isValid()) { - valid &= _dateTime <= _maximumDateTime; + if (_maximumDate.isValid()) { + valid &= _date <= _maximumDate; } return valid; diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index caea5ea2f4c5d..5ed813c47a579 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -14,7 +14,7 @@ #pragma once -#include +#include #include namespace OCC @@ -26,61 +26,61 @@ class DateFieldBackend : public QObject { Q_OBJECT - Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged) - Q_PROPERTY(qint64 dateTimeMsecs READ dateTimeMsecs WRITE setDateTimeMsecs NOTIFY dateTimeMsecsChanged) - Q_PROPERTY(QString dateTimeString READ dateTimeString WRITE setDateTimeString NOTIFY dateTimeStringChanged) + Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged) + Q_PROPERTY(qint64 dateMsecs READ dateMsecs WRITE setDateMsecs NOTIFY dateMsecsChanged) + Q_PROPERTY(QString dateString READ dateString WRITE setDateString NOTIFY dateStringChanged) - Q_PROPERTY(QDateTime minimumDateTime READ minimumDateTime WRITE setMinimumDateTime NOTIFY minimumDateTimeChanged) - Q_PROPERTY(qint64 minimumDateTimeMsecs READ minimumDateTimeMsecs WRITE setMinimumDateTimeMsecs NOTIFY minimumDateTimeMsecsChanged) + Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate NOTIFY minimumDateChanged) + Q_PROPERTY(qint64 minimumDateMsecs READ minimumDateMsecs WRITE setMinimumDateMsecs NOTIFY minimumDateMsecsChanged) - Q_PROPERTY(QDateTime maximumDateTime READ maximumDateTime WRITE setMaximumDateTime NOTIFY maximumDateTimeChanged) - Q_PROPERTY(qint64 maximumDateTimeMsecs READ maximumDateTimeMsecs WRITE setMaximumDateTimeMsecs NOTIFY maximumDateTimeMsecsChanged) + Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate NOTIFY maximumDateChanged) + Q_PROPERTY(qint64 maximumDateMsecs READ maximumDateMsecs WRITE setMaximumDateMsecs NOTIFY maximumDateMsecsChanged) - Q_PROPERTY(bool validDateTime READ validDateTime NOTIFY validDateTimeChanged) + Q_PROPERTY(bool validDate READ validDate NOTIFY validDateChanged) public: explicit DateFieldBackend(QObject *const parent = nullptr); - [[nodiscard]] QDateTime dateTime() const; - [[nodiscard]] qint64 dateTimeMsecs() const; - [[nodiscard]] QString dateTimeString() const; + [[nodiscard]] QDate date() const; + [[nodiscard]] qint64 dateMsecs() const; + [[nodiscard]] QString dateString() const; - [[nodiscard]] QDateTime minimumDateTime() const; - [[nodiscard]] qint64 minimumDateTimeMsecs() const; + [[nodiscard]] QDate minimumDate() const; + [[nodiscard]] qint64 minimumDateMsecs() const; - [[nodiscard]] QDateTime maximumDateTime() const; - [[nodiscard]] qint64 maximumDateTimeMsecs() const; + [[nodiscard]] QDate maximumDate() const; + [[nodiscard]] qint64 maximumDateMsecs() const; - [[nodiscard]] bool validDateTime() const; + [[nodiscard]] bool validDate() const; public slots: - void setDateTime(const QDateTime &dateTime); - void setDateTimeMsecs(const qint64 dateTimeMsecs); - void setDateTimeString(const QString &dateTimeString); + void setDate(const QDate &date); + void setDateMsecs(const qint64 dateMsecs); + void setDateString(const QString &dateString); - void setMinimumDateTime(const QDateTime &minimumDateTime); - void setMinimumDateTimeMsecs(const qint64 minimumDateTimeMsecs); + void setMinimumDate(const QDate &minimumDate); + void setMinimumDateMsecs(const qint64 minimumDateMsecs); - void setMaximumDateTime(const QDateTime &maximumDateTime); - void setMaximumDateTimeMsecs(const qint64 maximumDateTimeMsecs); + void setMaximumDate(const QDate &maximumDate); + void setMaximumDateMsecs(const qint64 maximumDateMsecs); signals: - void dateTimeChanged(); - void dateTimeMsecsChanged(); - void dateTimeStringChanged(); + void dateChanged(); + void dateMsecsChanged(); + void dateStringChanged(); - void minimumDateTimeChanged(); - void minimumDateTimeMsecsChanged(); + void minimumDateChanged(); + void minimumDateMsecsChanged(); - void maximumDateTimeChanged(); - void maximumDateTimeMsecsChanged(); + void maximumDateChanged(); + void maximumDateMsecsChanged(); - void validDateTimeChanged(); + void validDateChanged(); private: - QDateTime _dateTime = QDateTime::currentDateTimeUtc(); - QDateTime _minimumDateTime; - QDateTime _maximumDateTime; + QDate _date = QDate::currentDate(); + QDate _minimumDate; + QDate _maximumDate; QString _dateFormat; }; From ff4e1a5892130bad757c5980284c1427bf4bbd4d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 18:28:51 +0800 Subject: [PATCH 17/26] Add date field input method hint Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index ea3f0b6e8f1ae..9d1e7ce76cce4 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -37,6 +37,7 @@ NCInputTextField { property alias maximumDate: backend.maximumDate property alias maximumDateMs: backend.maximumDateMsecs + inputMethodHints: Qt.ImhDate validInput: backend.validDate text: backend.dateString onTextChanged: backend.dateString = text From 7cfd4d877b88a252cd31c012b281abb8c1f1cf79 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 8 Aug 2023 18:48:35 +0800 Subject: [PATCH 18/26] Ensure leading zero dates are also correctly parsed Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 28 +++++++++++++++++++----- src/gui/filedetails/datefieldbackend.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 6c043697809fb..89556c8cc3e18 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -25,16 +25,29 @@ namespace Quick DateFieldBackend::DateFieldBackend(QObject *const parent) : QObject(parent) { + _dateFormat = QLocale::system().dateFormat(QLocale::ShortFormat); + // Ensure the date format is for a full year. QLocale::ShortFormat often // provides a short year format that is only two years, which is an absolute - // pain to work with -- ensure instead we have the full, unambiguous year - _dateFormat = QLocale::system().dateFormat(QLocale::ShortFormat); + // pain to work with -- ensure instead we have the full, unambiguous year. // Check for specifically two y's, no more and no fewer, within format date - const QRegularExpression re("(? Date: Tue, 8 Aug 2023 18:49:15 +0800 Subject: [PATCH 19/26] Ensure qdatetimes are UTC in datefieldbackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 89556c8cc3e18..0b806547cced7 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -16,6 +16,7 @@ #include #include +#include namespace OCC { @@ -71,16 +72,16 @@ void DateFieldBackend::setDate(const QDate &date) qint64 DateFieldBackend::dateMsecs() const { - return _date.startOfDay().toMSecsSinceEpoch(); + return _date.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch(); } void DateFieldBackend::setDateMsecs(const qint64 dateMsecs) { - if (_date.startOfDay().toMSecsSinceEpoch() == dateMsecs) { + if (_date.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch() == dateMsecs) { return; } - const auto dt = QDateTime::fromMSecsSinceEpoch(dateMsecs); + const auto dt = QDateTime::fromMSecsSinceEpoch(dateMsecs).toUTC(); setDate(dt.date()); } @@ -120,12 +121,12 @@ void DateFieldBackend::setMinimumDate(const QDate &minimumDate) qint64 DateFieldBackend::minimumDateMsecs() const { - return _minimumDate.startOfDay().toMSecsSinceEpoch(); + return _minimumDate.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch(); } void DateFieldBackend::setMinimumDateMsecs(const qint64 minimumDateMsecs) { - if (_minimumDate.startOfDay().toMSecsSinceEpoch() == minimumDateMsecs) { + if (_minimumDate.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch() == minimumDateMsecs) { return; } @@ -152,12 +153,12 @@ void DateFieldBackend::setMaximumDate(const QDate &maximumDate) qint64 DateFieldBackend::maximumDateMsecs() const { - return _maximumDate.startOfDay().toMSecsSinceEpoch(); + return _maximumDate.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch(); } void DateFieldBackend::setMaximumDateMsecs(const qint64 maximumDateMsecs) { - if (_maximumDate.startOfDay().toMSecsSinceEpoch() == maximumDateMsecs) { + if (_maximumDate.startOfDay(QTimeZone::utc()).toMSecsSinceEpoch() == maximumDateMsecs) { return; } From ea44c0f7bdc5c3559dcc535cb5ae8cb808bb2a5b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 12 Sep 2023 21:11:03 +0800 Subject: [PATCH 20/26] Add date field backend test file Signed-off-by: Claudio Cambra --- test/CMakeLists.txt | 1 + test/testdatefieldbackend.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 test/testdatefieldbackend.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 69ebc08d518b4..e1ea80801ded0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -74,6 +74,7 @@ nextcloud_add_test(SortedShareModel) nextcloud_add_test(SecureFileDrop) nextcloud_add_test(FileTagModel) nextcloud_add_test(SyncConflictsModel) +nextcloud_add_test(DateFieldBackend) target_link_libraries(SecureFileDropTest PRIVATE Nextcloud::sync) configure_file(fake2eelocksucceeded.json "${PROJECT_BINARY_DIR}/bin/fake2eelocksucceeded.json" COPYONLY) diff --git a/test/testdatefieldbackend.cpp b/test/testdatefieldbackend.cpp new file mode 100644 index 0000000000000..eb1dfe5b36eca --- /dev/null +++ b/test/testdatefieldbackend.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) by Claudio Cambra + * + * 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. + */ + +#include "gui/filedetails/datefieldbackend.h" + +#include +#include + +using namespace OCC; + +class TestDateFieldBackend : public QObject +{ + Q_OBJECT +} From 8ac48c2a4fc9cb5a46828b371c3f016a0767dae7 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 12 Sep 2023 22:03:55 +0800 Subject: [PATCH 21/26] Add simple test for basic. default date properties of DateFieldBackend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.h | 6 +++++- test/testdatefieldbackend.cpp | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.h b/src/gui/filedetails/datefieldbackend.h index b44fc6822cd4f..c9e275dd04243 100644 --- a/src/gui/filedetails/datefieldbackend.h +++ b/src/gui/filedetails/datefieldbackend.h @@ -17,6 +17,8 @@ #include #include +class TestDateFieldBackend; + namespace OCC { namespace Quick @@ -78,6 +80,8 @@ public slots: void validDateChanged(); private: + friend class ::TestDateFieldBackend; + QDate _date = QDate::currentDate(); QDate _minimumDate; QDate _maximumDate; @@ -87,4 +91,4 @@ public slots: }; } // namespace Quick -} // namespace OCC \ No newline at end of file +} // namespace OCC diff --git a/test/testdatefieldbackend.cpp b/test/testdatefieldbackend.cpp index eb1dfe5b36eca..eb1480207516b 100644 --- a/test/testdatefieldbackend.cpp +++ b/test/testdatefieldbackend.cpp @@ -22,4 +22,24 @@ using namespace OCC; class TestDateFieldBackend : public QObject { Q_OBJECT -} + +private slots: + void testDefaultBehaviour() + { + constexpr auto dateStringFormat = "dd/MM/yyyy"; + + Quick::DateFieldBackend backend; + backend._dateFormat = dateStringFormat; + + const auto currentDate = QDate::currentDate(); + const auto currentDateMSecs = currentDate.startOfDay(Qt::UTC).toMSecsSinceEpoch(); + const auto currentDateString = currentDate.toString(dateStringFormat); + + QCOMPARE(backend.date(), currentDate); + QCOMPARE(backend.dateMsecs(), currentDateMSecs); + QCOMPARE(backend.dateString(), currentDateString); + } +}; + +QTEST_MAIN(TestDateFieldBackend) +#include "testdatefieldbackend.moc" From a8a6474c359d90789983975ebfade1d48eed7978 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 13 Sep 2023 00:03:43 +0800 Subject: [PATCH 22/26] Add test for date field backend boundaries Signed-off-by: Claudio Cambra --- test/testdatefieldbackend.cpp | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/testdatefieldbackend.cpp b/test/testdatefieldbackend.cpp index eb1480207516b..918d633dec6a7 100644 --- a/test/testdatefieldbackend.cpp +++ b/test/testdatefieldbackend.cpp @@ -39,6 +39,47 @@ private slots: QCOMPARE(backend.dateMsecs(), currentDateMSecs); QCOMPARE(backend.dateString(), currentDateString); } + + void testDateBoundaries() + { + Quick::DateFieldBackend backend; + const auto minDate = QDate::currentDate().addDays(-5); + const auto maxDate = QDate::currentDate().addDays(5); + const auto minDateMs = minDate.startOfDay(Qt::UTC).toMSecsSinceEpoch(); + const auto maxDateMs = maxDate.startOfDay(Qt::UTC).toMSecsSinceEpoch(); + const auto invalidMinDate = minDate.addDays(-1); + const auto invalidMaxDate = maxDate.addDays(1); + + // Set by QDate + backend.setMinimumDate(minDate); + backend.setMaximumDate(maxDate); + + QCOMPARE(backend.minimumDate(), minDate); + QCOMPARE(backend.maximumDate(), maxDate); + QCOMPARE(backend.minimumDateMsecs(), minDateMs); + QCOMPARE(backend.maximumDateMsecs(), maxDateMs); + + // Reset and try when setting by MSecs + backend.setMinimumDate({}); + backend.setMaximumDate({}); + backend.setMinimumDateMsecs(minDateMs); + backend.setMaximumDateMsecs(maxDateMs); + + QCOMPARE(backend.minimumDate(), minDate); + QCOMPARE(backend.maximumDate(), maxDate); + QCOMPARE(backend.minimumDateMsecs(), minDateMs); + QCOMPARE(backend.maximumDateMsecs(), maxDateMs); + + // Since we default to the current date, the date should be valid + QVERIFY(backend.validDate()); + + // Now try with invalid dates + backend.setDate(invalidMinDate); + QVERIFY(!backend.validDate()); + + backend.setDate(invalidMaxDate); + QVERIFY(!backend.validDate()); + } }; QTEST_MAIN(TestDateFieldBackend) From 836b8c5f9d43a665a910c8b23909b898eeb1ad1c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 13 Sep 2023 00:42:39 +0800 Subject: [PATCH 23/26] Check that signals were correctly emitted in testDateBoundaries Signed-off-by: Claudio Cambra --- test/testdatefieldbackend.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/testdatefieldbackend.cpp b/test/testdatefieldbackend.cpp index 918d633dec6a7..776c61e1032da 100644 --- a/test/testdatefieldbackend.cpp +++ b/test/testdatefieldbackend.cpp @@ -43,6 +43,13 @@ private slots: void testDateBoundaries() { Quick::DateFieldBackend backend; + + QSignalSpy minimumDateChangedSpy(&backend, &Quick::DateFieldBackend::minimumDateChanged); + QSignalSpy maximumDateChangedSpy(&backend, &Quick::DateFieldBackend::maximumDateChanged); + QSignalSpy minimumDateMsecsChangedSpy(&backend, &Quick::DateFieldBackend::minimumDateMsecsChanged); + QSignalSpy maximumDateMsecsChangedSpy(&backend, &Quick::DateFieldBackend::maximumDateMsecsChanged); + QSignalSpy validDateChangedSpy(&backend, &Quick::DateFieldBackend::validDateChanged); + const auto minDate = QDate::currentDate().addDays(-5); const auto maxDate = QDate::currentDate().addDays(5); const auto minDateMs = minDate.startOfDay(Qt::UTC).toMSecsSinceEpoch(); @@ -59,6 +66,12 @@ private slots: QCOMPARE(backend.minimumDateMsecs(), minDateMs); QCOMPARE(backend.maximumDateMsecs(), maxDateMs); + QCOMPARE(minimumDateChangedSpy.count(), 1); + QCOMPARE(maximumDateChangedSpy.count(), 1); + QCOMPARE(minimumDateMsecsChangedSpy.count(), 1); + QCOMPARE(maximumDateMsecsChangedSpy.count(), 1); + QCOMPARE(validDateChangedSpy.count(), 2); // Changes per each min/max date set + // Reset and try when setting by MSecs backend.setMinimumDate({}); backend.setMaximumDate({}); @@ -70,15 +83,23 @@ private slots: QCOMPARE(backend.minimumDateMsecs(), minDateMs); QCOMPARE(backend.maximumDateMsecs(), maxDateMs); + QCOMPARE(minimumDateChangedSpy.count(), 3); + QCOMPARE(maximumDateChangedSpy.count(), 3); + QCOMPARE(minimumDateMsecsChangedSpy.count(), 3); + QCOMPARE(maximumDateMsecsChangedSpy.count(), 3); + QCOMPARE(validDateChangedSpy.count(), 6); + // Since we default to the current date, the date should be valid QVERIFY(backend.validDate()); // Now try with invalid dates backend.setDate(invalidMinDate); QVERIFY(!backend.validDate()); + QCOMPARE(validDateChangedSpy.count(), 7); backend.setDate(invalidMaxDate); QVERIFY(!backend.validDate()); + QCOMPARE(validDateChangedSpy.count(), 8); } }; From 6a01bb78ea51987943b72187b25e14c80a2e98d6 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 13 Sep 2023 00:59:57 +0800 Subject: [PATCH 24/26] Add a test for the various ways of setting a date on datefieldbackend Signed-off-by: Claudio Cambra --- test/testdatefieldbackend.cpp | 49 +++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/test/testdatefieldbackend.cpp b/test/testdatefieldbackend.cpp index 776c61e1032da..c80aa3a2db2e4 100644 --- a/test/testdatefieldbackend.cpp +++ b/test/testdatefieldbackend.cpp @@ -23,11 +23,12 @@ class TestDateFieldBackend : public QObject { Q_OBJECT +private: + static constexpr auto dateStringFormat = "dd/MM/yyyy"; + private slots: void testDefaultBehaviour() { - constexpr auto dateStringFormat = "dd/MM/yyyy"; - Quick::DateFieldBackend backend; backend._dateFormat = dateStringFormat; @@ -101,6 +102,50 @@ private slots: QVERIFY(!backend.validDate()); QCOMPARE(validDateChangedSpy.count(), 8); } + + void testDateSettingMethods() + { + Quick::DateFieldBackend backend; + backend._dateFormat = dateStringFormat; + + QSignalSpy dateChangedSpy(&backend, &Quick::DateFieldBackend::dateChanged); + QSignalSpy dateMsecsChangedSpy(&backend, &Quick::DateFieldBackend::dateMsecsChanged); + QSignalSpy dateStringChangedSpy(&backend, &Quick::DateFieldBackend::dateStringChanged); + + const auto testDate = QDate::currentDate().addDays(800); + const auto testDateMsecs = testDate.startOfDay(Qt::UTC).toMSecsSinceEpoch(); + const auto testDateString = testDate.toString(dateStringFormat); + + backend.setDate(testDate); + QCOMPARE(backend.date(), testDate); + QCOMPARE(dateChangedSpy.count(), 1); + QCOMPARE(dateMsecsChangedSpy.count(), 1); + QCOMPARE(dateStringChangedSpy.count(), 1); + + backend.setDate({}); + QVERIFY(backend.date() != testDate); + QCOMPARE(dateChangedSpy.count(), 2); + QCOMPARE(dateMsecsChangedSpy.count(), 2); + QCOMPARE(dateStringChangedSpy.count(), 2); + + backend.setDateMsecs(testDateMsecs); + QCOMPARE(backend.date(), testDate); + QCOMPARE(dateChangedSpy.count(), 3); + QCOMPARE(dateMsecsChangedSpy.count(), 3); + QCOMPARE(dateStringChangedSpy.count(), 3); + + backend.setDate({}); + QVERIFY(backend.date() != testDate); + QCOMPARE(dateChangedSpy.count(), 4); + QCOMPARE(dateMsecsChangedSpy.count(), 4); + QCOMPARE(dateStringChangedSpy.count(), 4); + + backend.setDateString(testDateString); + QCOMPARE(backend.date(), testDate); + QCOMPARE(dateChangedSpy.count(), 5); + QCOMPARE(dateMsecsChangedSpy.count(), 5); + QCOMPARE(dateStringChangedSpy.count(), 5); + } }; QTEST_MAIN(TestDateFieldBackend) From 9201eaa1721889cafcf31cc1e85930ba782fb0a0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 13 Sep 2023 01:05:20 +0800 Subject: [PATCH 25/26] Do not present NCInputDateField backend as public property Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputDateField.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/filedetails/NCInputDateField.qml b/src/gui/filedetails/NCInputDateField.qml index 9d1e7ce76cce4..4c0ea491c7fe4 100644 --- a/src/gui/filedetails/NCInputDateField.qml +++ b/src/gui/filedetails/NCInputDateField.qml @@ -25,7 +25,7 @@ NCInputTextField { text = backend.dateString; } - property DateFieldBackend backend: DateFieldBackend { + DateFieldBackend { id: backend onDateStringChanged: if (!root.activeFocus) root.updateText() } From dddb83bd7ece7fa8ae11e54a971ce03fba634fd8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 13 Sep 2023 01:27:56 +0800 Subject: [PATCH 26/26] More stringently check for min and max date validity in date field backend Signed-off-by: Claudio Cambra --- src/gui/filedetails/datefieldbackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/filedetails/datefieldbackend.cpp b/src/gui/filedetails/datefieldbackend.cpp index 0b806547cced7..036ee7ee8302d 100644 --- a/src/gui/filedetails/datefieldbackend.cpp +++ b/src/gui/filedetails/datefieldbackend.cpp @@ -170,11 +170,11 @@ bool DateFieldBackend::validDate() const { auto valid = _date.isValid(); - if (_minimumDate.isValid()) { + if (_minimumDate.isValid() && minimumDateMsecs() > 0) { valid &= _date >= _minimumDate; } - if (_maximumDate.isValid()) { + if (_maximumDate.isValid() && maximumDateMsecs() > 0) { valid &= _date <= _maximumDate; }