LayerShellWindow is-active

This commit is contained in:
Ashley Wulber 2022-01-21 11:54:55 -05:00
parent 9273ea91e8
commit df088dfe8e
4 changed files with 124 additions and 85 deletions

View file

@ -64,7 +64,7 @@ impl AppGrid {
.iter_mut() .iter_mut()
.for_each(|xdg_data_path| { .for_each(|xdg_data_path| {
xdg_data_path.push("applications"); 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) { if let Ok(dir_iter) = std::fs::read_dir(xdg_data_path) {
dir_iter.for_each(|dir_entry| { dir_iter.for_each(|dir_entry| {
if let Ok(dir_entry) = dir_entry { if let Ok(dir_entry) = dir_entry {
@ -74,10 +74,10 @@ impl AppGrid {
if app_info.should_show() { if app_info.should_show() {
app_model.append(&app_info) app_model.append(&app_info)
} else { } else {
println!("Ignoring {}", path); // println!("Ignoring {}", path);
} }
} else { } else {
println!("error loading {}", path); // println!("error loading {}", path);
} }
} }
} }
@ -132,7 +132,7 @@ impl AppGrid {
app_grid_view.connect_activate(move |list_view, i| { app_grid_view.connect_activate(move |list_view, i| {
// on activation change the group filter model to use the app names, and category // 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 // Launch the application when an item of the list is activated
let model = list_view.model().unwrap(); let model = list_view.model().unwrap();
if let Some(item) = model.item(i) { if let Some(item) = model.item(i) {

View file

@ -1,12 +1,11 @@
use cascade::cascade; use cascade::cascade;
use glib::Object; use glib::Object;
use glib::{FromVariant, Variant}; use glib::{FromVariant, Variant};
use gtk4::prelude::*;
use gtk4::subclass::prelude::*; use gtk4::subclass::prelude::*;
use gtk4::{ use gtk4::{
gio, glib, Dialog, Entry, GridView, Label, PolicyType, ScrolledWindow, SignalListItemFactory, gio, glib, Dialog, Entry, GridView, Label, PolicyType, ScrolledWindow, SignalListItemFactory,
Window,
}; };
use gtk4::{prelude::*, CustomFilter};
use std::fs::File; use std::fs::File;
use crate::app_group::AppGroup; use crate::app_group::AppGroup;

View file

@ -1,29 +1,17 @@
use std::rc::Rc;
use crate::app_grid::AppGrid;
use crate::group_grid::GroupGrid;
use crate::window_inner::AppLibraryWindowInner; use crate::window_inner::AppLibraryWindowInner;
use cascade::cascade; use cascade::cascade;
use gdk4::subclass::prelude::ObjectSubclassExt; use gdk4::subclass::prelude::ObjectSubclassExt;
use gdk4_x11::X11Display; use gdk4_x11::X11Display;
use glib::Object; use glib::Object;
use gtk4::prelude::*; use gtk4::prelude::*;
use gtk4::Align;
use gtk4::Application; 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 gtk4::{gdk, gio, glib};
use libcosmic::x; use libcosmic::x;
use once_cell::sync::OnceCell;
pub fn create(app: &Application, monitor: gdk::Monitor) { pub fn create(app: &Application, monitor: gdk::Monitor) {
//quit shortcut //quit shortcut
app.set_accels_for_action("win.quit", &["<primary>W", "Escape"]); app.set_accels_for_action("app.quit", &["<primary>W", "Escape"]);
setup_shortcuts(app);
#[cfg(feature = "layer-shell")] #[cfg(feature = "layer-shell")]
if let Some(wayland_monitor) = monitor.downcast_ref() { 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); let action_quit = gio::SimpleAction::new("quit", None);
action_quit.connect_activate(glib::clone!(@weak window => move |_, _| { action_quit.connect_activate(glib::clone!(@weak app => move |_, _| {
window.close(); app.quit();
})); }));
window.add_action(&action_quit); app.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")] #[cfg(feature = "layer-shell")]
fn wayland_create(app: &Application, monitor: &gdk4_wayland::WaylandMonitor) { 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! { let window = cascade! {
LayerShellWindow::new(Some(monitor), Layer::Top, ""); LayerShellWindow::new(Some(monitor), Layer::Top, "");
..set_width_request(1200); ..set_width_request(800);
..set_height_request(800); ..set_height_request(600);
// ..set_title(Some("Cosmic App Library")); // ..set_title(Some("Cosmic App Library"));
// ..set_decorated(false); // ..set_decorated(false);
..set_keyboard_interactivity(KeyboardInteractivity::OnDemand);
..add_css_class("root_window"); ..add_css_class("root_window");
..set_anchor(Anchor::empty()); ..set_anchor(Anchor::empty());
..show(); ..show();
@ -75,13 +52,13 @@ fn wayland_create(app: &Application, monitor: &gdk4_wayland::WaylandMonitor) {
let app_library = AppLibraryWindowInner::new(); let app_library = AppLibraryWindowInner::new();
window.set_child(Some(&app_library)); window.set_child(Some(&app_library));
dbg!(&window); dbg!(&window);
window.connect_is_active_notify(glib::clone!(@weak app => move |w| {
if !w.is_active() {
app.quit();
}
}));
window.show(); 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::<gtk4::ApplicationWindow>()); // setup_shortcuts(window.clone().upcast::<gtk4::ApplicationWindow>());
// XXX // XXX
unsafe { window.set_data("cosmic-app-hold", app.hold()) }; unsafe { window.set_data("cosmic-app-hold", app.hold()) };
@ -112,52 +89,18 @@ impl AppLibraryWindow {
let app_library = AppLibraryWindowInner::new(); let app_library = AppLibraryWindowInner::new();
self_.set_child(Some(&app_library)); self_.set_child(Some(&app_library));
imp.inner.set(app_library).unwrap(); 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_); Self::setup_callbacks(&self_);
setup_shortcuts(&self_.clone().upcast::<gtk4::ApplicationWindow>());
self_ self_
} }
fn setup_callbacks(&self) { fn setup_callbacks(&self) {
// Get state // Get state
let imp = imp::AppLibraryWindow::from_instance(self);
let window = self.clone().upcast::<gtk4::Window>(); let window = self.clone().upcast::<gtk4::Window>();
window.connect_realize(move |window| { window.connect_realize(move |window| {
println!("gtk window setup");
if let Some((display, surface)) = x::get_window_x11(window) { if let Some((display, surface)) = x::get_window_x11(window) {
// ignore all x11 errors... // ignore all x11 errors...
let xdisplay = display let xdisplay = display
@ -210,5 +153,25 @@ impl AppLibraryWindow {
println!("failed to get X11 window"); 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);
} }
} }

View file

@ -4,11 +4,17 @@ use derivative::Derivative;
use gdk4_wayland::prelude::*; use gdk4_wayland::prelude::*;
use gtk4::{ use gtk4::{
cairo, gdk, cairo, gdk,
glib::{self, clone, subclass::prelude::*, translate::*}, glib::{
self, clone,
subclass::{prelude::*, Signal},
translate::*,
ParamFlags, ParamSpec, ParamSpecBoolean, SignalHandlerId, Value,
},
gsk::{self, traits::GskRendererExt}, gsk::{self, traits::GskRendererExt},
prelude::*, prelude::*,
subclass::prelude::*, subclass::prelude::*,
}; };
use once_cell::sync::Lazy;
use std::{ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
os::raw::c_int, os::raw::c_int,
@ -110,6 +116,7 @@ pub struct LayerShellWindowInner {
keyboard_interactivity: Cell<KeyboardInteractivity>, keyboard_interactivity: Cell<KeyboardInteractivity>,
namespace: DerefCell<String>, namespace: DerefCell<String>,
focus_widget: RefCell<Option<gtk4::Widget>>, focus_widget: RefCell<Option<gtk4::Widget>>,
is_active: Rc<Cell<bool>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -128,6 +135,58 @@ impl ObjectImpl for LayerShellWindowInner {
obj.add_css_class("background"); obj.add_css_class("background");
} }
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<Vec<ParamSpec>> = 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<Vec<Signal>> = 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 { impl WidgetImpl for LayerShellWindowInner {
@ -157,10 +216,17 @@ impl WidgetImpl for LayerShellWindowInner {
}; };
true true
}); });
surface.connect_event(|_, event| { surface.connect_event(
unsafe { gtk_main_do_event(event.to_glib_none().0) }; glib::clone!(@weak widget => @default-return true, move |_, event| {
true if event.event_type() == gdk4::EventType::FocusChange {
}); let is_active = event.downcast_ref::<gdk4::FocusEvent>().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); self.parent_realize(widget);
@ -306,6 +372,17 @@ impl LayerShellWindow {
*child = w.map(|x| x.clone().upcast()); *child = w.map(|x| x.clone().upcast());
} }
pub fn is_active(&self) -> bool {
self.property::<bool>("is-active")
}
pub fn connect_is_active_notify<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
self.connect_local("is-active-notify", false, move |args| {
f(&args[0].get::<Self>().unwrap());
None
})
}
fn layer_shell_init(&self, surface: &WaylandCustomSurface) { fn layer_shell_init(&self, surface: &WaylandCustomSurface) {
let width = self.measure(gtk4::Orientation::Horizontal, -1).1; let width = self.measure(gtk4::Orientation::Horizontal, -1).1;
let height = self.measure(gtk4::Orientation::Vertical, width).1; let height = self.measure(gtk4::Orientation::Vertical, width).1;