Files
qbz/crates/qbz-qt/qml/controls/QbzTextArea.qml
T
vicrodh 461238bd6f fix(qt): modal forms open clean, never with the last session's text
QbzLineEdit re-seeds its box only when `text` CHANGES and only while the
box is not focused; QbzTextArea only while unfocused. Modals that reset
their drafts on the close edge (the box still holds focus for that
frame) or reopen with the seed they closed on produced neither, so the
box kept the previous input while the draft said otherwise. Both
controls gain `reset(value)`, which writes the box directly, and every
modal form calls it from its open handler: playlist edit and create,
MyQBZ create / rename / description, folder create and edit, the link
resolver, the rip wizard and the library folder editor. The MyQBZ
create name now resets on the open edge, and its edit panel re-seeds on
every open, also for the same collection.

Renaming a Qobuz playlist also patches the playlist manager's warm cache,
which the editor seeds from before any fetch: reopening the editor right
after Save showed the pre-save name until the background reload landed,
and forever while offline.

cc
2026-09-14 05:22:44 -06:00

90 lines
3.0 KiB
QML

// QbzTextArea — the port's first MULTILINE input. Slint uses std-widgets
// `TextEdit` (MyQbzEditModal.slint:90-99, 96px tall, word-wrap); QML's
// QtQuick.Controls TextArea would bring its own styled background, so this is
// the same Rectangle-plus-input recipe controls/QbzLineEdit.qml uses for the
// single-line case: r8 surface-elevated box, 1px border that turns accent on
// focus, 12/8 padding, a Flickable so a description longer than the box
// scrolls instead of clipping silently.
//
// One call site today (the Edit modal's "Edit description" body). The API is
// deliberately the same shape as QbzLineEdit's: `text` in, `edited(value)`
// out, and the external re-seed goes through a `Binding { when: !activeFocus }`
// so a republish while the user is typing cannot fight the caret.
//
// No Enter-submit: a newline is legitimate content here (MyQbzEditModal has
// no `accepted` handler on this body either).
import QtQuick
import com.blitzfc.qbz
import "../theme"
Rectangle {
id: root
property string text: ""
property string placeholder: ""
/// Exposed so a call site can pass TextEdit.WordWrap / .NoWrap explicitly;
/// the .slint body is `wrap: word-wrap`.
property int wrapMode: TextEdit.WordWrap
signal edited(string value)
/// Re-seed the body from `value` (default: `text`) NOW, focus or not —
/// the QbzLineEdit.reset() twin (the why is documented there).
function reset(value) {
input.text = (value === undefined) ? root.text : String(value)
}
QbzTheme { id: theme }
width: 240
height: 96
radius: theme.radiusSm
color: (theme.ambientOn ? theme.surfaceElevatedA50 : theme.surfaceElevated)
border.width: 1
border.color: input.activeFocus ? theme.accent : theme.borderSubtle
Flickable {
id: flick
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
anchors.topMargin: 8
anchors.bottomMargin: 8
clip: true
contentWidth: width
contentHeight: input.implicitHeight
boundsBehavior: Flickable.StopAtBounds
TextEdit {
QbzTextEditMenu { }
id: input
width: flick.width
text: root.text
color: theme.textPrimary
font.pixelSize: theme.fontBody
wrapMode: root.wrapMode
selectByMouse: true
onTextChanged: if (activeFocus) root.edited(text)
// External republish re-seeds the field while it is not being
// edited (the QbzLineEdit.qml:167-172 idiom).
Binding {
target: input
property: "text"
value: root.text
when: !input.activeFocus
}
Text {
visible: input.text === "" && root.placeholder !== ""
width: parent.width
text: root.placeholder
color: theme.textMuted
font.pixelSize: theme.fontBody
wrapMode: Text.WordWrap
}
}
}
}