From 7b263f7bf5f56e43aa4eb9ad41f0655e38cb6e0e Mon Sep 17 00:00:00 2001 From: Oliver Eftevaag Date: Wed, 10 Feb 2021 14:51:42 +0100 Subject: [PATCH] Improve visuals and usability Usability has been improved when entering new rows into the database. The user is now only able to write in the input form when editing an exiting entry or creating a new entry. The warning message associated with entering invalid input will now disappear after a few seconds. The ListView delegate is now slightly larger, and looks slightly better. Task-number: QTBUG-90884 Change-Id: Idf7ca9c0bb8b86ac06fed80e945800603f7b03d9 Reviewed-by: Mitch Curtis --- .../localstorage/localstorage/Header.qml | 61 +++++++------ .../localstorage/localstorage/MyDelegate.qml | 29 ++++--- .../localstorage/localstorage.qml | 86 ++++++++++++++++--- 3 files changed, 130 insertions(+), 46 deletions(-) diff --git a/examples/quick/localstorage/localstorage/Header.qml b/examples/quick/localstorage/localstorage/Header.qml index d1dedaec3f..3328a88559 100644 --- a/examples/quick/localstorage/localstorage/Header.qml +++ b/examples/quick/localstorage/localstorage/Header.qml @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. @@ -60,7 +59,8 @@ Item { height: Screen.height / 7 required property ListView listView - required property Text statusText + signal statusMessage(string msg) + enabled: false function insertrec() { var rowid = parseInt(JS.dbInsert(dateInput.text, descInput.text, distInput.text), 10) @@ -125,19 +125,19 @@ Item { anchors.fill: parent Label { - text: "Date" + text: qsTr("Date") font.pixelSize: 22 rightPadding: 10 } Label { - text: "Description" + text: qsTr("Description") font.pixelSize: 22 rightPadding: 10 } Label { - text: "Distance" + text: qsTr("Distance") font.pixelSize: 22 } @@ -146,14 +146,29 @@ Item { font.pixelSize: 22 activeFocusOnPress: true activeFocusOnTab: true - validator: RegularExpressionValidator { - regularExpression: /[0-9/,:.]+/ + + ToolTip { + parent: dateInput + x: parent.width + 3 + y: (parent.height - height) / 2 + text: qsTr("Date format = 'YYYY-MM-DD'") + visible: parent.enabled && parent.hovered + delay: 1000 } - onEditingFinished: { - if (dateInput.text == "") { - root.statusText.text = "Please fill in the date" - dateInput.forceActiveFocus() - } + + validator: RegularExpressionValidator { + regularExpression: /\d{4}[,.:/-]\d\d?[,.:/-]\d\d?/ + } + + onFocusChanged: ()=> { + if (!dateInput.focus && !acceptableInput && root.enabled) + root.statusMessage(qsTr("Please fill in the date")); + } + + onEditingFinished: ()=> { + let regex = /(\d+)[,.:/-](\d+)[,.:/-](\d+)/ + if (dateInput.text.match(regex)) + dateInput.text = dateInput.text.replace(regex, '$1-$2-$3') } } @@ -162,13 +177,11 @@ Item { font.pixelSize: 22 activeFocusOnPress: true activeFocusOnTab: true - onEditingFinished: { - if (descInput.text.length < 8) { - root.statusText.text = "Enter a description of minimum 8 characters" - descInput.forceActiveFocus() - } else { - root.statusText.text = "" - } + property string oldString + onFocusChanged: ()=> { if (focus) oldString = descInput.text; } + onEditingFinished: ()=> { + if (descInput.text.length < 8 && descInput.text != descInput.oldString && root.enabled) + root.statusMessage(qsTr("Enter a description of minimum 8 characters")) } } @@ -180,11 +193,11 @@ Item { validator: RegularExpressionValidator { regularExpression: /\d{1,3}/ } - onEditingFinished: { - if (distInput.text == "") { - root.statusText.text = "Please fill in the distance" - distInput.forceActiveFocus() - } + property string oldString + onFocusChanged: ()=> { if (focus) oldString = distInput.text; } + onEditingFinished: ()=> { + if (distInput.text == "" && distInput.text != distInput.oldString && root.enabled) + root.statusMessage(qsTr("Please fill in the distance")) } } } diff --git a/examples/quick/localstorage/localstorage/MyDelegate.qml b/examples/quick/localstorage/localstorage/MyDelegate.qml index 6fbb9544be..b715882ab4 100644 --- a/examples/quick/localstorage/localstorage/MyDelegate.qml +++ b/examples/quick/localstorage/localstorage/MyDelegate.qml @@ -57,8 +57,8 @@ import "Database.js" as JS Item { id: delegate - width: parent.width - height: rDate.implicitHeight + width: ListView.view.width + implicitHeight: rDate.implicitHeight * 1.5 required property int index required property int distance @@ -72,34 +72,43 @@ Item { anchors.fill: parent opacity: 0.8 color: delegate.index % 2 ? "lightgrey" : "grey" + border.width: 2 + border.color: Qt.lighter(color) + radius: 5 MouseArea { anchors.fill: parent onClicked: delegate.clicked() } - GridLayout { - anchors.fill:parent - columns: 3 + + RowLayout { + anchors.fill: parent Label { id: rDate + Layout.preferredWidth: 42 + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter text: delegate.date font.pixelSize: 22 - Layout.preferredWidth: parent.width / 4 color: "black" } + Label { - id: rDesc + Layout.preferredWidth: 42 + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter text: delegate.trip_desc - Layout.fillWidth: true font.pixelSize: 22 color: "black" } + Label { - id: rDistance + Layout.preferredWidth: 42 + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter text: delegate.distance font.pixelSize: 22 - Layout.alignment: Qt.AlignRight color: "black" } } diff --git a/examples/quick/localstorage/localstorage/localstorage.qml b/examples/quick/localstorage/localstorage/localstorage.qml index 5915b67367..15710fc55c 100644 --- a/examples/quick/localstorage/localstorage/localstorage.qml +++ b/examples/quick/localstorage/localstorage/localstorage.qml @@ -69,16 +69,18 @@ Window { ColumnLayout { anchors.fill: parent + anchors.margins: 10 Header { id: input Layout.fillWidth: true listView: listView - statusText: statustext + enabled: window.creatingNewEntry || window.editingEntry } + RowLayout { Button { - text: "New" + text: qsTr("New") onClicked: { input.initrec_new() window.creatingNewEntry = true @@ -88,7 +90,7 @@ Window { Button { id: saveButton enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex != -1 - text: "Save" + text: qsTr("Save") onClicked: { var insertedRow = false; if (listView.model.get(listView.currentIndex).id < 1) { @@ -99,7 +101,7 @@ Window { insertedRow = true } else { // Failed to insert a row; display an error message. - statustext.text = "Failed to insert row" + statustext.displayWarning(qsTr("Failed to insert row")) } } else { // edit mode @@ -120,7 +122,7 @@ Window { } Button { id: editButton - text: "Edit" + text: qsTr("Edit") enabled: !window.creatingNewEntry && !window.editingEntry && listView.currentIndex != -1 onClicked: { input.editrec(listView.model.get(listView.currentIndex).date, @@ -133,7 +135,7 @@ Window { } Button { id: deleteButton - text: "Delete" + text: qsTr("Delete") enabled: !window.creatingNewEntry && listView.currentIndex != -1 onClicked: { JS.dbDeleteRow(listView.model.get(listView.currentIndex).id) @@ -147,7 +149,7 @@ Window { } Button { id: cancelButton - text: "Cancel" + text: qsTr("Cancel") enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex != -1 onClicked: { if (listView.model.get(listView.currentIndex).id === 0) { @@ -162,10 +164,19 @@ Window { } } Button { - text: "Exit" + text: qsTr("Exit") onClicked: Qt.quit() } } + Item { + Layout.fillWidth: true + height: 5 + } + Label { + Layout.alignment: Qt.AlignCenter + text: qsTr("Saved activities") + font.pointSize: 15 + } Component { id: highlightBar Rectangle { @@ -180,7 +191,8 @@ Window { Layout.fillHeight: true model: MyModel {} delegate: MyDelegate { - onClicked: listView.currentIndex = index + width: listView.width + onClicked: ()=> listView.currentIndex = index } // Don't allow changing the currentIndex while the user is creating/editing values. enabled: !window.creatingNewEntry && !window.editingEntry @@ -188,20 +200,70 @@ Window { highlight: highlightBar highlightFollowsCurrentItem: true focus: true + clip: true header: Component { - Text { - text: "Saved activities" + RowLayout { + property var headerTitles: [qsTr("Date"), qsTr("Description"), qsTr("Distance")] + width: ListView.view.width + Repeater { + model: headerTitles + delegate: Label { + id: headerTitleDelegate + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 1 + text: modelData + font.pointSize: 15 + font.bold: true + font.underline: true + padding: 12 + horizontalAlignment: Label.AlignHCenter + } + } } } } Label { id: statustext color: "red" - Layout.fillWidth: true font.bold: true font.pointSize: 20 + opacity: 0.0 + visible: opacity !== 0 // properly cull item if effectively invisible + Layout.alignment: Layout.Center + function displayWarning(text) { + statustext.text = text + statusAnim.restart() + } + + Connections { + target: input + function onStatusMessage(msg) { statustext.displayWarning(msg); } + } + + SequentialAnimation { + id: statusAnim + + OpacityAnimator { + target: statustext + from: 0.0 + to: 1.0 + duration: 50 + } + + PauseAnimation { + duration: 2000 + } + + OpacityAnimator { + target: statustext + from: 1.0 + to: 0.0 + duration: 50 + } + } } } }