From 03300c788a23fadbc1e9722d6e442c8812ccade7 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 21 Dec 2021 13:01:32 -0500 Subject: [PATCH] show active apps --- examples/dock/application_object/imp.rs | 213 ------------------------ examples/dock/application_object/mod.rs | 72 -------- examples/dock/dock_item/mod.rs | 41 +---- examples/dock/dock_object/imp.rs | 15 ++ examples/dock/dock_object/mod.rs | 7 +- examples/dock/main.rs | 31 +++- examples/dock/window/imp.rs | 4 +- examples/dock/window/mod.rs | 65 +++++++- examples/dock/window/window.ui | 4 +- 9 files changed, 113 insertions(+), 339 deletions(-) delete mode 100644 examples/dock/application_object/imp.rs delete mode 100644 examples/dock/application_object/mod.rs diff --git a/examples/dock/application_object/imp.rs b/examples/dock/application_object/imp.rs deleted file mode 100644 index 5048d2f..0000000 --- a/examples/dock/application_object/imp.rs +++ /dev/null @@ -1,213 +0,0 @@ -use glib::{FromVariant, ParamFlags, ParamSpec, ToVariant, Value, Variant, VariantTy}; -use gtk4::glib; -use gtk4::prelude::*; -use gtk4::subclass::prelude::*; -use once_cell::sync::Lazy; - -use std::cell::RefCell; -use std::rc::Rc; - -use super::ApplicationData; - -// Object holding the state -#[derive(Default)] -pub struct ApplicationObject { - data: Rc>, -} - -// The central trait for subclassing a GObject -#[glib::object_subclass] -impl ObjectSubclass for ApplicationObject { - const NAME: &'static str = "ApplicationObject"; - type Type = super::ApplicationObject; - type ParentType = glib::Object; -} - -// Trait shared by all GObjects -impl ObjectImpl for ApplicationObject { - fn properties() -> &'static [ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { - vec![ - ParamSpec::new_uint( - // Name - "id", - // Nickname - "id", - // Short description - "ID of application in launcher search result", - 0, - u32::MAX, - // Default value - 0, - // The property can be read and written to - ParamFlags::READWRITE, - ), - ParamSpec::new_string( - // Name - "name", - // Nickname - "name", - // Short description - "Name of application in launcher search result", - // Default value - Some(""), - // The property can be read and written to - ParamFlags::READWRITE, - ), - ParamSpec::new_string( - // Name - "description", - // Nickname - "description", - // Short description - "Description of application in launcher search result", - // Default value - Some(""), - // The property can be read and written to - ParamFlags::READWRITE, - ), - ParamSpec::new_variant( - // Name - "icon", - // Nickname - "icon", - // Short description - "Icon of application in launcher search result", - VariantTy::new("(is)").expect("Oops invalid string for VariantTy tuple."), - // Default value - None, - // The property can be read and written to - ParamFlags::READWRITE, - ), - ParamSpec::new_variant( - // Name - "categoryicon", - // Nickname - "categoryicon", - // Short description - "Category icon of application in launcher search result", - VariantTy::new("(is)").expect("Oops invalid string for VariantTy tuple."), - // Default value - None, - // The property can be read and written to - ParamFlags::READWRITE, - ), - ParamSpec::new_variant( - // Name - "window", - // Nickname - "window", - // Short description - "Window of application in launcher search result", - // type (tuple of two uint32) - VariantTy::new("(uu)").expect("Oops invalid string for VariantTy tuple."), - // Default value - None, - // The property can be read and written to - ParamFlags::READWRITE, - ), - ] - }); - PROPERTIES.as_ref() - } - - fn set_property(&self, _obj: &Self::Type, _id: usize, value: &Value, pspec: &ParamSpec) { - match pspec.name() { - "id" => { - let id = value.get().expect("The value needs to be of type `u32`."); - self.data.borrow_mut().0.id = id; - } - "name" => { - let name = value - .get() - .expect("The value needs to be of type `String`."); - self.data.borrow_mut().0.name = name; - } - "description" => { - let description = value - .get() - .expect("The description needs to be of type `String`"); - self.data.borrow_mut().0.description = description; - } - "icon" => { - let icon = <(i32, String)>::from_variant( - &value - .get::() - .expect("The icon needs to be a Variant"), - ) - .expect("The icon variant needs to be an (i32, String)"); - self.data.borrow_mut().0.icon = match icon { - (i_type, name) if i_type == pop_launcher::IconSource::Name as i32 => { - Some(pop_launcher::IconSource::Name(name.into())) - } - (i_type, name) if i_type == pop_launcher::IconSource::Mime as i32 => { - Some(pop_launcher::IconSource::Mime(name.into())) - } - (i_type, name) => { - println!("Failed to set icon. {} {}", i_type, name); - None - } - }; - } - "categoryicon" => { - let icon = <(i32, String)>::from_variant( - &value - .get::() - .expect("The icon needs to be a Variant"), - ) - .expect("The icon variant needs to be an Option<(i32, String)>"); - self.data.borrow_mut().0.category_icon = match icon { - (i_type, name) if i_type == pop_launcher::IconSource::Name as i32 => { - Some(pop_launcher::IconSource::Name(name.into())) - } - (i_type, name) if i_type == pop_launcher::IconSource::Mime as i32 => { - Some(pop_launcher::IconSource::Mime(name.into())) - } - (i_type, name) => { - println!("Failed to set icon. {} {}", i_type, name); - None - } - }; - } - "window" => { - unimplemented!() - } - _ => unimplemented!(), - } - } - - fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value { - match pspec.name() { - "id" => self.data.borrow().0.id.to_value(), - "name" => self.data.borrow().0.name.to_value(), - "description" => self.data.borrow().0.description.to_value(), - "icon" => match &self.data.borrow().0.icon { - Some(pop_launcher::IconSource::Name(icon_name)) => { - (pop_launcher::IconSource::Name as i32, icon_name.to_string()) - .to_variant() - .to_value() - } - Some(pop_launcher::IconSource::Mime(icon_name)) => { - (pop_launcher::IconSource::Mime as i32, icon_name.to_string()) - .to_variant() - .to_value() - } - _ => None::.to_value(), - }, - "categoryicon" => match &self.data.borrow().0.category_icon { - Some(pop_launcher::IconSource::Name(icon_name)) => { - (pop_launcher::IconSource::Name as i32, icon_name.to_string()) - .to_variant() - .to_value() - } - Some(pop_launcher::IconSource::Mime(icon_name)) => { - (pop_launcher::IconSource::Mime as i32, icon_name.to_string()) - .to_variant() - .to_value() - } - _ => None::.to_value(), - }, - _ => unimplemented!(), - } - } -} diff --git a/examples/dock/application_object/mod.rs b/examples/dock/application_object/mod.rs deleted file mode 100644 index 1ce0337..0000000 --- a/examples/dock/application_object/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -mod imp; - -use gdk4::glib::Object; -use glib::ObjectExt; -use glib::ToVariant; -use gtk4::glib; - -glib::wrapper! { - pub struct ApplicationObject(ObjectSubclass); -} - -impl ApplicationObject { - pub fn new(application_search_result: &pop_launcher::SearchResult) -> Self { - let self_: Self = Object::new(&[ - ("id", &application_search_result.id), - ("name", &application_search_result.name), - ("description", &application_search_result.description), - ]) - .expect("Failed to create `ApplicationObject`."); - if let Some(icon) = &application_search_result.icon { - if let Err(e) = self_.set_property( - "icon", - match icon { - pop_launcher::IconSource::Name(name) => { - (pop_launcher::IconSource::Name as i32, name.to_string()).to_variant() - } - pop_launcher::IconSource::Mime(name) => { - (pop_launcher::IconSource::Mime as i32, name.to_string()).to_variant() - } - }, - ) { - println!("failed to set icon property"); - dbg!(e); - }; - } - if let Some(icon) = &application_search_result.category_icon { - if let Err(e) = self_.set_property( - "categoryicon", - match icon { - pop_launcher::IconSource::Name(name) => { - (pop_launcher::IconSource::Name as i32, name.to_string()).to_variant() - } - pop_launcher::IconSource::Mime(name) => { - (pop_launcher::IconSource::Mime as i32, name.to_string()).to_variant() - } - }, - ) { - println!("failed to set category icon property"); - dbg!(e); - }; - } - - self_ - } -} - -// Object holding the state -pub struct ApplicationData(pop_launcher::SearchResult); - -impl Default for ApplicationData { - fn default() -> Self { - let default_application = pop_launcher::SearchResult { - id: 0, - name: String::default(), - description: String::default(), - icon: None, - category_icon: None, - window: None, - }; - Self(default_application) - } -} diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs index f8ad14a..284d5f1 100644 --- a/examples/dock/dock_item/mod.rs +++ b/examples/dock/dock_item/mod.rs @@ -53,10 +53,15 @@ impl DockItem { pub fn set_app_info(&self, app_info: &DockObject, i: u32, saved_app_model: &ListStore) { if let Ok(app_info_value) = app_info.property("appinfo") { if let Ok(Some(app_info)) = app_info_value.get::>() { - dbg!("setting app info"); + println!("setting app info {}", &app_info.name()); let self_ = imp::DockItem::from_instance(self); self_.image.set_tooltip_text(Some(&app_info.name())); + let icon = app_info.icon().unwrap_or( + Icon::for_string("image-missing").expect("Failed to set default icon"), + ); + + self_.image.set_from_gicon(&icon); if let Some(drag_controller) = self_.drag_controller.get() { if let Some(file) = app_info.filename() { let provider = @@ -108,39 +113,9 @@ impl DockItem { }), ); } - - let icon = app_info.icon().unwrap_or( - Icon::for_string("image-missing").expect("Failed to set default icon"), - ); - - self_.image.set_from_gicon(&icon); } + } else { + println!("initializing dock item failed..."); } } - - // pub fn set_app_info(&self, app_obj: ApplicationObject) { - // let self_ = imp::DockItem::from_instance(self); - - // if let Ok(name) = app_obj.property("name") { - // self_.image.set_tooltip_text(Some( - // &name - // .get::() - // .expect("Property name needs to be a String."), - // )); - // } - // if let Ok(icon) = app_obj.property("icon") { - // if let Ok(icon) = icon.get::() { - // let icon = match <(i32, String)>::from_variant(&icon) { - // Some((i_type, name)) if i_type == pop_launcher::IconSource::Name as i32 => { - // Some(pop_launcher::IconSource::Name(name.into())) - // } - // Some((i_type, name)) if i_type == pop_launcher::IconSource::Mime as i32 => { - // Some(pop_launcher::IconSource::Mime(name.into())) - // } - // _ => None, - // }; - // icon_source(&self_.image, &icon); - // } - // } - // } } diff --git a/examples/dock/dock_object/imp.rs b/examples/dock/dock_object/imp.rs index 50bfb7c..66fc50d 100644 --- a/examples/dock/dock_object/imp.rs +++ b/examples/dock/dock_object/imp.rs @@ -5,6 +5,7 @@ use gtk4::glib; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use once_cell::sync::Lazy; +use std::cell::Cell; use std::cell::RefCell; // Object holding the state @@ -12,6 +13,7 @@ use std::cell::RefCell; pub struct DockObject { appinfo: RefCell>, active: RefCell, + saved: Cell, } // The central trait for subclassing a GObject @@ -49,6 +51,13 @@ impl ObjectImpl for DockObject { // The property can be read and written to ParamFlags::READWRITE, ), + ParamSpec::new_boolean( + "saved", + "saved", + "Indicates whether app is saved to the dock", + false, + ParamFlags::READWRITE, + ), ] }); PROPERTIES.as_ref() @@ -66,6 +75,10 @@ impl ObjectImpl for DockObject { let active = value.get().expect("Value needs to be BoxedSearchResults"); self.active.replace(active); } + "saved" => { + self.saved + .replace(value.get().expect("Value needs to be a boolean")); + } _ => unimplemented!(), } } @@ -73,6 +86,8 @@ impl ObjectImpl for DockObject { fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value { match pspec.name() { "appinfo" => self.appinfo.borrow().to_value(), + "active" => self.active.borrow().to_value(), + "saved" => self.saved.get().to_value(), _ => unimplemented!(), } } diff --git a/examples/dock/dock_object/mod.rs b/examples/dock/dock_object/mod.rs index 962e57e..5cf6ddc 100644 --- a/examples/dock/dock_object/mod.rs +++ b/examples/dock/dock_object/mod.rs @@ -12,7 +12,8 @@ glib::wrapper! { impl DockObject { pub fn new(appinfo: DesktopAppInfo) -> Self { - Object::new(&[("appinfo", &Some(appinfo))]).expect("Failed to create `DockObject`.") + Object::new(&[("appinfo", &Some(appinfo)), ("saved", &true)]) + .expect("Failed to create `DockObject`.") } pub fn from_search_results(results: BoxedSearchResults) -> Self { @@ -23,7 +24,6 @@ impl DockObject { .iter_mut() .filter_map(|xdg_data_path| { xdg_data_path.push("applications"); - dbg!(&xdg_data_path); std::fs::read_dir(xdg_data_path).ok() }) .flatten() @@ -35,7 +35,7 @@ impl DockObject { if app_info.should_show() && first.description.as_str() == app_info.name().as_str() { - return Some(DockObject::new(app_info)); + return Some(app_info); } } } @@ -47,6 +47,7 @@ impl DockObject { } else { None }; + dbg!(&appinfo); Object::new(&[("appinfo", &appinfo), ("active", &results)]) .expect("Failed to create `DockObject`.") } diff --git a/examples/dock/main.rs b/examples/dock/main.rs index 9e71d45..b860ca5 100644 --- a/examples/dock/main.rs +++ b/examples/dock/main.rs @@ -1,9 +1,9 @@ -mod application_object; mod dock_item; mod dock_object; mod utils; mod window; +use crate::utils::BoxedSearchResults; use async_io::Timer; use gdk4::Display; use gio::DesktopAppInfo; @@ -18,9 +18,11 @@ use once_cell::sync::OnceCell; use pop_launcher_service::IpcClient; use postage::mpsc::Sender; use postage::prelude::*; +use std::collections::HashMap; use std::time::Duration; use x11rb::rust_connection::RustConnection; +use self::dock_object::DockObject; use self::window::Window; const NUM_LAUNCHER_ITEMS: u8 = 10; @@ -48,10 +50,10 @@ fn spawn_launcher(tx: Sender) -> IpcClient { let mut sender = tx.clone(); glib::MainContext::default().spawn_local(async move { - loop { - Timer::after(Duration::from_secs(1)).await; - let _ = sender.send(Event::Search(String::new())).await; - } + // loop { + let _ = sender.send(Event::Search(String::new())).await; + Timer::after(Duration::from_secs(1)).await; + // } }); launcher @@ -120,8 +122,23 @@ fn main() { let _ = launcher.send(pop_launcher::Request::Activate(index)).await; } Event::Response(event) => { - if let pop_launcher::Response::Update(_results) = event { - println!("updating active apps") + if let pop_launcher::Response::Update(results) = event { + println!("updating active apps"); + let model = window.active_app_model(); + let model_len = model.n_items(); + let stack_active = results.iter().fold(HashMap::new(), |mut acc: HashMap, elem| { + if let Some(v) = acc.get_mut(&elem.description) { + v.0.push(elem.clone()); + } else { + acc.insert(elem.description.clone(), BoxedSearchResults(vec![elem.clone()])); + } + acc + }); + let new_results: Vec = stack_active + .into_values() + .map(|v| DockObject::from_search_results(v).upcast()) + .collect(); + model.splice(0, model_len, &new_results[..]); } else if let pop_launcher::Response::DesktopEntry { path, diff --git a/examples/dock/window/imp.rs b/examples/dock/window/imp.rs index 5b49e2f..aa3e138 100644 --- a/examples/dock/window/imp.rs +++ b/examples/dock/window/imp.rs @@ -18,7 +18,7 @@ pub struct Window { #[template_child] pub saved_app_list_view: TemplateChild, #[template_child] - pub unsaved_open_app_list_view: TemplateChild, + pub active_app_list_view: TemplateChild, #[template_child] pub revealer: TemplateChild, #[template_child] @@ -26,7 +26,7 @@ pub struct Window { #[template_child] pub cursor_leave_handle: TemplateChild, pub saved_app_model: OnceCell, - pub unsaved_open_app_model: OnceCell, + pub active_app_model: OnceCell, pub enter_event_controller: OnceCell, pub leave_event_controller: OnceCell, pub drop_controller: OnceCell, diff --git a/examples/dock/window/mod.rs b/examples/dock/window/mod.rs index 4dbdd32..2d06432 100644 --- a/examples/dock/window/mod.rs +++ b/examples/dock/window/mod.rs @@ -46,13 +46,21 @@ impl Window { .expect("Could not get saved_app_model") } + pub fn active_app_model(&self) -> &gio::ListStore { + // Get state + let imp = imp::Window::from_instance(self); + imp.active_app_model + .get() + .expect("Could not get active_app_model") + } + fn setup_model(&self) { // Get state and set model let imp = imp::Window::from_instance(self); let saved_app_model = gio::ListStore::new(DockObject::static_type()); - let selection_model = gtk::SingleSelection::builder() + let saved_selection_model = gtk::SingleSelection::builder() .autoselect(false) .can_unselect(true) .selected(gtk4::INVALID_LIST_POSITION) @@ -93,7 +101,23 @@ impl Window { .set(saved_app_model) .expect("Could not set model"); // Wrap model with selection and pass it to the list view - imp.saved_app_list_view.set_model(Some(&selection_model)); + imp.saved_app_list_view + .set_model(Some(&saved_selection_model)); + + let active_app_model = gio::ListStore::new(DockObject::static_type()); + let active_selection_model = gtk::SingleSelection::builder() + .autoselect(false) + .can_unselect(true) + .selected(gtk4::INVALID_LIST_POSITION) + .model(&active_app_model) + .build(); + + imp.active_app_model + .set(active_app_model) + .expect("Could not set model"); + // Wrap model with selection and pass it to the list view + imp.active_app_list_view + .set_model(Some(&active_selection_model)); } fn setup_callbacks(&self) { @@ -346,8 +370,8 @@ impl Window { } fn setup_factory(&self) { - let factory = SignalListItemFactory::new(); - factory.connect_setup(move |_, list_item| { + let saved_app_factory = SignalListItemFactory::new(); + saved_app_factory.connect_setup(move |_, list_item| { let dock_item = DockItem::new(); list_item.set_child(Some(&dock_item)); }); @@ -356,7 +380,7 @@ impl Window { .saved_app_model .get() .expect("Failed to get saved app model."); - factory.connect_bind(glib::clone!(@weak saved_app_model => move |_, list_item| { + saved_app_factory.connect_bind(glib::clone!(@weak saved_app_model => move |_, list_item| { let application_object = list_item .item() .expect("The item has to exist.") @@ -372,6 +396,35 @@ impl Window { dock_item.set_app_info(&application_object, i, &saved_app_model); })); // Set the factory of the list view - imp.saved_app_list_view.set_factory(Some(&factory)); + imp.saved_app_list_view + .set_factory(Some(&saved_app_factory)); + + let active_app_model = imp + .active_app_model + .get() + .expect("Failed to get saved app model."); + let active_factory = SignalListItemFactory::new(); + active_factory.connect_setup(move |_, list_item| { + let dock_item = DockItem::new(); + list_item.set_child(Some(&dock_item)); + }); + active_factory.connect_bind(glib::clone!(@weak active_app_model => move |_, list_item| { + let application_object = list_item + .item() + .expect("The item has to exist.") + .downcast::() + .expect("The item has to be a `DockObject`"); + let dock_item = list_item + .child() + .expect("The list item child needs to exist.") + .downcast::() + .expect("The list item type needs to be `DockItem`"); + + let i = list_item.position(); + println!("setting position {} of active app list.", i); + dock_item.set_app_info(&application_object, i, &active_app_model); + })); + // Set the factory of the list view + imp.active_app_list_view.set_factory(Some(&active_factory)); } } diff --git a/examples/dock/window/window.ui b/examples/dock/window/window.ui index 44e2b80..f237d40 100644 --- a/examples/dock/window/window.ui +++ b/examples/dock/window/window.ui @@ -31,7 +31,6 @@ horizontal - true @@ -44,9 +43,8 @@ - + horizontal - true