feat(slint): NowPlayingBar rebuild, Settings constraints, Discover fixes

NowPlayingBar (start of the playback-bar work):
- height raised to 110px
- SeekBar component: three-line track (total / cached / progress), a
  hover thumb, elapsed/remaining times, and a hover time bubble with a
  downward caret marking the seek position
- song card: background and border dropped, gains the extra height,
  album/artist links and the context icon kept (context icon now
  always shown)
- transport + secondary buttons copied from Tauri (shuffle, prev,
  play, next, repeat, add, favorite; cast, picture-in-picture,
  fullscreen, normalization; volume cluster). Queue/Lyrics stay in the
  header — and the header now orders them lyrics-then-queue.

Settings constraints (1:1 with Tauri):
- Exclusive Mode enabled only on ALSA; DAC Passthrough only on
  PipeWire; Lock Output Device disabled while DAC Passthrough is on;
  Gapless disabled while Streaming Only is on
- backend change cascades: forces off DAC passthrough / force-bit-
  perfect / exclusive / gapless as appropriate; DAC-passthrough and
  streaming-only cascades; initial-buffer slider right-aligned

Discover fixes:
- navigation [<] [>] pair homologated to the same top/left margin
  across every view
- album/artist description moved back into the metadata column (it now
  fills the width there) with uniform secondary text color
- genre filter button hugs its label instead of stretching
- Recently Played Albums and Tracks modules always render, with a
  placeholder until the play history has data
- wider spacing between Discover rows; the Discover gear is no longer
  a Settings shortcut (it is the per-tab sort/show-hide control)

cc
This commit is contained in:
vicrodh committed 2026-05-18 21:22:40 -06:00
1 parent edca636a16
commit 99e3513d68
18 files changed
+589 -150

No files matched your search

@@ -137,8 +137,11 @@ export component AudioSettings inherits VerticalLayout {
SettingRow {
label: @tr("Exclusive mode");
description: @tr("Lock the device so no other app can resample it.");
// ALSA-only — disabled for PipeWire / Pulse / System default.
enabled: SettingsState.backend-is-alsa;
QbzToggle {
checked: SettingsState.exclusive-mode;
enabled: SettingsState.backend-is-alsa;
toggled(v) => {
SettingsState.exclusive-mode = v;
root.settings-bool("exclusive-mode", v);
@@ -159,8 +162,11 @@ export component AudioSettings inherits VerticalLayout {
SettingRow {
label: @tr("DAC passthrough");
description: @tr("Send the bitstream untouched to the DAC.");
// PipeWire-only — disabled for ALSA / Pulse / System default.
enabled: SettingsState.backend-is-pipewire;
QbzToggle {
checked: SettingsState.dac-passthrough;
enabled: SettingsState.backend-is-pipewire;
toggled(v) => {
SettingsState.dac-passthrough = v;
root.settings-bool("dac-passthrough", v);
@@ -210,8 +216,11 @@ export component AudioSettings inherits VerticalLayout {
if SettingsState.backend-is-pipewire: SettingRow {
label: @tr("Lock output device");
description: @tr("Keep external routing intact — skip switching the default sink.");
// Mutually exclusive with DAC passthrough — disabled while it is on.
enabled: !SettingsState.dac-passthrough;
QbzToggle {
checked: SettingsState.skip-sink-switch;
enabled: !SettingsState.dac-passthrough;
toggled(v) => {
SettingsState.skip-sink-switch = v;
root.settings-bool("skip-sink-switch", v);
@@ -57,8 +57,11 @@ export component PlaybackSettings inherits VerticalLayout {
SettingRow {
label: @tr("Gapless playback");
description: @tr("Play consecutive same-format tracks without a gap.");
// Disabled while Streaming only is on — gapless needs local cache.
enabled: !SettingsState.streaming-only;
QbzToggle {
checked: SettingsState.gapless;
enabled: !SettingsState.streaming-only;
toggled(v) => {
SettingsState.gapless = v;
root.settings-bool("gapless", v);
@@ -117,10 +120,14 @@ export component PlaybackSettings inherits VerticalLayout {
if SettingsState.stream-uncached: SettingRow {
label: @tr("Initial buffer size");
description: @tr("Seconds of audio buffered before streaming playback starts.");
// Fixed width so the slider row right-aligns flush with the toggle
// and select rows instead of stretching across the row.
HorizontalLayout {
width: 200px;
spacing: 12px;
VerticalLayout {
alignment: center;
horizontal-stretch: 1;
QbzSlider {
minimum: 1;
maximum: 10;
@@ -133,6 +140,7 @@ export component PlaybackSettings inherits VerticalLayout {
}
VerticalLayout {
alignment: center;
horizontal-stretch: 0;
Text {
text: SettingsState.buffer-seconds + @tr("s");
color: Theme.text-secondary;
@@ -74,7 +74,7 @@ export component SettingsView inherits Rectangle {
VerticalLayout {
padding-left: 32px;
padding-right: 32px;
padding-top: 14px;
padding-top: 11px;
spacing: 12px;
alignment: start;