From 30bf68c67ea32898d05a39c013cf60750392b439 Mon Sep 17 00:00:00 2001 From: Tian Shilin Date: Tue, 3 Feb 2026 21:05:28 +0800 Subject: [PATCH] fix: Optimise Password Change Interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit log: Within the password modification window of the account plugin, ensure that when the ‘New Password / Confirm Password’ fields are non-compliant and receive focus, the blue focus border no longer obscures the red error state. Instead, maintain the red colour (the focus border turns red during error state and reverts to blue upon compliance), whilst retaining the red background and prompt. pms: bug-300203 --- .../operation/accountscontroller.cpp | 10 + .../operation/accountscontroller.h | 2 + src/plugin-accounts/qml/PasswordLayout.qml | 173 ++++++++++++++---- 3 files changed, 154 insertions(+), 31 deletions(-) diff --git a/src/plugin-accounts/operation/accountscontroller.cpp b/src/plugin-accounts/operation/accountscontroller.cpp index 053a11771c..295ea21117 100644 --- a/src/plugin-accounts/operation/accountscontroller.cpp +++ b/src/plugin-accounts/operation/accountscontroller.cpp @@ -854,6 +854,16 @@ QString AccountsController::checkPassword(const QString &name, const QString &pw return QString(); } +QString AccountsController::checkPasswordSilently(const QString &name, const QString &pwd) +{ + auto error = PwqualityManager::instance()->verifyPassword(name, pwd); + if (error != PwqualityManager::ERROR_TYPE::PW_NO_ERR) { + return PwqualityManager::instance()->getErrorTips(error); + } + + return QString(); +} + QVariantMap AccountsController::checkPasswordResult(int code, const QString &msg, const QString &name, const QString &pwd) { if (code == 0) diff --git a/src/plugin-accounts/operation/accountscontroller.h b/src/plugin-accounts/operation/accountscontroller.h index 9b726e28b6..eb25600893 100644 --- a/src/plugin-accounts/operation/accountscontroller.h +++ b/src/plugin-accounts/operation/accountscontroller.h @@ -90,6 +90,8 @@ public slots: QString checkUsername(const QString &name); QString checkFullname(const QString &name); QString checkPassword(const QString &name, const QString &pwd); + // Used by UI realtime validation (no safety-page popup side effects). + QString checkPasswordSilently(const QString &name, const QString &pwd); QVariantMap checkPasswordResult(int code, const QString &msg, const QString &name, const QString &pwd); void showDefender(); void playSystemSound(int soundType); diff --git a/src/plugin-accounts/qml/PasswordLayout.qml b/src/plugin-accounts/qml/PasswordLayout.qml index f702fbaf55..1eb3293d8e 100644 --- a/src/plugin-accounts/qml/PasswordLayout.qml +++ b/src/plugin-accounts/qml/PasswordLayout.qml @@ -346,6 +346,99 @@ ColumnLayout { } else { pwdIndicator.update(0) } + + // Realtime validation (red border only, no tips). + let edit0 = pwdContainter.eidtItems[0] + if (!edit0) + return + + if (text.length === 0) { + edit0.showAlert = false + edit0.alertText = "" + edit0.hasErrorBorder = false + return + } + + // username match + if (pwdLayout.currentName.length > 0 && text === pwdLayout.currentName) { + edit0.showAlert = true + edit0.alertText = "" + edit0.hasErrorBorder = true + return + } + + let err = dccData.checkPasswordSilently(pwdLayout.currentName, text) + edit0.showAlert = (err.length > 0) + edit0.alertText = "" + edit0.hasErrorBorder = (err.length > 0) + } else if (index == 1) { + // Repeat password: realtime red border when mismatch (no tips text yet) + let edit0 = pwdContainter.eidtItems[0] + let edit1 = pwdContainter.eidtItems[1] + if (!edit0 || !edit1) + return + + if (text.length === 0) { + edit1.showAlert = false + edit1.alertText = "" + edit1.hasErrorBorder = false + return + } + + if (edit0.text.length > 0 && text !== edit0.text) { + edit1.showAlert = true + edit1.alertText = "" + edit1.hasErrorBorder = true + } else { + edit1.showAlert = false + edit1.alertText = "" + edit1.hasErrorBorder = false + } + } + } + onEditingFinished: { + // Validate and show tips on focus-out (v20 behavior) + if (index == 0) { + let edit0 = pwdContainter.eidtItems[0] + if (!edit0) + return + + if (edit0.text.length === 0) { + edit0.showAlertText(qsTr("Password cannot be empty")) + return + } + + if (pwdLayout.currentName.length > 0 && edit0.text === pwdLayout.currentName) { + edit0.showAlertText(qsTr("The password cannot be the same as the username.")) + return + } + + let err = dccData.checkPasswordSilently(pwdLayout.currentName, edit0.text) + if (err.length > 0) { + edit0.showAlertText(err) + } else { + edit0.showAlert = false + edit0.alertText = "" + edit0.hasErrorBorder = false + } + } else if (index == 1) { + let edit0 = pwdContainter.eidtItems[0] + let edit1 = pwdContainter.eidtItems[1] + if (!edit0 || !edit1) + return + + if (edit1.text.length === 0) { + edit1.showAlertText(qsTr("Password cannot be empty")) + return + } + + if (edit0.text.length > 0 && edit1.text !== edit0.text) { + edit1.showAlertText(qsTr("Passwords do not match")) + } else { + edit1.showAlert = false + edit1.alertText = "" + edit1.hasErrorBorder = false + } } } Component.onCompleted: { @@ -355,6 +448,11 @@ ColumnLayout { background: DccItemBackground { separatorVisible: true + focusBorderVisible: !(control + && control.contentItem + && control.contentItem.edit + && (control.contentItem.edit.hasErrorBorder + || control.contentItem.edit.showAlert)) } } } @@ -376,6 +474,7 @@ ColumnLayout { property alias label: leftItem property alias edit: rightItem signal textChanged(string text) + signal editingFinished() spacing: 10 Layout.alignment: Qt.AlignVCenter @@ -396,45 +495,57 @@ ColumnLayout { } } - D.PasswordEdit { - id: rightItem + Item { + id: editWrapper Layout.preferredHeight: 30 - topPadding: 0 - bottomPadding: 0 - font: D.DTK.fontManager.t7 - canCopy: false - canCut: false - inputMethodHints: Qt.ImhHiddenText | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase Layout.fillWidth: true Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - verticalAlignment: TextInput.AlignVCenter - echoMode: echoButtonVisible ? TextInput.Password : TextInput.Normal - alertDuration: 3000 - onTextChanged: { - if (showAlert) - showAlert = false - - if (!echoButtonVisible && text.length > 14) { - rightItem.text = text.substring(0, 14) - playErrorSound() - return + + D.PasswordEdit { + id: rightItem + anchors.fill: parent + property bool hasErrorBorder: false + // Cache the normal focus color so we can restore it. + property color normalHighlight: "transparent" + Component.onCompleted: normalHighlight = palette.highlight + palette.highlight: (hasErrorBorder || showAlert) ? "#FF5736" : normalHighlight + topPadding: 0 + bottomPadding: 0 + font: D.DTK.fontManager.t7 + canCopy: false + canCut: false + inputMethodHints: Qt.ImhHiddenText | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + verticalAlignment: TextInput.AlignVCenter + echoMode: echoButtonVisible ? TextInput.Password : TextInput.Normal + alertDuration: 3000 + onTextChanged: { + if (rightItem.hasErrorBorder) + rightItem.hasErrorBorder = false + + if (!echoButtonVisible && text.length > 14) { + rightItem.text = text.substring(0, 14) + playErrorSound() + return + } + + pwdItem.textChanged(text) } - - pwdItem.textChanged(text) - } - onEditingFinished: { - if (echoButtonVisible && pwdContainter.eidtItems[2] != rightItem) { - if (text === pwdLayout.currentName && text.length > 0) { - showAlertText(qsTr("The password cannot be the same as the username.")) + onEditingFinished: { + if (echoButtonVisible && pwdContainter.eidtItems[2] != rightItem) { + if (text === pwdLayout.currentName && text.length > 0) { + showAlertText(qsTr("The password cannot be the same as the username.")) + } } + pwdItem.editingFinished() } - } - function showAlertText(text) { - rightItem.showAlert = false - rightItem.showAlert = true - rightItem.alertText = text + function showAlertText(text) { + rightItem.hasErrorBorder = true + rightItem.showAlert = false + rightItem.showAlert = true + rightItem.alertText = text + } } } }