audrey/src/ui/playbar.rs
2024-11-13 19:40:04 +01:00

178 lines
5.1 KiB
Rust

mod imp {
use crate::model::Song;
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::subclass::InitializingObject;
use gtk::{gdk, glib};
use std::cell::{Cell, RefCell};
use tracing::{event, Level};
#[derive(glib::Properties, gtk::CompositeTemplate, Default)]
#[properties(wrapper_type = super::Playbar)]
#[template(resource = "/eu/callcc/audrey/playbar.ui")]
pub struct Playbar {
#[template_child]
#[property(get)]
pulse_bar: TemplateChild<gtk::ProgressBar>,
#[property(get, set, nullable)]
song: RefCell<Option<Song>>,
#[property(get, set)]
playing_cover_art: RefCell<Option<gdk::Paintable>>,
#[property(get, set, default = true)]
show_cover_art: Cell<bool>,
// synced with root window
#[property(get, set)]
_volume: Cell<u32>,
#[property(get, set)]
_mute: Cell<bool>,
#[property(get, set)]
_pause: Cell<bool>,
#[property(get, set)]
_idle_active: Cell<bool>,
#[property(get, set)]
_playlist_count: Cell<u32>,
#[property(get, set)]
position: Cell<f64>,
#[property(get, set)]
duration: Cell<f64>,
#[property(get, set)]
_show_pulse_bar: Cell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for Playbar {
const NAME: &'static str = "AudreyUiPlaybar";
type Type = super::Playbar;
type ParentType = adw::Bin;
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]
impl ObjectImpl for Playbar {}
impl WidgetImpl for Playbar {}
impl BinImpl for Playbar {}
#[gtk::template_callbacks]
impl Playbar {
#[template_callback]
fn format_timestamp(&self, s: f64) -> String {
format!("{:02}:{:02}", (s as i64) / 60, (s as i64) % 60)
}
#[template_callback]
fn mute_button_icon_name(&self, mute: bool) -> &'static str {
if mute {
"audio-volume-muted"
} else {
"audio-volume-high"
}
}
#[template_callback]
fn on_play_position_seek(
&self,
_scroll_type: gtk::ScrollType,
value: f64,
_range: &gtk::Range,
) -> bool {
self.window().seek(value);
false
}
#[template_callback]
fn on_skip_forward_clicked(&self) {
if self.window().playlist_pos() + 1 < self.window().playlist_count() {
self.window().playlist_next();
} else {
self.window().playlist_play_index(None);
}
}
#[template_callback]
fn on_skip_backward_clicked(&self) {
if self.window().playlist_pos() > 0 {
self.window().playlist_prev();
} else {
self.window().playlist_play_index(None);
}
}
#[template_callback]
fn on_play_pause_clicked(&self, _button: &gtk::Button) {
if self.window().idle_active() {
self.window().playlist_play_index(Some(0));
self.window().set_pause(false);
} else {
self.window().set_pause(!self.window().pause());
}
}
#[template_callback]
fn on_mute_toggle(&self) {
self.obj().set_mute(!self.obj().mute());
}
#[template_callback]
fn play_pause_icon_name(&self, idle_active: bool, pause: bool) -> &'static str {
match (idle_active, pause) {
(true, _) => "media-playback-start",
(false, true) => "media-playback-start",
(false, false) => "media-playback-pause",
}
}
#[template_callback]
fn play_pause_sensitive(&self, playlist_count: u32) -> bool {
playlist_count > 0
}
fn window(&self) -> crate::ui::Window {
self.obj().root().unwrap().dynamic_cast().unwrap()
}
// these are nedeed because with regular bindings, if song becomes None, the labes are not
// updated
#[template_callback]
fn song_title(&self, song: Option<&Song>) -> String {
song.map(Song::title).unwrap_or("".to_string())
}
#[template_callback]
fn song_artist(&self, song: Option<&Song>) -> String {
song.map(Song::artist).unwrap_or("".to_string())
}
#[template_callback]
fn song_album(&self, song: Option<&Song>) -> String {
song.map(Song::album).unwrap_or("".to_string())
}
}
impl Drop for Playbar {
fn drop(&mut self) {
event!(Level::DEBUG, "dropping AudreyUiPlaybar");
}
}
}
use gtk::glib;
glib::wrapper! {
pub struct Playbar(ObjectSubclass<imp::Playbar>)
@extends adw::Bin, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}