media: make popdown window

This commit is contained in:
2 * r + 2 * t 2025-01-25 16:54:43 +11:00
parent fc7e061817
commit 0eacbc0ea2
14 changed files with 313 additions and 2 deletions

View file

@ -0,0 +1,5 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m 12 1 v 2 h -12 v 2 h 12 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 8 v 2 h -12 v 2 h 12 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 0" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 746 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,8 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="#000000">
<path d="m 3 1 h 3 c 0.550781 0 1 0.449219 1 1 v 12 c 0 0.550781 -0.449219 1 -1 1 h -3 c -0.550781 0 -1 -0.449219 -1 -1 v -12 c 0 -0.550781 0.449219 -1 1 -1 z m 0 0"/>
<path d="m 10 1 h 3 c 0.550781 0 1 0.449219 1 1 v 12 c 0 0.550781 -0.449219 1 -1 1 h -3 c -0.550781 0 -1 -0.449219 -1 -1 v -12 c 0 -0.550781 0.449219 -1 1 -1 z m 0 0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 608 B

View file

@ -0,0 +1,5 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m 2 2.5 v 11 c 0 1.5 1.269531 1.492188 1.269531 1.492188 h 0.128907 c 0.246093 0.003906 0.488281 -0.050782 0.699218 -0.171876 l 9.796875 -5.597656 c 0.433594 -0.242187 0.65625 -0.734375 0.65625 -1.226562 c 0 -0.492188 -0.222656 -0.984375 -0.65625 -1.222656 l -9.796875 -5.597657 c -0.210937 -0.121093 -0.453125 -0.175781 -0.699218 -0.175781 h -0.128907 s -1.269531 0 -1.269531 1.5 z m 0 0" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 640 B

View file

