qml: OptionsPopup: Resize, rework scene graph

This commit is contained in:
Tom Dewey 2024-07-22 16:50:15 +01:00 committed by Tom Dewey
parent c693b5e3f7
commit 3fed45ef43

View file

@ -12,11 +12,17 @@ import "qmlcomponents"
Window { Window {
id: popup id: popup
width: cl.implicitWidth+cl.spacing width: Math.min(550, optionsStack.minimumWidth)
height: Math.min(420, optionsStack.minimumHeight)
minimumWidth: width minimumWidth: width
maximumWidth: width // Deliberately do not set a maximum width - if the user wants to resize, let them.
minimumHeight: 125 //maximumWidth: width
height: Math.min(750, cl.implicitHeight)
minimumHeight: height
// Deliberately do not set a maximum height - if the user wants to resize, let them.
//maximumHeight: height
title: qsTr("OS Customization") title: qsTr("OS Customization")
property bool initialized: false property bool initialized: false
@ -31,455 +37,462 @@ Window {
signal saveSettingsSignal(var settings) signal saveSettingsSignal(var settings)
ColumnLayout { Keys.onEscapePressed: {
id: cl popup.close()
spacing: 10 }
anchors.fill: parent
// Keys handlers can only be attached to Items. Window is not an TabBar {
// Item, but ColumnLayout is, so put this handler here. id: bar
Keys.onEscapePressed: {
popup.close() anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
TabButton {
text: qsTr("General")
onClicked: {
if (chkSetUser.checked && !fieldUserPassword.length) {
fieldUserPassword.forceActiveFocus()
}
}
} }
TabButton {
text: qsTr("Services")
}
TabButton {
text: qsTr("Options")
}
}
ScrollView { ScrollView {
id: popupbody id: popupbody
font.family: roboto.name font.family: roboto.name
//Layout.maximumWidth: popup.width-30
Layout.fillWidth: true anchors.right: parent.right
Layout.fillHeight: true anchors.left: parent.left
Layout.leftMargin: 10 anchors.top: bar.bottom
Layout.rightMargin: 10 anchors.bottom: buttonsRow.top
Layout.topMargin: 10
clip: true clip: true
//ScrollBar.vertical.policy: ScrollBar.AlwaysOn //ScrollBar.vertical.policy: ScrollBar.AlwaysOn
StackLayout {
id: optionsStack
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
anchors.rightMargin: 10
anchors.leftMargin: 10
currentIndex: bar.currentIndex
ColumnLayout { ColumnLayout {
TabBar { // General tab
id: bar
Layout.fillWidth: true
TabButton { RowLayout {
text: qsTr("General") ImCheckBox {
onClicked: { id: chkHostname
if (chkSetUser.checked && !fieldUserPassword.length) { text: qsTr("Set hostname:")
fieldUserPassword.forceActiveFocus() onCheckedChanged: {
if (checked) {
fieldHostname.forceActiveFocus()
} }
} }
} }
TabButton { // Spacer item
text: qsTr("Services") Item {
Layout.fillWidth: true
} }
TabButton { TextField {
text: qsTr("Options") id: fieldHostname
enabled: chkHostname.checked
text: "raspberrypi"
selectByMouse: true
maximumLength: 253
validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z][0-9A-Za-z-]{0,62}/ }
Layout.minimumWidth: 200
}
Text {
text : ".local"
color: chkHostname.checked ? "black" : "grey"
} }
} }
GroupBox { ImCheckBox {
StackLayout { id: chkSetUser
width: parent.width text: qsTr("Set username and password")
currentIndex: bar.currentIndex onCheckedChanged: {
if (!checked && chkSSH.checked && radioPasswordAuthentication.checked) {
checked = true;
}
if (checked && !fieldUserPassword.length) {
fieldUserPassword.forceActiveFocus()
}
}
}
ColumnLayout { RowLayout {
// General tab Text {
text: qsTr("Username:")
color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldUserName
enabled: chkSetUser.checked
text: "pi"
Layout.minimumWidth: 200
selectByMouse: true
property bool indicateError: false
maximumLength: 31
validator: RegularExpressionValidator { regularExpression: /^[a-z][a-z0-9-]{0,30}$/ }
RowLayout { onTextEdited: {
ImCheckBox { indicateError = false
id: chkHostname }
text: qsTr("Set hostname:") }
onCheckedChanged: { }
if (checked) { RowLayout {
fieldHostname.forceActiveFocus() Text {
} text: qsTr("Password:")
} color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey"
} Layout.leftMargin: 40
// Spacer item }
Item { // Spacer item
Layout.fillWidth: true Item {
} Layout.fillWidth: true
TextField { }
id: fieldHostname TextField {
enabled: chkHostname.checked id: fieldUserPassword
text: "raspberrypi" enabled: chkSetUser.checked
selectByMouse: true echoMode: TextInput.Password
maximumLength: 253 passwordMaskDelay: 2000 //ms
validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z][0-9A-Za-z-]{0,62}/ } Layout.minimumWidth: 200
Layout.minimumWidth: 200 selectByMouse: true
} property bool alreadyCrypted: false
Text { property bool indicateError: false
text : ".local"
color: chkHostname.checked ? "black" : "grey" Keys.onPressed: (event)=> {
} if (alreadyCrypted) {
/* User is trying to edit saved
(crypted) password, clear field */
alreadyCrypted = false
clear()
} }
ImCheckBox { // Do not mark the event as accepted, so that it may
id: chkSetUser // propagate down to the underlying TextField.
text: qsTr("Set username and password") event.accepted = false
onCheckedChanged: {
if (!checked && chkSSH.checked && radioPasswordAuthentication.checked) {
checked = true;
}
if (checked && !fieldUserPassword.length) {
fieldUserPassword.forceActiveFocus()
}
}
}
RowLayout {
Text {
text: qsTr("Username:")
color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldUserName
enabled: chkSetUser.checked
text: "pi"
Layout.minimumWidth: 200
selectByMouse: true
property bool indicateError: false
maximumLength: 31
validator: RegularExpressionValidator { regularExpression: /^[a-z][a-z0-9-]{0,30}$/ }
onTextEdited: {
indicateError = false
}
}
}
RowLayout {
Text {
text: qsTr("Password:")
color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldUserPassword
enabled: chkSetUser.checked
echoMode: TextInput.Password
passwordMaskDelay: 2000 //ms
Layout.minimumWidth: 200
selectByMouse: true
property bool alreadyCrypted: false
property bool indicateError: false
Keys.onPressed: (event)=> {
if (alreadyCrypted) {
/* User is trying to edit saved
(crypted) password, clear field */
alreadyCrypted = false
clear()
}
// Do not mark the event as accepted, so that it may
// propagate down to the underlying TextField.
event.accepted = false
}
onTextEdited: {
if (indicateError) {
indicateError = false
}
}
}
}
ImCheckBox {
id: chkWifi
text: qsTr("Configure wireless LAN")
onCheckedChanged: {
if (checked) {
if (!fieldWifiSSID.length) {
fieldWifiSSID.forceActiveFocus()
} else if (!fieldWifiPassword.length) {
fieldWifiPassword.forceActiveFocus()
}
}
}
}
RowLayout {
Text {
text: qsTr("SSID:")
color: chkWifi.checked ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldWifiSSID
// placeholderText: qsTr("SSID")
enabled: chkWifi.checked
Layout.minimumWidth: 200
selectByMouse: true
property bool indicateError: false
onTextEdited: {
indicateError = false
}
}
}
RowLayout {
Text {
text: qsTr("Password:")
color: chkWifi.checked ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldWifiPassword
enabled: chkWifi.checked
Layout.minimumWidth: 200
selectByMouse: true
echoMode: chkShowPassword.checked ? TextInput.Normal : TextInput.Password
property bool indicateError: false
onTextEdited: {
indicateError = false
}
}
}
RowLayout {
// Spacer item
Item {
Layout.fillWidth: true
}
ImCheckBox {
id: chkShowPassword
enabled: chkWifi.checked
text: qsTr("Show password")
checked: true
}
// Spacer item
Item {
Layout.fillWidth: true
}
ImCheckBox {
id: chkWifiSSIDHidden
enabled: chkWifi.checked
Layout.columnSpan: 2
text: qsTr("Hidden SSID")
checked: false
}
// Spacer item
Item {
Layout.fillWidth: true
}
}
RowLayout {
Text {
text: qsTr("Wireless LAN country:")
color: chkWifi.checked ? "black" : "grey"
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
id: fieldWifiCountry
enabled: chkWifi.checked
editable: true
}
}
ImCheckBox {
id: chkLocale
text: qsTr("Set locale settings")
}
RowLayout {
Text {
text: qsTr("Time zone:")
color: chkLocale.checked ? "black" : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
enabled: chkLocale.checked
id: fieldTimezone
editable: true
Layout.minimumWidth: 200
}
}
RowLayout {
Text {
text: qsTr("Keyboard layout:")
color: chkLocale.checked ? "black" : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
enabled: chkLocale.checked
id: fieldKeyboardLayout
editable: true
Layout.minimumWidth: 200
}
}
} }
ColumnLayout { onTextEdited: {
// Remote access tab if (indicateError) {
indicateError = false
ImCheckBox {
id: chkSSH
text: qsTr("Enable SSH")
onCheckedChanged: {
if (checked) {
if (!radioPasswordAuthentication.checked && !radioPubKeyAuthentication.checked) {
radioPasswordAuthentication.checked = true
}
if (radioPasswordAuthentication.checked) {
chkSetUser.checked = true
}
}
}
}
ImRadioButton {
id: radioPasswordAuthentication
enabled: chkSSH.checked
text: qsTr("Use password authentication")
onCheckedChanged: {
if (checked) {
chkSetUser.checked = true
//fieldUserPassword.forceActiveFocus()
}
}
}
ImRadioButton {
id: radioPubKeyAuthentication
enabled: chkSSH.checked
text: qsTr("Allow public-key authentication only")
onCheckedChanged: {
if (checked) {
if (chkSetUser.checked && fieldUserName.text == "pi" && fieldUserPassword.text.length == 0) {
chkSetUser.checked = false
}
fieldPublicKey.forceActiveFocus()
}
}
}
Text {
text: qsTr("Set authorized_keys for '%1':").arg(fieldUserName.text)
color: radioPubKeyAuthentication.checked ? "black" : "grey"
textFormat: Text.PlainText
Layout.leftMargin: 40
}
TextArea {
id: fieldPublicKey
enabled: radioPubKeyAuthentication.checked
wrapMode: TextEdit.WrapAnywhere
Layout.minimumWidth: 400
Layout.leftMargin: 40
selectByMouse: true
}
ImButton {
text: qsTr("RUN SSH-KEYGEN")
Layout.leftMargin: 40
enabled: imageWriter.hasSshKeyGen() && !imageWriter.hasPubKey()
onClicked: {
enabled = false
imageWriter.generatePubKey()
fieldPublicKey.text = imageWriter.getDefaultPubKey()
}
}
}
ColumnLayout {
// Options tab
ImCheckBox {
id: chkBeep
text: qsTr("Play sound when finished")
}
ImCheckBox {
id: chkEject
text: qsTr("Eject media when finished")
}
ImCheckBox {
id: chkTelemtry
text: qsTr("Enable telemetry")
} }
} }
} }
} }
ImCheckBox {
id: chkWifi
text: qsTr("Configure wireless LAN")
onCheckedChanged: {
if (checked) {
if (!fieldWifiSSID.length) {
fieldWifiSSID.forceActiveFocus()
} else if (!fieldWifiPassword.length) {
fieldWifiPassword.forceActiveFocus()
}
}
}
}
RowLayout {
Text {
text: qsTr("SSID:")
color: chkWifi.checked ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldWifiSSID
// placeholderText: qsTr("SSID")
enabled: chkWifi.checked
Layout.minimumWidth: 200
selectByMouse: true
property bool indicateError: false
onTextEdited: {
indicateError = false
}
}
}
RowLayout {
Text {
text: qsTr("Password:")
color: chkWifi.checked ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
TextField {
id: fieldWifiPassword
enabled: chkWifi.checked
Layout.minimumWidth: 200
selectByMouse: true
echoMode: chkShowPassword.checked ? TextInput.Normal : TextInput.Password
property bool indicateError: false
onTextEdited: {
indicateError = false
}
}
}
RowLayout {
// Spacer item
Item {
Layout.fillWidth: true
}
ImCheckBox {
id: chkShowPassword
enabled: chkWifi.checked
text: qsTr("Show password")
checked: true
}
// Spacer item
Item {
Layout.fillWidth: true
}
ImCheckBox {
id: chkWifiSSIDHidden
enabled: chkWifi.checked
Layout.columnSpan: 2
text: qsTr("Hidden SSID")
checked: false
}
// Spacer item
Item {
Layout.fillWidth: true
}
}
RowLayout {
Text {
text: qsTr("Wireless LAN country:")
color: chkWifi.checked ? "black" : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
id: fieldWifiCountry
enabled: chkWifi.checked
editable: true
}
}
ImCheckBox {
id: chkLocale
text: qsTr("Set locale settings")
}
RowLayout {
Text {
text: qsTr("Time zone:")
color: chkLocale.checked ? "black" : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
enabled: chkLocale.checked
id: fieldTimezone
editable: true
Layout.minimumWidth: 200
}
}
RowLayout {
Text {
text: qsTr("Keyboard layout:")
color: chkLocale.checked ? "black" : "grey"
Layout.leftMargin: 40
}
// Spacer item
Item {
Layout.fillWidth: true
}
ComboBox {
enabled: chkLocale.checked
id: fieldKeyboardLayout
editable: true
Layout.minimumWidth: 200
}
}
}
ColumnLayout {
// Remote access tab
ImCheckBox {
id: chkSSH
text: qsTr("Enable SSH")
onCheckedChanged: {
if (checked) {
if (!radioPasswordAuthentication.checked && !radioPubKeyAuthentication.checked) {
radioPasswordAuthentication.checked = true
}
if (radioPasswordAuthentication.checked) {
chkSetUser.checked = true
}
}
}
}
ImRadioButton {
id: radioPasswordAuthentication
enabled: chkSSH.checked
text: qsTr("Use password authentication")
onCheckedChanged: {
if (checked) {
chkSetUser.checked = true
//fieldUserPassword.forceActiveFocus()
}
}
}
ImRadioButton {
id: radioPubKeyAuthentication
enabled: chkSSH.checked
text: qsTr("Allow public-key authentication only")
onCheckedChanged: {
if (checked) {
if (chkSetUser.checked && fieldUserName.text == "pi" && fieldUserPassword.text.length == 0) {
chkSetUser.checked = false
}
fieldPublicKey.forceActiveFocus()
}
}
}
Text {
text: qsTr("Set authorized_keys for '%1':").arg(fieldUserName.text)
color: radioPubKeyAuthentication.checked ? "black" : "grey"
// textFormat: Text.PlainText
Layout.leftMargin: 40
}
TextArea {
id: fieldPublicKey
enabled: radioPubKeyAuthentication.checked
textFormat: TextEdit.PlainText
wrapMode: TextEdit.WrapAnywhere
Layout.fillWidth: true
Layout.minimumWidth: 350
Layout.leftMargin: 40
selectByMouse: true
}
ImButton {
text: qsTr("RUN SSH-KEYGEN")
Layout.leftMargin: 40
enabled: imageWriter.hasSshKeyGen() && !imageWriter.hasPubKey()
onClicked: {
enabled = false
imageWriter.generatePubKey()
fieldPublicKey.text = imageWriter.getDefaultPubKey()
}
}
}
ColumnLayout {
// Options tab
ImCheckBox {
id: chkBeep
text: qsTr("Play sound when finished")
}
ImCheckBox {
id: chkEject
text: qsTr("Eject media when finished")
}
ImCheckBox {
id: chkTelemtry
text: qsTr("Enable telemetry")
}
} }
} }
}
RowLayout { RowLayout {
Layout.alignment: Qt.AlignCenter | Qt.AlignBottom id: buttonsRow
Layout.bottomMargin: 10 anchors.right: parent.right
spacing: 20 anchors.left: parent.left
anchors.bottom: parent.bottom
ImButtonRed { Item {
text: qsTr("SAVE") Layout.fillWidth: true
onClicked: { }
if (chkSetUser.checked && fieldUserPassword.text.length == 0)
{
fieldUserPassword.indicateError = true
fieldUserPassword.forceActiveFocus()
bar.currentIndex = 0
return
}
if (chkSetUser.checked && fieldUserName.text.length == 0)
{
fieldUserName.indicateError = true
fieldUserName.forceActiveFocus()
bar.currentIndex = 0
return
}
if (chkWifi.checked) ImButtonRed {
{ text: qsTr("SAVE")
// Valid Wi-Fi PSKs are: onClicked: {
// - 0 characters (indicating an open network) if (chkSetUser.checked && fieldUserPassword.text.length == 0)
// - 8-63 characters (passphrase) {
// - 64 characters (hashed passphrase, as hex) fieldUserPassword.indicateError = true
if (fieldWifiPassword.text.length > 0 && fieldUserPassword.forceActiveFocus()
(fieldWifiPassword.text.length < 8 || fieldWifiPassword.text.length > 64)) bar.currentIndex = 0
{ return
fieldWifiPassword.indicateError = true }
fieldWifiPassword.forceActiveFocus() if (chkSetUser.checked && fieldUserName.text.length == 0)
} {
if (fieldWifiSSID.text.length == 0) fieldUserName.indicateError = true
{ fieldUserName.forceActiveFocus()
fieldWifiSSID.indicateError = true bar.currentIndex = 0
fieldWifiSSID.forceActiveFocus() return
}
if (fieldWifiSSID.indicateError || fieldWifiPassword.indicateError)
{
bar.currentIndex = 0
return
}
}
applySettings()
saveSettings()
popup.close()
} }
}
Text { text: " " } if (chkWifi.checked)
{
// Valid Wi-Fi PSKs are:
// - 0 characters (indicating an open network)
// - 8-63 characters (passphrase)
// - 64 characters (hashed passphrase, as hex)
if (fieldWifiPassword.text.length > 0 &&
(fieldWifiPassword.text.length < 8 || fieldWifiPassword.text.length > 64))
{
fieldWifiPassword.indicateError = true
fieldWifiPassword.forceActiveFocus()
}
if (fieldWifiSSID.text.length == 0)
{
fieldWifiSSID.indicateError = true
fieldWifiSSID.forceActiveFocus()
}
if (fieldWifiSSID.indicateError || fieldWifiPassword.indicateError)
{
bar.currentIndex = 0
return
}
}
applySettings()
saveSettings()
popup.close()
}
}
Item {
Layout.fillWidth: true
} }
} }