diff --git a/Cargo.lock b/Cargo.lock index f19c6fe6..f6a55b65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,7 @@ dependencies = [ "libpulse-binding", "pulsectl-rs", "relm4", + "tracker", ] [[package]] diff --git a/applets/cosmic-applet-audio/Cargo.toml b/applets/cosmic-applet-audio/Cargo.toml index 7d6266c2..db88c39b 100644 --- a/applets/cosmic-applet-audio/Cargo.toml +++ b/applets/cosmic-applet-audio/Cargo.toml @@ -11,5 +11,6 @@ libcosmic-widgets = { git = "https://github.com/pop-os/libcosmic", branch = "luc libpulse-binding = "2.26.0" pulsectl-rs = "0.3.2" relm4 = { git = "https://github.com/AaronErhardt/relm4", branch = "new-approach", features = ["macros"] } +tracker = "0.1.1" [features] diff --git a/applets/cosmic-applet-audio/src/app.rs b/applets/cosmic-applet-audio/src/app.rs index 0173cbb0..5261381c 100644 --- a/applets/cosmic-applet-audio/src/app.rs +++ b/applets/cosmic-applet-audio/src/app.rs @@ -5,7 +5,10 @@ use libpulse_binding::{ volume::Volume, }; use pulsectl::{ - controllers::{types::DeviceInfo, AppControl, DeviceControl, SinkController, SourceController}, + controllers::{ + types::{ApplicationInfo, DeviceInfo}, + AppControl, DeviceControl, SinkController, SourceController, + }, Handler, }; use relm4::{ @@ -20,6 +23,7 @@ use relm4::{ view, ComponentParts, RelmContainerExt, Sender, SimpleComponent, }; use std::rc::Rc; +use tracker::track; pub enum AppInput { Inputs, @@ -29,13 +33,20 @@ pub enum AppInput { NowPlaying, } +#[track] pub struct App { + #[no_eq] default_input: Option, + #[no_eq] inputs: Vec, + #[no_eq] default_output: Option, + #[no_eq] outputs: Vec, + #[no_eq] + now_playing: Vec, + #[do_not_track] handler: Handler, - update_now_playing: bool, } impl Default for App { @@ -48,6 +59,7 @@ impl Default for App { SinkController::create().expect("failed to create output controller"); let default_output = output_controller.get_default_device().ok(); let outputs = output_controller.list_devices().unwrap_or_default(); + let now_playing = output_controller.list_applications().unwrap_or_default(); let handler = Handler::connect("com.system76.cosmic.applets.audio") .expect("failed to connect to pulse"); relm4::spawn_local(clone!(@weak handler.mainloop as main_loop => async move { @@ -62,8 +74,9 @@ impl Default for App { inputs, default_output, outputs, + now_playing, handler, - update_now_playing: false, + tracker: 0, } } } @@ -89,7 +102,7 @@ impl App { } } - fn update_default_input(&mut self) { + fn refresh_default_input(&mut self) { let mut input_controller = SourceController::create().expect("failed to create input controller"); self.default_input = match self.default_input.as_ref() { @@ -101,7 +114,7 @@ impl App { }; } - fn update_default_output(&mut self) { + fn refresh_default_output(&mut self) { let mut output_controller = SinkController::create().expect("failed to create output controller"); self.default_output = match self.default_output.as_ref() { @@ -133,15 +146,18 @@ impl App { context.subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE, |_| {}); } - fn update_inputs(&self, widgets: &AppWidgets) { + fn refresh_input_list(&mut self) { let mut input_controller = SourceController::create().expect("failed to create input controller"); - let inputs = input_controller.list_devices().unwrap_or_default(); + self.set_inputs(input_controller.list_devices().unwrap_or_default()); + } + + fn refresh_input_widgets(&self, widgets: &AppWidgets) { while let Some(row) = widgets.inputs.row_at_index(0) { widgets.inputs.remove(&row); } - for input in inputs { - let input = Rc::new(input); + for input in self.get_inputs() { + let input = Rc::new(input.clone()); let name = match &input.name { Some(name) => name.to_owned(), None => continue, // Why doesn't this have a name? Whatever, it's invalid. @@ -166,15 +182,18 @@ impl App { } } - fn update_outputs(&self, widgets: &AppWidgets) { + fn refresh_output_list(&mut self) { let mut output_controller = SinkController::create().expect("failed to create output controller"); - let outputs = output_controller.list_devices().unwrap_or_default(); + self.set_outputs(output_controller.list_devices().unwrap_or_default()); + } + + fn refresh_output_widgets(&self, widgets: &AppWidgets) { while let Some(row) = widgets.outputs.row_at_index(0) { widgets.outputs.remove(&row); } - for output in outputs { - let output = Rc::new(output); + for output in self.get_outputs() { + let output = Rc::new(output.clone()); let name = match &output.name { Some(name) => name.to_owned(), None => continue, // Why doesn't this have a name? Whatever, it's invalid. @@ -200,21 +219,24 @@ impl App { } } - fn update_now_playing(&self, widgets: &AppWidgets) { + fn refresh_now_playing(&mut self) { let mut output_controller = SinkController::create().expect("failed to create output controller"); - let apps = output_controller.list_applications().unwrap_or_default(); + self.set_now_playing(output_controller.list_applications().unwrap_or_default()); + } + + fn refresh_now_playing_widgets(&self, widgets: &AppWidgets) { while let Some(row) = widgets.playing_apps.row_at_index(0) { widgets.playing_apps.remove(&row); } - for app in apps { + for app in self.get_now_playing() { let icon_name = app .proplist .get_str("application.icon_name") .unwrap_or_default(); - let name = app.name.unwrap_or_default(); + let name = app.name.clone().unwrap_or_default(); view! { - GtkBox { + item = GtkBox { set_orientation: Orientation::Horizontal, append: icon = &Image { set_icon_name: Some(&icon_name), @@ -225,6 +247,7 @@ impl App { } } } + widgets.playing_apps.container_add(&item); } } } @@ -351,37 +374,37 @@ impl SimpleComponent for App { &mut self, msg: Self::Input, _input: &Sender, - _ouput: &Sender, + _output: &Sender, ) { + self.reset(); match msg { AppInput::Outputs => { - self.outputs.clear(); + self.refresh_output_list(); } AppInput::Inputs => { - self.inputs.clear(); + self.refresh_input_list(); } AppInput::InputVolume => { - self.update_default_input(); + self.refresh_default_input(); } AppInput::OutputVolume => { - self.update_default_output(); + self.refresh_default_output(); } AppInput::NowPlaying => { - self.update_now_playing = true; + self.refresh_now_playing(); } } } fn pre_view() { - if self.outputs.is_empty() { - model.update_outputs(widgets); + if model.changed(App::outputs()) { + model.refresh_output_widgets(widgets); } - if self.inputs.is_empty() { - model.update_inputs(widgets); + if model.changed(App::inputs()) { + model.refresh_input_widgets(widgets); } - if self.update_now_playing { - model.update_now_playing(widgets); - self.update_now_playing = false; + if model.changed(App::now_playing()) { + model.refresh_now_playing_widgets(widgets); } } }