2024-10-30 11:45:50 +00:00
|
|
|
mod imp {
|
2024-11-03 18:36:52 +00:00
|
|
|
use crate::PlaybinSong;
|
2024-10-30 11:45:50 +00:00
|
|
|
use adw::prelude::*;
|
|
|
|
use adw::subclass::prelude::*;
|
|
|
|
use glib::subclass::InitializingObject;
|
|
|
|
use gtk::{gdk, glib};
|
|
|
|
use std::cell::{Cell, RefCell};
|
2024-11-04 09:12:13 +00:00
|
|
|
use tracing::{event, Level};
|
2024-10-30 10:42:03 +00:00
|
|
|
|
2024-10-30 11:45:50 +00:00
|
|
|
#[derive(glib::Properties, gtk::CompositeTemplate, Default)]
|
|
|
|
#[properties(wrapper_type = super::Playbar)]
|
|
|
|
#[template(resource = "/eu/callcc/audrey/playbar.ui")]
|
|
|
|
pub struct Playbar {
|
2024-11-05 10:33:59 +00:00
|
|
|
#[template_child]
|
|
|
|
#[property(get)]
|
|
|
|
pulse_bar: TemplateChild<gtk::ProgressBar>,
|
|
|
|
|
2024-11-05 14:38:46 +00:00
|
|
|
#[property(get, set, nullable)]
|
2024-11-03 18:36:52 +00:00
|
|
|
song: RefCell<Option<PlaybinSong>>,
|
2024-10-30 11:45:50 +00:00
|
|
|
#[property(get, set)]
|
|
|
|
playing_cover_art: RefCell<Option<gdk::Paintable>>,
|
|
|
|
#[property(get, set, default = true)]
|
|
|
|
show_cover_art: Cell<bool>,
|
|
|
|
|
2024-11-05 09:01:07 +00:00
|
|
|
// synced with root window
|
2024-11-01 16:57:06 +00:00
|
|
|
#[property(get, set)]
|
2024-11-05 09:01:07 +00:00
|
|
|
_volume: Cell<u32>,
|
2024-11-03 15:17:54 +00:00
|
|
|
#[property(get, set)]
|
2024-11-04 11:31:43 +00:00
|
|
|
_mute: Cell<bool>,
|
|
|
|
#[property(get, set)]
|
|
|
|
_pause: Cell<bool>,
|
2024-11-04 12:43:30 +00:00
|
|
|
#[property(get, set)]
|
|
|
|
_idle_active: Cell<bool>,
|
2024-11-05 09:01:07 +00:00
|
|
|
#[property(get, set)]
|
|
|
|
_playlist_count: Cell<u32>,
|
2024-11-04 11:31:43 +00:00
|
|
|
|
2024-11-03 18:25:32 +00:00
|
|
|
#[property(get, set)]
|
|
|
|
position: Cell<f64>,
|
|
|
|
#[property(get, set)]
|
|
|
|
duration: Cell<f64>,
|
2024-11-05 10:33:59 +00:00
|
|
|
|
|
|
|
#[property(get, set)]
|
|
|
|
_show_pulse_bar: Cell<bool>,
|
2024-10-30 10:42:03 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 11:45:50 +00:00
|
|
|
#[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();
|
|
|
|
}
|
2024-10-30 10:42:03 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 11:45:50 +00:00
|
|
|
#[glib::derived_properties]
|
|
|
|
impl ObjectImpl for Playbar {}
|
|
|
|
|
|
|
|
impl WidgetImpl for Playbar {}
|
|
|
|
|
|
|
|
impl BinImpl for Playbar {}
|
|
|
|
|
|
|
|
#[gtk::template_callbacks]
|
|
|
|
impl Playbar {
|
|
|
|
#[template_callback]
|
2024-11-03 19:00:46 +00:00
|
|
|
fn format_timestamp(&self, s: f64) -> String {
|
2024-11-05 09:38:43 +00:00
|
|
|
format!("{:02}:{:02}", (s as i64) / 60, (s as i64) % 60)
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn mute_button_icon_name(&self, mute: bool) -> &'static str {
|
2024-10-30 11:59:16 +00:00
|
|
|
if mute {
|
|
|
|
"audio-volume-muted"
|
|
|
|
} else {
|
|
|
|
"audio-volume-high"
|
|
|
|
}
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
2024-10-30 11:59:16 +00:00
|
|
|
fn on_play_position_seek(
|
|
|
|
&self,
|
|
|
|
_scroll_type: gtk::ScrollType,
|
2024-11-05 09:16:22 +00:00
|
|
|
value: f64,
|
2024-11-03 19:18:12 +00:00
|
|
|
_range: >k::Range,
|
2024-10-30 11:59:16 +00:00
|
|
|
) -> bool {
|
2024-11-05 09:16:22 +00:00
|
|
|
self.window().seek(value);
|
|
|
|
false
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn on_skip_forward_clicked(&self) {
|
2024-11-05 14:38:46 +00:00
|
|
|
if self.window().playlist_pos() + 1 < self.window().playlist_count() as i32 {
|
|
|
|
self.window().playlist_next();
|
|
|
|
} else {
|
|
|
|
self.window().playlist_play_index(None);
|
|
|
|
}
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn on_skip_backward_clicked(&self) {
|
2024-11-05 14:38:46 +00:00
|
|
|
if self.window().playlist_pos() > 0 {
|
|
|
|
self.window().playlist_prev();
|
|
|
|
} else {
|
|
|
|
self.window().playlist_play_index(None);
|
|
|
|
}
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn seek_backward(&self) {
|
|
|
|
// 10 seconds
|
2024-11-05 09:16:22 +00:00
|
|
|
let mut new_position = self.window().time_pos() - 10.0;
|
2024-10-31 06:59:09 +00:00
|
|
|
if new_position < 0.0 {
|
|
|
|
new_position = 0.0;
|
|
|
|
}
|
2024-11-05 09:16:22 +00:00
|
|
|
self.window().seek(new_position);
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn seek_forward(&self) {
|
|
|
|
// 10 seconds
|
2024-11-05 09:16:22 +00:00
|
|
|
let new_position = self.window().time_pos() + 10.0;
|
|
|
|
if new_position > self.window().duration() {
|
|
|
|
// just seek to the next track
|
|
|
|
self.on_skip_forward_clicked();
|
2024-10-31 06:59:09 +00:00
|
|
|
}
|
2024-11-05 09:16:22 +00:00
|
|
|
self.window().seek(new_position);
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
2024-10-30 11:59:16 +00:00
|
|
|
|
2024-10-30 11:45:50 +00:00
|
|
|
#[template_callback]
|
2024-10-30 11:59:16 +00:00
|
|
|
fn on_play_pause_clicked(&self, _button: >k::Button) {
|
2024-11-05 14:38:46 +00:00
|
|
|
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());
|
|
|
|
}
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn on_mute_toggle(&self) {
|
2024-11-03 15:17:54 +00:00
|
|
|
self.obj().set_mute(!self.obj().mute());
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|
2024-11-04 11:31:43 +00:00
|
|
|
|
2024-11-04 12:43:30 +00:00
|
|
|
#[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",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-05 09:01:07 +00:00
|
|
|
#[template_callback]
|
|
|
|
fn play_pause_sensitive(&self, playlist_count: u32) -> bool {
|
|
|
|
playlist_count > 0
|
|
|
|
}
|
|
|
|
|
2024-11-04 11:31:43 +00:00
|
|
|
fn window(&self) -> crate::ui::Window {
|
|
|
|
self.obj().root().unwrap().dynamic_cast().unwrap()
|
|
|
|
}
|
2024-11-05 14:38:46 +00:00
|
|
|
|
|
|
|
// these are nedeed because with regular bindings, if song becomes None, the labes are not
|
|
|
|
// updated
|
|
|
|
#[template_callback]
|
|
|
|
fn song_title(&self, song: Option<&PlaybinSong>) -> String {
|
|
|
|
song.map(PlaybinSong::title).unwrap_or("".to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn song_artist(&self, song: Option<&PlaybinSong>) -> String {
|
|
|
|
song.map(PlaybinSong::artist).unwrap_or("".to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
fn song_album(&self, song: Option<&PlaybinSong>) -> String {
|
|
|
|
song.map(PlaybinSong::album).unwrap_or("".to_string())
|
|
|
|
}
|
2024-10-30 10:42:03 +00:00
|
|
|
}
|
2024-11-02 20:15:55 +00:00
|
|
|
|
|
|
|
impl Drop for Playbar {
|
|
|
|
fn drop(&mut self) {
|
2024-11-04 09:12:13 +00:00
|
|
|
event!(Level::DEBUG, "dropping AudreyUiPlaybar");
|
2024-11-02 20:15:55 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-30 10:42:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
use gtk::glib;
|
|
|
|
|
|
|
|
glib::wrapper! {
|
2024-10-30 11:45:50 +00:00
|
|
|
pub struct Playbar(ObjectSubclass<imp::Playbar>)
|
2024-10-30 10:42:03 +00:00
|
|
|
@extends adw::Bin, gtk::Widget,
|
|
|
|
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
2024-10-30 11:45:50 +00:00
|
|
|
}
|