// Album detail page. // // Mirrors the Svelte AlbumDetailView: back button, an album header // (artwork + title / artist / info line / action buttons), a divider, // and the track list with a column header. Data comes from the // AlbumState global; the action buttons are visual for now. import { Theme } from "../foundation/semantic-colors.slint"; import { Typography } from "../foundation/typography.slint"; import { Radius } from "../foundation/radius.slint"; import { Spacing } from "../foundation/spacing.slint"; import { AlbumState, AlbumTrackItem, NavState, ContentView } from "../state.slint"; import { QbzIcon } from "../primitives/QbzIcon.slint"; import { ScrollView } from "std-widgets.slint"; component CircleAction inherits Rectangle { in property icon; in property primary: false; in property diameter: 40px; callback clicked(); width: root.diameter; height: root.diameter; border-radius: root.diameter / 2; background: root.primary ? (ta.has-hover ? Theme.accent-hover : Theme.accent) : (ta.has-hover ? Theme.surface-hover : Theme.surface-elevated); QbzIcon { source: root.icon; width: 17px; height: 17px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; tint: root.primary ? Theme.accent-text : Theme.text-secondary; } ta := TouchArea { mouse-cursor: pointer; clicked => { root.clicked(); } } } component TrackRow inherits Rectangle { in property track; in property index; height: 56px; border-radius: 8px; background: ta.has-hover ? Theme.surface-hover : (Math.mod(root.index, 2) == 1 ? Theme.surface-card : transparent); HorizontalLayout { padding-left: 12px; padding-right: 12px; spacing: 16px; Text { text: root.track.number; width: 32px; color: Theme.text-muted; font-size: Typography.body; vertical-alignment: center; } VerticalLayout { alignment: center; horizontal-stretch: 1; spacing: 2px; HorizontalLayout { spacing: 6px; Text { text: root.track.title; color: Theme.text-primary; font-size: Typography.body; font-weight: Typography.medium; vertical-alignment: center; overflow: elide; } if root.track.explicit: Rectangle { width: 16px; height: 16px; y: (parent.height - self.height) / 2; border-radius: 3px; background: Theme.surface-elevated; Text { text: "E"; color: Theme.text-muted; font-size: 9px; font-weight: Typography.semibold; horizontal-alignment: center; vertical-alignment: center; } } } Text { text: root.track.artist; visible: root.track.artist != ""; color: Theme.text-muted; font-size: Typography.link; overflow: elide; } } Text { text: root.track.duration; width: 80px; color: Theme.text-muted; font-size: Typography.body; horizontal-alignment: center; vertical-alignment: center; } Text { text: root.track.quality-tier == "hires" ? "Hi-Res" : (root.track.quality-tier == "cd" ? "CD" : ""); width: 80px; color: Theme.text-muted; font-size: Typography.legal; horizontal-alignment: center; vertical-alignment: center; } } ta := TouchArea { mouse-cursor: pointer; } } export component AlbumPageView inherits Rectangle { callback open-artist(string); background: Theme.surface-main; ScrollView { horizontal-scrollbar-policy: ScrollBarPolicy.always-off; VerticalLayout { padding-left: 24px; padding-right: 24px; padding-top: 14px; padding-bottom: 100px; spacing: 0px; alignment: start; // --- Back button ------------------------------------------------- back := TouchArea { width: back-row.preferred-width; height: 28px; mouse-cursor: pointer; clicked => { NavState.view = ContentView.home; } back-row := HorizontalLayout { spacing: 6px; VerticalLayout { alignment: center; QbzIcon { source: @image-url("../assets/icons/arrow-left.svg"); width: 16px; height: 16px; tint: back.has-hover ? Theme.text-secondary : Theme.text-muted; } } Text { text: @tr("Back"); color: back.has-hover ? Theme.text-secondary : Theme.text-muted; font-size: Typography.body; vertical-alignment: center; } } } Rectangle { height: 22px; } // --- Album header ------------------------------------------------ HorizontalLayout { spacing: 32px; alignment: start; Rectangle { width: 224px; height: 224px; border-radius: 12px; background: Theme.surface-elevated; drop-shadow-blur: 32px; drop-shadow-color: #00000080; drop-shadow-offset-y: 8px; clip: true; Image { width: 100%; height: 100%; source: AlbumState.artwork; image-fit: cover; } } VerticalLayout { alignment: start; padding-top: 4px; spacing: 0px; horizontal-stretch: 1; Text { text: AlbumState.title; color: Theme.text-primary; font-size: Typography.title; font-weight: Typography.bold; overflow: elide; } Rectangle { height: 4px; } HorizontalLayout { alignment: start; artist-area := TouchArea { width: artist-text.preferred-width; height: artist-text.preferred-height; mouse-cursor: pointer; clicked => { root.open-artist(AlbumState.artist-id); } artist-text := Text { text: AlbumState.artist; color: artist-area.has-hover ? Theme.accent-hover : Theme.accent; font-size: Typography.heading; font-weight: Typography.medium; } } } Rectangle { height: 10px; } Text { text: AlbumState.info-line; color: Theme.text-muted; font-size: Typography.body; overflow: elide; } Rectangle { height: 20px; } HorizontalLayout { alignment: start; spacing: 12px; CircleAction { icon: @image-url("../assets/icons/play.svg"); primary: true; diameter: 44px; } VerticalLayout { alignment: center; CircleAction { icon: @image-url("../assets/icons/shuffle.svg"); } } VerticalLayout { alignment: center; CircleAction { icon: @image-url("../assets/icons/heart.svg"); } } VerticalLayout { alignment: center; CircleAction { icon: @image-url("../assets/icons/ellipsis.svg"); } } } } } Rectangle { height: 20px; } Rectangle { height: 1px; background: Theme.border-subtle; } Rectangle { height: 8px; } // --- Track list -------------------------------------------------- if AlbumState.loading && AlbumState.tracks.length == 0: Text { text: @tr("Loading album..."); color: Theme.text-muted; font-size: Typography.body; } // Column header. HorizontalLayout { height: 40px; padding-left: 12px; padding-right: 12px; spacing: 16px; Text { text: "#"; width: 32px; color: Theme.text-muted; font-size: Typography.legal; letter-spacing: 0.5px; vertical-alignment: center; } Text { text: @tr("TITLE"); horizontal-stretch: 1; color: Theme.text-muted; font-size: Typography.legal; letter-spacing: 0.5px; vertical-alignment: center; } Text { text: @tr("DURATION"); width: 80px; color: Theme.text-muted; font-size: Typography.legal; letter-spacing: 0.5px; horizontal-alignment: center; vertical-alignment: center; } Text { text: @tr("QUALITY"); width: 80px; color: Theme.text-muted; font-size: Typography.legal; letter-spacing: 0.5px; horizontal-alignment: center; vertical-alignment: center; } } for track[index] in AlbumState.tracks: TrackRow { track: track; index: index; } } } }