-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improv: Watch shortcut changes safely
- Loading branch information
Showing
5 changed files
with
91 additions
and
102 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
use gio::prelude::*; | ||
use gtk::prelude::*; | ||
use std::collections::HashMap; | ||
use std::rc::Rc; | ||
|
||
const LAPTOP_DARK: &[u8] = include_bytes!("../assets/laptop-dark.svg"); | ||
const DISPLAY_DARK: &[u8] = include_bytes!("../assets/display-dark.svg"); | ||
|
@@ -199,6 +199,45 @@ pub enum Event { | |
CloseWindow, | ||
} | ||
|
||
pub struct State { | ||
// Holds the schema settings that are to be connected later | ||
pub registration: HashMap<String, (gio::Settings, HashMap<String, gtk::Box>)>, | ||
} | ||
|
||
impl Default for State { | ||
fn default() -> Self { | ||
Self { | ||
registration: HashMap::default(), | ||
} | ||
} | ||
} | ||
|
||
impl State { | ||
/// Register an intention to watch a key on a schema for changes | ||
pub fn register(&mut self, schema: String, settings: gio::Settings, key: String, shortcuts: gtk::Box) { | ||
self.registration.entry(schema) | ||
.or_insert_with(|| (settings, HashMap::new())) | ||
.1 | ||
.insert(key, shortcuts); | ||
} | ||
|
||
/// Watch for changes to keys on schemas, with one signal per `gio::Settings` | ||
pub fn connect_schemas(&mut self) { | ||
for (_, (settings, keys)) in self.registration.drain() { | ||
settings.connect_changed(move |settings, changed| { | ||
if let Some(widget) = keys.get(changed) { | ||
widget.foreach(|w| widget.remove(w)); | ||
for shortcut in settings.get_strv(changed) { | ||
widget.add(>k::ShortcutLabel::new(&shortcut)); | ||
} | ||
widget.show_all(); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
|
||
pub struct Section { | ||
pub header: &'static str, | ||
pub shortcuts: &'static [Shortcut], | ||
|
@@ -242,15 +281,14 @@ pub fn main(app: >k::Application) { | |
); | ||
} | ||
|
||
// let laptop = &svg_draw_area(LAPTOP_DARK, 300, 230); | ||
// let display = &svg_draw_area(DISPLAY_DARK, 300, 300); | ||
let mut state = State::default(); | ||
let shortcuts_section = shortcuts_section(&mut state); | ||
state.connect_schemas(); | ||
|
||
let shortcuts = cascade! { | ||
gtk::Box::new(gtk::Orientation::Vertical, 24); | ||
..set_border_width(8); | ||
//..add(&legend()); | ||
..add(&shortcuts_section()); | ||
//..add(&settings_reference()); | ||
..add(&shortcuts_section); | ||
}; | ||
|
||
let scroller = cascade! { | ||
|
@@ -264,7 +302,6 @@ pub fn main(app: >k::Application) { | |
let content = cascade! { | ||
gtk::Box::new(gtk::Orientation::Vertical, 24); | ||
..set_border_width(8); | ||
//..add(&demo_section(&laptop, display)); | ||
..add(&scroller); | ||
}; | ||
|
||
|
@@ -365,7 +402,7 @@ fn settings_reference() -> gtk::Box { | |
container | ||
} | ||
|
||
fn shortcuts_section() -> gtk::FlowBox { | ||
fn shortcuts_section(state: &mut State) -> gtk::FlowBox { | ||
let key_sg = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal); | ||
|
||
let container = cascade! { | ||
|
@@ -376,17 +413,9 @@ fn shortcuts_section() -> gtk::FlowBox { | |
..set_column_spacing(12); | ||
}; | ||
|
||
let event_handler: Rc<dyn Fn(>k::EventBox, Event)> = Rc::new(|widget, event| { | ||
println!("clicked {:?}", event); | ||
}); | ||
|
||
let mut settings_map = HashMap::new(); | ||
let iter = COLUMNS.iter().flat_map(|i| i.iter()).map(|section| { | ||
let section = cascade! { | ||
crate::widgets::Section::new(&key_sg, section, &event_handler, &mut settings_map); | ||
}; | ||
|
||
section | ||
crate::widgets::Section::new(&key_sg, section, state, &mut settings_map) | ||
}); | ||
|
||
for widget in iter { | ||
|
@@ -396,27 +425,19 @@ fn shortcuts_section() -> gtk::FlowBox { | |
container | ||
} | ||
|
||
/* | ||
fn svg_draw_area(svg: &[u8], width: i32, height: i32) -> gtk::DrawingArea { | ||
let drawing_area = gtk::DrawingArea::new(); | ||
let opt = resvg::Options::default(); | ||
let tree = resvg::usvg::Tree::from_data(svg, &opt.usvg).unwrap(); | ||
drawing_area.connect_draw(move |w, cr| { | ||
let screen = resvg::ScreenSize::new( | ||
w.get_allocated_width() as u32, | ||
w.get_allocated_height() as u32, | ||
pub fn open_schema(schema: &str) -> gio::Settings { | ||
if schema == "org.gnome.shell.extensions.pop-shell" { | ||
let settings_schema = gio::SettingsSchemaSource::from_directory( | ||
"/usr/share/gnome-shell/extensions/[email protected]/schemas", | ||
None, | ||
false | ||
).unwrap().lookup(schema, false).unwrap(); | ||
gio::Settings::new_full::<gio::SettingsBackend>( | ||
&settings_schema, | ||
None, | ||
None, | ||
) | ||
.unwrap(); | ||
resvg::backend_cairo::render_to_canvas(&tree, &opt, screen, cr); | ||
gtk::Inhibit(false) | ||
}); | ||
drawing_area.set_size_request(width, height); | ||
drawing_area | ||
} | ||
*/ | ||
} else { | ||
gio::Settings::new(schema) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,13 @@ | ||
use crate::{ | ||
app::{Event, Schema, Section as SectionInfo}, | ||
app::{Schema, Section as SectionInfo}, | ||
misc::keycap, | ||
}; | ||
|
||
use gio::prelude::*; | ||
use glib::clone; | ||
use glib::translate::{FromGlibPtrContainer, ToGlibPtr}; | ||
use gtk::prelude::*; | ||
use std::collections::HashMap; | ||
use std::rc::Rc; | ||
|
||
use crate::app::State; | ||
|
||
#[derive(AsRef, Deref)] | ||
#[as_ref] | ||
|
@@ -19,7 +18,7 @@ impl Section { | |
pub fn new( | ||
key_sg: >k::SizeGroup, | ||
section: &SectionInfo, | ||
func: &Rc<dyn Fn(>k::EventBox, Event)>, | ||
state: &mut State, | ||
settings_map: &mut HashMap<&'static str, gio::Settings>, | ||
) -> Self { | ||
let label = gtk::LabelBuilder::new() | ||
|
@@ -40,55 +39,6 @@ impl Section { | |
|
||
key_sg.add_widget(&keys); | ||
|
||
match shortcut.schema { | ||
Schema::GSettings { schema, key } => { | ||
let settings = | ||
settings_map.entry(schema).or_insert_with(|| { | ||
if schema == "org.gnome.shell.extensions.pop-shell" { | ||
let settings_schema = gio::SettingsSchemaSource::from_directory( | ||
"/usr/share/gnome-shell/extensions/[email protected]/schemas", | ||
None, | ||
false | ||
).unwrap().lookup(schema, false).unwrap(); | ||
gio::Settings::new_full::<gio::SettingsBackend>( | ||
&settings_schema, | ||
None, | ||
None, | ||
) | ||
} else { | ||
gio::Settings::new(schema) | ||
} | ||
}); | ||
|
||
for i in settings.get_strv(key) { | ||
keys.add(>k::ShortcutLabel::new(&i)); | ||
//let (keysym, mask) = gtk::accelerator_parse(&i); | ||
//keys.add(&keycap(>k::accelerator_get_label(keysym, mask).unwrap())); | ||
} | ||
let action = settings.create_action(key).unwrap(); | ||
action.connect_property_state_notify(clone!(@weak keys => @default-panic, move |action| { | ||
let state = action.get_state().unwrap(); | ||
let strv: Vec<glib::GString> = unsafe { FromGlibPtrContainer::from_glib_container(glib_sys::g_variant_get_strv(state.to_glib_none().0, std::ptr::null_mut())) }; | ||
keys.foreach(|w| keys.remove(w)); | ||
for i in strv { | ||
keys.add(>k::ShortcutLabel::new(&i)); | ||
//let (keysym, mask) = gtk::accelerator_parse(&i); | ||
//keys.add(&keycap(>k::accelerator_get_label(keysym, mask).unwrap())); | ||
} | ||
keys.show_all(); | ||
|
||
})); | ||
|
||
// Trick so that `action` is freed when the `keys` GObject is freed | ||
unsafe { keys.set_data("action", action) }; | ||
} | ||
Schema::Hardcoded(binding) => { | ||
binding.iter().for_each(|binding| { | ||
keys.add(&keycap(binding)); | ||
}); | ||
} | ||
} | ||
|
||
let event_box = gtk::EventBoxBuilder::new() | ||
.can_focus(false) | ||
.hexpand(true) | ||
|
@@ -101,10 +51,28 @@ impl Section { | |
..add(>k::Label::new(shortcut.description.into())); | ||
}); | ||
|
||
event_box.connect_button_press_event(enclose!((func) move |event_box, _| { | ||
func(event_box, shortcut.event); | ||
event_box.connect_button_press_event(move |_, _| { | ||
// TODO: Someday handle click event | ||
gtk::Inhibit(true) | ||
})); | ||
}); | ||
|
||
match shortcut.schema { | ||
Schema::GSettings { schema, key } => { | ||
let settings = settings_map.entry(schema) | ||
.or_insert_with(|| crate::app::open_schema(schema)); | ||
|
||
for i in settings.get_strv(key) { | ||
keys.add(>k::ShortcutLabel::new(&i)); | ||
} | ||
|
||
state.register(schema.into(), settings.clone(), key.into(), keys); | ||
} | ||
Schema::Hardcoded(binding) => { | ||
binding.iter().for_each(|binding| { | ||
keys.add(&keycap(binding)); | ||
}); | ||
} | ||
} | ||
|
||
bindings.add(&event_box); | ||
} | ||
|