diff --git a/examples/app_library/app_grid/mod.rs b/examples/app_library/app_grid/mod.rs index b57d1767..5a51e780 100644 --- a/examples/app_library/app_grid/mod.rs +++ b/examples/app_library/app_grid/mod.rs @@ -64,7 +64,7 @@ impl AppGrid { .iter_mut() .for_each(|xdg_data_path| { xdg_data_path.push("applications"); - dbg!(&xdg_data_path); + // dbg!(&xdg_data_path); if let Ok(dir_iter) = std::fs::read_dir(xdg_data_path) { dir_iter.for_each(|dir_entry| { if let Ok(dir_entry) = dir_entry { @@ -74,10 +74,10 @@ impl AppGrid { if app_info.should_show() { app_model.append(&app_info) } else { - println!("Ignoring {}", path); + // println!("Ignoring {}", path); } } else { - println!("error loading {}", path); + // println!("error loading {}", path); } } } @@ -132,7 +132,7 @@ impl AppGrid { app_grid_view.connect_activate(move |list_view, i| { // on activation change the group filter model to use the app names, and category - println!("selected app {}", i); + // 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) { diff --git a/examples/app_library/group_grid/mod.rs b/examples/app_library/group_grid/mod.rs index 88eedac8..3acece30 100644 --- a/examples/app_library/group_grid/mod.rs +++ b/examples/app_library/group_grid/mod.rs @@ -1,12 +1,11 @@ use cascade::cascade; use glib::Object; use glib::{FromVariant, Variant}; +use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::{ gio, glib, Dialog, Entry, GridView, Label, PolicyType, ScrolledWindow, SignalListItemFactory, - Window, }; -use gtk4::{prelude::*, CustomFilter}; use std::fs::File; use crate::app_group::AppGroup; diff --git a/examples/app_library/window/mod.rs b/examples/app_library/window/mod.rs index b5365e41..5eaa38fe 100644 --- a/examples/app_library/window/mod.rs +++ b/examples/app_library/window/mod.rs @@ -1,29 +1,17 @@ -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::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::{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"]); + app.set_accels_for_action("app.quit", &["W", "Escape"]); + setup_shortcuts(app); #[cfg(feature = "layer-shell")] if let Some(wayland_monitor) = monitor.downcast_ref() { @@ -37,36 +25,25 @@ pub fn create(app: &Application, monitor: gdk::Monitor) { }; } -fn setup_shortcuts(window: &ApplicationWindow) { +fn setup_shortcuts(app: &Application) { let action_quit = gio::SimpleAction::new("quit", None); - action_quit.connect_activate(glib::clone!(@weak window => move |_, _| { - window.close(); + action_quit.connect_activate(glib::clone!(@weak app => move |_, _| { + app.quit(); })); - 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(); - } - }); + app.add_action(&action_quit); } #[cfg(feature = "layer-shell")] fn wayland_create(app: &Application, monitor: &gdk4_wayland::WaylandMonitor) { - use libcosmic::wayland::{Anchor, Layer, LayerShellWindow}; + use libcosmic::wayland::{Anchor, KeyboardInteractivity, Layer, LayerShellWindow}; let window = cascade! { LayerShellWindow::new(Some(monitor), Layer::Top, ""); - ..set_width_request(1200); - ..set_height_request(800); + ..set_width_request(800); + ..set_height_request(600); // ..set_title(Some("Cosmic App Library")); // ..set_decorated(false); + ..set_keyboard_interactivity(KeyboardInteractivity::OnDemand); ..add_css_class("root_window"); ..set_anchor(Anchor::empty()); ..show(); @@ -75,13 +52,13 @@ fn wayland_create(app: &Application, monitor: &gdk4_wayland::WaylandMonitor) { let app_library = AppLibraryWindowInner::new(); window.set_child(Some(&app_library)); dbg!(&window); + window.connect_is_active_notify(glib::clone!(@weak app => move |w| { + if !w.is_active() { + app.quit(); + } + })); 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()) }; @@ -112,52 +89,18 @@ impl AppLibraryWindow { 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 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 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(); Self::setup_callbacks(&self_); - setup_shortcuts(&self_.clone().upcast::()); self_ } fn setup_callbacks(&self) { // Get state - let imp = imp::AppLibraryWindow::from_instance(self); let window = self.clone().upcast::(); window.connect_realize(move |window| { + println!("gtk window setup"); if let Some((display, surface)) = x::get_window_x11(window) { // ignore all x11 errors... let xdisplay = display @@ -210,5 +153,25 @@ impl AppLibraryWindow { println!("failed to get X11 window"); } }); + window.connect_is_active_notify(move |win| { + if !win.is_active() { + win.close(); + } + }); + // let focus_ctrl = gtk4::EventControllerFocus::builder() + // .propagation_phase(gtk4::PropagationPhase::Capture) + // .propagation_limit(gtk4::PropagationLimit::None) + // .build(); + // focus_ctrl.connect_leave(move |_| { + // println!("Exiting"); + // }); + // focus_ctrl.connect_enter(move |_| { + // println!("hewwo"); + // }); + // focus_ctrl.connect_contains_focus_notify(move |_| { + // println!("hewwo"); + // }); + + // window.add_controller(&focus_ctrl); } } diff --git a/src/wayland.rs b/src/wayland.rs index a6a93d72..9f2fea33 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -4,11 +4,17 @@ use derivative::Derivative; use gdk4_wayland::prelude::*; use gtk4::{ cairo, gdk, - glib::{self, clone, subclass::prelude::*, translate::*}, + glib::{ + self, clone, + subclass::{prelude::*, Signal}, + translate::*, + ParamFlags, ParamSpec, ParamSpecBoolean, SignalHandlerId, Value, + }, gsk::{self, traits::GskRendererExt}, prelude::*, subclass::prelude::*, }; +use once_cell::sync::Lazy; use std::{ cell::{Cell, RefCell}, os::raw::c_int, @@ -110,6 +116,7 @@ pub struct LayerShellWindowInner { keyboard_interactivity: Cell, namespace: DerefCell, focus_widget: RefCell>, + is_active: Rc>, } #[glib::object_subclass] @@ -128,6 +135,58 @@ impl ObjectImpl for LayerShellWindowInner { obj.add_css_class("background"); } + + fn properties() -> &'static [ParamSpec] { + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ParamSpecBoolean::new( + // Name + "is-active", + // Nickname + "is-active", + // Short description + "Whether the window has keyboard focus", + // Default value + false, + // 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() { + "is-active" => { + let is_active = value + .get() + .expect("The is_active property needs to be of type `bool`"); + self.is_active.replace(is_active); + } + _ => unimplemented!(), + } + } + + fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value { + match pspec.name() { + "is-active" => self.is_active.get().to_value(), + _ => unimplemented!(), + } + } + + fn signals() -> &'static [Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![Signal::builder( + // Signal name + "is-active-notify", + // Types of the values which will be sent to the signal handler + &[bool::static_type().into()], + // Type of the value the signal handler sends back + <()>::static_type().into(), + ) + .build()] + }); + SIGNALS.as_ref() + } } impl WidgetImpl for LayerShellWindowInner { @@ -157,10 +216,17 @@ impl WidgetImpl for LayerShellWindowInner { }; true }); - surface.connect_event(|_, event| { - unsafe { gtk_main_do_event(event.to_glib_none().0) }; - true - }); + surface.connect_event( + glib::clone!(@weak widget => @default-return true, move |_, event| { + if event.event_type() == gdk4::EventType::FocusChange { + let is_active = event.downcast_ref::().unwrap().is_in(); + widget.set_property("is-active", is_active); + widget.emit_by_name::<()>("is-active-notify", &[&is_active]); + } + unsafe { gtk_main_do_event(event.to_glib_none().0) }; + true + }), + ); self.parent_realize(widget); @@ -306,6 +372,17 @@ impl LayerShellWindow { *child = w.map(|x| x.clone().upcast()); } + pub fn is_active(&self) -> bool { + self.property::("is-active") + } + + pub fn connect_is_active_notify(&self, f: F) -> SignalHandlerId { + self.connect_local("is-active-notify", false, move |args| { + f(&args[0].get::().unwrap()); + None + }) + } + fn layer_shell_init(&self, surface: &WaylandCustomSurface) { let width = self.measure(gtk4::Orientation::Horizontal, -1).1; let height = self.measure(gtk4::Orientation::Vertical, width).1;