From 9273ea91e87f45845c7c3242d7d33e4a524b949a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 19 Jan 2022 16:00:02 -0500 Subject: [PATCH] initial support for app library --- Cargo.toml | 2 +- examples/app_library/app_grid/mod.rs | 8 +- examples/app_library/group_grid/mod.rs | 42 ++--- examples/app_library/main.rs | 7 +- examples/app_library/window/imp.rs | 20 +-- examples/app_library/window/mod.rs | 212 +++++++++++------------ examples/app_library/window_inner/mod.rs | 20 +-- src/wayland.rs | 14 +- 8 files changed, 154 insertions(+), 171 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 806fd968..6d33bdd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" cascade = "1.0.0" derivative = "2.2.0" gdk4 = "0.4.4" -gdk4-wayland = { version = "0.4.2", optional = true } +gdk4-wayland = { version = "0.4.2", features = [ "wayland_crate" ], optional = true } gdk4-x11 = { version = "0.4.2", features = [ "xlib" ] } gio = "0.15.2" gobject-sys = "0.15.1" diff --git a/examples/app_library/app_grid/mod.rs b/examples/app_library/app_grid/mod.rs index 5642161d..b57d1767 100644 --- a/examples/app_library/app_grid/mod.rs +++ b/examples/app_library/app_grid/mod.rs @@ -131,25 +131,19 @@ impl AppGrid { let app_grid_view = &imp.app_grid_view.get().unwrap(); app_grid_view.connect_activate(move |list_view, i| { - let window = list_view - .root() - .unwrap() - .downcast::() - .unwrap(); // on activation change the group filter model to use the app names, and category println!("selected app {}", i); // Launch the application when an item of the list is activated let model = list_view.model().unwrap(); if let Some(item) = model.item(i) { let app_info = item.downcast::().unwrap(); - let context = window.display().app_launch_context(); + let context = list_view.display().app_launch_context(); if let Err(err) = app_info.launch(&[], Some(&context)) { gtk4::MessageDialog::builder() .text(&format!("Failed to start {}", app_info.name())) .secondary_text(&err.to_string()) .message_type(gtk4::MessageType::Error) .modal(true) - .transient_for(&window) .build() .show(); } diff --git a/examples/app_library/group_grid/mod.rs b/examples/app_library/group_grid/mod.rs index 76cc43b9..88eedac8 100644 --- a/examples/app_library/group_grid/mod.rs +++ b/examples/app_library/group_grid/mod.rs @@ -150,7 +150,7 @@ impl GroupGrid { let self_clone = self.clone(); group_grid_view.connect_activate(move |group_grid_view, i| { // on activation change the group filter model to use the app names, and category - let window = group_grid_view.root().unwrap().downcast::().unwrap(); + // let window = group_grid_view.root().unwrap().downcast::().unwrap(); println!("grid view activated. {}", i); let group_model = group_grid_view.model().unwrap().downcast::().unwrap() .model() @@ -180,15 +180,15 @@ impl GroupGrid { .resizable(false) .use_header_bar(true.into()) .destroy_with_parent(true) - .transient_for(&window) + // .transient_for(&window) .title("New App Group") .child(&vbox) .build(); - let app = window - .application() - .expect("could not get application from window"); + // let app = window + // .application() + // .expect("could not get application from window"); - dialog.set_application(Some(&app)); + // dialog.set_application(Some(&app)); dialog.add_buttons(&[ ("Apply", gtk4::ResponseType::Apply), ("Cancel", gtk4::ResponseType::Cancel), @@ -215,20 +215,20 @@ impl GroupGrid { dialog.emit_close(); }), ); - dialog.connect_is_active_notify(move |win| { - let app = win - .application() - .expect("could not get application from window"); - let active_window = app - .active_window() - .expect("no active window available, closing app library."); - dbg!(&active_window); - if win == &active_window && !win.is_active() { - println!("no focus"); - // close top level window - window.close(); - } - }); + // dialog.connect_is_active_notify(move |win| { + // let app = win + // .application() + // .expect("could not get application from window"); + // let active_window = app + // .active_window() + // .expect("no active window available, closing app library."); + // dbg!(&active_window); + // if win == &active_window && !win.is_active() { + // println!("no focus"); + // // close top level window + // window.close(); + // } + // }); dialog.show(); return; }; @@ -257,7 +257,7 @@ impl GroupGrid { } }); self_clone - .emit_by_name::("group-changed", &[&new_filter]); + .emit_by_name::<()>("group-changed", &[&new_filter]); }); } diff --git a/examples/app_library/main.rs b/examples/app_library/main.rs index f68d05d7..e6ff850c 100644 --- a/examples/app_library/main.rs +++ b/examples/app_library/main.rs @@ -11,6 +11,7 @@ mod grid_item; mod group_grid; mod utils; mod window; +mod window_inner; fn main() { let app = gtk4::Application::new(Some("com.cosmic.app_library"), Default::default()); @@ -40,7 +41,9 @@ fn load_css() { fn build_ui(app: >k4::Application) { // Create a new custom window and show it - let window = AppLibraryWindow::new(app); + let display = Display::default().unwrap(); + window::create(app, display.monitors().item(0).unwrap().downcast().unwrap()); + // let window = AppLibraryWindow::new(app); - window.show(); + // window.show(); } diff --git a/examples/app_library/window/imp.rs b/examples/app_library/window/imp.rs index 7bafa232..657e12c6 100644 --- a/examples/app_library/window/imp.rs +++ b/examples/app_library/window/imp.rs @@ -1,18 +1,13 @@ -use glib::signal::Inhibit; +use crate::window_inner::AppLibraryWindowInner; use gtk4::glib; use gtk4::subclass::prelude::*; -use gtk4::SearchEntry; use once_cell::sync::OnceCell; -use crate::app_grid::AppGrid; -use crate::group_grid::GroupGrid; - // Object holding the state #[derive(Default)] + pub struct AppLibraryWindow { - pub entry: OnceCell, - pub app_grid: OnceCell, - pub group_grid: OnceCell, + pub(super) inner: OnceCell, } // The central trait for subclassing a GObject @@ -31,14 +26,7 @@ impl ObjectImpl for AppLibraryWindow {} impl WidgetImpl for AppLibraryWindow {} // Trait shared by all windows -impl WindowImpl for AppLibraryWindow { - fn close_request(&self, window: &Self::Type) -> Inhibit { - let imp = AppLibraryWindow::from_instance(window); - imp.group_grid.get().unwrap().store_data(); - // Pass close request on to the parent - self.parent_close_request(window) - } -} +impl WindowImpl for AppLibraryWindow {} // Trait shared by all application impl ApplicationWindowImpl for AppLibraryWindow {} diff --git a/examples/app_library/window/mod.rs b/examples/app_library/window/mod.rs index 3b6d33d2..b5365e41 100644 --- a/examples/app_library/window/mod.rs +++ b/examples/app_library/window/mod.rs @@ -1,19 +1,91 @@ +use std::rc::Rc; + use crate::app_grid::AppGrid; use crate::group_grid::GroupGrid; +use crate::window_inner::AppLibraryWindowInner; use cascade::cascade; +use gdk4::subclass::prelude::ObjectSubclassExt; use gdk4_x11::X11Display; use glib::Object; use gtk4::prelude::*; -use gtk4::subclass::prelude::*; use gtk4::Align; use gtk4::Application; +use gtk4::ApplicationWindow; use gtk4::Box; use gtk4::CustomFilter; +use gtk4::Inhibit; use gtk4::Orientation; use gtk4::SearchEntry; use gtk4::Separator; -use gtk4::{gio, glib}; +use gtk4::{gdk, gio, glib}; use libcosmic::x; +use once_cell::sync::OnceCell; + +pub fn create(app: &Application, monitor: gdk::Monitor) { + //quit shortcut + app.set_accels_for_action("win.quit", &["W", "Escape"]); + + #[cfg(feature = "layer-shell")] + if let Some(wayland_monitor) = monitor.downcast_ref() { + wayland_create(&app, wayland_monitor); + return; + } + + cascade! { + AppLibraryWindow::new(&app); + ..show(); + }; +} + +fn setup_shortcuts(window: &ApplicationWindow) { + let action_quit = gio::SimpleAction::new("quit", None); + action_quit.connect_activate(glib::clone!(@weak window => move |_, _| { + window.close(); + })); + window.add_action(&action_quit); + window.connect_is_active_notify(move |win| { + let app = win + .application() + .expect("could not get application from window"); + let active_window = app + .active_window() + .expect("no active window available, closing app library."); + dbg!(&active_window); + if win == &active_window && !win.is_active() { + win.close(); + } + }); +} + +#[cfg(feature = "layer-shell")] +fn wayland_create(app: &Application, monitor: &gdk4_wayland::WaylandMonitor) { + use libcosmic::wayland::{Anchor, Layer, LayerShellWindow}; + + let window = cascade! { + LayerShellWindow::new(Some(monitor), Layer::Top, ""); + ..set_width_request(1200); + ..set_height_request(800); + // ..set_title(Some("Cosmic App Library")); + // ..set_decorated(false); + ..add_css_class("root_window"); + ..set_anchor(Anchor::empty()); + ..show(); + }; + + let app_library = AppLibraryWindowInner::new(); + window.set_child(Some(&app_library)); + dbg!(&window); + window.show(); + // window.connect_close_request( + // glib::clone!(@strong app_library => @default-return Inhibit(false), move |_| { + // app_library.group_grid().unwrap().store_data(); + // Inhibit(false) + // }), + // ); + // setup_shortcuts(window.clone().upcast::()); + // XXX + unsafe { window.set_data("cosmic-app-hold", app.hold()) }; +} mod imp; @@ -26,12 +98,6 @@ glib::wrapper! { impl AppLibraryWindow { pub fn new(app: &Application) -> Self { - //quit shortcut - app.set_accels_for_action("win.quit", &["W", "Escape"]); - //launch shortcuts - for i in 1..10 { - app.set_accels_for_action(&format!("win.launch{}", i), &[&format!("{}", i)]); - } let self_: Self = Object::new(&[("application", app)]).expect("Failed to create `AppLibraryWindow`."); let imp = imp::AppLibraryWindow::from_instance(&self_); @@ -43,42 +109,46 @@ impl AppLibraryWindow { ..set_decorated(false); ..add_css_class("root_window"); }; - - let app_library = cascade! { - Box::new(Orientation::Vertical, 0); - ..add_css_class("app_library_container"); - }; + let app_library = AppLibraryWindowInner::new(); self_.set_child(Some(&app_library)); + imp.inner.set(app_library).unwrap(); + // let app_library = cascade! { + // Box::new(Orientation::Vertical, 0); + // ..add_css_class("app_library_container"); + // }; + // self_.set_child(Some(&app_library)); - let entry = cascade! { - SearchEntry::new(); - ..set_width_request(300); - ..set_halign(Align::Center); - ..set_margin_top(12); - ..set_margin_bottom(12); - ..set_placeholder_text(Some(" Type to search")); - }; - app_library.append(&entry); + // let entry = cascade! { + // SearchEntry::new(); + // ..set_width_request(300); + // ..set_halign(Align::Center); + // ..set_margin_top(12); + // ..set_margin_bottom(12); + // ..set_placeholder_text(Some(" Type to search")); + // }; + // app_library.append(&entry); - let app_grid = AppGrid::new(); - app_library.append(&app_grid); + // let app_grid = AppGrid::new(); + // app_library.append(&app_grid); - let separator = cascade! { - Separator::new(Orientation::Horizontal); - ..set_hexpand(true); - ..set_margin_bottom(12); - ..set_margin_top(12); - }; - app_library.append(&separator); + // let separator = cascade! { + // Separator::new(Orientation::Horizontal); + // ..set_hexpand(true); + // ..set_margin_bottom(12); + // ..set_margin_top(12); + // }; + // app_library.append(&separator); - let group_grid = GroupGrid::new(); - app_library.append(&group_grid); + // let group_grid = GroupGrid::new(); + // app_library.append(&group_grid); - imp.entry.set(entry).unwrap(); - imp.app_grid.set(app_grid).unwrap(); - imp.group_grid.set(group_grid).unwrap(); + // imp.entry.set(entry).unwrap(); + // imp.app_grid.set(app_grid).unwrap(); + // imp.group_grid.set(group_grid).unwrap(); Self::setup_callbacks(&self_); + setup_shortcuts(&self_.clone().upcast::()); + self_ } @@ -86,58 +156,6 @@ impl AppLibraryWindow { // Get state let imp = imp::AppLibraryWindow::from_instance(self); let window = self.clone().upcast::(); - let app_grid = &imp.app_grid.get().unwrap(); - let group_grid = &imp.group_grid.get().unwrap(); - - let entry = &imp.entry.get().unwrap(); - - group_grid.connect_local( - "group-changed", - false, - glib::clone!(@weak app_grid => @default-return None, move |args| { - let new_filter = args[1].get::().unwrap(); - app_grid.set_group_filter(&new_filter); - None - }), - ); - - entry.connect_changed( - glib::clone!(@weak app_grid => move |search: >k4::SearchEntry| { - let search_text = search.text().to_string().to_lowercase(); - let new_filter: gtk4::CustomFilter = gtk4::CustomFilter::new(move |obj| { - let search_res = obj.downcast_ref::() - .expect("The Object needs to be of type AppInfo"); - search_res.name().to_string().to_lowercase().contains(&search_text) - }); - let search_text = search.text().to_string().to_lowercase(); - let new_sorter: gtk4::CustomSorter = gtk4::CustomSorter::new(move |obj1, obj2| { - let app_info1 = obj1.downcast_ref::().unwrap(); - let app_info2 = obj2.downcast_ref::().unwrap(); - if search_text == "" { - return app_info1 - .name() - .to_lowercase() - .cmp(&app_info2.name().to_lowercase()) - .into(); - } - - let i_1 = app_info1.name().to_lowercase().find(&search_text); - let i_2 = app_info2.name().to_lowercase().find(&search_text); - match (i_1, i_2) { - (Some(i_1), Some(i_2)) => i_1.cmp(&i_2).into(), - (Some(_), None) => std::cmp::Ordering::Less.into(), - (None, Some(_)) => std::cmp::Ordering::Greater.into(), - _ => app_info1 - .name() - .to_lowercase() - .cmp(&app_info2.name().to_lowercase()) - .into() - } - }); - app_grid.set_search_filter(&new_filter); - app_grid.set_app_sorter(&new_sorter); - }), - ); window.connect_realize(move |window| { if let Some((display, surface)) = x::get_window_x11(window) { @@ -192,23 +210,5 @@ impl AppLibraryWindow { println!("failed to get X11 window"); } }); - - let action_quit = gio::SimpleAction::new("quit", None); - action_quit.connect_activate(glib::clone!(@weak window => move |_, _| { - window.close(); - })); - self.add_action(&action_quit); - window.connect_is_active_notify(move |win| { - let app = win - .application() - .expect("could not get application from window"); - let active_window = app - .active_window() - .expect("no active window available, closing app library."); - dbg!(&active_window); - if win == &active_window && !win.is_active() { - win.close(); - } - }); } } diff --git a/examples/app_library/window_inner/mod.rs b/examples/app_library/window_inner/mod.rs index ca73de11..2972aef8 100644 --- a/examples/app_library/window_inner/mod.rs +++ b/examples/app_library/window_inner/mod.rs @@ -77,17 +77,15 @@ impl AppLibraryWindowInner { let entry = &imp.entry.get().unwrap(); - group_grid - .connect_local( - "group-changed", - false, - glib::clone!(@weak app_grid => @default-return None, move |args| { - let new_filter = args[1].get::().unwrap(); - app_grid.set_group_filter(&new_filter); - None - }), - ) - .unwrap(); + group_grid.connect_local( + "group-changed", + false, + glib::clone!(@weak app_grid => @default-return None, move |args| { + let new_filter = args[1].get::().unwrap(); + app_grid.set_group_filter(&new_filter); + None + }), + ); entry.connect_changed( glib::clone!(@weak app_grid => move |search: >k4::SearchEntry| { diff --git a/src/wayland.rs b/src/wayland.rs index 065147e5..a6a93d72 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -5,7 +5,7 @@ use gdk4_wayland::prelude::*; use gtk4::{ cairo, gdk, glib::{self, clone, subclass::prelude::*, translate::*}, - gsk::{self, traits::RendererExt}, + gsk::{self, traits::GskRendererExt}, prelude::*, subclass::prelude::*, }; @@ -497,7 +497,7 @@ pub struct GtkRootInterface { unsafe extern "C" fn get_surface(native: *mut gtk4::ffi::GtkNative) -> *mut gdk::ffi::GdkSurface { let instance = &*(native as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); imp.surface.borrow().as_ref().map_or(ptr::null_mut(), |x| { x.upcast_ref::().to_glib_none().0 }) @@ -505,7 +505,7 @@ unsafe extern "C" fn get_surface(native: *mut gtk4::ffi::GtkNative) -> *mut gdk: unsafe extern "C" fn get_renderer(native: *mut gtk4::ffi::GtkNative) -> *mut gsk::ffi::GskRenderer { let instance = &*(native as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); imp.renderer .borrow() .as_ref() @@ -530,7 +530,7 @@ unsafe extern "C" fn layout(native: *mut gtk4::ffi::GtkNative, width: c_int, hei unsafe extern "C" fn get_display(root: *mut gtk4::ffi::GtkRoot) -> *mut gdk::ffi::GdkDisplay { let instance = &*(root as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); imp.display.upcast_ref::().to_glib_none().0 } @@ -538,13 +538,13 @@ unsafe extern "C" fn get_constraint_solver( root: *mut gtk4::ffi::GtkRoot, ) -> *mut GtkConstraintSolver { let instance = &*(root as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); imp.constraint_solver.to_glib_none().0 } unsafe extern "C" fn get_focus(root: *mut gtk4::ffi::GtkRoot) -> *mut gtk4::ffi::GtkWidget { let instance = &*(root as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); imp.focus_widget .borrow() .as_ref() @@ -554,7 +554,7 @@ unsafe extern "C" fn get_focus(root: *mut gtk4::ffi::GtkRoot) -> *mut gtk4::ffi: unsafe extern "C" fn set_focus(root: *mut gtk4::ffi::GtkRoot, focus: *mut gtk4::ffi::GtkWidget) { // TODO: `GtkWindow` does more here let instance = &*(root as *mut ::Instance); - let imp = instance.impl_(); + let imp = instance.imp(); *imp.focus_widget.borrow_mut() = if focus.is_null() { None } else {