@ -0,0 +1,5 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m 7 1 v 2 h -3 c -2.199219 0 -4 1.800781 -4 4 v 2 c 0 1.019531 0.386719 1.964844 1.019531 2.671875 c 0.367188 0.410156 1 0.445313 1.410157 0.078125 c 0.414062 -0.367188 0.449218 -1 0.078124 -1.414062 c -0.316406 -0.351563 -0.507812 -0.8125 -0.507812 -1.335938 v -2 c 0 -1.125 0.875 -2 2 -2 h 3 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.207031 -0.207031 0.3125 -0.496093 0.289063 -0.789062 c 0 -0.015625 -0.003906 -0.03125 -0.007813 -0.046875 c -0.007812 -0.070313 -0.023437 -0.136719 -0.046875 -0.203125 c -0.011718 -0.035157 -0.023437 -0.070313 -0.042968 -0.101563 c 0 -0.007812 -0.003907 -0.011718 -0.007813 -0.019531 c -0.011719 -0.023437 -0.027344 -0.046875 -0.039063 -0.070313 c -0.007812 -0.011718 -0.019531 -0.027343 -0.027343 -0.039062 c -0.015625 -0.019531 -0.027344 -0.042969 -0.042969 -0.0625 c -0.007812 -0.007812 -0.015625 -0.015625 -0.023438 -0.023438 c -0.015624 -0.019531 -0.03125 -0.039062 -0.050781 -0.058593 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 7 1 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 1 5 c -0.550781 0 -1 0.449219 -1 1 v 1.007812 c 0 1.125 -0.875 2 -2 2 h -4 v -2.007812 h -1 v 0.007812 c -0.265625 -0.003906 -0.519531 0.101563 -0.707031 0.285157 l -2 2 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 2 2 c 0.1875 0.183594 0.441406 0.289063 0.707031 0.285157 v 0.007812 h 1 v -1.992188 h 4 c 2.199219 0 4 -1.804687 4 -4 v -1.007812 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,5 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 1 v 2 h -4 c -2.199219 0 -4 1.800781 -4 4 v 2 c 0 1.019531 0.386719 1.964844 1.019531 2.671875 c 0.367188 0.410156 1 0.445313 1.410157 0.078125 c 0.414062 -0.367188 0.449218 -1 0.078124 -1.414062 c -0.316406 -0.351563 -0.507812 -0.8125 -0.507812 -1.335938 v -2 c 0 -1.125 0.875 -2 2 -2 h 4 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 6.289062 3 c -0.265624 -0.011719 -0.523437 0.078125 -0.71875 0.257812 c -0.414062 0.367188 -0.449218 1 -0.078124 1.410157 c 0.316406 0.355469 0.507812 0.816406 0.507812 1.339843 v 2 c 0 1.125 -0.875 2 -2 2 h -4 v -2.007812 h -1 v 0.007812 c -0.265625 -0.003906 -0.519531 0.101563 -0.707031 0.285157 l -2 2 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 l 2 2 c 0.1875 0.183594 0.441406 0.289063 0.707031 0.285157 v 0.007812 h 1 v -1.992188 h 4 c 2.199219 0 4 -1.804687 4 -4 v -2 c 0 -1.023437 -0.386719 -1.96875 -1.019531 -2.675781 c -0.175781 -0.199219 -0.425781 -0.316406 -0.691407 -0.332031 z m 0 0" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,5 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m 12 1 v 2 h -3 c -0.859375 0 -1.59375 0.480469 -2.011719 1.007812 c -0.417969 0.523438 -0.648437 1.074219 -0.882812 1.542969 l -0.105469 0.210938 l -0.105469 -0.210938 c -0.191406 -0.382812 -0.386719 -0.816406 -0.671875 -1.25 c -0.414062 -0.632812 -1.207031 -1.300781 -2.222656 -1.300781 h -3 v 2 h 3 c 0.296875 0 0.316406 0.039062 0.550781 0.398438 c 0.164063 0.246093 0.34375 0.625 0.554688 1.050781 l 0.777343 1.550781 l -0.777343 1.550781 c -0.261719 0.523438 -0.476563 0.96875 -0.65625 1.199219 c -0.183594 0.226562 -0.199219 0.25 -0.449219 0.25 h -3 v 2 h 3 c 0.859375 0 1.59375 -0.480469 2.011719 -1.007812 c 0.417969 -0.523438 0.648437 -1.074219 0.882812 -1.542969 l 0.105469 -0.210938 l 0.105469 0.210938 c 0.164062 0.328125 0.335937 0.703125 0.5625 1.078125 c 0.414062 0.6875 1.222656 1.472656 2.332031 1.472656 h 3 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 h -1 v 2 h -3 c -0.324219 0 -0.351562 -0.058594 -0.617188 -0.503906 c -0.148437 -0.242188 -0.304687 -0.574219 -0.488281 -0.945313 l -0.777343 -1.550781 l 0.777343 -1.550781 c 0.261719 -0.523438 0.476563 -0.96875 0.65625 -1.199219 c 0.183594 -0.226562 0.199219 -0.25 0.449219 -0.25 h 3 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 0" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,8 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="#000000">
<path d="m 1 3 v 10 c 0 1 1.085938 1 1.085938 1 h 0.113281 c 0.210937 0 0.417969 -0.046875 0.601562 -0.148438 l 8.398438 -4.800781 c 0.375 -0.207031 0.5625 -0.628906 0.5625 -1.050781 s -0.1875 -0.84375 -0.5625 -1.050781 l -8.398438 -4.800781 c -0.183593 -0.101563 -0.390625 -0.148438 -0.601562 -0.148438 h -0.113281 s -1.085938 0 -1.085938 1 z m 0 0"/>
<path d="m 14.5 2 h -1 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 11 c 0 0.277344 0.222656 0.5 0.5 0.5 h 1 c 0.277344 0 0.5 -0.222656 0.5 -0.5 v -11 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 819 B

View file

@ -0,0 +1,8 @@
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="#000000">
<path d="m 15 3 v 10 c 0 1 -1.085938 1 -1.085938 1 h -0.113281 c -0.210937 0 -0.417969 -0.046875 -0.601562 -0.148438 l -8.398438 -4.800781 c -0.375 -0.207031 -0.5625 -0.628906 -0.5625 -1.050781 s 0.1875 -0.84375 0.5625 -1.050781 l 8.398438 -4.800781 c 0.183593 -0.101563 0.390625 -0.148438 0.601562 -0.148438 h 0.113281 s 1.085938 0 1.085938 1 z m 0 0"/>
<path d="m 1.5 2 h 1 c 0.277344 0 0.5 0.222656 0.5 0.5 v 11 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -1 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -11 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 820 B

