🔧 chore: better implementation of the media copy link feature, fix Cliboard.copyAsync() always returning an error
This commit is contained in:
@@ -134,11 +134,14 @@ $color-hover: colors.$bg-primary;
|
|||||||
margin-right: $spacing;
|
margin-right: $spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .media-controls {
|
& .buttons {
|
||||||
|
margin-left: $spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .button-row {
|
||||||
border-top-right-radius: 12px;
|
border-top-right-radius: 12px;
|
||||||
border-bottom-right-radius: 12px;
|
border-bottom-right-radius: 12px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
margin-left: $spacing;
|
|
||||||
|
|
||||||
& > button image {
|
& > button image {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
& .bottom {
|
& .bottom {
|
||||||
& .controls {
|
& .button-row {
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
|
|
||||||
& button {
|
& button {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class Clipboard extends GObject.Object {
|
|||||||
|
|
||||||
const stderr = Gio.DataInputStream.new(proc.get_stderr_pipe()!);
|
const stderr = Gio.DataInputStream.new(proc.get_stderr_pipe()!);
|
||||||
|
|
||||||
if(!proc.wait_check()) {
|
if(!proc.wait_check(null)) {
|
||||||
try {
|
try {
|
||||||
const [err, ] = stderr.read_upto('\x00', -1);
|
const [err, ] = stderr.read_upto('\x00', -1);
|
||||||
console.error(`Clipboard: An error occurred while copying text. Stderr: ${err}`);
|
console.error(`Clipboard: An error occurred while copying text. Stderr: ${err}`);
|
||||||
|
|||||||
+29
-25
@@ -80,31 +80,35 @@ export const Media = () => {
|
|||||||
revealChild={false}>
|
revealChild={false}>
|
||||||
|
|
||||||
<With value={player(pl => pl.available)}>
|
<With value={player(pl => pl.available)}>
|
||||||
{(available: boolean) => available && <Gtk.Box class={"media-controls button-row"}>
|
{(available: boolean) => available && <Gtk.Box class={"buttons"} spacing={4}>
|
||||||
<Gtk.Button class={"link"} iconName={"edit-paste-symbolic"}
|
<Gtk.Box class={"extra button-row"}>
|
||||||
visible={variableToBoolean(accessMediaUrl(player.get()))}
|
<Gtk.Button class={"link"} iconName={"edit-paste-symbolic"}
|
||||||
tooltipText={"Copy link to Clipboard"} onClicked={() => {
|
visible={variableToBoolean(accessMediaUrl(player.get()))}
|
||||||
const url = accessMediaUrl(player.get()).get();
|
tooltipText={"Copy link to Clipboard"} onClicked={() => {
|
||||||
url && Clipboard.getDefault().copyAsync(url);
|
const url = accessMediaUrl(player.get()).get();
|
||||||
}}
|
url && Clipboard.getDefault().copyAsync(url);
|
||||||
/>
|
}}
|
||||||
<Gtk.Button class={"previous"} iconName={"media-skip-backward-symbolic"}
|
/>
|
||||||
tooltipText={"Previous"} onClicked={() =>
|
</Gtk.Box>
|
||||||
player.get().canGoPrevious && player.get().previous()}
|
<Gtk.Box class={"media-controls button-row"}>
|
||||||
/>
|
<Gtk.Button class={"previous"} iconName={"media-skip-backward-symbolic"}
|
||||||
<Gtk.Button class={"play-pause"} iconName={createBinding(player.get(), "playbackStatus").as(status =>
|
tooltipText={"Previous"} onClicked={() =>
|
||||||
status === AstalMpris.PlaybackStatus.PAUSED ?
|
player.get().canGoPrevious && player.get().previous()}
|
||||||
"media-playback-start-symbolic"
|
/>
|
||||||
: "media-playback-pause-symbolic")}
|
<Gtk.Button class={"play-pause"} iconName={createBinding(player.get(), "playbackStatus").as(status =>
|
||||||
tooltipText={
|
status === AstalMpris.PlaybackStatus.PAUSED ?
|
||||||
createBinding(player.get(), "playbackStatus").as(status =>
|
"media-playback-start-symbolic"
|
||||||
status === AstalMpris.PlaybackStatus.PAUSED ? "Play" : "Pause")
|
: "media-playback-pause-symbolic")}
|
||||||
} onClicked={() => player.get().play_pause()}
|
tooltipText={
|
||||||
/>
|
createBinding(player.get(), "playbackStatus").as(status =>
|
||||||
<Gtk.Button class={"next"} iconName={"media-skip-forward-symbolic"}
|
status === AstalMpris.PlaybackStatus.PAUSED ? "Play" : "Pause")
|
||||||
tooltipText={"Next"} onClicked={() => player.get().canGoNext &&
|
} onClicked={() => player.get().play_pause()}
|
||||||
player.get().next()}
|
/>
|
||||||
/>
|
<Gtk.Button class={"next"} iconName={"media-skip-forward-symbolic"}
|
||||||
|
tooltipText={"Next"} onClicked={() => player.get().canGoNext &&
|
||||||
|
player.get().next()}
|
||||||
|
/>
|
||||||
|
</Gtk.Box>
|
||||||
</Gtk.Box>}
|
</Gtk.Box>}
|
||||||
</With>
|
</With>
|
||||||
</Gtk.Revealer>
|
</Gtk.Revealer>
|
||||||
|
|||||||
@@ -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 { Astal, Gtk } from "ags/gtk4";
|
||||||
import { Clipboard } from "../../modules/clipboard";
|
import { Clipboard } from "../../modules/clipboard";
|
||||||
import { accessMediaUrl } from "../../modules/media";
|
import { accessMediaUrl } from "../../modules/media";
|
||||||
import { player, setPlayer } from "../../modules/media";
|
import { player, setPlayer } from "../../modules/media";
|
||||||
import { createBinding, For } from "ags";
|
|
||||||
import { pathToURI, variableToBoolean } from "../../modules/utils";
|
import { pathToURI, variableToBoolean } from "../../modules/utils";
|
||||||
|
|
||||||
import AstalMpris from "gi://AstalMpris";
|
import AstalMpris from "gi://AstalMpris";
|
||||||
import AstalIO from "gi://AstalIO";
|
|
||||||
import Pango from "gi://Pango?version=1.0";
|
import Pango from "gi://Pango?version=1.0";
|
||||||
import Adw from "gi://Adw?version=1";
|
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 = () => {
|
export const BigMedia = () => {
|
||||||
const availablePlayers = createBinding(AstalMpris.get_default(), "players").as(pls =>
|
const availablePlayers = createBinding(AstalMpris.get_default(), "players").as(pls =>
|
||||||
@@ -47,6 +46,7 @@ export const BigMedia = () => {
|
|||||||
@register({ GTypeName: "PlayerWidget" })
|
@register({ GTypeName: "PlayerWidget" })
|
||||||
class PlayerWidget extends Gtk.Box {
|
class PlayerWidget extends Gtk.Box {
|
||||||
#player!: AstalMpris.Player;
|
#player!: AstalMpris.Player;
|
||||||
|
#copyClickTimeout?: GLib.Source;
|
||||||
|
|
||||||
get player() { return this.#player; }
|
get player() { return this.#player; }
|
||||||
|
|
||||||
@@ -98,15 +98,17 @@ class PlayerWidget extends Gtk.Box {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(!dragTimer) {
|
if(!dragTimer) {
|
||||||
dragTimer = timeout(200, () =>
|
dragTimer = setTimeout(() =>
|
||||||
player.position = Math.floor(value));
|
player.position = Math.floor(value)
|
||||||
|
, 200);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dragTimer.cancel();
|
dragTimer.destroy();
|
||||||
dragTimer = timeout(200, () =>
|
dragTimer = setTimeout(() =>
|
||||||
player.position = Math.floor(value));
|
player.position = Math.floor(value)
|
||||||
|
, 200);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Gtk.Box> as Gtk.Box
|
</Gtk.Box> as Gtk.Box
|
||||||
@@ -123,58 +125,99 @@ class PlayerWidget extends Gtk.Box {
|
|||||||
})} $type="start"
|
})} $type="start"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Gtk.Box class={"controls button-row"} $type="center">
|
<Gtk.Box spacing={4} $type="center">
|
||||||
<Gtk.Button class={"link"} iconName={"edit-paste-symbolic"}
|
<Gtk.Box class={"extra button-row"}>
|
||||||
tooltipText={"Copy link to clipboard"}
|
<Gtk.Button class={"link"}
|
||||||
visible={variableToBoolean(accessMediaUrl(player))}
|
tooltipText={"Copy link to clipboard"}
|
||||||
onClicked={() => {
|
visible={variableToBoolean(accessMediaUrl(player))}
|
||||||
const url = accessMediaUrl(player).get();
|
onClicked={(self) => {
|
||||||
url && Clipboard.getDefault().copyAsync(url);
|
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)
|
||||||
<Gtk.Button class={"shuffle"} visible={createBinding(player, "shuffleStatus").as(status =>
|
url &&
|
||||||
status !== AstalMpris.Shuffle.UNSUPPORTED)} iconName={
|
Clipboard.getDefault().copyAsync(url).then(() => {
|
||||||
createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ?
|
if(this.#copyClickTimeout && !this.#copyClickTimeout.is_destroyed())
|
||||||
"media-playlist-shuffle-symbolic"
|
this.#copyClickTimeout.destroy();
|
||||||
: "media-playlist-consecutive-symbolic")} tooltipText={
|
|
||||||
createBinding(player, "shuffleStatus").as(status => status === AstalMpris.Shuffle.ON ?
|
|
||||||
"Shuffle"
|
|
||||||
: "No shuffle")} onClicked={() => player.shuffle()}
|
|
||||||
/>
|
|
||||||
<Gtk.Button class={"previous"} iconName={"media-skip-backward-symbolic"}
|
|
||||||
tooltipText={"Previous"} onClicked={() => player.canGoPrevious && player.previous()}
|
|
||||||
/>
|
|
||||||
<Gtk.Button class={"play-pause"} tooltipText={
|
|
||||||
createBinding(player, "playbackStatus").as(status =>
|
|
||||||
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()}
|
|
||||||
/>
|
|
||||||
<Gtk.Button class={"next"} iconName={"media-skip-forward-symbolic"}
|
|
||||||
tooltipText={"Next"} onClicked={() => player.canGoNext && player.next()}
|
|
||||||
/>
|
|
||||||
<Gtk.Button class={"repeat"} iconName={createBinding(player, "loopStatus").as(status => {
|
|
||||||
if(status === AstalMpris.Loop.TRACK)
|
|
||||||
return "media-playlist-repeat-song-symbolic";
|
|
||||||
|
|
||||||
if(status === AstalMpris.Loop.PLAYLIST)
|
(self.get_child() as Gtk.Stack).set_visible_child_name("done-icon");
|
||||||
return "media-playlist-repeat-symbolic";
|
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";
|
(self.get_child() as Gtk.Stack).set_visible_child_name("error-icon");
|
||||||
})} visible={createBinding(player, "loopStatus").as(status =>
|
this.#copyClickTimeout = setTimeout(() => {
|
||||||
status !== AstalMpris.Loop.UNSUPPORTED)}
|
(self.get_child() as Gtk.Stack).set_visible_child_name("copy-icon");
|
||||||
tooltipText={createBinding(player, "loopStatus").as(status => {
|
this.#copyClickTimeout!.destroy();
|
||||||
if(status === AstalMpris.Loop.TRACK)
|
this.#copyClickTimeout = undefined;
|
||||||
return "Loop song";
|
}, 900);
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
|
||||||
if(status === AstalMpris.Loop.PLAYLIST)
|
<Gtk.Stack transitionType={Gtk.StackTransitionType.CROSSFADE}
|
||||||
return "Loop playlist";
|
transitionDuration={340}>
|
||||||
|
|
||||||
return "No loop";
|
<Gtk.StackPage name={"copy-icon"} child={
|
||||||
})} onClicked={() => player.loop()}
|
<Gtk.Image iconName={"edit-paste-symbolic"} /> as Gtk.Widget
|
||||||
/>
|
} />
|
||||||
|
<Gtk.StackPage name={"done-icon"} child={
|
||||||
|
<Gtk.Image iconName={"object-select-symbolic"} /> as Gtk.Widget
|
||||||
|
} />
|
||||||
|
<Gtk.StackPage name={"error-icon"} child={
|
||||||
|
<Gtk.Image iconName={"window-close-symbolic"} /> as Gtk.Widget
|
||||||
|
} />
|
||||||
|
</Gtk.Stack>
|
||||||
|
</Gtk.Button>
|
||||||
|
</Gtk.Box>
|
||||||
|
<Gtk.Box class={"media-controls button-row"}>
|
||||||
|
<Gtk.Button class={"shuffle"} visible={createBinding(player, "shuffleStatus").as(status =>
|
||||||
|
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()}
|
||||||
|
/>
|
||||||
|
<Gtk.Button class={"previous"} iconName={"media-skip-backward-symbolic"}
|
||||||
|
tooltipText={"Previous"} onClicked={() => player.canGoPrevious && player.previous()}
|
||||||
|
/>
|
||||||
|
<Gtk.Button class={"play-pause"} tooltipText={
|
||||||
|
createBinding(player, "playbackStatus").as(status =>
|
||||||
|
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()}
|
||||||
|
/>
|
||||||
|
<Gtk.Button class={"next"} iconName={"media-skip-forward-symbolic"}
|
||||||
|
tooltipText={"Next"} onClicked={() => player.canGoNext && player.next()}
|
||||||
|
/>
|
||||||
|
<Gtk.Button class={"repeat"} iconName={createBinding(player, "loopStatus").as(status => {
|
||||||
|
if(status === AstalMpris.Loop.TRACK)
|
||||||
|
return "media-playlist-repeat-song-symbolic";
|
||||||
|
|
||||||
|
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()}
|
||||||
|
/>
|
||||||
|
</Gtk.Box>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
<Gtk.Label class={"length"} xalign={1} yalign={0}
|
<Gtk.Label class={"length"} xalign={1} yalign={0}
|
||||||
halign={Gtk.Align.END} label={createBinding(player, "length").as(len => { /* bananananananana */
|
halign={Gtk.Align.END} label={createBinding(player, "length").as(len => { /* bananananananana */
|
||||||
|
|||||||
Reference in New Issue
Block a user