feat(slint): full track context menu (13 items, ported from Tauri)
New shared TrackContextMenu primitive with every option the Tauri TrackMenu shows (flat — no submenus): Play now / Play next / Add to queue / Create radio / Add to library / Add to mixtape / Add to playlist / Share Qobuz link / Share Song.link / Make available offline / Go to album / Go to artist / Track info. Items already wired in the MVP (play / play-next / queue / favorite) fire as before; the rest emit their media-action and stay no-op until the backend lands. Used by SearchTrackRow, AlbumPageView TrackRow, and ArtistPageView TrackRow. cc
This commit is contained in:
5 files changed
+130
-103
No files matched your search
@@ -15,6 +15,7 @@ import { ContextMenu } from "../primitives/ContextMenu.slint";
|
|||||||
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
|
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
|
||||||
import { QualityBadgeFull } from "../primitives/QualityBadgeFull.slint";
|
import { QualityBadgeFull } from "../primitives/QualityBadgeFull.slint";
|
||||||
import { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
import { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
||||||
|
import { TrackContextMenu } from "../primitives/TrackContextMenu.slint";
|
||||||
import { NavButtons } from "../shell/NavButtons.slint";
|
import { NavButtons } from "../shell/NavButtons.slint";
|
||||||
|
|
||||||
component CircleAction inherits Rectangle {
|
component CircleAction inherits Rectangle {
|
||||||
@@ -239,40 +240,12 @@ component TrackRow inherits Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track-menu := PopupWindow {
|
track-menu := TrackContextMenu {
|
||||||
x: parent.width - 208px;
|
x: parent.width - self.width - 12px;
|
||||||
y: 46px;
|
y: 48px;
|
||||||
width: 196px;
|
track-id: root.track.id;
|
||||||
height: 109px;
|
media-action(k, i, a) => {
|
||||||
ContextMenu {
|
root.media-action(k, i, a);
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/play.svg");
|
|
||||||
label: @tr("Play");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-plus.svg");
|
|
||||||
label: @tr("Play next");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play-next");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-end.svg");
|
|
||||||
label: @tr("Add to queue");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "queue");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/heart.svg");
|
|
||||||
label: @tr("Add to favorites");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "favorite");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { QbzIcon } from "../primitives/QbzIcon.slint";
|
|||||||
import { ContextMenu } from "../primitives/ContextMenu.slint";
|
import { ContextMenu } from "../primitives/ContextMenu.slint";
|
||||||
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
|
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
|
||||||
import { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
import { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
||||||
|
import { TrackContextMenu } from "../primitives/TrackContextMenu.slint";
|
||||||
import { Carousel } from "../discover/Carousel.slint";
|
import { Carousel } from "../discover/Carousel.slint";
|
||||||
import { NavButtons } from "../shell/NavButtons.slint";
|
import { NavButtons } from "../shell/NavButtons.slint";
|
||||||
|
|
||||||
@@ -147,40 +148,12 @@ component TrackRow inherits Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track-menu := PopupWindow {
|
track-menu := TrackContextMenu {
|
||||||
x: parent.width - 208px;
|
x: parent.width - self.width - 12px;
|
||||||
y: 44px;
|
y: 44px;
|
||||||
width: 196px;
|
track-id: root.track.id;
|
||||||
height: 109px;
|
media-action(k, i, a) => {
|
||||||
ContextMenu {
|
root.media-action(k, i, a);
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/play.svg");
|
|
||||||
label: @tr("Play");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-plus.svg");
|
|
||||||
label: @tr("Play next");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play-next");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-end.svg");
|
|
||||||
label: @tr("Add to queue");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "queue");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/heart.svg");
|
|
||||||
label: @tr("Add to favorites");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "favorite");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<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="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
||||||
|
After Width: | Height: | Size: 326 B |
@@ -0,0 +1,109 @@
|
|||||||
|
// Track-row "more" context menu — flat, ported 1:1 from the Tauri
|
||||||
|
// TrackMenu component. Items that have no Slint wiring yet emit a
|
||||||
|
// media-action with an action string the runtime currently ignores
|
||||||
|
// (no-op until backend wiring lands).
|
||||||
|
|
||||||
|
import { ContextMenu } from "ContextMenu.slint";
|
||||||
|
import { ContextMenuItem } from "ContextMenuItem.slint";
|
||||||
|
|
||||||
|
export component TrackContextMenu inherits PopupWindow {
|
||||||
|
in property <string> track-id;
|
||||||
|
callback media-action(string, string, string);
|
||||||
|
|
||||||
|
width: 224px;
|
||||||
|
height: 392px;
|
||||||
|
|
||||||
|
ContextMenu {
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/play.svg");
|
||||||
|
label: @tr("Play now");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "play");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/list-plus.svg");
|
||||||
|
label: @tr("Play next");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "play-next");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/list-end.svg");
|
||||||
|
label: @tr("Add to queue");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "queue");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/radio.svg");
|
||||||
|
label: @tr("Create radio");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "create-radio");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/heart.svg");
|
||||||
|
label: @tr("Add to library");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "favorite");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/cassette-tape.svg");
|
||||||
|
label: @tr("Add to mixtape");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "add-to-mixtape");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/list-music.svg");
|
||||||
|
label: @tr("Add to playlist");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "add-to-playlist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/link.svg");
|
||||||
|
label: @tr("Share Qobuz link");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "share-qobuz");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/link.svg");
|
||||||
|
label: @tr("Share Song.link");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "share-songlink");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/cloud-download.svg");
|
||||||
|
label: @tr("Make available offline");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "download");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/disc-3.svg");
|
||||||
|
label: @tr("Go to album");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "go-to-album");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/user.svg");
|
||||||
|
label: @tr("Go to artist");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "go-to-artist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextMenuItem {
|
||||||
|
icon: @image-url("../assets/icons/info.svg");
|
||||||
|
label: @tr("Track info");
|
||||||
|
clicked => {
|
||||||
|
root.media-action("track", root.track-id, "track-info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,9 +10,8 @@ import { Typography } from "../foundation/typography.slint";
|
|||||||
import { Radius } from "../foundation/radius.slint";
|
import { Radius } from "../foundation/radius.slint";
|
||||||
import { SearchTrackItem } from "../state.slint";
|
import { SearchTrackItem } from "../state.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 { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
import { TrackPlayCell } from "../primitives/TrackPlayCell.slint";
|
||||||
|
import { TrackContextMenu } from "../primitives/TrackContextMenu.slint";
|
||||||
|
|
||||||
export component SearchTrackRow inherits Rectangle {
|
export component SearchTrackRow inherits Rectangle {
|
||||||
in property <SearchTrackItem> track;
|
in property <SearchTrackItem> track;
|
||||||
@@ -153,40 +152,12 @@ export component SearchTrackRow inherits Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track-menu := PopupWindow {
|
track-menu := TrackContextMenu {
|
||||||
x: parent.width - 208px;
|
x: parent.width - self.width - 12px;
|
||||||
y: 46px;
|
y: 48px;
|
||||||
width: 196px;
|
track-id: root.track.id;
|
||||||
height: 109px;
|
media-action(k, i, a) => {
|
||||||
ContextMenu {
|
root.media-action(k, i, a);
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/play.svg");
|
|
||||||
label: @tr("Play");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-plus.svg");
|
|
||||||
label: @tr("Play next");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "play-next");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/list-end.svg");
|
|
||||||
label: @tr("Add to queue");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "queue");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContextMenuItem {
|
|
||||||
icon: @image-url("../assets/icons/heart.svg");
|
|
||||||
label: @tr("Add to favorites");
|
|
||||||
clicked => {
|
|
||||||
root.media-action("track", root.track.id, "favorite");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user