diff --git a/resources/styles/_bar.scss b/resources/styles/_bar.scss index 7b87197..2a7461c 100644 --- a/resources/styles/_bar.scss +++ b/resources/styles/_bar.scss @@ -134,11 +134,14 @@ $color-hover: colors.$bg-primary; margin-right: $spacing; } - & .media-controls { + & .buttons { + margin-left: $spacing; + } + + & .button-row { border-top-right-radius: 12px; border-bottom-right-radius: 12px; padding: 4px 0; - margin-left: $spacing; & > button image { margin: 0; diff --git a/resources/styles/_center-window.scss b/resources/styles/_center-window.scss index 966bf78..2363bd9 100644 --- a/resources/styles/_center-window.scss +++ b/resources/styles/_center-window.scss @@ -52,7 +52,7 @@ } & .bottom { - & .controls { + & .button-row { margin-top: 9px; & button { diff --git a/src/modules/clipboard.ts b/src/modules/clipboard.ts index 8c0fc52..bd292b2 100644 --- a/src/modules/clipboard.ts +++ b/src/modules/clipboard.ts @@ -102,7 +102,7 @@ class Clipboard extends GObject.Object { const stderr = Gio.DataInputStream.new(proc.get_stderr_pipe()!); - if(!proc.wait_check()) { + if(!proc.wait_check(null)) { try { const [err, ] = stderr.read_upto('\x00', -1); console.error(`Clipboard: An error occurred while copying text. Stderr: ${err}`); diff --git a/src/widget/bar/Media.tsx b/src/widget/bar/Media.tsx index 403ecc4..0365a89 100644 --- a/src/widget/bar/Media.tsx +++ b/src/widget/bar/Media.tsx @@ -80,31 +80,35 @@ export const Media = () => { revealChild={false}> pl.available)}> - {(available: boolean) => available && - { - const url = accessMediaUrl(player.get()).get(); - url && Clipboard.getDefault().copyAsync(url); - }} - /> - - player.get().canGoPrevious && player.get().previous()} - /> - - status === AstalMpris.PlaybackStatus.PAUSED ? - "media-playback-start-symbolic" - : "media-playback-pause-symbolic")} - tooltipText={ - createBinding(player.get(), "playbackStatus").as(status => - status === AstalMpris.PlaybackStatus.PAUSED ? "Play" : "Pause") - } onClicked={() => player.get().play_pause()} - /> - player.get().canGoNext && - player.get().next()} - /> + {(available: boolean) => available && + + { + const url = accessMediaUrl(player.get()).get(); + url && Clipboard.getDefault().copyAsync(url); + }} + /> + + + + player.get().canGoPrevious && player.get().previous()} + /> + + status === AstalMpris.PlaybackStatus.PAUSED ? + "media-playback-start-symbolic" + : "media-playback-pause-symbolic")} + tooltipText={ + createBinding(player.get(), "playbackStatus").as(status => + status === AstalMpris.PlaybackStatus.PAUSED ? "Play" : "Pause") + } onClicked={() => player.get().play_pause()} + /> + player.get().canGoNext && + player.get().next()} + /> + } diff --git a/src/widget/center-window/BigMedia.tsx b/src/widget/center-window/BigMedia.tsx index f87af6c..48a10df 100644 --- a/src/widget/center-window/BigMedia.tsx +++ b/src/widget/center-window/BigMedia.tsx @@ -1,19 +1,18 @@ -import { timeout } from "ags/time"; +import { createBinding, For } from "ags"; +import { register } from "ags/gobject"; import { Astal, Gtk } from "ags/gtk4"; import { Clipboard } from "../../modules/clipboard"; import { accessMediaUrl } from "../../modules/media"; import { player, setPlayer } from "../../modules/media"; -import { createBinding, For } from "ags"; import { pathToURI, variableToBoolean } from "../../modules/utils"; import AstalMpris from "gi://AstalMpris"; -import AstalIO from "gi://AstalIO"; import Pango from "gi://Pango?version=1.0"; import Adw from "gi://Adw?version=1"; -import { register } from "ags/gobject"; +import GLib from "gi://GLib?version=2.0"; -let dragTimer: (AstalIO.Time|undefined); +let dragTimer: (GLib.Source|undefined); export const BigMedia = () => { const availablePlayers = createBinding(AstalMpris.get_default(), "players").as(pls => @@ -47,6 +46,7 @@ export const BigMedia = () => { @register({ GTypeName: "PlayerWidget" }) class PlayerWidget extends Gtk.Box { #player!: AstalMpris.Player; + #copyClickTimeout?: GLib.Source; get player() { return this.#player; } @@ -98,15 +98,17 @@ class PlayerWidget extends Gtk.Box { return; if(!dragTimer) { - dragTimer = timeout(200, () => - player.position = Math.floor(value)); + dragTimer = setTimeout(() => + player.position = Math.floor(value) + , 200); return; } - dragTimer.cancel(); - dragTimer = timeout(200, () => - player.position = Math.floor(value)); + dragTimer.destroy(); + dragTimer = setTimeout(() => + player.position = Math.floor(value) + , 200); }} /> as Gtk.Box @@ -123,58 +125,99 @@ class PlayerWidget extends Gtk.Box { })} $type="start" /> - - { - const url = accessMediaUrl(player).get(); - url && Clipboard.getDefault().copyAsync(url); - }} - /> - - status !== AstalMpris.Shuffle.UNSUPPORTED)} iconName={ - createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ? - "media-playlist-shuffle-symbolic" - : "media-playlist-consecutive-symbolic")} tooltipText={ - createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ? - "Shuffle" - : "No shuffle")} onClicked={() => player.shuffle()} - /> - player.canGoPrevious && player.previous()} - /> - - status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play")} - iconName={createBinding(player, "playbackStatus").as(status => - status === AstalMpris.PlaybackStatus.PLAYING ? - "media-playback-pause-symbolic" - : "media-playback-start-symbolic")} onClicked={() => player.play_pause()} - /> - player.canGoNext && player.next()} - /> - { - if(status === AstalMpris.Loop.TRACK) - return "media-playlist-repeat-song-symbolic"; + + + { + const url = accessMediaUrl(player).get(); + // a widget that supports adding multiple icons and allows switching + // through them would be pretty nice!! (i'll probably do this later) + url && + Clipboard.getDefault().copyAsync(url).then(() => { + if(this.#copyClickTimeout && !this.#copyClickTimeout.is_destroyed()) + this.#copyClickTimeout.destroy(); - if(status === AstalMpris.Loop.PLAYLIST) - return "media-playlist-repeat-symbolic"; + (self.get_child() as Gtk.Stack).set_visible_child_name("done-icon"); + this.#copyClickTimeout = setTimeout(() => { + (self.get_child() as Gtk.Stack).set_visible_child_name("copy-icon"); + this.#copyClickTimeout!.destroy(); + this.#copyClickTimeout = undefined; + }, 1100); + }).catch(() => { + if(this.#copyClickTimeout && !this.#copyClickTimeout.is_destroyed()) + this.#copyClickTimeout.destroy(); - return "loop-arrow-symbolic"; - })} visible={createBinding(player, "loopStatus").as(status => - status !== AstalMpris.Loop.UNSUPPORTED)} - tooltipText={createBinding(player, "loopStatus").as(status => { - if(status === AstalMpris.Loop.TRACK) - return "Loop song"; + (self.get_child() as Gtk.Stack).set_visible_child_name("error-icon"); + this.#copyClickTimeout = setTimeout(() => { + (self.get_child() as Gtk.Stack).set_visible_child_name("copy-icon"); + this.#copyClickTimeout!.destroy(); + this.#copyClickTimeout = undefined; + }, 900); + }); + }}> + + - if(status === AstalMpris.Loop.PLAYLIST) - return "Loop playlist"; + as Gtk.Widget + } /> + as Gtk.Widget + } /> + as Gtk.Widget + } /> + + + + + + status !== AstalMpris.Shuffle.UNSUPPORTED)} iconName={ + createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ? + "media-playlist-shuffle-symbolic" + : "media-playlist-consecutive-symbolic")} tooltipText={ + createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ? + "Shuffle" + : "No shuffle")} onClicked={() => player.shuffle()} + /> + player.canGoPrevious && player.previous()} + /> + + status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play")} + iconName={createBinding(player, "playbackStatus").as(status => + status === AstalMpris.PlaybackStatus.PLAYING ? + "media-playback-pause-symbolic" + : "media-playback-start-symbolic")} onClicked={() => player.play_pause()} + /> + player.canGoNext && player.next()} + /> + { + if(status === AstalMpris.Loop.TRACK) + return "media-playlist-repeat-song-symbolic"; - return "No loop"; - })} onClicked={() => player.loop()} - /> + if(status === AstalMpris.Loop.PLAYLIST) + return "media-playlist-repeat-symbolic"; + + return "loop-arrow-symbolic"; + })} visible={createBinding(player, "loopStatus").as(status => + status !== AstalMpris.Loop.UNSUPPORTED)} + tooltipText={createBinding(player, "loopStatus").as(status => { + if(status === AstalMpris.Loop.TRACK) + return "Loop song"; + + if(status === AstalMpris.Loop.PLAYLIST) + return "Loop playlist"; + + return "No loop"; + })} onClicked={() => player.loop()} + /> + { /* bananananananana */