A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 194 lines 6.2 kB view raw
1use crate::state::AppState; 2use crate::ui::window::{self, RbApplicationWindow}; 3use crate::{app, ui::media_controls}; 4use adw::prelude::*; 5use adw::subclass::prelude::*; 6use gio::subclass::prelude::ApplicationImpl; 7use glib::subclass::types::ObjectSubclass; 8use glib::WeakRef; 9use gtk::{gdk::Display, gio, glib, CssProvider, STYLE_PROVIDER_PRIORITY_APPLICATION}; 10use std::cell::OnceCell; 11 12mod imp { 13 14 use super::*; 15 pub struct RbApplication { 16 pub state: OnceCell<AppState>, 17 pub window: OnceCell<WeakRef<RbApplicationWindow>>, 18 } 19 20 #[glib::object_subclass] 21 impl ObjectSubclass for RbApplication { 22 const NAME: &'static str = "RockboxApplication"; 23 type Type = app::RbApplication; 24 type ParentType = adw::Application; 25 26 fn new() -> Self { 27 Self { 28 state: OnceCell::new(), 29 window: OnceCell::new(), 30 } 31 } 32 } 33 34 impl ObjectImpl for RbApplication {} 35 36 impl ApplicationImpl for RbApplication { 37 fn activate(&self) { 38 let app = self.obj(); 39 load_css(); 40 41 if let Some(weak_window) = self.window.get() { 42 weak_window.upgrade().unwrap().present(); 43 return; 44 } 45 46 let window = app.create_window(); 47 let _ = self.window.set(window.downgrade()); 48 49 app.setup_gactions(); 50 } 51 52 fn shutdown(&self) { 53 self.parent_shutdown(); 54 } 55 } 56 57 impl GtkApplicationImpl for RbApplication {} 58 impl AdwApplicationImpl for RbApplication {} 59} 60 61glib::wrapper! { 62 pub struct RbApplication(ObjectSubclass<imp::RbApplication>) 63 @extends gio::Application, gtk::Application, adw::Application, 64 @implements gio::ActionMap, gio::ActionGroup; 65} 66 67fn load_css() { 68 let provider = CssProvider::new(); 69 provider.load_from_string(include_str!("styles.css")); 70 gtk::style_context_add_provider_for_display( 71 &Display::default().expect("Error initializing gtk css provider."), 72 &provider, 73 STYLE_PROVIDER_PRIORITY_APPLICATION, 74 ); 75} 76 77impl RbApplication { 78 pub fn run() -> glib::ExitCode { 79 // Create new GObject and downcast it into RbApplication 80 let app = glib::Object::builder::<RbApplication>() 81 .property("application-id", Some("io.github.tsirysndr.Rockbox")) 82 .property("flags", gio::ApplicationFlags::empty()) 83 .property("resource-base-path", Some("/io/github/tsirysndr/Rockbox")) 84 .build(); 85 86 // Start running gtk::Application 87 app.run() 88 } 89 90 pub fn create_window(&self) -> RbApplicationWindow { 91 let app_state = self.imp().state.get_or_init(AppState::new); 92 let window = RbApplicationWindow::new(app_state.clone()); 93 94 self.add_window(&window); 95 96 window.present(); 97 window 98 } 99 100 pub fn setup_gactions(&self) { 101 let window = self.imp().window.get(); 102 let window = window.unwrap().upgrade().unwrap(); 103 104 let media_controls = window.imp().media_control_bar.get(); 105 let like = gio::ActionEntry::builder("like") 106 .activate(move |_, _, _| { 107 media_controls.like(); 108 }) 109 .build(); 110 111 let media_controls = window.imp().media_control_bar.get(); 112 let play = gio::ActionEntry::builder("play_pause") 113 .activate(move |_, _, _| { 114 media_controls.play(); 115 }) 116 .build(); 117 118 let media_controls = window.imp().media_control_bar.get(); 119 let previous = gio::ActionEntry::builder("previous") 120 .activate(move |_, _, _| { 121 media_controls.previous(); 122 }) 123 .build(); 124 125 let media_controls = window.imp().media_control_bar.get(); 126 let next = gio::ActionEntry::builder("next") 127 .activate(move |_, _, _| { 128 media_controls.next(); 129 }) 130 .build(); 131 132 let media_controls = window.imp().media_control_bar.get(); 133 let shuffle = gio::ActionEntry::builder("shuffle") 134 .activate(move |_, _, _| { 135 media_controls.shuffle(); 136 }) 137 .build(); 138 139 let window_clone = window.clone(); 140 let refresh_library = gio::ActionEntry::builder("refresh_library") 141 .activate(move |_, _, _| { 142 window_clone.imp().refresh_library(); 143 }) 144 .build(); 145 146 let media_controls = window.imp().media_control_bar.get(); 147 let seek_backward = gio::ActionEntry::builder("seek_backward") 148 .activate(move |_, _, _| { 149 media_controls.seek_backward(); 150 }) 151 .build(); 152 153 let media_controls = window.imp().media_control_bar.get(); 154 let seek_forward = gio::ActionEntry::builder("seek_forward") 155 .activate(move |_, _, _| { 156 media_controls.seek_forward(); 157 }) 158 .build(); 159 160 self.add_action_entries([ 161 like, 162 play, 163 previous, 164 next, 165 shuffle, 166 refresh_library, 167 seek_backward, 168 seek_forward, 169 ]); 170 171 self.set_accels_for_action("win.toggle_search", &["<primary>f"]); 172 self.set_accels_for_action("app.preferences", &["<primary>comma"]); 173 self.set_accels_for_action("app.quit", &["<primary>q"]); 174 175 self.set_accels_for_action("app.refresh_library", &["<primary>r"]); 176 self.set_accels_for_action("app.like", &["<primary>l"]); 177 178 self.set_accels_for_action("app.play_pause", &["<primary>space"]); 179 self.set_accels_for_action("app.previous", &["<primary>Left"]); 180 self.set_accels_for_action("app.next", &["<primary>Right"]); 181 self.set_accels_for_action("app.seek_backward", &["<Shift><primary>Left"]); 182 self.set_accels_for_action("app.seek_forward", &["<Shift><primary>Right"]); 183 self.set_accels_for_action("app.shuffle", &["<primary>s"]); 184 } 185} 186 187impl Default for RbApplication { 188 fn default() -> Self { 189 gio::Application::default() 190 .expect("Could not get default GApplication") 191 .downcast() 192 .unwrap() 193 } 194}