Compare commits

..

2 commits

Author SHA1 Message Date
b53b3d8362 mostly translate play queue song widget 2024-10-31 11:17:12 +01:00
620d5803e6 was that really useless 2024-10-31 08:25:33 +01:00
9 changed files with 309 additions and 157 deletions

View file

@ -110,8 +110,8 @@ template $AudreyUiPlayQueueSong: Box {
actions: move; actions: move;
propagation-phase: capture; propagation-phase: capture;
prepare => $on_drag_prepare (); prepare => $on_drag_prepare () swapped;
drag-begin => $on_drag_begin (); drag-begin => $on_drag_begin () swapped;
} }
DropTarget { DropTarget {
@ -119,7 +119,7 @@ template $AudreyUiPlayQueueSong: Box {
formats: "AudreyUiPlayQueueSong"; formats: "AudreyUiPlayQueueSong";
preload: true; preload: true;
drop => $on_drop (); drop => $on_drop () swapped;
} }
} }

View file

@ -1,10 +1,10 @@
mod song; pub mod song;
pub use song::Song; pub use song::Song;
mod state; mod state;
pub use state::State; pub use state::State;
mod ffi { pub mod ffi {
use gtk::glib; use gtk::glib;
#[repr(C)] #[repr(C)]
@ -31,6 +31,15 @@ mod ffi {
pub fn audrey_playbin_get_duration(self_: *mut AudreyPlaybin) -> f64; pub fn audrey_playbin_get_duration(self_: *mut AudreyPlaybin) -> f64;
pub fn audrey_playbin_get_mute(self_: *mut AudreyPlaybin) -> glib::ffi::gboolean; pub fn audrey_playbin_get_mute(self_: *mut AudreyPlaybin) -> glib::ffi::gboolean;
pub fn audrey_playbin_set_mute(self_: *mut AudreyPlaybin, mute: glib::ffi::gboolean); pub fn audrey_playbin_set_mute(self_: *mut AudreyPlaybin, mute: glib::ffi::gboolean);
pub fn audrey_playbin_get_play_queue_position(
self_: *mut AudreyPlaybin,
) -> std::ffi::c_uint;
pub fn audrey_playbin_move_track(
self_: *mut AudreyPlaybin,
from: std::ffi::c_uint,
to: std::ffi::c_uint,
);
pub fn audrey_playbin_remove_track(self_: *mut AudreyPlaybin, position: std::ffi::c_uint);
} }
} }
@ -95,4 +104,16 @@ impl Playbin {
pub fn set_mute(&self, mute: bool) { pub fn set_mute(&self, mute: bool) {
unsafe { ffi::audrey_playbin_set_mute(self.to_glib_none().0, mute.into_glib()) } unsafe { ffi::audrey_playbin_set_mute(self.to_glib_none().0, mute.into_glib()) }
} }
pub fn play_queue_position(&self) -> u32 {
unsafe { ffi::audrey_playbin_get_play_queue_position(self.to_glib_none().0) }
}
pub fn move_track(&self, from: u32, to: u32) {
unsafe { ffi::audrey_playbin_move_track(self.to_glib_none().0, from, to) }
}
pub fn remove_track(&self, position: u32) {
unsafe { ffi::audrey_playbin_remove_track(self.to_glib_none().0, position) }
}
} }

View file

