wip translate window.vala

This commit is contained in:
Erica Z 2024-11-02 12:24:25 +01:00
parent 34bec8fdae
commit 8a6a056b36
10 changed files with 213 additions and 171 deletions

View file

@ -16,7 +16,7 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
Button { Button {
icon-name: "media-playlist-shuffle"; icon-name: "media-playlist-shuffle";
sensitive: bind template.can_click_shuffle_all; sensitive: bind template.can_click_shuffle_all;
clicked => $shuffle_all (); clicked => $shuffle_all () swapped;
} }
title-widget: Adw.ViewSwitcher { title-widget: Adw.ViewSwitcher {
@ -27,7 +27,7 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
[end] [end]
Button { Button {
icon-name: "applications-system"; icon-name: "applications-system";
clicked => $show_setup_dialog (); clicked => $show_setup_dialog () swapped;
} }
} }

View file

@ -46,10 +46,7 @@ mod imp {
crate::Mpris::setup(conn.object_server(), &window) crate::Mpris::setup(conn.object_server(), &window)
.await .await
.expect("could not serve mpris"); .expect("could not serve mpris");
crate::mpris::Player::setup( crate::mpris::Player::setup(conn.object_server(), &window.playbin())
conn.object_server(),
&window.playbin().unwrap(),
)
.await .await
.expect("could not serve mpris player"); .expect("could not serve mpris player");

View file

@ -3,7 +3,6 @@ audrey_sources = [
'playbin.vala', 'playbin.vala',
'rust.vapi', 'rust.vapi',
'subsonic.vala', 'subsonic.vala',
'ui/window.vala',
] ]
audrey_deps = [ audrey_deps = [

View file

@ -19,12 +19,18 @@ pub mod ffi {
extern "C" { extern "C" {
pub fn audrey_playbin_get_type() -> glib::ffi::GType; pub fn audrey_playbin_get_type() -> glib::ffi::GType;
pub fn audrey_playbin_new() -> *mut AudreyPlaybin;
pub fn audrey_playbin_get_state(self_: *mut AudreyPlaybin) -> super::state::ffi::State; pub fn audrey_playbin_get_state(self_: *mut AudreyPlaybin) -> super::state::ffi::State;
pub fn audrey_playbin_pause(self_: *mut AudreyPlaybin); pub fn audrey_playbin_pause(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_play(self_: *mut AudreyPlaybin); pub fn audrey_playbin_play(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_stop(self_: *mut AudreyPlaybin); pub fn audrey_playbin_stop(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_clear(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_get_volume(self_: *mut AudreyPlaybin) -> std::ffi::c_int; pub fn audrey_playbin_get_volume(self_: *mut AudreyPlaybin) -> std::ffi::c_int;
pub fn audrey_playbin_set_volume(self_: *mut AudreyPlaybin, volume: std::ffi::c_int); pub fn audrey_playbin_set_volume(self_: *mut AudreyPlaybin, volume: std::ffi::c_int);
pub fn audrey_playbin_set_api(
self_: *mut AudreyPlaybin,
api: *mut crate::subsonic_vala::client::ffi::AudreySubsonicClient,
);
pub fn audrey_playbin_seek(self_: *mut AudreyPlaybin, position: f64); pub fn audrey_playbin_seek(self_: *mut AudreyPlaybin, position: f64);
pub fn audrey_playbin_go_to_next_track(self_: *mut AudreyPlaybin); pub fn audrey_playbin_go_to_next_track(self_: *mut AudreyPlaybin);
pub fn audrey_playbin_go_to_prev_track(self_: *mut AudreyPlaybin); pub fn audrey_playbin_go_to_prev_track(self_: *mut AudreyPlaybin);
@ -61,6 +67,12 @@ glib::wrapper! {
} }
} }
impl Default for Playbin {
fn default() -> Self {
unsafe { from_glib_none(ffi::audrey_playbin_new()) }
}
}
impl Playbin { impl Playbin {
pub fn state(&self) -> State { pub fn state(&self) -> State {
unsafe { glib::translate::from_glib(ffi::audrey_playbin_get_state(self.to_glib_none().0)) } unsafe { glib::translate::from_glib(ffi::audrey_playbin_get_state(self.to_glib_none().0)) }
@ -134,6 +146,10 @@ impl Playbin {
unsafe { ffi::audrey_playbin_stop(self.to_glib_none().0) } unsafe { ffi::audrey_playbin_stop(self.to_glib_none().0) }
} }
pub fn clear(&self) {
unsafe { ffi::audrey_playbin_clear(self.to_glib_none().0) }
}
pub fn play_queue(&self) -> gio::ListModel { pub fn play_queue(&self) -> gio::ListModel {
unsafe { from_glib_none(ffi::audrey_playbin_get_play_queue(self.to_glib_none().0)) } unsafe { from_glib_none(ffi::audrey_playbin_get_play_queue(self.to_glib_none().0)) }
} }
@ -167,4 +183,8 @@ impl Playbin {
glib::spawn_future_local(f(self_, param_spec)); glib::spawn_future_local(f(self_, param_spec));
}); });
} }
pub fn set_api(&self, api: &crate::subsonic_vala::Client) {
unsafe { ffi::audrey_playbin_set_api(self.to_glib_none().0, api.to_glib_none().0) }
}
} }

View file

@ -43,4 +43,20 @@ namespace Audrey {
public void save (); public void save ();
} }
public class Ui.Window : Adw.ApplicationWindow {
public int volume { get; set; }
public bool mute { get; set; }
public PlaybinSong? song { get; }
public Gdk.Paintable? playing_cover_art { get; set; }
public bool cover_art_loading { get; set; }
public Playbin playbin { get; }
public Window (Gtk.Application app);
public bool can_click_shuffle_all { get; }
}
} }

