// My QBZ — Rename / Description / Delete-confirm modals (spec 12 §11). // // Driven from the hero overflow (⋯) menu. A single global (MyQbzEditState) // backs all three; `mode` ("rename" | "description" | "delete") selects which // body renders. Chrome mirrors EditPlaylistModal / CreateMyQbzModal: a 420px // panel, h2 title (18px bold), the m-input field family, and an m-footer with // secondary Cancel + a primary/destructive submit. Backdrop click + Cancel // close without committing; Enter submits the rename. Submit is disabled while // busy or (rename) the trimmed name is blank. // // Per spec §23 the Cancel / Save button labels are HARDCODED in the Tauri // source; we keep them @tr here (DQ2 — translate hardcoded UI strings on port). import { Theme } from "../foundation/semantic-colors.slint"; import { Typography } from "../foundation/typography.slint"; import { Radius } from "../foundation/radius.slint"; import { MyQbzEditState, MyQbzEditActions , UiFocusState } from "../state.slint"; import { QbzIcon } from "../primitives/QbzIcon.slint"; import { QbzPrimaryButton } from "../primitives/QbzPrimaryButton.slint"; import { SecondaryButton } from "../primitives/SecondaryButton.slint"; import { LineEdit, TextEdit } from "std-widgets.slint"; export component MyQbzEditModal inherits Rectangle { visible: MyQbzEditState.open; property is-rename: MyQbzEditState.mode == "rename"; property is-description: MyQbzEditState.mode == "description"; property is-delete: MyQbzEditState.mode == "delete"; // Rename submit needs a non-blank trimmed name; description/delete always // allowed (description "" clears to null). Trim check approximated by // comparing against a stripped copy is not available in Slint, so we gate // on a non-empty raw string for rename (the Rust side trims again). property can-submit: MyQbzEditState.busy ? false : (root.is-rename ? (MyQbzEditState.draft-name != "") : true); if MyQbzEditState.open: Rectangle { background: #000000bf; TouchArea { mouse-cursor: default; clicked => { MyQbzEditActions.close(); } } Rectangle { width: Math.min(root.width - 80px, 420px); height: panel.preferred-height; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; border-radius: Radius.md; background: Theme.surface-card; border-width: 1px; border-color: Theme.border-subtle; drop-shadow-blur: 32px; drop-shadow-color: #00000080; // Swallow clicks on the panel itself (so they don't hit the // backdrop's close TouchArea below). TouchArea { } panel := VerticalLayout { padding: 24px; spacing: 16px; // --- Title (h2) ------------------------------------------ Text { text: root.is-rename ? @tr("Rename") : (root.is-description ? @tr("Edit description") : @tr("Delete")); color: Theme.text-primary; font-size: 18px; font-weight: Typography.bold; } // --- Body: rename (name input) --------------------------- if root.is-rename: rename-input := LineEdit { text <=> MyQbzEditState.draft-name; // Hotkey-guard probe: a use-site `changed has-focus` on a std-widgets // input never fires (has-focus is an alias into the inner TextInput), // so observe it through a local property instead (#619). property guard-focused: self.has-focus; changed guard-focused => { UiFocusState.text-input-focused = self.guard-focused; } accepted => { if (root.can-submit) { MyQbzEditActions.submit-rename(); } } } // --- Body: description (textarea) ------------------------ if root.is-description: desc-input := TextEdit { height: 96px; wrap: word-wrap; text <=> MyQbzEditState.draft-description; // Hotkey-guard probe: a use-site `changed has-focus` on a std-widgets // input never fires (has-focus is an alias into the inner TextInput), // so observe it through a local property instead (#619). property guard-focused: self.has-focus; changed guard-focused => { UiFocusState.text-input-focused = self.guard-focused; } } // --- Body: delete-confirm (prompt) ----------------------- if root.is-delete: Text { // Delete "{name}"? This cannot be undone. — interpolated // with the collection name (spec §11.3 deleteConfirm, double quotes). text: @tr("Delete \"{}\"? This cannot be undone.", MyQbzEditState.name); color: Theme.text-secondary; font-size: 14px; wrap: word-wrap; } // --- Footer (m-footer) — shared labeled primitives ------- HorizontalLayout { alignment: end; spacing: 8px; // Cancel (secondary). SecondaryButton { label: @tr("Cancel"); height: 38px; enabled: !MyQbzEditState.busy; clicked => { MyQbzEditActions.close(); } } // Submit — primary (rename/description) or destructive // (delete) variant of the shared QbzPrimaryButton. QbzPrimaryButton { label: root.is-delete ? @tr("Delete") : @tr("Save"); height: 38px; destructive: root.is-delete; enabled: root.can-submit; clicked => { if (root.is-rename) { MyQbzEditActions.submit-rename(); } else if (root.is-description) { MyQbzEditActions.submit-description(); } else { MyQbzEditActions.confirm-delete(); } } } } } } } }