feat(slint): match queue footer to Tauri and add the track menu

Footer now carries the five Tauri controls with Tauri's icons — clear
(trash-list), save (add-to-list), infinite, sleep timer (clock), and
search; the timer button is visual-only for now. Queue rows gain a
context menu 1:1 with the Tauri queue track menu (Remove from queue,
Stop after this, Remove all after, Add to playlist, Track info); only
Remove is wired, the rest are placeholders.

cc
This commit is contained in:
vicrodh committed 2026-05-19 02:50:21 -06:00
1 parent 21b0ade357
commit 0606826df4
7 files changed
+84 -14

No files matched your search

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
␍<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" stroke="#000000" stroke-width="0.4800000000000001">
␍<g id="SVGRepo_bgCarrier" stroke-width="0"/>
␍<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="0.048"/>
␍<g id="SVGRepo_iconCarrier"> <path d="M18,7 L20.5,7 C20.7761424,7 21,7.22385763 21,7.5 C21,7.77614237 20.7761424,8 20.5,8 L18,8 L18,10.5 C18,10.7761424 17.7761424,11 17.5,11 C17.2238576,11 17,10.7761424 17,10.5 L17,8 L14.5,8 C14.2238576,8 14,7.77614237 14,7.5 C14,7.22385763 14.2238576,7 14.5,7 L17,7 L17,4.5 C17,4.22385763 17.2238576,4 17.5,4 C17.7761424,4 18,4.22385763 18,4.5 L18,7 Z M11.5,7 C11.7761424,7 12,7.22385763 12,7.5 C12,7.77614237 11.7761424,8 11.5,8 L3.5,8 C3.22385763,8 3,7.77614237 3,7.5 C3,7.22385763 3.22385763,7 3.5,7 L11.5,7 Z M14.5,12 C14.7761424,12 15,12.2238576 15,12.5 C15,12.7761424 14.7761424,13 14.5,13 L3.5,13 C3.22385763,13 3,12.7761424 3,12.5 C3,12.2238576 3.22385763,12 3.5,12 L14.5,12 Z M20.5,17 C20.7761424,17 21,17.2238576 21,17.5 C21,17.7761424 20.7761424,18 20.5,18 L3.5,18 C3.22385763,18 3,17.7761424 3,17.5 C3,17.2238576 3.22385763,17 3.5,17 L20.5,17 Z"/> </g>
␍</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" />
<rect x="9" y="9" width="6" height="6" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 271 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>

After

Width:  |  Height:  |  Size: 261 B

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M6 16c5 0 7-8 12-8a4 4 0 0 1 0 8c-5 0-7-8-12-8a4 4 0 1 0 0 8" /> <path d="M18.178 8c5.096 0 5.096 8 0 8-5.095 0-7.133-8-12.739-8-4.781 0-4.781 8 0 8 5.606 0 7.644-8 12.739-8z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 300 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 6h18" />
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
<line x1="10" x2="10" y1="11" y2="17" />
<line x1="14" x2="14" y1="11" y2="17" />
</svg>

After

Width:  |  Height:  |  Size: 396 B

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10V17M8 10V17M19 9H22M19 14H22M19 19H21M16 6V16.2C16 17.8802 16 18.7202 15.673 19.362C15.3854 19.9265 14.9265 20.3854 14.362 20.673C13.7202 21 12.8802 21 11.2 21H8.8C7.11984 21 6.27976 21 5.63803 20.673C5.07354 20.3854 4.6146 19.9265 4.32698 19.362C4 18.7202 4 17.8802 4 16.2V6M2 6H18M14 6L13.7294 5.18807C13.4671 4.40125 13.3359 4.00784 13.0927 3.71698C12.8779 3.46013 12.6021 3.26132 12.2905 3.13878C11.9376 3 11.523 3 10.6936 3H9.30643C8.47705 3 8.06236 3 7.70951 3.13878C7.39792 3.26132 7.12208 3.46013 6.90729 3.71698C6.66405 4.00784 6.53292 4.40125 6.27064 5.18807L6 6" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 902 B

