import { Theme } from "theme.slint"; import { Cover, Transport, ProgressBar, VolumeBars, RoundButton, GlassPanel } from "widgets.slint"; import { NowPlaying, TrackRow } from "models.slint"; // The bar along the bottom of the browse screen. export component PlayerBar inherits Rectangle { in property now; in property playing; in property progress; in property smooth; in property volume; callback toggle(); callback next(); callback previous(); callback seek(float); callback set-volume(int); callback mute(); callback open-play-view(); height: 96px; background: Theme.bar-bg; // The accent rule along the top is what separates the bar from the stage without a // shadow, which over a gradient reads as a smudge. Rectangle { y: 0; height: 3px; width: 100%; background: Theme.accent.with-alpha(0.5); } HorizontalLayout { padding-left: 24px; padding-right: 24px; padding-top: 16px; padding-bottom: 16px; spacing: 18px; // Cover: the way back into the full-screen view. VerticalLayout { alignment: center; Rectangle { width: root.now.is-book ? 46px : 56px; height: 56px; Cover { source: root.now.cover; is-book: root.now.is-book; radius: 10px; width: 100%; height: 100%; } TouchArea { clicked => { root.open-play-view(); } } } } VerticalLayout { alignment: center; width: 240px; Text { text: root.now.title; font-size: Theme.text-body; font-weight: 800; color: Theme.paper; overflow: elide; } Text { text: root.now.subtitle; font-size: Theme.text-small; font-weight: 600; color: Theme.text-sub.with-alpha(0.75); overflow: elide; } if root.now.has-album: Text { text: root.now.counter; font-size: Theme.text-tiny; font-weight: 800; color: Theme.accent-dim; overflow: elide; } } VerticalLayout { alignment: center; horizontal-stretch: 1; ProgressBar { progress: root.progress; smooth: root.smooth; on-dark: true; bar-height: 10px; seek(fraction) => { root.seek(fraction); } } } VerticalLayout { alignment: center; Transport { playing: root.playing; size: 48px; toggle => { root.toggle(); } next => { root.next(); } previous => { root.previous(); } } } VerticalLayout { alignment: center; VolumeBars { volume: root.volume; bar-width: 11px; bar-height: 30px; set-volume(v) => { root.set-volume(v); } mute => { root.mute(); } } } } } // The full-screen now-playing view. export component PlayView inherits VerticalLayout { in property now; in property playing; in property progress; in property smooth; in property volume; callback toggle(); callback next(); callback previous(); callback seek(float); callback set-volume(int); callback mute(); callback open-album(); alignment: center; spacing: 18px; padding: 32px; HorizontalLayout { alignment: center; Rectangle { height: 320px; width: root.now.is-book ? 262px : 320px; drop-shadow-color: #0016208c; drop-shadow-blur: 40px; drop-shadow-offset-y: 20px; if root.now.has-album: Cover { source: root.now.hero-cover; is-book: root.now.is-book; radius: 28px; width: 100%; height: 100%; } if !root.now.has-album: Rectangle { background: #2a4f57; border-radius: 28px; } TouchArea { clicked => { root.open-album(); } } } } VerticalLayout { spacing: 6px; Text { text: root.now.title; font-size: Theme.text-hero; font-weight: 900; color: Theme.paper; horizontal-alignment: center; wrap: word-wrap; } Text { text: root.now.subtitle; font-size: Theme.text-heading; font-weight: 700; color: Theme.text-dim.with-alpha(0.8); horizontal-alignment: center; overflow: elide; } if root.now.has-album: Text { text: root.now.counter; font-size: 15px; font-weight: 800; color: Theme.accent-dim; horizontal-alignment: center; } } HorizontalLayout { alignment: center; VerticalLayout { width: 680px; spacing: 8px; ProgressBar { progress: root.progress; smooth: root.smooth; bar-height: 14px; seek(fraction) => { root.seek(fraction); } } HorizontalLayout { Text { text: root.now.clock-label; font-family: Theme.mono; font-size: Theme.text-small + 1px; font-weight: 800; color: Theme.text-dim.with-alpha(0.8); } Rectangle { horizontal-stretch: 1; } Text { text: root.now.remaining-label; font-size: Theme.text-small + 1px; font-weight: 800; color: Theme.text-dim.with-alpha(0.8); } } } } Transport { playing: root.playing; size: 72px; toggle => { root.toggle(); } next => { root.next(); } previous => { root.previous(); } } VolumeBars { volume: root.volume; bar-width: 18px; bar-height: 44px; set-volume(v) => { root.set-volume(v); } mute => { root.mute(); } } } // The track list for one album, over a dimmed stage. export component AlbumSheet inherits Rectangle { in property title; in property subtitle; in property meta; in property cover; in property is-book; in property <[TrackRow]> tracks; in property sel-index; callback close(); callback play(int); callback play-all(); background: Theme.backdrop.with-alpha(0.6); // Catches every click that misses the sheet, so tapping the dimmed area closes it. TouchArea { clicked => { root.close(); } } Rectangle { width: 640px; height: min(root.height - 80px, 720px); background: Theme.sheet-bg; border-radius: Theme.radius-sheet; drop-shadow-color: #0016208c; drop-shadow-blur: 60px; drop-shadow-offset-y: 24px; // Without this the backdrop's TouchArea above would receive clicks aimed at the // sheet's own background and close it. TouchArea { } VerticalLayout { padding: 26px; spacing: 16px; HorizontalLayout { spacing: 18px; Cover { source: root.cover; is-book: root.is-book; width: root.is-book ? 123px : 150px; height: 150px; } VerticalLayout { alignment: start; spacing: 6px; Text { text: root.title; font-size: Theme.text-title; font-weight: 900; color: Theme.ink; wrap: word-wrap; } Text { text: root.subtitle; font-size: Theme.text-body; font-weight: 700; color: Theme.ink.with-alpha(0.7); overflow: elide; } Text { text: root.meta; font-size: Theme.text-small; font-weight: 800; color: Theme.count-pink; overflow: elide; } Rectangle { height: 8px; } Rectangle { background: play-all-touch.pressed ? Theme.accent-dim : Theme.accent; border-radius: Theme.pill; width: play-all.preferred-width; height: play-all.preferred-height; play-all := HorizontalLayout { padding-left: 18px; padding-right: 18px; padding-top: 9px; padding-bottom: 9px; Text { text: "▶ Alle abspielen"; font-size: Theme.text-small + 1px; font-weight: 800; color: white; } } play-all-touch := TouchArea { clicked => { root.play-all(); } } } } VerticalLayout { alignment: start; RoundButton { label: "×"; size: 40px; clicked => { root.close(); } } } } Flickable { content-height: list.preferred-height; content-width: self.width; // Follow the keyboard through the list without measuring: every row is // the same height, so the selected one's offset is arithmetic. content-y: -max(0px, min( max(0px, self.content-height - self.height), root.sel-index * 52px - self.height / 2)); animate content-y { duration: 150ms; easing: ease-out; } list := VerticalLayout { spacing: 4px; for track[i] in root.tracks: Rectangle { height: 48px; background: track.current ? Theme.accent.with-alpha(0.18) : transparent; border-radius: 14px; border-width: root.sel-index == i ? 3px : 0px; border-color: Theme.accent; HorizontalLayout { padding-left: 14px; padding-right: 14px; spacing: 12px; VerticalLayout { alignment: center; Rectangle { width: 30px; height: 30px; border-radius: 15px; background: track.current ? Theme.accent : Theme.ink.with-alpha(0.12); Text { text: i + 1; font-size: Theme.text-tiny; font-weight: 800; color: track.current ? white : Theme.ink; vertical-alignment: center; horizontal-alignment: center; } } } VerticalLayout { alignment: center; Text { text: track.title; font-size: Theme.text-small + 1px; font-weight: 700; color: Theme.ink; overflow: elide; } } VerticalLayout { alignment: center; Text { text: track.duration; font-family: Theme.mono; font-size: Theme.text-tiny; font-weight: 700; color: Theme.ink.with-alpha(0.6); } } } TouchArea { clicked => { root.play(i); } } } } } } } }