diff --git a/Editor.qml b/Editor.qml index 1b79b0b..7e1c105 100644 --- a/Editor.qml +++ b/Editor.qml @@ -80,9 +80,26 @@ Item { function rowShowsSeconds(row) { return Model.showsSeconds(row, root.defaultSeconds) } // QML only re-renders a var array when the reference changes, so every - // mutation rebuilds it rather than editing in place. + // mutation rebuilds it rather than editing in place. That rebuild is also + // why edits are committed on focus loss rather than on every keystroke: + // replacing the array re-creates the row delegates, which would throw away + // the field you are typing into. function replaceDraft(next) { root.draft = next } + // Run an action that reads or replaces the draft, but only after every + // control has committed what is currently in it. + // + // A text field commits on `editingFinished` and a spin box parses its typed + // text on focus loss, so a value typed and then acted on directly — type a + // label, click Save — was never in the draft the action read, and the typed + // text was lost with the delegate. Moving focus first makes each control + // commit through its own normal path; focus goes back to the panel's key + // scope so Escape keeps working. + function withPendingEdits(action) { + keyScope.forceActiveFocus() + Qt.callLater(action) + } + // Copies every key the row already had. Rebuilding it from a fixed pair of // fields silently dropped anything else on it — editing a label would have // thrown away that clock's own seconds setting. @@ -181,6 +198,7 @@ Item { MouseArea { anchors.fill: parent; onClicked: {} } Item { + id: keyScope anchors.fill: parent anchors.margins: card.padding focus: true @@ -250,7 +268,10 @@ Item { triggerLabel: row.modelData.timezone || "Choose a timezone" foreground: root.foreground accent: root.accent - onChanged: function (value) { root.updateRow(row.index, "timezone", value) } + onChanged: function (value) { + var i = row.index + root.withPendingEdits(function () { root.updateRow(i, "timezone", value) }) + } } // The live offset, so the effect of a choice is visible @@ -276,7 +297,11 @@ Item { : "Show seconds on this clock" foreground: root.foreground accent: root.accent - onClicked: root.updateRow(row.index, "showSeconds", !root.rowShowsSeconds(row.modelData)) + onClicked: { + var i = row.index + var next = !root.rowShowsSeconds(row.modelData) + root.withPendingEdits(function () { root.updateRow(i, "showSeconds", next) }) + } } Button { @@ -284,7 +309,10 @@ Item { tooltipText: "Remove this clock" foreground: root.foreground accent: root.urgent - onClicked: root.removeRow(row.index) + onClicked: { + var i = row.index + root.withPendingEdits(function () { root.removeRow(i) }) + } } } } @@ -306,7 +334,7 @@ Item { bordered: true foreground: root.foreground accent: root.accent - onClicked: root.addRow() + onClicked: root.withPendingEdits(root.addRow) } PanelSeparator { Layout.fillWidth: true } @@ -459,7 +487,7 @@ Item { selected: true foreground: root.foreground accent: root.accent - onClicked: root.save() + onClicked: root.withPendingEdits(root.save) } } }