View file

@ -1,4 +1,4 @@
mod client; pub mod client;
pub use client::Client; pub use client::Client;
mod song; mod song;

View file

@ -1,4 +1,4 @@
mod ffi { pub mod ffi {
use gtk::glib; use gtk::glib;
use std::ffi::c_char; use std::ffi::c_char;

View file

@ -158,6 +158,12 @@ glib::wrapper! {
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
} }
impl Default for Setup {
fn default() -> Self {
glib::Object::new()
}
}
impl Setup { impl Setup {
pub fn load(&self) { pub fn load(&self) {
glib::spawn_future_local(glib::clone!( glib::spawn_future_local(glib::clone!(

View file

@ -1,45 +1,184 @@
pub mod ffi { mod imp {
use gtk::glib; use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::subclass::InitializingObject;
use gtk::{gdk, glib};
use std::cell::{Cell, RefCell};
#[repr(C)] #[derive(gtk::CompositeTemplate, glib::Properties, Default)]
pub struct AudreyUiWindow { #[template(resource = "/eu/callcc/audrey/window.ui")]
parent_instance: adw::ffi::AdwApplicationWindow, #[properties(wrapper_type = super::Window)]
pub struct Window {
#[template_child]
pub(super) playbar: TemplateChild<crate::ui::Playbar>,
#[property(get, set)]
playbin: RefCell<crate::Playbin>,
#[property(get, set, default = false)]
can_click_shuffle_all: Cell<bool>,
#[property(get, set, nullable)]
playing_cover_art: RefCell<Option<gdk::Paintable>>,
#[property(get, set, nullable)]
song: RefCell<Option<crate::playbin::Song>>,
pub(super) setup: crate::ui::Setup,
pub(super) api: RefCell<Option<crate::subsonic_vala::Client>>,
} }
#[repr(C)] #[glib::object_subclass]
pub struct AudreyUiWindowClass { impl ObjectSubclass for Window {
parent_class: adw::ffi::AdwApplicationWindowClass, const NAME: &'static str = "AudreyUiWindow";
type Type = super::Window;
type ParentType = adw::ApplicationWindow;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
klass.bind_template_callbacks();
} }
extern "C" { fn instance_init(obj: &InitializingObject<Self>) {
pub fn audrey_ui_window_get_type() -> glib::ffi::GType; obj.init_template();
pub fn audrey_ui_window_new(app: *mut gtk::ffi::GtkApplication) -> *mut AudreyUiWindow; }
pub fn audrey_ui_window_get_playbin( }
self_: *mut AudreyUiWindow,
) -> *mut crate::playbin::ffi::AudreyPlaybin; #[glib::derived_properties]
impl ObjectImpl for Window {}
impl WidgetImpl for Window {}
impl WindowImpl for Window {}
impl ApplicationWindowImpl for Window {}
impl AdwApplicationWindowImpl for Window {}
#[gtk::template_callbacks]
impl Window {
#[template_callback]
fn show_playbar_cover_art(&self, stack_child: Option<&str>) -> bool {
stack_child != Some("play-queue")
}
#[template_callback]
async fn shuffle_all(&self) {
/*
this.can_click_shuffle_all = false;
this.playbin.clear ();
api.get_random_songs.begin (null, (song) => {
this.playbin.append_track (song);
}, (obj, res) => {
try {
api.get_random_songs.end (res);
} catch (Error e) {
error ("could not get random songs: %s", e.message);
}
this.can_click_shuffle_all = true;
this.playbin.select_track (0);
});*/
todo!()
}
#[template_callback]
fn show_setup_dialog(&self) {
self.setup.present(Some(self.obj().as_ref()));
}
pub(super) fn now_playing(&self, _song: &crate::playbin::Song) {
/*
this.song = song;
// api.scrobble.begin (this.song.id); TODO
if (this.cancel_loading_art != null) {
this.cancel_loading_art.cancel ();
}
this.cancel_loading_art = new GLib.Cancellable ();
this.playing_cover_art = null; // TODO: preload next art somehow
this.cover_art_loading = true;
string song_id = this.song.id;
this.api.cover_art.begin (song_id, -1, Priority.DEFAULT, this.cancel_loading_art, (obj, res) => {
try {
this.playing_cover_art = Gdk.Texture.for_pixbuf (this.api.cover_art.end (res));
this.cover_art_loading = false;
} catch (Error e) {
if (!(e is IOError.CANCELLED)) {
warning ("could not load cover for %s: %s", song_id, e.message);
this.cover_art_loading = false;
}
}
});
*/
todo!()
}
} }
} }
use adw::prelude::*; use adw::prelude::*;
use glib::translate::{from_glib_none, ToGlibPtr}; use adw::subclass::prelude::*;
use gtk::{gio, glib}; use gtk::{gdk, gio, glib};
glib::wrapper! { glib::wrapper! {
pub struct Window(Object<ffi::AudreyUiWindow, ffi::AudreyUiWindowClass>) pub struct Window(ObjectSubclass<imp::Window>)
@extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget, @extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget,
@implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager; @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
match fn {
type_ => || ffi::audrey_ui_window_get_type(),
}
} }
impl Window { impl Window {
pub fn new(app: &impl IsA<gtk::Application>) -> Self { pub fn new(app: &impl IsA<gtk::Application>) -> Self {
unsafe { from_glib_none(ffi::audrey_ui_window_new(app.as_ref().to_glib_none().0)) } let window: Self = glib::Object::builder().property("application", app).build();
}
pub fn playbin(&self) -> Option<crate::Playbin> { window
unsafe { from_glib_none(ffi::audrey_ui_window_get_playbin(self.to_glib_none().0)) } .playbin()
.bind_property("volume", &*window.imp().playbar, "volume")
.bidirectional()
.sync_create()
.build();
window.imp().setup.connect_closure(
"connected",
false,
glib::closure_local!(
#[weak]
window,
move |_setup: crate::ui::Setup, api: crate::subsonic_vala::Client| {
window.imp().api.replace(Some(api.clone()));
window.playbin().set_api(&api);
window.set_can_click_shuffle_all(true);
}
),
);
window.imp().setup.load();
window.playbin().connect_closure(
"new-track",
false,
glib::closure_local!(
#[weak]
window,
move |_playbin: crate::Playbin, song: crate::playbin::Song| {
window.imp().now_playing(&song);
}
),
);
window.playbin().connect_closure(
"stopped",
false,
glib::closure_local!(
#[weak]
window,
move |_playbin: crate::Playbin| {
window.set_playing_cover_art(None::<gdk::Paintable>);
window.set_song(None::<crate::playbin::Song>);
}
),
);
window
} }
} }

View file

@ -1,135 +0,0 @@
[GtkTemplate (ui = "/eu/callcc/audrey/window.ui")]
public class Audrey.Ui.Window : Adw.ApplicationWindow {
[GtkChild] public unowned PlayQueue play_queue;
[GtkChild] public unowned Playbar playbar;
//[GtkChild] public unowned Adw.ButtonRow shuffle_all_tracks;
private Setup setup;
private Subsonic.Client api;
public int volume {
get { return this.playbin.volume; }
set { this.playbin.volume = value; }
}
public bool mute {
get { return this.playbin.mute; }
set { this.playbin.mute = value; }
}
public PlaybinSong? song { get; private set; }
public Gdk.Paintable? playing_cover_art { get; set; default = null; }
private Cancellable cancel_loading_art;
public bool cover_art_loading { get; set; default = false; }
public Playbin playbin { get; private set; default = new Playbin (); }
public Window (Gtk.Application app) {
Object (application: app);
this.playbin.bind_property("volume", this.playbar, "volume", BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
}
private void now_playing (PlaybinSong song) {
this.song = song;
// api.scrobble.begin (this.song.id); TODO
if (this.cancel_loading_art != null) {
this.cancel_loading_art.cancel ();
}
this.cancel_loading_art = new GLib.Cancellable ();
this.playing_cover_art = null; // TODO: preload next art somehow
this.cover_art_loading = true;
string song_id = this.song.id;
this.api.cover_art.begin (song_id, -1, Priority.DEFAULT, this.cancel_loading_art, (obj, res) => {
try {
this.playing_cover_art = Gdk.Texture.for_pixbuf (this.api.cover_art.end (res));
this.cover_art_loading = false;
} catch (Error e) {
if (!(e is IOError.CANCELLED)) {
warning ("could not load cover for %s: %s", song_id, e.message);
this.cover_art_loading = false;
}
}
});
}
construct {
/*
Bus.own_name (
BusType.SESSION,
"org.mpris.MediaPlayer2.audrey",
BusNameOwnerFlags.NONE,
(conn) => {
try {
this.mpris_player = new MprisPlayer (conn, this.playbin);
conn.register_object ("/org/mpris/MediaPlayer2", this.mpris_player);
} catch (IOError e) {
error ("could not register dbus service: %s", e.message);
}
},
() => {},
() => { error ("could not acquire dbus name"); });
*/
this.setup = new Setup ();
this.setup.connected.connect ((api) => {
this.api = api;
this.playbin.api = api;
//this.mpris_player.api = api;
this.can_click_shuffle_all = true;
});
this.setup.load ();
this.playbin.new_track.connect (() => {
this.now_playing (this.playbin.play_queue.get_item (this.playbin.play_queue_position) as PlaybinSong);
});
this.playbin.stopped.connect (() => {
this.playing_cover_art = null;
this.song = null;
});
}
[GtkCallback] private void show_setup_dialog () {
this.setup.present (this);
}
public bool can_click_shuffle_all { get; private set; default = false; }
[GtkCallback] private void shuffle_all () {
this.can_click_shuffle_all = false;
this.playbin.clear ();
api.get_random_songs.begin (null, (song) => {
this.playbin.append_track (song);
}, (obj, res) => {
try {
api.get_random_songs.end (res);
} catch (Error e) {
error ("could not get random songs: %s", e.message);
}
this.can_click_shuffle_all = true;
this.playbin.select_track (0);
});
}
[GtkCallback] private bool show_playbar_cover_art (string? stack_child) {
return stack_child != "play-queue";
}
public override bool close_request () {
// stop playback on close
this.playbin.stop ();
return false;
}
~Window () {
debug ("destroying main window");
}
}