Files
Panama/config/dot/quickshell/modules/settings/LiveFieldRow.qml
T

108 lines
3.8 KiB
QML

// A row whose trailing control is a free-text field that reports every
// keystroke.
//
// LiveFieldRow {
// label: "Username"
// detail: root.userNameProblem !== "" ? root.userNameProblem : "Lowercase…"
// invalid: root.userNameProblem !== ""
// onEdited: value => root.newUserName = value
// }
//
// TextFieldRow commits on Enter or blur, which is right when every commit is a
// privileged write that prompts. It is wrong for a form that has to say whether
// what is being typed is acceptable *while* it is being typed: the add-user
// form used TextFieldRow and its Create button stayed dark until the field lost
// focus, which reads as a broken button rather than as a field awaiting blur.
//
// So this reports twice. `edited` fires per keystroke, for validation and for
// state a page holds until a submit button is pressed. `accepted` fires on
// Enter or when focus leaves, for the values that go straight to a privileged
// write and must not prompt once per character.
import QtQuick
import qs.config
SettingRow {
id: root
property string text: ""
property string placeholder: ""
property bool enabled: true
// Draws the field as rejected. The page decides what invalid means; this
// only shows it, next to the detail line that says why.
property bool invalid: false
property int maximumLength: 0
signal edited(value: string)
signal accepted(value: string)
controlWidth: 220
function clear(): void {
input.text = "";
}
// An external change replaces what is shown, unless it would yank the field
// out from under someone mid-edit.
onTextChanged: if (!input.activeFocus && input.text !== root.text) input.text = root.text
Rectangle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: root.controlWidth
height: 32
radius: 9
color: Theme.alpha(Theme.fg, root.enabled ? 0.06 : 0.03)
border.width: input.activeFocus ? 2 : 1
border.color: root.invalid
? Theme.alpha(Theme.danger, 0.6)
: (input.activeFocus ? Theme.alpha(Theme.accent, 0.55) : Theme.alpha(Theme.fg, 0.1))
opacity: root.enabled ? 1 : 0.5
TextInput {
id: input
anchors.fill: parent
anchors.leftMargin: 11
anchors.rightMargin: 11
enabled: root.enabled
activeFocusOnTab: true
text: root.text
color: root.invalid ? Theme.danger : Theme.fg
selectByMouse: true
selectionColor: Theme.alpha(Theme.accent, 0.5)
selectedTextColor: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
verticalAlignment: TextInput.AlignVCenter
maximumLength: root.maximumLength > 0 ? root.maximumLength : 32767
clip: true
onTextEdited: root.edited(input.text)
onAccepted: if (input.text !== root.text) root.accepted(input.text)
// Unchanged text is not re-committed: a page whose `accepted`
// handler writes through polkit would otherwise prompt every time
// the field was tabbed past.
onActiveFocusChanged: {
if (input.activeFocus)
return;
if (input.text !== root.text)
root.accepted(input.text);
else
input.text = root.text;
}
Text {
anchors.fill: parent
visible: input.text === ""
text: root.placeholder
color: Theme.fgMuted
font: input.font
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
}
}