@ -1,14 +1,14 @@
mod ffi { pub mod ffi {
use gtk::glib; use gtk::glib;
#[repr(C)] #[repr(C)]
pub struct AudreyPlaybinSong { pub struct AudreyPlaybinSong {
parent_instance: glib::gobject_ffi::GObject, _data: [u8; 0],
} }
#[repr(C)] #[repr(C)]
pub struct AudreyPlaybinSongClass { pub struct AudreyPlaybinSongClass {
parent_class: glib::gobject_ffi::GObjectClass, _data: [u8; 0],
} }
extern "C" { extern "C" {

View file

@ -1 +1,17 @@
typedef struct _AudreyUiPlaybar AudreyUiPlaybar; #include <glib-object.h>
#include <glib.h>
// ui::playbar
typedef void AudreyUiPlaybar;
// ui::play_queue::Song
typedef void AudreyUiPlayQueueSong;
#define AUDREY_UI_TYPE_PLAY_QUEUE_SONG (audrey_ui_play_queue_song_get_type ())
GType audrey_ui_play_queue_song_get_type(void);
AudreyUiPlayQueueSong *audrey_ui_play_queue_song_new(void *playbin);
void audrey_ui_play_queue_song_set_draggable(AudreyUiPlayQueueSong *self, gboolean draggable);
void audrey_ui_play_queue_song_set_show_position(AudreyUiPlayQueueSong *self, gboolean show_position);
void audrey_ui_play_queue_song_set_show_artist(AudreyUiPlayQueueSong *self, gboolean show_artist);
void audrey_ui_play_queue_song_set_show_cover(AudreyUiPlayQueueSong *self, gboolean show_cover);
void audrey_ui_play_queue_song_bind(AudreyUiPlayQueueSong *self, guint position, void *song);
void audrey_ui_play_queue_song_unbind(AudreyUiPlayQueueSong *self);

View file

@ -9,4 +9,18 @@ namespace Audrey {
public int volume { get; set; } public int volume { get; set; }
} }
public class Ui.PlayQueueSong : Gtk.Box {
public bool draggable { get; set; }
public bool show_position { get; set; }
public bool show_artist { get; set; }
public bool show_cover { get; set; }
public bool current { get; set; }
public uint displayed_position { get; set; }
public PlaybinSong song { get; set; }
public PlayQueueSong (Playbin playbin);
public void bind (uint position, PlaybinSong song);
public void unbind ();
}
} }

View file

@ -6,3 +6,5 @@ pub use playbar::Playbar;
mod setup; mod setup;
pub use setup::Setup; pub use setup::Setup;
pub mod play_queue;

View file

@ -1,116 +1,3 @@
// song widget+drag behavior taken from gnome music
[GtkTemplate (ui = "/eu/callcc/audrey/play_queue_song.ui")]
class Audrey.Ui.PlayQueueSong : Gtk.Box {
public bool draggable { get; set; default = false; }
public bool show_position { get; set; default = false; }
public bool show_artist { get; set; default = false; }
public bool show_cover { get; set; default = false; }
private bool _current = false;
public bool current {
get { return _current; }
set {
this._current = value;
if (value) {
this.add_css_class ("playing");
} else {
this.remove_css_class ("playing");
}
}
}
public uint displayed_position { get; set; }
public PlaybinSong song { get; set; }
private weak Playbin playbin;
public PlayQueueSong (Playbin playbin) {
this.playbin = playbin;
var action_group = new SimpleActionGroup ();
var remove = new SimpleAction ("remove", null);
remove.activate.connect (() => {
this.playbin.remove_track (this.displayed_position-1);
});
action_group.add_action (remove);
this.insert_action_group ("song", action_group);
}
private ulong connection;
public void bind (uint position, PlaybinSong song) {
this.displayed_position = position+1;
this.song = song;
this.current = this.playbin.play_queue_position == position;
this.connection = this.playbin.notify["play-queue-position"].connect (() => {
this.current = this.playbin.play_queue_position == position;
});
song.need_cover_art ();
}
public void unbind () {
this.playbin.disconnect (this.connection);
}
[GtkCallback] private string format_duration (int duration) {
return "%02d:%02d".printf(duration/60, duration%60);
}
[GtkCallback] private string star_button_icon_name (DateTime? starred) {
return starred == null ? "non-starred" : "starred";
}
private double drag_x;
private double drag_y;
[GtkCallback] private Gdk.ContentProvider? on_drag_prepare (double x, double y) {
if (this.draggable) {
this.drag_x = x;
this.drag_y = y;
return new Gdk.ContentProvider.for_value (this);
}
else return null;
}
private Gtk.ListBox? drag_widget;
[GtkCallback] private void on_drag_begin (Gtk.DragSource source, Gdk.Drag drag) {
this.drag_widget = new Gtk.ListBox ();
var drag_row = new PlayQueueSong (this.playbin);
drag_row.draggable = false;
drag_row.show_position = this.show_position;
drag_row.show_artist = this.show_artist;
drag_row.show_cover = this.show_cover;
drag_row.current = false;
drag_row.displayed_position = this.displayed_position;
drag_row.song = this.song;
drag_row.set_size_request (this.get_width (), this.get_height ());
var drag_row_real = new Gtk.ListBoxRow ();
drag_row_real.child = drag_row;
this.drag_widget.append (drag_row_real);
this.drag_widget.drag_highlight_row (drag_row_real);
var drag_icon = Gtk.DragIcon.get_for_drag (drag);
drag_icon.set("child", this.drag_widget);
drag.set_hotspot ((int) this.drag_x, (int) this.drag_y);
}
[GtkCallback] private bool on_drop (Value value, double x, double y) {
this.drag_widget = null;
this.drag_x = 0.0;
this.drag_y = 0.0;
var source = value as PlayQueueSong;
debug ("dropped %u on %u", source.displayed_position, this.displayed_position);
this.playbin.move_track (source.displayed_position-1, this.displayed_position-1);
return false;
}
}
[GtkTemplate (ui = "/eu/callcc/audrey/play_queue.ui")] [GtkTemplate (ui = "/eu/callcc/audrey/play_queue.ui")]
public class Audrey.Ui.PlayQueue : Adw.Bin { public class Audrey.Ui.PlayQueue : Adw.Bin {
private weak Playbin _playbin; private weak Playbin _playbin;

View file

@ -1,32 +1,35 @@
mod imp { mod imp {
use std::cell::RefCell; use glib::{subclass::InitializingObject, WeakRef};
//use crate::playbin; use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*};
use gtk::{glib, prelude::*, subclass::prelude::*}; use std::cell::{Cell, RefCell};
#[derive(gtk::CompositeTemplate, glib::Properties, Default)] #[derive(gtk::CompositeTemplate, glib::Properties, Default)]
#[template(resource = "/eu/callcc/audrey/play_queue_song.ui")] #[template(resource = "/eu/callcc/audrey/play_queue_song.ui")]
#[properties(wrapper_type = super::Song)] #[properties(wrapper_type = super::Song)]
pub struct Song { pub struct Song {
#[property(get, set, default = false)] pub(super) playbin: RefCell<Option<WeakRef<crate::Playbin>>>,
draggable: RefCell<bool>,
#[property(get, set, default = false)]
show_position: RefCell<bool>,
#[property(get, set, default = false)]
show_artist: RefCell<bool>,
#[property(get, set, default = false)]
show_cover: RefCell<bool>,
#[property(get, set = Self::set_current, default = false)]
current: RefCell<bool>,
#[property(get, set)]
displayed_position: RefCell<u32>,
//#[property(get, set)]
//song: playbin::Song,
//playbin: playbin::Playbin, #[property(set, get)]
connection: RefCell<u64>, draggable: Cell<bool>,
drag_x: RefCell<f64>, #[property(set, get)]
drag_y: RefCell<f64>, show_position: Cell<bool>,
drag_widget: RefCell<Option<gtk::ListBox>>, #[property(set, get)]
show_artist: Cell<bool>,
#[property(set, get)]
show_cover: Cell<bool>,
#[property(set = Self::set_current, get)]
current: Cell<bool>,
#[property(set, get)]
displayed_position: Cell<u32>,
#[property(get, set)]
song: RefCell<Option<crate::playbin::Song>>,
pub(super) connection: Cell<Option<glib::SignalHandlerId>>,
drag_pos: Cell<(i32, i32)>,
drag_widget: Cell<Option<gtk::ListBox>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -34,10 +37,40 @@ mod imp {
const NAME: &'static str = "AudreyUiPlayQueueSong"; const NAME: &'static str = "AudreyUiPlayQueueSong";
type Type = super::Song; type Type = super::Song;
type ParentType = gtk::Box; type ParentType = gtk::Box;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
klass.bind_template_callbacks();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
} }
#[glib::derived_properties] #[glib::derived_properties]
impl ObjectImpl for Song {} impl ObjectImpl for Song {
fn constructed(&self) {
self.parent_constructed();
let action_remove = gio::ActionEntry::builder("remove")
.activate(glib::clone!(
#[weak(rename_to = self_)]
self,
move |_, _, _| {
self_
.obj()
.playbin()
.remove_track(self_.obj().displayed_position() - 1)
}
))
.build();
let actions = gio::SimpleActionGroup::new();
actions.add_action_entries([action_remove]);
self.obj().insert_action_group("song", Some(&actions));
}
}
impl WidgetImpl for Song {} impl WidgetImpl for Song {}
@ -46,16 +79,93 @@ mod imp {
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl Song { impl Song {
fn set_current(&self, value: bool) { fn set_current(&self, value: bool) {
*self.current.borrow_mut() = value; self.current.replace(value);
if value { if value {
self.obj().add_css_class("playing"); self.obj().add_css_class("playing");
} else { } else {
self.obj().remove_css_class("playing"); self.obj().remove_css_class("playing");
} }
} }
#[template_callback]
fn format_duration(&self, duration: i64) -> String {
format!("{:02}:{:02}", duration / 60, duration % 60)
}
#[template_callback]
fn star_button_icon_name(
&self, /* TODO , starred: Option<&glib::DateTime> */
) -> &'static str {
/* TODO
match starred {
None => "non-starred",
Some(_) => "starred",
} */
"non-starred"
}
// song widget+drag behavior taken from gnome music
#[template_callback]
fn on_drag_prepare(&self, x: f64, y: f64) -> Option<gdk::ContentProvider> {
if self.draggable.get() {
self.drag_pos.replace((x as i32, y as i32));
let value = self.obj().to_value();
Some(gdk::ContentProvider::for_value(&value))
} else {
None
}
}
#[template_callback]
fn on_drag_begin(&self, drag: &gdk::Drag) {
let drag_widget = gtk::ListBox::new();
let drag_row = super::Song::new(self.obj().playbin());
drag_row.set_draggable(false);
drag_row.set_show_position(self.obj().show_position());
drag_row.set_show_artist(self.obj().show_artist());
drag_row.set_show_cover(self.obj().show_cover());
drag_row.set_current(self.obj().current());
drag_row.set_displayed_position(self.obj().displayed_position());
drag_row.set_song(self.obj().song().unwrap());
drag_row.set_size_request(self.obj().width(), self.obj().height());
let drag_row_real = gtk::ListBoxRow::new();
drag_row_real.set_child(Some(&drag_row));
drag_widget.append(&drag_row_real);
drag_widget.drag_highlight_row(&drag_row_real);
let drag_icon = gtk::DragIcon::for_drag(drag);
drag_icon.set_child(Some(&drag_widget));
let (drag_x, drag_y) = self.drag_pos.get();
drag.set_hotspot(drag_x, drag_y);
self.drag_widget.replace(Some(drag_widget));
}
#[template_callback]
fn on_drop(&self, _value: glib::Value, _x: f64, _y: f64) -> bool {
/* FIXME: WrongValueType(ValueTypeMismatchError { actual: GValue, requested: AudreyUiPlayQueueSong })
let source: super::Song = value.get().unwrap();
source.imp().drag_widget.set(None);
self.obj().playbin().move_track(
source.displayed_position() - 1,
self.obj().displayed_position() - 1,
);
true */
false
}
} }
} }
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::Object;
use gtk::glib; use gtk::glib;
glib::wrapper! { glib::wrapper! {
@ -63,3 +173,116 @@ glib::wrapper! {
@extends gtk::Box, gtk::Widget, @extends gtk::Box, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
} }
impl Song {
pub fn new(playbin: crate::Playbin) -> Self {
let song: Self = Object::new();
song.imp().playbin.replace(Some(playbin.downgrade()));
song
}
fn playbin(&self) -> crate::Playbin {
let playbin = self.imp().playbin.borrow();
let playbin = playbin.as_ref().unwrap(); // weak
playbin.upgrade().unwrap().clone() // strong
}
pub fn bind(&self, position: u32, song: &crate::playbin::Song) {
self.set_displayed_position(position + 1);
self.set_song(song);
self.set_current(self.playbin().play_queue_position() == position);
self.imp()
.connection
.replace(Some(self.playbin().connect_notify_local(
Some("play-queue-position"),
glib::clone!(
#[weak(rename_to = self_)]
self,
move |playbin: &crate::Playbin, _| {
self_.set_current(playbin.play_queue_position() == position)
}
),
)));
}
pub fn unbind(&self) {
self.playbin()
.disconnect(self.imp().connection.take().unwrap());
}
}
pub mod ffi {
use adw::prelude::*;
use glib::ffi::{gboolean, GType};
use glib::subclass::basic::InstanceStruct;
use glib::translate::{from_glib, from_glib_none, IntoGlib, IntoGlibPtr};
use gtk::glib;
use std::ffi::c_uint;
type AudreyUiPlayQueueSong = InstanceStruct<super::imp::Song>;
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_get_type() -> GType {
super::Song::static_type().into_glib()
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_new(
playbin: *mut crate::playbin::ffi::AudreyPlaybin,
) -> *mut AudreyUiPlayQueueSong {
unsafe { super::Song::new(from_glib_none(playbin)).into_glib_ptr() }
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_set_draggable(
self_: *mut AudreyUiPlayQueueSong,
draggable: gboolean,
) {
let self_: super::Song = unsafe { from_glib_none(self_) };
self_.set_draggable(unsafe { from_glib::<_, bool>(draggable) });
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_set_show_position(
self_: *mut AudreyUiPlayQueueSong,
show_position: gboolean,
) {
let self_: super::Song = unsafe { from_glib_none(self_) };
self_.set_show_position(unsafe { from_glib::<_, bool>(show_position) });
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_set_show_artist(
self_: *mut AudreyUiPlayQueueSong,
show_artist: gboolean,
) {
let self_: super::Song = unsafe { from_glib_none(self_) };
self_.set_show_artist(unsafe { from_glib::<_, bool>(show_artist) });
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_set_show_cover(
self_: *mut AudreyUiPlayQueueSong,
show_cover: gboolean,
) {
let self_: super::Song = unsafe { from_glib_none(self_) };
self_.set_show_cover(unsafe { from_glib::<_, bool>(show_cover) });
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_bind(
self_: *mut AudreyUiPlayQueueSong,
position: c_uint,
song: *mut crate::playbin::song::ffi::AudreyPlaybinSong,
) {
let self_: super::Song = unsafe { from_glib_none(self_) };
let song: crate::playbin::Song = unsafe { from_glib_none(song) };
self_.bind(position, &song);
}
#[no_mangle]
extern "C" fn audrey_ui_play_queue_song_unbind(self_: *mut AudreyUiPlayQueueSong) {
let self_: super::Song = unsafe { from_glib_none(self_) };
self_.unbind();
}
}

View file

@ -191,14 +191,3 @@ glib::wrapper! {
@extends adw::Bin, gtk::Widget, @extends adw::Bin, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
} }
mod ffi {
use glib::translate::IntoGlib;
use gtk::glib;
use gtk::prelude::*;
#[no_mangle]
pub extern "C" fn audrey_ui_playbar_get_type() -> glib::ffi::GType {
super::Playbar::static_type().into_glib()
}
}