Merge branch 'settings' into 'master'
Implement the word-of-mouth layout selection See merge request Librem5/squeekboard!260
This commit is contained in:
@ -30,7 +30,7 @@ Most common testing is done in CI. Occasionally, and for each release, do perfor
|
||||
- the application draws correctly
|
||||
- it shows when relevant
|
||||
- it changes layouts
|
||||
- it changes levels
|
||||
- it changes views
|
||||
|
||||
Testing with an application:
|
||||
|
||||
@ -50,10 +50,8 @@ Testing layouts:
|
||||
Layouts can be selected using the GNOME Settings application.
|
||||
|
||||
```
|
||||
# define all available layouts
|
||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
||||
# choose the active layout
|
||||
$ gsettings set org.gnome.desktop.input-sources current 1
|
||||
# define all available layouts. First one is currently selected.
|
||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
|
||||
```
|
||||
|
||||
Coding
|
||||
|
||||
@ -207,22 +207,8 @@ static void
|
||||
settings_get_layout(GSettings *settings, char **type, char **layout)
|
||||
{
|
||||
GVariant *inputs = g_settings_get_value(settings, "sources");
|
||||
guint32 index;
|
||||
g_settings_get(settings, "current", "u", &index);
|
||||
|
||||
GVariantIter *iter;
|
||||
g_variant_get(inputs, "a(ss)", &iter);
|
||||
|
||||
for (unsigned i = 0;
|
||||
g_variant_iter_loop(iter, "(ss)", type, layout);
|
||||
i++) {
|
||||
if (i == index) {
|
||||
//printf("Found layout %s %s\n", *type, *layout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_variant_iter_free(iter);
|
||||
g_variant_unref(inputs);
|
||||
// current layout is always first
|
||||
g_variant_get_child(inputs, 0, "(ss)", type, layout);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -18,8 +18,11 @@ use std::io::Write;
|
||||
|
||||
mod variants {
|
||||
use glib;
|
||||
use glib::Variant;
|
||||
use glib_sys;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use glib::ToVariant;
|
||||
use glib::translate::FromGlibPtrFull;
|
||||
use glib::translate::ToGlibPtr;
|
||||
|
||||
@ -49,6 +52,44 @@ mod variants {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// "a(ss)" variant
|
||||
/// Rust doesn't allow implementing existing traits for existing types
|
||||
pub struct ArrayPairString(pub Vec<(String, String)>);
|
||||
|
||||
impl ToVariant for ArrayPairString {
|
||||
fn to_variant(&self) -> Variant {
|
||||
let tspec = "a(ss)".to_glib_none();
|
||||
let builder = unsafe {
|
||||
let vtype = glib_sys::g_variant_type_checked_(tspec.0);
|
||||
glib_sys::g_variant_builder_new(vtype)
|
||||
};
|
||||
let ispec = "(ss)".to_glib_none();
|
||||
for (a, b) in &self.0 {
|
||||
let a = a.to_glib_none();
|
||||
let b = b.to_glib_none();
|
||||
// string pointers are weak references
|
||||
// and will get silently invalidated
|
||||
// as soon as the source is out of scope
|
||||
{
|
||||
let a: *const c_char = a.0;
|
||||
let b: *const c_char = b.0;
|
||||
unsafe {
|
||||
glib_sys::g_variant_builder_add(
|
||||
builder,
|
||||
ispec.0,
|
||||
a, b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let ret = glib_sys::g_variant_builder_end(builder);
|
||||
glib_sys::g_variant_builder_unref(builder);
|
||||
glib::Variant::from_glib_full(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
||||
@ -88,12 +129,15 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
||||
fn set_layout(kind: String, name: String) {
|
||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||
let inputs = settings.get_value("sources").unwrap();
|
||||
let inputs = variants::get_tuples(inputs).into_iter();
|
||||
for (index, (ikind, iname)) in inputs.enumerate() {
|
||||
if (&ikind, &iname) == (&kind, &name) {
|
||||
settings.set_uint("current", index as u32);
|
||||
}
|
||||
}
|
||||
let current = (kind.clone(), name.clone());
|
||||
let inputs = variants::get_tuples(inputs).into_iter()
|
||||
.filter(|t| t != ¤t);
|
||||
let inputs = vec![(kind, name)].into_iter()
|
||||
.chain(inputs).collect();
|
||||
settings.set_value(
|
||||
"sources",
|
||||
&variants::ArrayPairString(inputs).to_variant()
|
||||
);
|
||||
settings.apply();
|
||||
}
|
||||
|
||||
@ -103,7 +147,6 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
||||
|
||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||
let inputs = settings.get_value("sources").unwrap();
|
||||
let current = settings.get_uint("current") as usize;
|
||||
let inputs = variants::get_tuples(inputs);
|
||||
|
||||
let input_names: Vec<&str> = inputs.iter()
|
||||
@ -152,21 +195,26 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
||||
height: position.width.floor() as i32,
|
||||
});
|
||||
|
||||
let initial_state = input_names[current].to_variant();
|
||||
let action = input_names.get(0).map(|current_name| {
|
||||
let current_name = current_name.to_variant();
|
||||
|
||||
let layout_action = gio::SimpleAction::new_stateful(
|
||||
"layout",
|
||||
Some(initial_state.type_()),
|
||||
&initial_state,
|
||||
Some(current_name.type_()),
|
||||
¤t_name,
|
||||
);
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
action_group.add_action(&layout_action);
|
||||
|
||||
menu.insert_action_group("popup", Some(&action_group));
|
||||
layout_action
|
||||
});
|
||||
|
||||
menu.bind_model(Some(&model), Some("popup"));
|
||||
|
||||
menu.connect_closed(move |_menu| {
|
||||
if let Some(layout_action) = &action {
|
||||
let state = match layout_action.get_state() {
|
||||
Some(v) => {
|
||||
let s = v.get::<String>().or_else(|| {
|
||||
@ -184,6 +232,7 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
||||
},
|
||||
};
|
||||
set_layout("xkb".into(), state.unwrap_or("us".into()));
|
||||
}
|
||||
});
|
||||
|
||||
menu.popup();
|
||||
|
||||
Reference in New Issue
Block a user