Feature: Password input
This commit is contained in:
@@ -44,7 +44,13 @@ Item {
|
||||
return null
|
||||
}
|
||||
|
||||
// Only scan for networks while the panel is actually open.
|
||||
// Only scan for networks while the panel is actually open. Deliberately
|
||||
// NOT paused while a password prompt is up: scannerEnabled: false stops
|
||||
// NetworkManager from refreshing its AP cache at all, so the existing
|
||||
// list quietly ages out with nothing to replace it — the fix for typing
|
||||
// being interrupted by scan-driven refreshes lives below instead (the
|
||||
// password field itself is pulled out of the repeated row so refreshes
|
||||
// can't touch it), not here.
|
||||
Binding {
|
||||
target: root.wifiDevice
|
||||
property: "scannerEnabled"
|
||||
@@ -135,8 +141,17 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// SSID of the row currently showing its inline password field, if any.
|
||||
// SSID awaiting a password, if any. The input itself lives in a single
|
||||
// persistent widget below the network list (see passwordEntryInput),
|
||||
// not inside the per-row NetworkRow delegate — wifiRows is rebuilt from
|
||||
// scratch on every scan result, and a Repeater over a plain array has no
|
||||
// way to diff that, so it destroys and recreates every delegate each
|
||||
// time. Keeping the field outside that churn is what lets typing survive
|
||||
// a scan landing mid-keystroke.
|
||||
property string passwordSsid: ""
|
||||
// Cleared whenever the target SSID changes so stale text from a
|
||||
// cancelled/previous prompt never carries over to the next one.
|
||||
onPasswordSsidChanged: passwordEntryInput.text = ""
|
||||
|
||||
// Wired connections don't have a user-facing "name" on the device or
|
||||
// Network object — those just expose the interface name (e.g. "eth0").
|
||||
@@ -179,6 +194,16 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function submitPassword() {
|
||||
const n = root.networkForSsid(root.passwordSsid)
|
||||
if (n && passwordEntryInput.text.length > 0) n.connectWithPsk(passwordEntryInput.text)
|
||||
root.passwordSsid = ""
|
||||
}
|
||||
|
||||
function cancelPassword() {
|
||||
root.passwordSsid = ""
|
||||
}
|
||||
|
||||
// --- Live stats --------------------------------------------------
|
||||
// None of this comes from Quickshell's Networking API — it only knows
|
||||
// about devices/networks, not throughput, ping, or routing. So this
|
||||
@@ -529,13 +554,16 @@ Item {
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: root.connectedDevice ? Colors.foreground : Colors.muted
|
||||
// Wired/disconnected use their own distinct glyphs; only the WiFi
|
||||
// case renders one of wifiIcon()'s flat/wide wifi-fan glyphs, so
|
||||
// only that case needs the vertical-stretch correction below.
|
||||
readonly property bool isWifiFan: root.connectedDevice !== null && root.connectedDevice.type !== DeviceType.Wired
|
||||
// Only Wired renders its own distinct glyph; disconnected uses
|
||||
// "signal_wifi_off" — the actual off companion to the
|
||||
// network_wifi/signal_wifi fan family used below (not "wifi_off",
|
||||
// which belongs to a visually different, newer glyph family and
|
||||
// looked inconsistent next to the connected bars) — so it needs the
|
||||
// same vertical-stretch correction as the rest of that family.
|
||||
readonly property bool isWifiFan: root.connectedDevice === null || root.connectedDevice.type !== DeviceType.Wired
|
||||
text: {
|
||||
if (!root.connectedDevice)
|
||||
return "wifi_off"
|
||||
return "signal_wifi_off"
|
||||
if (root.connectedDevice.type === DeviceType.Wired)
|
||||
return "settings_ethernet"
|
||||
return root.wifiIcon(root.connectedWifiSignal)
|
||||
@@ -593,20 +621,17 @@ Item {
|
||||
id: netRow
|
||||
required property var net
|
||||
signal activate()
|
||||
// Still tracked for the highlight below — the actual input lives
|
||||
// outside this (repeatedly rebuilt) delegate, in the persistent
|
||||
// password entry widget under the network list.
|
||||
readonly property bool passwordOpen: root.passwordSsid === netRow.net.ssid
|
||||
width: parent ? parent.width : 0
|
||||
radius: 0
|
||||
implicitHeight: rowContent.implicitHeight + 12 + (passwordOpen ? passwordRow.implicitHeight + 6 : 0)
|
||||
color: netRow.net.connected
|
||||
implicitHeight: rowContent.implicitHeight + 12
|
||||
color: (netRow.net.connected || netRow.passwordOpen)
|
||||
? Qt.rgba(Colors.primary.r, Colors.primary.g, Colors.primary.b, 0.18)
|
||||
: (rowMouse.containsMouse ? Colors.primary : "transparent")
|
||||
|
||||
function submit() {
|
||||
const n = root.networkForSsid(netRow.net.ssid)
|
||||
if (n && passwordInput.text.length > 0) n.connectWithPsk(passwordInput.text)
|
||||
root.passwordSsid = ""
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
@@ -676,53 +701,6 @@ Item {
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: passwordRow
|
||||
visible: netRow.passwordOpen
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 24
|
||||
radius: 0
|
||||
color: Colors.background
|
||||
border.color: Colors.muted
|
||||
border.width: 1
|
||||
|
||||
TextInput {
|
||||
id: passwordInput
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
echoMode: TextInput.Password
|
||||
color: Colors.foreground
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
clip: true
|
||||
focus: netRow.passwordOpen
|
||||
onAccepted: netRow.submit()
|
||||
// Explicit fallback alongside onAccepted, which only
|
||||
// fires when acceptableInput is true — covers both
|
||||
// the main Return key and numpad Enter directly.
|
||||
Keys.onReturnPressed: netRow.submit()
|
||||
Keys.onEnterPressed: netRow.submit()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "check"
|
||||
font.family: Appearance.iconFontFamilyRounded
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: Colors.primary
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: netRow.submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -1080,6 +1058,97 @@ Item {
|
||||
color: Colors.muted
|
||||
}
|
||||
|
||||
// Password entry — a single persistent widget rather
|
||||
// than living inside the NetworkRow delegate for
|
||||
// root.passwordSsid, so a wifi scan landing mid-keystroke
|
||||
// (which rebuilds the whole Known/Other Networks list
|
||||
// below) can't destroy the focused TextInput and wipe
|
||||
// out what's been typed. See passwordSsid's own comment.
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
visible: root.passwordSsid !== ""
|
||||
radius: 0
|
||||
color: Colors.background
|
||||
border.color: Colors.muted
|
||||
border.width: 1
|
||||
implicitHeight: passwordEntryColumn.implicitHeight + 12
|
||||
|
||||
Column {
|
||||
id: passwordEntryColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Password for “" + root.passwordSsid + "”"
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 24
|
||||
radius: 0
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 1
|
||||
|
||||
TextInput {
|
||||
id: passwordEntryInput
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
echoMode: TextInput.Password
|
||||
color: Colors.foreground
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
clip: true
|
||||
focus: root.passwordSsid !== ""
|
||||
onAccepted: root.submitPassword()
|
||||
// Explicit fallback alongside onAccepted, which only
|
||||
// fires when acceptableInput is true — covers both
|
||||
// the main Return key and numpad Enter directly.
|
||||
Keys.onReturnPressed: root.submitPassword()
|
||||
Keys.onEnterPressed: root.submitPassword()
|
||||
Keys.onEscapePressed: root.cancelPassword()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "check"
|
||||
font.family: Appearance.iconFontFamilyRounded
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: Colors.primary
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.submitPassword()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "close"
|
||||
font.family: Appearance.iconFontFamilyRounded
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: Colors.muted
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.cancelPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Known/Other Networks — capped height, scrolls once
|
||||
// there are more networks than fit rather than growing
|
||||
// the popup indefinitely.
|
||||
|
||||
Reference in New Issue
Block a user