+57 -13
View File
@@ -14,6 +14,8 @@ import { Spacing } from "../foundation/spacing.slint";
import { Typography } from "../foundation/typography.slint"; import { Typography } from "../foundation/typography.slint";
import { Radius } from "../foundation/radius.slint"; import { Radius } from "../foundation/radius.slint";
import { QbzIcon } from "../primitives/QbzIcon.slint"; import { QbzIcon } from "../primitives/QbzIcon.slint";
import { ContextMenu } from "../primitives/ContextMenu.slint";
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
import { ShellState, QueueState, QueueItem } from "../state.slint"; import { ShellState, QueueState, QueueItem } from "../state.slint";
// Close button — mirrors the X control used by LyricsSidebar. // Close button — mirrors the X control used by LyricsSidebar.
@@ -198,28 +200,28 @@ component QueueRow inherits Rectangle {
} }
} }
// Remove control — only on UP NEXT rows, only on hover. // Context-menu trigger — only on UP NEXT rows, shown on hover.
if root.removable: VerticalLayout { if root.removable: VerticalLayout {
alignment: center; alignment: center;
width: 24px; width: 24px;
visible: ta.has-hover; visible: ta.has-hover;
remove-btn := Rectangle { menu-btn := Rectangle {
width: 24px; width: 24px;
height: 24px; height: 24px;
border-radius: Radius.sm; border-radius: Radius.sm;
background: rta.has-hover ? Theme.surface-hover : transparent; background: mta.has-hover ? Theme.surface-hover : transparent;
QbzIcon { QbzIcon {
source: @image-url("../assets/icons/x.svg"); source: @image-url("../assets/icons/ellipsis.svg");
width: 13px; width: 15px;
height: 13px; height: 15px;
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
tint: rta.has-hover ? Theme.text-primary : Theme.text-muted; tint: mta.has-hover ? Theme.text-primary : Theme.text-muted;
} }
rta := TouchArea { mta := TouchArea {
mouse-cursor: pointer; mouse-cursor: pointer;
clicked => { clicked => {
root.remove(); track-menu.show();
} }
} }
} }
@@ -227,13 +229,47 @@ component QueueRow inherits Rectangle {
} }
ta := TouchArea { ta := TouchArea {
// The remove control sits above this area and consumes its own // The menu trigger sits above this area and consumes its own
// clicks, so a row click never doubles as a remove. // clicks, so a row click never doubles as a menu open.
mouse-cursor: pointer; mouse-cursor: pointer;
clicked => { clicked => {
root.clicked(); root.clicked();
} }
} }
// Per-track context menu — 1:1 with the Tauri queue track menu.
// Only "Remove from queue" is wired; the rest are placeholders.
track-menu := PopupWindow {
x: root.width - 200px;
y: 44px;
width: 196px;
height: 175px;
ContextMenu {
ContextMenuItem {
icon: @image-url("../assets/icons/trash-2.svg");
label: @tr("Remove from queue");
clicked => {
root.remove();
}
}
ContextMenuItem {
icon: @image-url("../assets/icons/circle-stop.svg");
label: @tr("Stop after this");
}
ContextMenuItem {
icon: @image-url("../assets/icons/list-x.svg");
label: @tr("Remove all after");
}
ContextMenuItem {
icon: @image-url("../assets/icons/list-plus.svg");
label: @tr("Add to playlist");
}
ContextMenuItem {
icon: @image-url("../assets/icons/info.svg");
label: @tr("Track info");
}
}
}
} }
export component QueueSidebar inherits Rectangle { export component QueueSidebar inherits Rectangle {
@@ -578,7 +614,7 @@ export component QueueSidebar inherits Rectangle {
VerticalLayout { VerticalLayout {
alignment: center; alignment: center;
IconButton { IconButton {
icon: @image-url("../assets/icons/list-x.svg"); icon: @image-url("../assets/icons/trash-list.svg");
clicked => { clicked => {
QueueState.clear-queue(); QueueState.clear-queue();
} }
@@ -588,7 +624,7 @@ export component QueueSidebar inherits Rectangle {
VerticalLayout { VerticalLayout {
alignment: center; alignment: center;
IconButton { IconButton {
icon: @image-url("../assets/icons/list-plus.svg"); icon: @image-url("../assets/icons/add-to-list.svg");
clicked => { clicked => {
QueueState.save-as-playlist(); QueueState.save-as-playlist();
} }
@@ -605,6 +641,14 @@ export component QueueSidebar inherits Rectangle {
} }
} }
} }
// Action 4 — Sleep timer. Visual parity with Tauri; the
// timer popover / functionality is not wired yet.
VerticalLayout {
alignment: center;
IconButton {
icon: @image-url("../assets/icons/clock.svg");
}
}
// Spacer. // Spacer.
Rectangle { horizontal-stretch: 1; } Rectangle { horizontal-stretch: 1; }