controlcenter: password dialog matches lockscreen

This commit is contained in:
ATMDA 2025-11-13 22:42:46 -05:00
parent 97e1bde96b
commit 144d04b08b
2 changed files with 181 additions and 39 deletions

View file

@ -13,7 +13,7 @@ TextField {
placeholderTextColor: Colours.palette.m3outline placeholderTextColor: Colours.palette.m3outline
font.family: Appearance.font.family.sans font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.smaller font.pointSize: Appearance.font.size.smaller
renderType: TextField.NativeRendering renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
cursorVisible: !readOnly cursorVisible: !readOnly
background: null background: null

View file

@ -8,6 +8,7 @@ import qs.components.effects
import qs.components.containers import qs.components.containers
import qs.services import qs.services
import qs.config import qs.config
import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
@ -27,7 +28,8 @@ Item {
return null; return null;
} }
visible: session.network.showPasswordDialog property bool isClosing: false
visible: session.network.showPasswordDialog && !isClosing
enabled: visible enabled: visible
focus: visible focus: visible
@ -38,10 +40,14 @@ Item {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.5) color: Qt.rgba(0, 0, 0, 0.5)
opacity: root.visible ? 1 : 0 opacity: root.visible && !root.isClosing ? 1 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: 200 } Anim {
property: "opacity"
duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.standardDecel
}
} }
MouseArea { MouseArea {
@ -60,15 +66,23 @@ Item {
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
color: Colours.tPalette.m3surface color: Colours.tPalette.m3surface
opacity: root.visible ? 1 : 0 opacity: root.visible && !root.isClosing ? 1 : 0
scale: root.visible ? 1 : 0.9 scale: root.visible && !root.isClosing ? 1 : 0.9
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: 200 } Anim {
property: "opacity"
duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.standardDecel
}
} }
Behavior on scale { Behavior on scale {
NumberAnimation { duration: 200 } Anim {
property: "scale"
duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.standardDecel
}
} }
Keys.onEscapePressed: closeDialog(); Keys.onEscapePressed: closeDialog();
@ -123,52 +137,150 @@ Item {
} }
Item { Item {
id: passwordContainer
Layout.topMargin: Appearance.spacing.large Layout.topMargin: Appearance.spacing.large
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: passwordField.implicitHeight + Appearance.padding.normal * 2 implicitHeight: Math.max(48, charList.implicitHeight + Appearance.padding.normal * 2)
focus: true
Keys.onPressed: event => {
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
if (connectButton.enabled) {
connectButton.clicked();
}
} else if (event.key === Qt.Key_Backspace) {
if (event.modifiers & Qt.ControlModifier) {
passwordBuffer = "";
} else {
passwordBuffer = passwordBuffer.slice(0, -1);
}
} else if (event.text && event.text.length > 0) {
passwordBuffer += event.text;
}
}
property string passwordBuffer: ""
Connections {
target: root
function onVisibleChanged(): void {
if (root.visible) {
passwordContainer.forceActiveFocus();
passwordContainer.passwordBuffer = "";
}
}
}
StyledRect { StyledRect {
anchors.fill: parent anchors.fill: parent
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer color: Colours.tPalette.m3surfaceContainer
border.width: passwordField.activeFocus ? 2 : 1 border.width: passwordContainer.activeFocus ? 2 : 1
border.color: passwordField.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline border.color: passwordContainer.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline
Behavior on border.color { Behavior on border.color {
CAnim {} CAnim {}
} }
} }
StyledTextField { StateLayer {
id: passwordField hoverEnabled: false
cursorShape: Qt.IBeamCursor
anchors.left: parent.left function onClicked(): void {
anchors.right: parent.right passwordContainer.forceActiveFocus();
anchors.verticalCenter: parent.verticalCenter }
anchors.margins: Appearance.padding.normal }
echoMode: TextField.Password StyledText {
placeholderText: qsTr("Password") id: placeholder
anchors.centerIn: parent
text: qsTr("Password")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.normal
font.family: Appearance.font.family.mono
opacity: passwordContainer.passwordBuffer ? 0 : 1
Connections { Behavior on opacity {
target: root Anim {}
function onVisibleChanged(): void { }
if (root.visible) { }
passwordField.forceActiveFocus();
passwordField.text = ""; ListView {
id: charList
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
anchors.centerIn: parent
implicitWidth: fullWidth
implicitHeight: Appearance.font.size.normal
orientation: Qt.Horizontal
spacing: Appearance.spacing.small / 2
interactive: false
model: ScriptModel {
values: passwordContainer.passwordBuffer.split("")
}
delegate: StyledRect {
id: ch
implicitWidth: implicitHeight
implicitHeight: charList.implicitHeight
color: Colours.palette.m3onSurface
radius: Appearance.rounding.small / 2
opacity: 0
scale: 0
Component.onCompleted: {
opacity = 1;
scale = 1;
}
ListView.onRemove: removeAnim.start()
SequentialAnimation {
id: removeAnim
PropertyAction {
target: ch
property: "ListView.delayRemove"
value: true
}
ParallelAnimation {
Anim {
target: ch
property: "opacity"
to: 0
}
Anim {
target: ch
property: "scale"
to: 0.5
}
}
PropertyAction {
target: ch
property: "ListView.delayRemove"
value: false
}
}
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
} }
} }
} }
Keys.onReturnPressed: { Behavior on implicitWidth {
if (connectButton.enabled) { Anim {}
connectButton.clicked();
}
}
Keys.onEnterPressed: {
if (connectButton.enabled) {
connectButton.clicked();
}
} }
} }
} }
@ -196,7 +308,7 @@ Item {
color: Colours.palette.m3primary color: Colours.palette.m3primary
onColor: Colours.palette.m3onPrimary onColor: Colours.palette.m3onPrimary
text: qsTr("Connect") text: qsTr("Connect")
enabled: passwordField.text.length > 0 && !connecting enabled: passwordContainer.passwordBuffer.length > 0 && !connecting
property bool connecting: false property bool connecting: false
@ -205,7 +317,7 @@ Item {
return; return;
} }
const password = passwordField.text; const password = passwordContainer.passwordBuffer;
if (!password || password.length === 0) { if (!password || password.length === 0) {
return; return;
} }
@ -220,7 +332,19 @@ Item {
root.network.ssid, root.network.ssid,
password, password,
root.network.bssid || "", root.network.bssid || "",
null (result) => {
if (result && result.success) {
// Connection successful, monitor will handle the rest
} else if (result && result.needsPassword) {
// Shouldn't happen since we provided password
connectionMonitor.stop();
connecting = false;
enabled = true;
text = qsTr("Connect");
} else {
// Connection failed, monitor will handle timeout
}
}
); );
// Start monitoring connection // Start monitoring connection
@ -295,13 +419,31 @@ Item {
checkConnectionStatus(); checkConnectionStatus();
} }
} }
function onConnectionFailed(ssid: string) {
if (root.visible && root.network && root.network.ssid === ssid && connectButton.connecting) {
connectionMonitor.stop();
connectButton.connecting = false;
connectButton.enabled = true;
connectButton.text = qsTr("Connect");
}
}
} }
function closeDialog(): void { function closeDialog(): void {
session.network.showPasswordDialog = false; if (isClosing) {
passwordField.text = ""; return;
}
isClosing = true;
passwordContainer.passwordBuffer = "";
connectButton.connecting = false; connectButton.connecting = false;
connectButton.text = qsTr("Connect"); connectButton.text = qsTr("Connect");
connectionMonitor.stop(); connectionMonitor.stop();
// Wait for fade-out animation to complete before actually hiding
Qt.callLater(() => {
session.network.showPasswordDialog = false;
isClosing = false;
}, Appearance.anim.durations.normal);
} }
} }