From 09bdb53f8e876b69f296dab38bfb275004efd7aa Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 14 Dec 2021 11:53:18 -0500 Subject: [PATCH] very simple alignment of dock on bottom edge --- Cargo.toml | 6 +- examples/dock/application_object/imp.rs | 213 ++++++++++++++++++++++++ examples/dock/application_object/mod.rs | 72 ++++++++ examples/dock/dock_item/dock_item.ui | 60 +++++++ examples/dock/dock_item/imp.rs | 40 +++++ examples/dock/dock_item/mod.rs | 79 +++++++++ examples/dock/ipc.rs | 76 +++++++++ examples/dock/main.rs | 150 +++++++++++++++++ examples/dock/style.css | 14 ++ examples/dock/window/imp.rs | 54 ++++++ examples/dock/window/mod.rs | 177 ++++++++++++++++++++ examples/dock/window/window.ui | 19 +++ 12 files changed, 959 insertions(+), 1 deletion(-) create mode 100644 examples/dock/application_object/imp.rs create mode 100644 examples/dock/application_object/mod.rs create mode 100644 examples/dock/dock_item/dock_item.ui create mode 100644 examples/dock/dock_item/imp.rs create mode 100644 examples/dock/dock_item/mod.rs create mode 100644 examples/dock/ipc.rs create mode 100644 examples/dock/main.rs create mode 100644 examples/dock/style.css create mode 100644 examples/dock/window/imp.rs create mode 100644 examples/dock/window/mod.rs create mode 100644 examples/dock/window/window.ui diff --git a/Cargo.toml b/Cargo.toml index b528aacd..20b76b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,12 @@ serde_json = "1.0.72" pop-launcher-service = { git = "https://github.com/pop-os/launcher", branch = "master" } postage = "0.4.1" futures = "0.3.18" -glib = "0.14.8" +glib = "0.14.8" # examples/gtklauncher once_cell = "1.8.0" xdg = "2.4.0" serde = "1.0.130" +x11rb = "0.9.0" + +[dependencies.wnck] +path = "../wnck" diff --git a/examples/dock/application_object/imp.rs b/examples/dock/application_object/imp.rs new file mode 100644 index 00000000..5048d2fa --- /dev/null +++ b/examples/dock/application_object/imp.rs @@ -0,0 +1,213 @@ +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 new file mode 100644 index 00000000..1ce03377 --- /dev/null +++ b/examples/dock/application_object/mod.rs @@ -0,0 +1,72 @@ +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/dock_item.ui b/examples/dock/dock_item/dock_item.ui new file mode 100644 index 00000000..47062d89 --- /dev/null +++ b/examples/dock/dock_item/dock_item.ui @@ -0,0 +1,60 @@ + + + + diff --git a/examples/dock/dock_item/imp.rs b/examples/dock/dock_item/imp.rs new file mode 100644 index 00000000..adc89e35 --- /dev/null +++ b/examples/dock/dock_item/imp.rs @@ -0,0 +1,40 @@ +use gtk::glib; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk4 as gtk; + +use gtk::CompositeTemplate; + +#[derive(Debug, Default, CompositeTemplate)] +#[template(file = "application_row.ui")] +pub struct ApplicationRow { + #[template_child] + pub name: TemplateChild, + #[template_child] + pub description: TemplateChild, + #[template_child] + pub shortcut: TemplateChild, + #[template_child] + pub image: TemplateChild, + #[template_child] + pub categoryimage: TemplateChild, +} + +#[glib::object_subclass] +impl ObjectSubclass for ApplicationRow { + const NAME: &'static str = "ApplicationRow"; + type Type = super::ApplicationRow; + type ParentType = gtk::Box; + + fn class_init(klass: &mut Self::Class) { + Self::bind_template(klass); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } +} + +impl ObjectImpl for ApplicationRow {} +impl WidgetImpl for ApplicationRow {} +impl BoxImpl for ApplicationRow {} diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs new file mode 100644 index 00000000..08d09eb1 --- /dev/null +++ b/examples/dock/dock_item/mod.rs @@ -0,0 +1,79 @@ +use crate::icon_source; +use glib::FromVariant; +use glib::Variant; +use gtk4 as gtk; +mod imp; + +use crate::ApplicationObject; +use gtk::glib; +use gtk::prelude::*; +use gtk::subclass::prelude::*; + +glib::wrapper! { + pub struct ApplicationRow(ObjectSubclass) + @extends gtk::Widget, gtk::Box; +} + +impl Default for ApplicationRow { + fn default() -> Self { + Self::new() + } +} + +impl ApplicationRow { + pub fn new() -> Self { + glib::Object::new(&[]).expect("Failed to create ApplicationRow") + } + + pub fn set_app_info(&self, app_obj: ApplicationObject) { + let self_ = imp::ApplicationRow::from_instance(self); + + if let Ok(name) = app_obj.property("name") { + self_.name.set_text( + &name + .get::() + .expect("Property name needs to be a String."), + ); + } + if let Ok(desc) = app_obj.property("description") { + self_.description.set_text( + &desc + .get::() + .expect("Property description 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); + } + } + if let Ok(icon) = app_obj.property("categoryicon") { + 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_.categoryimage, &icon); + } + } + } + + pub fn set_shortcut(&self, indx: u32) { + let self_ = imp::ApplicationRow::from_instance(self); + self_.shortcut.set_text(&format!("Ctrl + {}", indx)); + } +} diff --git a/examples/dock/ipc.rs b/examples/dock/ipc.rs new file mode 100644 index 00000000..c1ce7576 --- /dev/null +++ b/examples/dock/ipc.rs @@ -0,0 +1,76 @@ +use pop_launcher::{Request, Response}; +use std::process; +use std::io::{self, BufRead, Write}; + +pub struct LauncherIpc { + child: process::Child, + stdin: process::ChildStdin, + stdout: io::BufReader, + exited: bool, +} + +impl LauncherIpc { + pub fn new() -> io::Result { + let mut child = process::Command::new("pop-launcher") + .stdin(process::Stdio::piped()) + .stdout(process::Stdio::piped()) + .spawn()?; + + let stdin = child.stdin.take().ok_or( + io::Error::new(io::ErrorKind::Other, "failed to find child stdin") + )?; + + let stdout = io::BufReader::new(child.stdout.take().ok_or( + io::Error::new(io::ErrorKind::Other, "failed to find child stdout") + )?); + + Ok(Self { + child, + stdin, + stdout, + exited: false, + }) + } + + fn send_request(&mut self, request: Request) -> io::Result<()> { + let mut request_json = serde_json::to_string(&request).map_err(|err| { + io::Error::new(io::ErrorKind::InvalidInput, err) + })?; + request_json.push('\n'); + self.stdin.write_all(request_json.as_bytes()) + } + + fn recv_response(&mut self) -> io::Result { + let mut response_json = String::new(); + self.stdout.read_line(&mut response_json)?; + serde_json::from_str(&response_json).map_err(|err| { + io::Error::new(io::ErrorKind::InvalidData, err) + }) + } + + pub fn request(&mut self, request: Request) -> io::Result { + self.send_request(request)?; + self.recv_response() + } + + //TODO: better exit implementation + pub fn exit(&mut self) -> io::Result> { + if ! self.exited { + self.send_request(Request::Exit)?; + let status = self.child.wait()?; + self.exited = true; + Ok(Some(status)) + } else { + Ok(None) + } + } +} + +impl Drop for LauncherIpc { + fn drop(&mut self) { + match self.exit() { + Ok(_) => (), + Err(err) => eprintln!("LauncherIpc::drop failed: {}", err), + } + } +} diff --git a/examples/dock/main.rs b/examples/dock/main.rs new file mode 100644 index 00000000..c8177db4 --- /dev/null +++ b/examples/dock/main.rs @@ -0,0 +1,150 @@ +mod application_object; +mod window; + +use gdk4::Display; +use gio::DesktopAppInfo; +use gtk::Application; +use gtk4 as gtk; +use gtk4::CssProvider; +use gtk4::StyleContext; +use once_cell::sync::OnceCell; + +use gtk::gio; +use gtk::glib; +use gtk::prelude::*; +use pop_launcher_service::IpcClient; +use postage::mpsc::Sender; +use postage::prelude::*; +use x11rb::rust_connection::RustConnection; + +use self::application_object::ApplicationObject; +use self::window::Window; + +const NUM_LAUNCHER_ITEMS: u8 = 10; +static TX: OnceCell> = OnceCell::new(); +static X11_CONN: OnceCell = OnceCell::new(); + +fn icon_source(icon: >k::Image, source: &Option) { + match source { + Some(pop_launcher::IconSource::Name(name)) => { + icon.set_from_icon_name(Some(name)); + } + Some(pop_launcher::IconSource::Mime(content_type)) => { + icon.set_from_gicon(&gio::content_type_get_icon(content_type)); + } + _ => { + icon.set_from_icon_name(None); + } + } +} + +pub enum Event { + Response(pop_launcher::Response), + Search(String), + Activate(u32), +} + +fn spawn_launcher(mut tx: Sender) -> IpcClient { + let (launcher, responses) = + pop_launcher_service::IpcClient::new().expect("failed to connect to launcher service"); + + glib::MainContext::default().spawn_local(async move { + use futures::StreamExt; + futures::pin_mut!(responses); + while let Some(event) = responses.next().await { + let _ = tx.send(Event::Response(event)).await; + } + }); + + launcher +} + +fn setup_shortcuts(app: &Application) { + //quit shortcut + app.set_accels_for_action("win.quit", &["W", "Escape"]); + //launch shortcuts + for i in 1..NUM_LAUNCHER_ITEMS { + app.set_accels_for_action(&format!("win.launch{}", i), &[&format!("{}", i)]); + } +} + +fn load_css() { + // Load the css file and add it to the provider + let provider = CssProvider::new(); + provider.load_from_data(include_bytes!("style.css")); + + // Add the provider to the default screen + StyleContext::add_provider_for_display( + &Display::default().expect("Error initializing GTK CSS provider."), + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, + ); +} + +fn main() { + let app = gtk::Application::builder() + .application_id("com.cosmic.Launcher") + .build(); + + app.connect_startup(|app| { + setup_shortcuts(app); + load_css() + }); + app.connect_activate(move |app| { + let (tx, mut rx) = postage::mpsc::channel(1); + let mut launcher = spawn_launcher(tx.clone()); + if TX.set(tx).is_err() { + println!("failed to set global Sender. Exiting"); + std::process::exit(1); + }; + + let (conn, screen_num) = x11rb::connect(None).expect("Failed to connect to X"); + if X11_CONN.set(conn).is_err() { + println!("failed to set X11_CONN. Exiting"); + std::process::exit(1); + }; + + let window = Window::new(app); + let wclone = window.clone(); + window.show(); + + glib::MainContext::default().spawn_local(async move { + while let Some(event) = rx.recv().await { + match event { + Event::Search(search) => { + let _ = launcher.send(pop_launcher::Request::Search(search)).await; + } + Event::Activate(index) => { + let _ = launcher.send(pop_launcher::Request::Activate(index)).await; + } + + Event::Response(event) => { + if let pop_launcher::Response::Update(results) = event { + let model = window.model(); + let model_len = model.n_items(); + dbg!(&results); + let new_results: Vec = results + [0..std::cmp::min(results.len(), NUM_LAUNCHER_ITEMS.into())] + .iter() + .map(|result| ApplicationObject::new(result).upcast()) + .collect(); + model.splice(0, model_len, &new_results[..]); + } else if let pop_launcher::Response::DesktopEntry { + path, + gpu_preference: _gpu_preference, // TODO use GPU preference when launching app + } = event + { + let app_info = + DesktopAppInfo::new(&path.file_name().expect("desktop entry path needs to be a valid filename").to_string_lossy()) + .expect("failed to create a Desktop App info for launching the application."); + app_info + .launch(&[], Some(&wclone.display().app_launch_context().clone())).expect("failed to launch the application."); + } + } + } + } + }) + }); + + app.run(); +} diff --git a/examples/dock/style.css b/examples/dock/style.css new file mode 100644 index 00000000..4bfa4acf --- /dev/null +++ b/examples/dock/style.css @@ -0,0 +1,14 @@ +row:hover { + transition: 100ms; + background: #888888; +} +row { + background: #333333; +} +listview { + background: #333333; +} +window { + background: #333333; + border-radius: 15px; +} diff --git a/examples/dock/window/imp.rs b/examples/dock/window/imp.rs new file mode 100644 index 00000000..36612e87 --- /dev/null +++ b/examples/dock/window/imp.rs @@ -0,0 +1,54 @@ +use gtk4 as gtk; + +use glib::subclass::InitializingObject; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{gio, glib}; +use gtk::{CompositeTemplate, Entry, ListView}; +use once_cell::sync::OnceCell; + +// Object holding the state +#[derive(CompositeTemplate, Default)] +#[template(file = "window.ui")] +pub struct Window { + #[template_child] + pub list_view: TemplateChild, + pub model: OnceCell, +} + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for Window { + // `NAME` needs to match `class` attribute of template + const NAME: &'static str = "LauncherWindow"; + type Type = super::Window; + type ParentType = gtk::ApplicationWindow; + + fn class_init(klass: &mut Self::Class) { + Self::bind_template(klass); + } + + fn instance_init(obj: &InitializingObject) { + obj.init_template(); + } +} +// Trait shared by all GObjects +impl ObjectImpl for Window { + fn constructed(&self, obj: &Self::Type) { + // Call "constructed" on parent + self.parent_constructed(obj); + + // Setup + obj.setup_model(); + obj.setup_callbacks(); + obj.setup_factory(); + } +} +// Trait shared by all widgets +impl WidgetImpl for Window {} + +// Trait shared by all windows +impl WindowImpl for Window {} + +// Trait shared by all application +impl ApplicationWindowImpl for Window {} diff --git a/examples/dock/window/mod.rs b/examples/dock/window/mod.rs new file mode 100644 index 00000000..a37d55e3 --- /dev/null +++ b/examples/dock/window/mod.rs @@ -0,0 +1,177 @@ +mod imp; +// use crate::ApplicationObject; +use crate::TX; +use crate::X11_CONN; +use gdk4::Rectangle; +use gdk4::Surface; +use gdk4_x11::X11Surface; +use gtk4 as gtk; +use gtk4::Allocation; +use postage::prelude::Sink; +use x11rb::connection::Connection; +use x11rb::protocol::xproto::ConnectionExt; + +// use crate::application_row::ApplicationRow; +use glib::Object; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{gio, glib}; +use gtk::{Application, SignalListItemFactory}; +use libcosmic::x; +use x11rb::protocol::xproto; + +glib::wrapper! { + pub struct Window(ObjectSubclass) + @extends gtk::ApplicationWindow, gtk::Window, gtk::Widget, + @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, + gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager; +} + +const NUM_LAUNCHER_ITEMS: u8 = 9; + +impl Window { + pub fn new(app: &Application) -> Self { + let self_: Self = Object::new(&[("application", app)]).expect("Failed to create `Window`."); + self_ + } + + pub fn model(&self) -> &gio::ListStore { + // Get state + let imp = imp::Window::from_instance(self); + imp.model.get().expect("Could not get model") + } + + fn setup_model(&self) { + // Get state and set model + let imp = imp::Window::from_instance(self); + // let model = gio::ListStore::new(ApplicationObject::static_type()); + + // let selection_model = gtk::SingleSelection::builder() + // .model(&slice_model) + // .autoselect(false) + // .can_unselect(true) + // .selected(gtk4::INVALID_LIST_POSITION) + // .build(); + + // imp.model.set(model).expect("Could not set model"); + // // Wrap model with selection and pass it to the list view + // imp.list_view.set_model(Some(&selection_model)); + } + + fn setup_callbacks(&self) { + // Get state + let imp = imp::Window::from_instance(self); + let window = self.clone().upcast::(); + let list_view = &imp.list_view; + let lv = list_view.get(); + + // let app_selection_model = list_view + // .model() + // .expect("List view missing selection model") + // .downcast::() + // .expect("could not downcast listview model to single selection model"); + + // app_selection_model.connect_selected_notify(glib::clone!(@weak window => move |model| { + // let i = model.selected(); + // println!("acitvating... {}", i + 1); + // let app_info = model.item(i); + // if app_info.is_none() { + // println!("oops no app for this row..."); + // return; + // } + // if let Ok(id) = app_info.unwrap().property("id") { + // let id = id.get::().expect("App ID must be u32"); + + // glib::MainContext::default().spawn_local(async move { + // if let Some(tx) = TX.get() { + // let mut tx = tx.clone(); + // let _ = tx.send(crate::Event::Activate(id)).await; + // } + // }); + // } + // })); + + window.connect_realize(move |window| { + if let Some((display, surface)) = x::get_window_x11(window) { + unsafe { + x::change_property( + &display, + &surface, + "_NET_WM_WINDOW_TYPE", + x::PropMode::Replace, + &[x::Atom::new(&display, "_NET_WM_WINDOW_TYPE_DOCK").unwrap()], + ); + } + let s = window.surface().unwrap(); + + s.connect_height_notify(glib::clone!(@weak window => move |s| { + if let Some((display, _surface)) = x::get_window_x11(&window) { + let width = s.width() * s.scale_factor(); + let height = s.height() * s.scale_factor(); + let monitor = display + .monitor_at_surface(s) + .expect("Failed to get Monitor"); + let Rectangle { + x: _, + y: _, + width: monitor_width, + height: monitor_height, + } = monitor.geometry(); + dbg!(monitor_width); + dbg!(monitor_height); + dbg!(width); + dbg!(height); + let w_conf = xproto::ConfigureWindowAux::default() + .x(monitor_width / 2 - width / 2) + .y(monitor_height - height); + let conn = X11_CONN.get().expect("Failed to get X11_CONN"); + + let x11surface = gdk4_x11::X11Surface::xid( + &s.clone().downcast::() + .expect("Failed to downcast Surface to X11Surface"), + ); + conn.configure_window( + x11surface.try_into().expect("Failed to convert XID"), + &w_conf, + ) + .expect("failed to configure window..."); + conn.flush().expect("failed to flush"); + + } else { + println!("failed to get X11 window"); + } + })); + } else { + println!("failed to get X11 window"); + } + }); + } + + fn setup_factory(&self) { + let factory = SignalListItemFactory::new(); + // factory.connect_setup(move |_, list_item| { + // let row = ApplicationRow::new():q ; + // list_item.set_child(Some(&row)) + // }); + // factory.connect_bind(move |_, list_item| { + // let application_object = list_item + // .item() + // .expect("The item has to exist.") + // .downcast::() + // .expect("The item has to be an `ApplicationObject`"); + // let row = list_item + // .child() + // .expect("The list item child needs to exist.") + // .downcast::() + // .expect("The list item type needs to be `ApplicationRow`"); + // if list_item.position() < 9 { + // row.set_shortcut(list_item.position() + 1); + // } + + // row.set_app_info(application_object); + // }); + // Set the factory of the list view + let imp = imp::Window::from_instance(self); + imp.list_view.set_factory(Some(&factory)); + } +} diff --git a/examples/dock/window/window.ui b/examples/dock/window/window.ui new file mode 100644 index 00000000..481cd2ca --- /dev/null +++ b/examples/dock/window/window.ui @@ -0,0 +1,19 @@ + + + +