// Lyrics display-settings flyout panel — 1:1 to Tauri's // `LyricsControlsPopover.svelte` (268px popover below-right of the sliders // trigger), ADAPTED to the Slint flyout idiom (alignment standard "Modals & // flyouts": padding 14px / spacing 12px, toggle rows = label // `horizontal-stretch: 1` + control trailing, footer actions right-aligned // `alignment: end` with the shared auto-width SecondaryButton; selects are // `QbzSelect` with explicit menu-width — never pill option buttons, ADR-008, // so Tauri's segmented S/M/L/XL and Off/Soft/Strong rows become selects). // // Row order is Tauri's exactly: Auto-follow / Font / Size / Active color / // Uppercase / Dimming / footer (Copy lyrics + Reset). // // Deviations (spec §6): D6 — active color is a fixed swatch palette (Slint // has no native color-picker input); the "Theme" reset link shows only while // a custom color is set (exact Tauri chip semantics). Q6 RESOLVED SHIPPED — // the font row is included: the four non-system families are bundled TTFs // embedded at compile time (see LyricsLinesView.slint). // // This component is the popup CONTENT; the PopupWindow shell (explicit // height + close-policy, the documented footgun) lives at the anchor site in // LyricsSidebar. Every mutation writes the LyricsState in-out pref directly // (live preview) and fires prefs-changed() so Rust persists. import { Theme } from "../foundation/semantic-colors.slint"; import { Radius } from "../foundation/radius.slint"; import { QbzToggle } from "../primitives/QbzToggle.slint"; import { QbzSelect } from "../primitives/QbzSelect.slint"; import { QbzTextLink } from "../primitives/QbzTextLink.slint"; import { SecondaryButton } from "../primitives/SecondaryButton.slint"; import { LyricsState } from "../state.slint"; // One selectable color swatch (D6 palette). Ring-highlighted while it is the // active custom color. component ColorSwatch inherits Rectangle { in property swatch; property selected: LyricsState.use-custom-color && LyricsState.custom-color == root.swatch; width: 18px; height: 18px; border-radius: 9px; background: root.swatch; border-width: root.selected ? 2px : (ta.has-hover ? 1px : 0px); border-color: root.selected ? Theme.text-primary : Theme.border-muted; ta := TouchArea { mouse-cursor: pointer; clicked => { LyricsState.use-custom-color = true; LyricsState.custom-color = root.swatch; LyricsState.prefs-changed(); } } } component RowLabel inherits Text { color: Theme.text-secondary; font-size: 12px; vertical-alignment: center; horizontal-stretch: 1; } export component LyricsControlsPanel inherits Rectangle { // Copy gate — Tauri canCopy: loaded && !error && lines > 0 // (LyricsSidebar.svelte:35). property can-copy: LyricsState.status == 2 && LyricsState.lines.length > 0; background: Theme.surface-main; border-radius: Radius.sm; border-width: 1px; border-color: Theme.border-muted; drop-shadow-blur: 24px; drop-shadow-color: Theme.card-shadow; drop-shadow-offset-y: 6px; VerticalLayout { padding: 14px; spacing: 12px; // Auto-follow. HorizontalLayout { height: 22px; spacing: 8px; RowLabel { text: @tr("Auto-follow"); } QbzToggle { checked: LyricsState.auto-follow; toggled(v) => { LyricsState.auto-follow = v; LyricsState.prefs-changed(); } } } // Font (Q6 shipped — embedded families). HorizontalLayout { height: 30px; spacing: 8px; RowLabel { text: @tr("Font"); } QbzSelect { sm: true; menu-width: 178px; options: [ @tr("System"), @tr("LINE Seed JP"), @tr("Montserrat"), @tr("Noto Sans"), @tr("Source Sans 3"), ]; current-index: LyricsState.font-index; selected(i) => { LyricsState.font-index = i; LyricsState.prefs-changed(); } } } // Size (Tauri segmented S/M/L/XL -> select, flyout idiom). HorizontalLayout { height: 30px; spacing: 8px; RowLabel { text: @tr("Size"); } QbzSelect { sm: true; menu-width: 120px; options: [@tr("S"), @tr("M"), @tr("L"), @tr("XL")]; current-index: LyricsState.size-index; selected(i) => { LyricsState.size-index = i; LyricsState.prefs-changed(); } } } // Active color — label (+ "Theme" reset while custom), swatch palette // underneath, right-aligned on the control grid (D6). VerticalLayout { height: 44px; spacing: 8px; HorizontalLayout { spacing: 8px; RowLabel { text: @tr("Active color"); } if LyricsState.use-custom-color: QbzTextLink { label: @tr("Theme"); clicked => { LyricsState.use-custom-color = false; LyricsState.prefs-changed(); } } } HorizontalLayout { alignment: end; spacing: 6px; ColorSwatch { swatch: #8b5cf6; } ColorSwatch { swatch: #ec4899; } ColorSwatch { swatch: #ef4444; } ColorSwatch { swatch: #f97316; } ColorSwatch { swatch: #eab308; } ColorSwatch { swatch: #22c55e; } ColorSwatch { swatch: #06b6d4; } ColorSwatch { swatch: #3b82f6; } } } // Uppercase. HorizontalLayout { height: 22px; spacing: 8px; RowLabel { text: @tr("Uppercase"); } QbzToggle { checked: LyricsState.uppercase; toggled(v) => { LyricsState.uppercase = v; LyricsState.prefs-changed(); } } } // Dimming (Tauri segmented Off/Soft/Strong -> select). HorizontalLayout { height: 30px; spacing: 8px; RowLabel { text: @tr("Dimming"); } QbzSelect { sm: true; menu-width: 130px; options: [@tr("Off"), @tr("Soft"), @tr("Strong")]; current-index: LyricsState.dimming-mode; selected(i) => { LyricsState.dimming-mode = i; LyricsState.prefs-changed(); } } } // Footer: Copy lyrics (gated) + Reset, right-aligned (standard). HorizontalLayout { height: 34px; alignment: end; spacing: 8px; SecondaryButton { label: @tr("Copy lyrics"); enabled: root.can-copy; clicked => { LyricsState.copy-lyrics(); } } SecondaryButton { label: @tr("Reset"); clicked => { LyricsState.reset-prefs(); } } } } }