Add ability to run as lockscreen widget
All checks were successful
PostmarketOS Build / Prepare (push) Successful in 11s
PostmarketOS Build / Build for aarch64 (push) Successful in 2m35s
PostmarketOS Build / Build for x86_64 (push) Successful in 56s

Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
Vasiliy Doylov 2025-07-11 04:37:22 +03:00
parent 0952dbaf81
commit e3877456bd
5 changed files with 115 additions and 53 deletions

View file

@ -7,22 +7,41 @@
*/
namespace MediaPlayer {
Gtk.CssProvider css;
[ModuleInit]
static Gtk.CssProvider css = null;
[CCode (cname = "g_io_phosh_plugin_media_player_load")]
public static void load (GLib.TypeModule module) {
public static void stub (GLib.TypeModule module) {
message ("Stub called");
Thread.usleep (1000);
if (typeof (Player) == 0) {
message ("Player type poshol po pizde");
message ("Trying to init %s", module.use () ? "Module OK" : "Module Fail");
load (module);
}
IOExtensionPoint.implement ("phosh-quick-setting-widget",
typeof (QuickSetting),
"media-player",
10);
css = new Gtk.CssProvider ();
css.load_from_resource ("/mobi/phosh/plugins/media-player/style.css");
Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
IOExtensionPoint.implement ("phosh-lockscreen-widget",
typeof (Utils.PlayerReplacer),
"media-player",
10);
if (css == null) {
message ("Loading CSS");
css = new Gtk.CssProvider ();
css.load_from_resource ("/mobi/phosh/plugins/media-player/style.css");
Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}
[ModuleInit]
public static void load (GLib.TypeModule module) {
}
[CCode (cname = "g_io_phosh_plugin_media_player_unload")]
public static void unload (GLib.IOModule module) {
Gtk.StyleContext.remove_provider_for_screen (Gdk.Screen.get_default (), css);
public static void unload (GLib.TypeModule module) {
message ("Unload");
}
[CCode (cname = "g_io_phosh_plugin_media_player_query")]
@ -30,6 +49,7 @@ namespace MediaPlayer {
StrvBuilder builder = new StrvBuilder ();
builder.add ("phosh-quick-setting-widget");
builder.add ("phosh-lockscreen-widget");
return builder.end ();
}
}

View file

@ -2,6 +2,6 @@
# Translators: This is an internal id, no need to translate it
Id=@name@
Name=Beautiful media player widget
Types=quick-setting;
Types=quick-setting;lockscreen
Comment=Yep, it was written in vala
Plugin=@plugins_dir@/libphosh-plugin-@name@.so

View file

@ -28,15 +28,24 @@ public class MediaPlayer.Player : Gtk.Box {
[GtkChild]
private unowned Gtk.ProgressBar progress;
delegate void OnStyleGenerated (Gdk.RGBA fg, Gdk.RGBA start, Gdk.RGBA end);
private string id = Uuid.string_random ();
private Gtk.CssProvider css_provider = new Gtk.CssProvider ();
construct {
warning ("Constructor");
Gtk.StyleContext.add_provider_for_screen
(
Gdk.Screen.get_default (),
css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
get_style_context ().add_class (id);
}
static construct {
warning ("Class Init");
}
static ~Player () {
warning ("Class Deinit");
}
~Player () {
warning ("Destructor");
@ -115,7 +124,7 @@ public class MediaPlayer.Player : Gtk.Box {
new Thread<void> ("Picture", () => generate_style (pb));
}
private async Icon ? process_icon (Icon? icon) {
private Icon ? process_icon (Icon? icon) {
Gdk.Pixbuf pixbuff = null;
if (icon == null)
return icon;
@ -125,7 +134,7 @@ public class MediaPlayer.Player : Gtk.Box {
else if (icon is FileIcon) {
var fi = (LoadableIcon) icon;
try {
pixbuff = yield new Gdk.Pixbuf.from_stream_async (yield fi.load_async (1024, null, null));
pixbuff = new Gdk.Pixbuf.from_stream (fi.load (1024, null, null));
} catch (Error err) {
warning ("Icon load failed %s", err.message);
return icon;
@ -142,31 +151,50 @@ public class MediaPlayer.Player : Gtk.Box {
return Utils.rect_pixbuff (pixbuff);
}
private Binding[] bindings = new Binding[0];
private Utils.SignalHelp[] signals = new Utils.SignalHelp[0];
public void bind_to_player (Gtk.Box player) {
player.bind_property ("visible", this, "visible", BindingFlags.SYNC_CREATE);
// Buttons
var play_btn = (Gtk.Button) Utils.find_widget_by_css_name (player, "btn_play");
pause.clicked.connect (() => play_btn.clicked ());
signals += Utils.SignalHelp () {
object = pause,
signal = pause.clicked.connect (() => play_btn.clicked ())
};
var play_img = (Gtk.Image) Utils.find_widget_by_css_name (player, "img_play");
play_img.bind_property ("icon-name", pause_img, "icon-name", BindingFlags.SYNC_CREATE);
bindings += play_img.bind_property ("icon-name", pause_img, "icon-name", BindingFlags.SYNC_CREATE);
var prev_btn = (Gtk.Button) Utils.find_widget_by_css_name (player, "btn_prev");
back.clicked.connect (() => prev_btn.clicked ());
signals += Utils.SignalHelp () {
object = back,
signal = back.clicked.connect (() => prev_btn.clicked ())
};
var next_btn = (Gtk.Button) Utils.find_widget_by_css_name (player, "btn_next");
next.clicked.connect (() => next_btn.clicked ());
signals += Utils.SignalHelp () {
object = next,
signal = next.clicked.connect (() => next_btn.clicked ())
};
// Labels
var _title = (Gtk.Label) Utils.find_widget_by_css_name (player, "lbl_title");
_title.bind_property ("label", title, "label", BindingFlags.SYNC_CREATE);
bindings += _title.bind_property ("label", title, "label", BindingFlags.SYNC_CREATE);
var _artist = (Gtk.Label) Utils.find_widget_by_css_name (player, "lbl_artist");
_artist.bind_property ("label", artist, "label", BindingFlags.SYNC_CREATE);
bindings += _artist.bind_property ("label", artist, "label", BindingFlags.SYNC_CREATE);
// Image
var _image = (Gtk.Image) Utils.find_widget_by_css_name (player, "img_art");
_image.notify["gicon"].connect (() => process_icon.begin (_image.gicon, (obj, res) => {
var cover = process_icon.end (res);
image.gicon = cover;
}));
_image.notify_property ("gicon");
bindings += _image.bind_property ("gicon", image, "gicon", BindingFlags.SYNC_CREATE, (binding, src, ref dst) => {
dst = process_icon ((Icon) src);
return true;
});
// Progress bar
var _progress = (Gtk.ProgressBar) Utils.find_widget_by_css_name (player, "prb_position");
_progress.bind_property ("fraction", progress, "fraction", BindingFlags.SYNC_CREATE);
bindings += _progress.bind_property ("fraction", progress, "fraction", BindingFlags.SYNC_CREATE);
}
public void unbind () {
foreach (var b in bindings) {
b.unbind ();
}
foreach (var s in signals) {
s.object.disconnect (s.signal);
}
}
}

View file

@ -13,33 +13,7 @@ using GLib;
public class MediaPlayer.QuickSetting : Phosh.QuickSetting {
private Player player = new Player () { margin_top = 24 };
private Gtk.Widget? old_player = null;
construct {
visible = false;
var ts = new TimeoutSource (500);
ts.set_callback (() => try_replace_player ());
ts.attach ();
}
~QuickSetting () {
if (old_player != null)
Utils.replace_widget (player, old_player);
}
bool try_replace_player () {
var anc = get_ancestor (Type.from_name ("PhoshSettings"));
if (anc == null)
return true;
old_player = Utils.find_widget_by_name (anc, "PhoshDefaultMediaPlayer");
if (old_player == null)
return true;
Utils.replace_widget (old_player, player);
player.bind_to_player ((Gtk.Box) old_player);
return false;
child = new Utils.PlayerReplacer ();
}
}

View file

@ -1,4 +1,4 @@
namespace Utils {
namespace MediaPlayer.Utils {
public delegate bool FindDelegate (Gtk.Widget widget);
public Gtk.Widget ? find_widget (Gtk.Widget widget, FindDelegate find_func) {
@ -34,13 +34,16 @@ namespace Utils {
public void replace_widget (Gtk.Widget old, Gtk.Widget replacement) {
var container = old.parent;
container.add (replacement);
if (container is Gtk.Box) {
var box = (Gtk.Box) container;
var position = get_box_position (box, old);
container.add (replacement);
box.reorder_child (replacement, position);
container.remove (old);
} else {
container.remove (old);
container.add (replacement);
}
container.remove (old);
}
public Gdk.Pixbuf round_pixbuff (Gdk.Pixbuf source, double roundness) {
@ -83,4 +86,41 @@ namespace Utils {
public double deg (double deg) {
return Math.PI / 180.0 * deg;
}
public struct SignalHelp {
Object object;
ulong signal;
}
public class PlayerReplacer : Gtk.Widget {
private Player? player = null;
private Gtk.Widget? old_player = null;
construct {
visible = false;
var ts = new TimeoutSource (100);
ts.set_callback (() => try_replace_player ());
ts.attach ();
}
~PlayerReplacer () {
if (old_player != null)
Utils.replace_widget (player, old_player);
player.unbind ();
}
bool try_replace_player () {
var anc = get_toplevel ();
if (anc == null)
return true;
old_player = Utils.find_widget_by_name (anc, "PhoshDefaultMediaPlayer");
if (old_player == null)
return true;
player = new Player () { margin_top = 24 };
Utils.replace_widget (old_player, player);
player.bind_to_player ((Gtk.Box) old_player);
return false;
}
}
}