diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-03-26 22:59:28 +1100 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-03-26 22:59:28 +1100 |
| commit | 17ddfc6fb1dc2ebb9b4ab0bbd3517ef7e1de97da (patch) | |
| tree | 9be24d9c93d30546ce89acc4ef359ff7de672dff | |
| parent | sidebar: fix dashboard media types (diff) | |
| download | caelestia-shell-17ddfc6fb1dc2ebb9b4ab0bbd3517ef7e1de97da.tar.gz caelestia-shell-17ddfc6fb1dc2ebb9b4ab0bbd3517ef7e1de97da.tar.bz2 caelestia-shell-17ddfc6fb1dc2ebb9b4ab0bbd3517ef7e1de97da.zip | |
sidebar: media pane
| -rw-r--r-- | scss/sidebar.scss | 223 | ||||
| -rw-r--r-- | src/modules/sidebar/audio.tsx | 8 | ||||
| -rw-r--r-- | src/modules/sidebar/index.tsx | 3 | ||||
| -rw-r--r-- | src/modules/sidebar/modules/media.tsx | 149 |
4 files changed, 306 insertions, 77 deletions
diff --git a/scss/sidebar.scss b/scss/sidebar.scss index 9bac679..83d1953 100644 --- a/scss/sidebar.scss +++ b/scss/sidebar.scss @@ -13,6 +13,57 @@ } } +@mixin button { + @include lib.element-decel; + + background-color: color.change(scheme.$surface1, $alpha: 0.5); + + &:hover, + &:focus { + background-color: color.change(scheme.$surface2, $alpha: 0.5); + } + + &:active { + background-color: color.change(scheme.$overlay0, $alpha: 0.5); + } + + &:disabled { + color: scheme.$subtext0; + } +} + +@mixin button-active { + @include lib.element-decel; + + background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 50%), $alpha: 0.5); + + &:hover, + &:focus { + background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 30%), $alpha: 0.5); + } + + &:active { + background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 20%), $alpha: 0.5); + } +} + +@mixin media-button { + @include lib.element-decel; + + &:disabled { + color: scheme.$overlay2; + } + + &:hover, + &:focus { + color: color.mix(scheme.$subtext1, scheme.$subtext0, 50%); + } + + &:active { + color: scheme.$subtext0; + } +} + .sidebar { @include font.mono; @@ -155,21 +206,8 @@ margin-bottom: lib.s(5); font-size: lib.s(24); - & > * { - @include lib.element-decel; - - &:disabled { - color: scheme.$overlay0; - } - - &:hover, - &:focus { - color: scheme.$subtext0; - } - - &:active { - color: scheme.$overlay2; - } + & > button { + @include media-button; } } @@ -219,20 +257,10 @@ @include lib.spacing; & > button { + @include button; @include lib.rounded(10); - @include lib.element-decel; padding: lib.s(5) lib.s(10); - background-color: color.change(scheme.$surface1, $alpha: 0.5); - - &:hover, - &:focus { - background-color: color.change(scheme.$surface2, $alpha: 0.5); - } - - &:active { - background-color: color.change(scheme.$overlay0, $alpha: 0.5); - } } } } @@ -288,6 +316,95 @@ } } + .audio .media { + @include lib.spacing(40, true); + + .cover-art { + @include lib.rounded(10); + @include lib.element-decel; + @include lib.shadow(scheme.$mantle, $blur: 5, $spread: 2); + + background-position: center; + background-repeat: no-repeat; + background-size: cover; + min-width: lib.s(256); + min-height: lib.s(256); + font-size: lib.s(96); + font-weight: bold; + background-color: scheme.$base; + color: scheme.$subtext0; + margin-top: lib.s(20); + } + + .progress { + margin: 0 lib.s(40); + + .slider { + @include lib.rounded(8); + @include lib.fluent-decel(1000ms); + + min-height: lib.s(15); + background-color: scheme.$overlay0; + color: scheme.$subtext1; + } + + .time { + margin-top: lib.s(5); + font-size: lib.s(13); + color: scheme.$subtext1; + } + } + + .details { + font-size: lib.s(14); + margin-top: lib.s(20); + + @include lib.spacing(3, true); + + .title { + font-size: lib.s(18); + color: scheme.$text; + font-weight: bold; + } + + .artist { + color: scheme.$green; + } + + .album { + color: scheme.$subtext0; + } + } + + .controls { + margin-top: lib.s(-20); + margin-bottom: lib.s(5); + + button { + @include media-button; + + // Cause some nerd font icons don't have the correct width + &.needs-adjustment { + padding-right: lib.s(5); + } + } + + .playback { + font-size: lib.s(32); + + @include lib.spacing(40); + } + + .options { + margin: 0 lib.s(40); + margin-top: lib.s(-10); + font-size: lib.s(20); + + @include lib.spacing(20); + } + } + } + .networks { .list { @include lib.spacing(10, true); @@ -305,16 +422,7 @@ background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 50%), $alpha: 0.4); & > button { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 50%), $alpha: 0.5); - - &:hover, - &:focus { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 30%), $alpha: 0.5); - } - - &:active { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 20%), $alpha: 0.5); - } + @include button-active; } } @@ -329,27 +437,13 @@ } & > button { + @include button; @include lib.rounded(1000); - @include lib.element-decel; @include font.icon; font-size: lib.s(18); min-width: lib.s(30); min-height: lib.s(30); - background-color: color.change(scheme.$surface1, $alpha: 0.5); - - &:hover, - &:focus { - background-color: color.change(scheme.$surface2, $alpha: 0.5); - } - - &:active { - background-color: color.change(scheme.$overlay0, $alpha: 0.5); - } - - &:disabled { - color: scheme.$subtext0; - } } } } @@ -371,16 +465,7 @@ background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 50%), $alpha: 0.4); & > button { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 50%), $alpha: 0.5); - - &:hover, - &:focus { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 30%), $alpha: 0.5); - } - - &:active { - background-color: color.change(color.mix(scheme.$surface1, scheme.$primary, 20%), $alpha: 0.5); - } + @include button-active; } } @@ -395,27 +480,13 @@ } & > button { + @include button; @include lib.rounded(1000); - @include lib.element-decel; @include font.icon; font-size: lib.s(18); min-width: lib.s(30); min-height: lib.s(30); - background-color: color.change(scheme.$surface1, $alpha: 0.5); - - &:hover, - &:focus { - background-color: color.change(scheme.$surface2, $alpha: 0.5); - } - - &:active { - background-color: color.change(scheme.$overlay0, $alpha: 0.5); - } - - &:disabled { - color: scheme.$subtext0; - } } } } diff --git a/src/modules/sidebar/audio.tsx b/src/modules/sidebar/audio.tsx new file mode 100644 index 0000000..2b4c6e9 --- /dev/null +++ b/src/modules/sidebar/audio.tsx @@ -0,0 +1,8 @@ +import Media from "./modules/media"; + +export default () => ( + <box vertical className="pane audio" name="audio"> + <Media /> + <box className="separator" /> + </box> +); diff --git a/src/modules/sidebar/index.tsx b/src/modules/sidebar/index.tsx index 5b9a3a3..5ae7670 100644 --- a/src/modules/sidebar/index.tsx +++ b/src/modules/sidebar/index.tsx @@ -2,6 +2,7 @@ import type { Monitor } from "@/services/monitors"; import { bind, idle, register, Variable } from "astal"; import { App, Astal, Gdk, Gtk, Widget } from "astal/gtk3"; import { sidebar as config } from "config"; +import Audio from "./audio"; import Connectivity from "./connectivity"; import Dashboard from "./dashboard"; import NotifPane from "./notifpane"; @@ -21,7 +22,7 @@ export default class SideBar extends Widget.Window { visible: false, }); - const panes = [<Dashboard />, <Connectivity />, <NotifPane />]; + const panes = [<Dashboard />, <Audio />, <Connectivity />, <NotifPane />]; this.shown = Variable(panes[0].name); this.add( diff --git a/src/modules/sidebar/modules/media.tsx b/src/modules/sidebar/modules/media.tsx new file mode 100644 index 0000000..4392aa7 --- /dev/null +++ b/src/modules/sidebar/modules/media.tsx @@ -0,0 +1,149 @@ +import Players from "@/services/players"; +import Slider from "@/widgets/slider"; +import { bind, Variable } from "astal"; +import { Gtk } from "astal/gtk3"; +import AstalMpris from "gi://AstalMpris"; + +const lengthStr = (length: number) => + `${Math.floor(length / 60)}:${Math.floor(length % 60) + .toString() + .padStart(2, "0")}`; + +const noNull = (s: string | null) => s ?? "-"; + +const NoMedia = () => ( + <box vertical className="media" name="none"> + <box homogeneous halign={Gtk.Align.CENTER} className="cover-art"> + <label xalign={0.4} label="" /> + </box> + <box vertical className="progress"> + <Slider value={bind(Variable(0))} /> + <box className="time"> + <label label="-1:-1" /> + <box hexpand /> + <label label="-1:-1" /> + </box> + </box> + <box vertical className="details"> + <label truncate className="title" label="No media" /> + <label truncate className="artist" label="Try play some music!" /> + <label truncate className="album" label="" /> + </box> + <box vertical className="controls"> + <box halign={Gtk.Align.CENTER} className="playback"> + <button sensitive={false} cursor="pointer" label="" /> + <button sensitive={false} cursor="pointer" label="" /> + <button sensitive={false} cursor="pointer" label="" /> + </box> + <box className="options"> + <button sensitive={false} cursor="pointer" label="" /> + <button sensitive={false} cursor="pointer" label="" /> + <box hexpand /> + <button className="needs-adjustment" sensitive={false} cursor="pointer" label="" /> + <button className="needs-adjustment" sensitive={false} cursor="pointer" label="" /> + </box> + </box> + </box> +); + +const Player = ({ player }: { player: AstalMpris.Player }) => { + const position = Variable.derive([bind(player, "position"), bind(player, "length")], (p, l) => p / l); + + return ( + <box vertical className="media" name={player.busName} onDestroy={() => position.drop()}> + <box + homogeneous + halign={Gtk.Align.CENTER} + className="cover-art" + css={bind(player, "coverArt").as(a => `background-image: url("${a}");`)} + > + {bind(player, "coverArt").as(a => (a ? <box visible={false} /> : <label xalign={0.4} label="" />))} + </box> + <box vertical className="progress"> + <Slider value={bind(position)} /> + <box className="time"> + <label label={bind(player, "position").as(lengthStr)} /> + <box hexpand /> + <label label={bind(player, "length").as(lengthStr)} /> + </box> + </box> + <box vertical className="details"> + <label truncate className="title" label={bind(player, "title").as(noNull)} /> + <label truncate className="artist" label={bind(player, "artist").as(noNull)} /> + <label truncate className="album" label={bind(player, "album").as(noNull)} /> + </box> + <box vertical className="controls"> + <box halign={Gtk.Align.CENTER} className="playback"> + <button + sensitive={bind(player, "canGoPrevious")} + cursor="pointer" + onClicked={() => player.next()} + label="" + /> + <button + sensitive={bind(player, "canControl")} + cursor="pointer" + onClicked={() => player.play_pause()} + label={bind(player, "playbackStatus").as(s => + s === AstalMpris.PlaybackStatus.PLAYING ? "" : "" + )} + /> + <button + sensitive={bind(player, "canGoNext")} + cursor="pointer" + onClicked={() => player.next()} + label="" + /> + </box> + <box className="options"> + <button + sensitive={bind(player, "canSetFullscreen")} + cursor="pointer" + onClicked={() => player.toggle_fullscreen()} + label={bind(player, "fullscreen").as(f => (f ? "" : ""))} + /> + <button + sensitive={bind(player, "canControl")} + cursor="pointer" + onClicked={() => player.shuffle()} + label={bind(player, "shuffleStatus").as(s => (s === AstalMpris.Shuffle.ON ? "" : ""))} + /> + <box hexpand /> + <button + className="needs-adjustment" + sensitive={bind(player, "canControl")} + cursor="pointer" + onClicked={() => player.loop()} + label={bind(player, "loopStatus").as(l => + l === AstalMpris.Loop.TRACK ? "" : l === AstalMpris.Loop.PLAYLIST ? "" : "" + )} + /> + <button + className="needs-adjustment" + sensitive={bind(player, "canRaise")} + cursor="pointer" + onClicked={() => player.raise()} + label="" + /> + </box> + </box> + </box> + ); +}; + +export default () => { + const active = Variable(Players.get_default().list[0]?.busName ?? "none"); + + return ( + <box vertical> + <stack + transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT} + transitionDuration={150} + shown={bind(active)} + > + <NoMedia /> + {bind(Players.get_default(), "list").as(ps => ps.map(p => <Player player={p} />))} + </stack> + </box> + ); +}; |