From e84a5dcb5f184b7dce93f2c07dafd0adc46ecf0a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 8 May 2023 17:16:50 +0800 Subject: [PATCH 1/5] Rename "moreMenu" ColumnLayout to "scrollContentsColumn" in ShareView, stop treating like menu which it used to be Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 102 +++++++++++------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index e8281f03af..304563ef5a 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -131,7 +131,6 @@ Page { } function resetMenu() { - moreMenu.close(); resetNoteField(); resetPasswordField(); @@ -240,7 +239,7 @@ Page { clip: true ColumnLayout { - id: moreMenu + id: scrollContentsColumn readonly property int rowIconWidth: Style.smallIconSize readonly property int indicatorItemWidth: 20 @@ -252,12 +251,12 @@ Page { RowLayout { Layout.fillWidth: true height: visible ? implicitHeight : 0 - spacing: moreMenu.indicatorSpacing + spacing: scrollContentsColumn.indicatorSpacing visible: root.isLinkShare Image { - Layout.preferredWidth: moreMenu.indicatorItemWidth + Layout.preferredWidth: scrollContentsColumn.indicatorItemWidth Layout.fillHeight: true verticalAlignment: Image.AlignVCenter @@ -265,8 +264,8 @@ Page { fillMode: Image.Pad source: "image://svgimage-custom-color/edit.svg/" + palette.dark - sourceSize.width: moreMenu.rowIconWidth - sourceSize.height: moreMenu.rowIconWidth + sourceSize.width: scrollContentsColumn.rowIconWidth + sourceSize.height: scrollContentsColumn.rowIconWidth } NCInputTextField { @@ -323,10 +322,11 @@ Page { toolTipBase: Style.backgroundColor toolTipText: Style.ncTextColor } - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding - indicator.width: moreMenu.indicatorItemWidth - indicator.height: moreMenu.indicatorItemWidth + + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding + indicator.width: scrollContentsColumn.indicatorItemWidth + indicator.height: scrollContentsColumn.indicatorItemWidth checkable: true checked: root.editingAllowed @@ -364,10 +364,10 @@ Page { enabled: !root.isSharePermissionChangeInProgress checked: root.currentPermissionMode === permissionMode text: qsTr("View only") - indicatorItemWidth: moreMenu.indicatorItemWidth - indicatorItemHeight: moreMenu.indicatorItemWidth - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding + indicatorItemWidth: scrollContentsColumn.indicatorItemWidth + indicatorItemHeight: scrollContentsColumn.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding onClicked: root.permissionModeChanged(permissionMode) } @@ -378,10 +378,10 @@ Page { enabled: !root.isSharePermissionChangeInProgress checked: root.currentPermissionMode === permissionMode text: qsTr("Allow upload and editing") - indicatorItemWidth: moreMenu.indicatorItemWidth - indicatorItemHeight: moreMenu.indicatorItemWidth - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding + indicatorItemWidth: scrollContentsColumn.indicatorItemWidth + indicatorItemHeight: scrollContentsColumn.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding onClicked: root.permissionModeChanged(permissionMode) NCBusyIndicator { @@ -399,10 +399,10 @@ Page { enabled: !root.isSharePermissionChangeInProgress checked: root.currentPermissionMode === permissionMode text: qsTr("File drop (upload only)") - indicatorItemWidth: moreMenu.indicatorItemWidth - indicatorItemHeight: moreMenu.indicatorItemWidth - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding + indicatorItemWidth: scrollContentsColumn.indicatorItemWidth + indicatorItemHeight: scrollContentsColumn.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding onClicked: root.permissionModeChanged(permissionMode) } } @@ -444,10 +444,11 @@ Page { toolTipText: Style.ncTextColor } - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding - indicator.width: moreMenu.indicatorItemWidth - indicator.height: moreMenu.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding + indicator.width: scrollContentsColumn.indicatorItemWidth + indicator.height: scrollContentsColumn.indicatorItemWidth + checked: root.hideDownload text: qsTr("Hide download") enabled: !root.isHideDownloadInProgress @@ -492,10 +493,10 @@ Page { toolTipText: Style.ncTextColor } - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding - indicator.width: moreMenu.indicatorItemWidth - indicator.height: moreMenu.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding + indicator.width: scrollContentsColumn.indicatorItemWidth + indicator.height: scrollContentsColumn.indicatorItemWidth checkable: true checked: root.passwordProtectEnabled @@ -519,12 +520,12 @@ Page { Layout.fillWidth: true height: visible ? implicitHeight : 0 - spacing: moreMenu.indicatorSpacing + spacing: scrollContentsColumn.indicatorSpacing visible: root.passwordProtectEnabled Image { - Layout.preferredWidth: moreMenu.indicatorItemWidth + Layout.preferredWidth: scrollContentsColumn.indicatorItemWidth Layout.fillHeight: true verticalAlignment: Image.AlignVCenter @@ -532,8 +533,8 @@ Page { fillMode: Image.Pad source: "image://svgimage-custom-color/lock-https.svg/" + palette.dark - sourceSize.width: moreMenu.rowIconWidth - sourceSize.height: moreMenu.rowIconWidth + sourceSize.width: scrollContentsColumn.rowIconWidth + sourceSize.height: scrollContentsColumn.rowIconWidth } NCInputTextField { @@ -621,10 +622,10 @@ Page { toolTipText: Style.ncTextColor } - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding - indicator.width: moreMenu.indicatorItemWidth - indicator.height: moreMenu.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding + indicator.width: scrollContentsColumn.indicatorItemWidth + indicator.height: scrollContentsColumn.indicatorItemWidth checkable: true checked: root.expireDateEnabled @@ -647,12 +648,12 @@ Page { RowLayout { Layout.fillWidth: true height: visible ? implicitHeight : 0 - spacing: moreMenu.indicatorSpacing + spacing: scrollContentsColumn.indicatorSpacing visible: root.expireDateEnabled Image { - Layout.preferredWidth: moreMenu.indicatorItemWidth + Layout.preferredWidth: scrollContentsColumn.indicatorItemWidth Layout.fillHeight: true verticalAlignment: Image.AlignVCenter @@ -660,8 +661,8 @@ Page { fillMode: Image.Pad source: "image://svgimage-custom-color/calendar.svg/" + palette.dark - sourceSize.width: moreMenu.rowIconWidth - sourceSize.height: moreMenu.rowIconWidth + sourceSize.width: scrollContentsColumn.rowIconWidth + sourceSize.height: scrollContentsColumn.rowIconWidth } // QML dates are essentially JavaScript dates, which makes them very finicky and unreliable. @@ -749,7 +750,6 @@ Page { 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 {} @@ -836,10 +836,10 @@ Page { toolTipText: Style.ncTextColor } - spacing: moreMenu.indicatorSpacing - padding: moreMenu.itemPadding - indicator.width: moreMenu.indicatorItemWidth - indicator.height: moreMenu.indicatorItemWidth + spacing: scrollContentsColumn.indicatorSpacing + padding: scrollContentsColumn.itemPadding + indicator.width: scrollContentsColumn.indicatorItemWidth + indicator.height: scrollContentsColumn.indicatorItemWidth checkable: true checked: root.noteEnabled @@ -862,12 +862,12 @@ Page { RowLayout { Layout.fillWidth: true height: visible ? implicitHeight : 0 - spacing: moreMenu.indicatorSpacing + spacing: scrollContentsColumn.indicatorSpacing visible: root.noteEnabled Image { - Layout.preferredWidth: moreMenu.indicatorItemWidth + Layout.preferredWidth: scrollContentsColumn.indicatorItemWidth Layout.fillHeight: true verticalAlignment: Image.AlignVCenter @@ -875,8 +875,8 @@ Page { fillMode: Image.Pad source: "image://svgimage-custom-color/edit.svg/" + palette.dark - sourceSize.width: moreMenu.rowIconWidth - sourceSize.height: moreMenu.rowIconWidth + sourceSize.width: scrollContentsColumn.rowIconWidth + sourceSize.height: scrollContentsColumn.rowIconWidth } NCInputTextEdit { From aaeaad991837414a8316e74e332d33c7cd5b7b27 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 8 May 2023 17:59:01 +0800 Subject: [PATCH 2/5] Make sure to imperatively set text for spinbox textfield after user breaks binding by editing Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index 304563ef5a..d1bec344f0 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -676,7 +676,10 @@ Page { 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 + onExpireDateReducedChanged: { + value = expireDateReduced; + expireDateSpinBoxTextField.text = textFromValue(value, locale); + } // 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 @@ -758,6 +761,8 @@ Page { padding: 0 background: null contentItem: NCInputTextField { + id: expireDateSpinBoxTextField + text: expireDateSpinBox.textFromValue(expireDateSpinBox.value, expireDateSpinBox.locale) readOnly: !expireDateSpinBox.editable validator: expireDateSpinBox.validator From da7d8c1f0a1b97695179cbc68fcc5776227a3a4d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 8 May 2023 20:00:34 +0800 Subject: [PATCH 3/5] Ensure the text for the expire date spin box is updated even in situations when the expire date is forced back down to the same one being currently held Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index d1bec344f0..90044734aa 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -111,7 +111,18 @@ Page { } function resetExpireDateField() { - // Expire date changing is handled by the expireDateSpinBox + // Expire date changing is handled through expireDateChanged listening in the expireDateSpinBox. + // + // When the user edits the expire date field they are changing the text, but the expire date + // value is only changed according to updates from the server. + // + // Sometimes the new expire date is the same -- say, because we were on the maximum allowed + // expire date already and we tried to push it beyond this, leading the server to just return + // the maximum allowed expire date. + // + // 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(); waitingForExpireDateChange = false; } @@ -671,6 +682,10 @@ Page { SpinBox { id: expireDateSpinBox + function updateText() { + expireDateSpinBoxTextField.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 @@ -678,7 +693,7 @@ Page { // Reset the model data after binding broken on user interact onExpireDateReducedChanged: { value = expireDateReduced; - expireDateSpinBoxTextField.text = textFromValue(value, locale); + updateText(); } // We can't use JS's convenient Infinity or Number.MAX_VALUE as From 00ec93f03111eca99a2bd0f2ce8d70dd103d3638 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 8 May 2023 20:02:43 +0800 Subject: [PATCH 4/5] Add ability for NCInputTextField to display red border when invalid input provided Signed-off-by: Claudio Cambra --- src/gui/filedetails/NCInputTextField.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/filedetails/NCInputTextField.qml b/src/gui/filedetails/NCInputTextField.qml index 37a7d23dc8..fa4bef0bfc 100644 --- a/src/gui/filedetails/NCInputTextField.qml +++ b/src/gui/filedetails/NCInputTextField.qml @@ -25,6 +25,7 @@ TextField { readonly property color accentColor: Style.ncBlue readonly property color secondaryColor: palette.dark readonly property alias submitButton: submitButton + property bool validInput: true implicitHeight: Style.talkReplyTextFieldPreferredHeight @@ -36,7 +37,7 @@ TextField { id: textFieldBorder radius: Style.slightlyRoundedButtonRadius border.width: Style.normalBorderWidth - border.color: root.activeFocus ? root.accentColor : root.secondaryColor + border.color: root.activeFocus ? root.validInput ? root.accentColor : Style.errorBoxBackgroundColor : root.secondaryColor color: palette.base } @@ -55,7 +56,7 @@ TextField { icon.source: "image://svgimage-custom-color/confirm.svg" + "/" + root.secondaryColor icon.color: hovered && enabled ? UserModel.currentUser.accentColor : root.secondaryColor - enabled: root.text !== "" + enabled: root.text !== "" && root.validInput onClicked: root.accepted() } From d0e8e67c724709ba7202d3a730925caffac51c65 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 8 May 2023 20:03:12 +0800 Subject: [PATCH 5/5] Track invalid input in ShareDetailsPage expireDateSpinBoxTextField Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareDetailsPage.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml index 90044734aa..a8755526f5 100644 --- a/src/gui/filedetails/ShareDetailsPage.qml +++ b/src/gui/filedetails/ShareDetailsPage.qml @@ -778,6 +778,11 @@ Page { 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