audrey/src/ui/window.rs

266 lines
8.6 KiB
Rust
Raw Normal View History

2024-11-02 11:24:25 +00:00
mod imp {
2024-11-03 09:37:37 +00:00
use crate::mpv;
2024-11-02 11:24:25 +00:00
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::subclass::InitializingObject;
use gtk::{gdk, glib};
use std::cell::{Cell, RefCell};
2024-11-03 09:37:37 +00:00
use std::rc::Rc;
2024-10-29 13:02:29 +00:00
2024-11-02 11:24:25 +00:00
#[derive(gtk::CompositeTemplate, glib::Properties, Default)]
#[template(resource = "/eu/callcc/audrey/window.ui")]
#[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>>,
pub(super) mpv: Rc<RefCell<mpv::Handle>>,
2024-10-29 13:02:29 +00:00
}
2024-11-02 11:24:25 +00:00
#[glib::object_subclass]
impl ObjectSubclass for Window {
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();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
2024-10-29 13:02:29 +00:00
}
2024-11-02 11:24:25 +00:00
#[glib::derived_properties]
2024-11-03 09:37:37 +00:00
impl ObjectImpl for Window {
fn constructed(&self) {
self.parent_constructed();
// set up mpv
let mpv = self.mpv.borrow();
mpv.set_property("audio-client-name", "audrey").unwrap();
mpv.set_property("user-agent", crate::USER_AGENT).unwrap();
mpv.set_property("video", false).unwrap();
mpv.set_property("prefetch-playlist", true).unwrap();
mpv.set_property("gapless-audio", true).unwrap();
2024-11-03 09:37:37 +00:00
// TODO: observe properties
mpv.command(["loadfile", "https://www.youtube.com/watch?v=19y8YTbvri8"])
.unwrap();
2024-11-03 09:37:37 +00:00
let mpv_weak = Rc::downgrade(&self.mpv);
glib::spawn_future_local(async move {
loop {
let mpv = match mpv_weak.upgrade() {
Some(mpv) => mpv,
2024-11-03 09:37:37 +00:00
None => break,
};
{
let mut mpv_ref = mpv.borrow_mut();
let listener = mpv_ref.wakeup_listener();
while let Some(event) = mpv_ref.wait_event(0.0) {
println!("mpv event {:?}", event);
2024-11-03 09:37:37 +00:00
}
listener
2024-11-03 09:37:37 +00:00
}
.await
2024-11-03 09:37:37 +00:00
}
});
// set up mpris
let window = self.obj().clone();
glib::spawn_future_local(async move {
let conn = zbus::connection::Builder::session()
.expect("could not connect to the session bus")
.internal_executor(false)
.build()
.await
.expect("could not build connection to the session bus");
// run this in glib's main loop
glib::spawn_future_local(glib::clone!(
#[strong]
conn,
async move {
loop {
conn.executor().tick().await;
}
}
));
crate::Mpris::setup(conn.object_server(), &window)
.await
.expect("could not serve mpris");
crate::mpris::Player::setup(conn.object_server(), &window.playbin())
.await
.expect("could not serve mpris player");
drop(window); // don't keep this alive
// always set up handlers before requesting service name
conn.request_name("org.mpris.MediaPlayer2.audrey")
.await
.expect("could not register name in session bus");
});
}
}
2024-11-02 11:24:25 +00:00
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!()
}
2024-11-03 09:37:37 +00:00
}
impl Drop for Window {
fn drop(&mut self) {
println!("dropping AudreyUiWindow");
self.mpv.borrow().command(["quit"]).unwrap();
2024-11-03 09:37:37 +00:00
}
2024-10-29 13:02:29 +00:00
}
}
use adw::prelude::*;
2024-11-02 11:24:25 +00:00
use adw::subclass::prelude::*;
use gtk::{gdk, gio, glib};
2024-10-29 13:02:29 +00:00
glib::wrapper! {
2024-11-02 11:24:25 +00:00
pub struct Window(ObjectSubclass<imp::Window>)
2024-10-29 13:02:29 +00:00
@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;
}
impl Window {
pub fn new(app: &impl IsA<gtk::Application>) -> Self {
2024-11-02 11:24:25 +00:00
let window: Self = glib::Object::builder().property("application", app).build();
window
.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>);
}
),
);
2024-11-01 12:50:29 +00:00
2024-11-02 11:24:25 +00:00
window
2024-11-01 12:50:29 +00:00
}
2024-10-29 13:02:29 +00:00
}