View file

@ -2,3 +2,4 @@
@forward "updates";
@forward "bluetoothdevices";
@forward "networks";
@forward "media";

113
scss/popdowns/media.scss Normal file
View file

@ -0,0 +1,113 @@
@use "sass:color";
@use "../scheme";
@use "../lib";
@use "../font";
$-accent: scheme.$lavender;
$-accent2: scheme.$pink;
.media {
@include lib.rounded(8);
@include lib.border($-accent, 0.4, 2);
@include lib.shadow;
@include font.mono;
background-color: scheme.$base;
color: $-accent;
padding: lib.s(12);
font-size: lib.s(14);
min-width: lib.s(500);
min-height: lib.s(220);
.icon {
font-size: lib.s(32);
}
.overlay {
@include lib.rounded(5);
background-color: color.change(scheme.$base, $alpha: 0.8);
}
.background {
@include lib.rounded(5);
transition: background 300ms ease-in-out;
background-color: scheme.$surface0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
.player {
padding: lib.s(35) lib.s(12);
font-weight: bold;
@include lib.spacing(10, true);
.title {
font-size: lib.s(18);
}
.artist {
color: scheme.$green;
}
.album {
color: $-accent2;
}
.controls {
margin-top: lib.s(3);
@include lib.spacing(10);
button {
@include lib.rounded(4);
@include lib.element-decel;
@include lib.border(scheme.$base, 0.7);
padding: lib.s(5) lib.s(12);
font-size: lib.s(18);
background-color: $-accent;
color: scheme.$base;
&:disabled {
background-color: scheme.$overlay2;
}
&:hover,
&:focus {
background-color: color.change($-accent, $alpha: 0.8);
}
&:active {
background-color: color.change($-accent, $alpha: 0.6);
}
}
}
// Progress bar
trough {
@include lib.rounded(3);
margin-top: lib.s(10);
background-color: scheme.$surface0;
highlight {
@include lib.rounded(3);
background-color: $-accent2;
padding: lib.s(3) 0;
}
slider {
@include lib.rounded(3);
background-color: scheme.$overlay0;
min-width: lib.s(15);
min-height: lib.s(15);
}
}
}
}

View file

@ -87,9 +87,9 @@ const MediaPlaying = () => {
players.lastPlayer ? `${players.lastPlayer.title} - ${players.lastPlayer.artist}` : fallback;
return (
<button
onClick={(_, event) => {
onClick={(self, event) => {
if (event.button === Astal.MouseButton.PRIMARY) {
// TODO: media panel
togglePopup(self, event, "media");
} else if (event.button === Astal.MouseButton.SECONDARY) players.lastPlayer?.play_pause();
else if (event.button === Astal.MouseButton.MIDDLE) players.lastPlayer?.raise();
}}

View file

@ -1,4 +1,5 @@
import BluetoothDevices from "./bluetoothdevices";
import Media from "./media";
import Networks from "./networks";
import Notifications from "./notifications";
import Updates from "./updates";
@ -8,6 +9,7 @@ export default () => {
<Updates />;
<BluetoothDevices />;
<Networks />;
<Media />;
return null;
};

View file

@ -0,0 +1,140 @@
import { bind, Variable } from "astal";
import { Astal, Gtk } from "astal/gtk3";
import AstalMpris from "gi://AstalMpris";
import Players from "../../services/players";
import { isRealPlayer } from "../../utils/mpris";
import PopupWindow from "../../widgets/popupwindow";
const shuffleToIcon = (s: AstalMpris.Shuffle) =>
`caelestia-${s === AstalMpris.Shuffle.ON ? "shuffle" : "consecutive"}-symbolic`;
const playbackToIcon = (s: AstalMpris.PlaybackStatus) =>
`caelestia-${s === AstalMpris.PlaybackStatus.PLAYING ? "pause" : "play"}-symbolic`;
const loopToIcon = (s: AstalMpris.Loop) => {
if (s === AstalMpris.Loop.PLAYLIST) return "caelestia-repeat-symbolic";
if (s === AstalMpris.Loop.TRACK) return "caelestia-repeat-one-symbolic";
return "caelestia-no-repeat-symbolic";
};
const lengthStr = (length: number) =>
`${Math.floor(length / 60)}:${Math.floor(length % 60)
.toString()
.padStart(2, "0")}`;
const Player = ({ player }: { player: AstalMpris.Player }) => {
const background = (
<box className="background" css={bind(player, "artUrl").as(u => u && `background-image: url("${u}");`)} />
);
return (
<overlay
overlays={[
<box className="overlay" />,
<box
vertical
className="player"
setup={self =>
self.connect("size-allocate", () =>
background.set_size_request(self.get_preferred_width()[1], self.get_preferred_height()[1])
)
}
>
<label className="title" label={bind(player, "title").as(t => t ?? "")} />
<label className="artist" label={bind(player, "artist").as(a => a ?? "")} />
<label className="album" label={bind(player, "album").as(a => a ?? "")} />
<box className="controls" halign={Gtk.Align.CENTER}>
<button cursor="pointer" onClicked={() => player.shuffle()} sensitive={player.canControl}>
<icon icon={bind(player, "shuffleStatus").as(shuffleToIcon)} />
</button>
<button cursor="pointer" onClicked={() => player.previous()} sensitive={player.canGoPrevious}>
<icon icon="caelestia-skip-previous-symbolic" />
</button>
<button cursor="pointer" onClicked={() => player.play_pause()} sensitive={player.canControl}>
<icon icon={bind(player, "playbackStatus").as(playbackToIcon)} />
</button>
<button cursor="pointer" onClicked={() => player.next()} sensitive={player.canGoNext}>
<icon icon="caelestia-skip-next-symbolic" />
</button>
<button cursor="pointer" onClicked={() => player.loop()} sensitive={player.canControl}>
<icon icon={bind(player, "loopStatus").as(loopToIcon)} />
</button>
</box>
<slider
hexpand
onDragged={self => player.set_position(self.value * player.length)}
setup={self => {
const update = () => {
self.set_tooltip_text(`${lengthStr(player.position)}/${lengthStr(player.length)}`);
self.set_value(player.position / player.length);
};
self.hook(player, "notify::position", update);
self.hook(player, "notify::length", update);
update();
}}
/>
</box>,
]}
>
{background}
</overlay>
);
};
export default () => {
const shown = Variable("none");
return (
<PopupWindow name="media">
<box className="media">
<eventbox
onClick={(_, event) => {
if (event.button === Astal.MouseButton.SECONDARY) {
const current = Players.get_default().list.find(p => p.busName === shown.get());
if (current) Players.get_default().makeCurrent(current);
}
}}
onScroll={(_, event) => {
const players = AstalMpris.get_default().players.filter(isRealPlayer);
const idx = players.findIndex(p => p.busName === shown.get());
if (idx === -1) return;
if (event.delta_y < 0) {
if (idx > 0) shown.set(players[idx - 1].busName);
} else if (idx < players.length - 1) shown.set(players[idx + 1].busName);
}}
>
<stack
expand
transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
transitionDuration={150}
shown={bind(shown)}
setup={self => {
const players = new Map<string, JSX.Element>();
const addPlayer = (player: AstalMpris.Player) => {
if (!isRealPlayer(player) || players.has(player.busName)) return;
const widget = <Player player={player} />;
self.add_named(widget, player.busName);
if (players.size === 0) shown.set(player.busName);
players.set(player.busName, widget);
};
for (const player of Players.get_default().list) addPlayer(player);
self.hook(AstalMpris.get_default(), "player-added", (_, player) => addPlayer(player));
self.hook(AstalMpris.get_default(), "player-closed", (_, player) => {
players.get(player.busName)?.destroy();
players.delete(player.busName);
if (shown.get() === player.busName)
shown.set(AstalMpris.get_default().players.find(isRealPlayer)?.busName ?? "none");
});
}}
>
<box vertical valign={Gtk.Align.CENTER} name="none">
<label className="icon" label="music_note" />
<label label="No media playing" />
</box>
</stack>
</eventbox>
</box>
</PopupWindow>
);
};