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
|
- the application draws correctly
|
||||||
- it shows when relevant
|
- it shows when relevant
|
||||||
- it changes layouts
|
- it changes layouts
|
||||||
- it changes levels
|
- it changes views
|
||||||
|
|
||||||
Testing with an application:
|
Testing with an application:
|
||||||
|
|
||||||
@ -50,10 +50,8 @@ Testing layouts:
|
|||||||
Layouts can be selected using the GNOME Settings application.
|
Layouts can be selected using the GNOME Settings application.
|
||||||
|
|
||||||
```
|
```
|
||||||
# define all available layouts
|
# define all available layouts. First one is currently selected.
|
||||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
|
||||||
# choose the active layout
|
|
||||||
$ gsettings set org.gnome.desktop.input-sources current 1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Coding
|
Coding
|
||||||
|
|||||||
@ -207,22 +207,8 @@ static void
|
|||||||
settings_get_layout(GSettings *settings, char **type, char **layout)
|
settings_get_layout(GSettings *settings, char **type, char **layout)
|
||||||
{
|
{
|
||||||
GVariant *inputs = g_settings_get_value(settings, "sources");
|
GVariant *inputs = g_settings_get_value(settings, "sources");
|
||||||
guint32 index;
|
// current layout is always first
|
||||||
g_settings_get(settings, "current", "u", &index);
|
g_variant_get_child(inputs, 0, "(ss)", type, layout);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
113
src/popover.rs
113
src/popover.rs
@ -18,8 +18,11 @@ use std::io::Write;
|
|||||||
|
|
||||||
mod variants {
|
mod variants {
|
||||||
use glib;
|
use glib;
|
||||||
|
use glib::Variant;
|
||||||
use glib_sys;
|
use glib_sys;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use glib::ToVariant;
|
||||||
use glib::translate::FromGlibPtrFull;
|
use glib::translate::FromGlibPtrFull;
|
||||||
use glib::translate::ToGlibPtr;
|
use glib::translate::ToGlibPtr;
|
||||||
|
|
||||||
@ -49,6 +52,44 @@ mod variants {
|
|||||||
})
|
})
|
||||||
.collect()
|
.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 {
|
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) {
|
fn set_layout(kind: String, name: String) {
|
||||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||||
let inputs = settings.get_value("sources").unwrap();
|
let inputs = settings.get_value("sources").unwrap();
|
||||||
let inputs = variants::get_tuples(inputs).into_iter();
|
let current = (kind.clone(), name.clone());
|
||||||
for (index, (ikind, iname)) in inputs.enumerate() {
|
let inputs = variants::get_tuples(inputs).into_iter()
|
||||||
if (&ikind, &iname) == (&kind, &name) {
|
.filter(|t| t != ¤t);
|
||||||
settings.set_uint("current", index as u32);
|
let inputs = vec![(kind, name)].into_iter()
|
||||||
}
|
.chain(inputs).collect();
|
||||||
}
|
settings.set_value(
|
||||||
|
"sources",
|
||||||
|
&variants::ArrayPairString(inputs).to_variant()
|
||||||
|
);
|
||||||
settings.apply();
|
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 settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||||
let inputs = settings.get_value("sources").unwrap();
|
let inputs = settings.get_value("sources").unwrap();
|
||||||
let current = settings.get_uint("current") as usize;
|
|
||||||
let inputs = variants::get_tuples(inputs);
|
let inputs = variants::get_tuples(inputs);
|
||||||
|
|
||||||
let input_names: Vec<&str> = inputs.iter()
|
let input_names: Vec<&str> = inputs.iter()
|
||||||
@ -152,38 +195,44 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
|||||||
height: position.width.floor() as i32,
|
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(
|
let layout_action = gio::SimpleAction::new_stateful(
|
||||||
"layout",
|
"layout",
|
||||||
Some(initial_state.type_()),
|
Some(current_name.type_()),
|
||||||
&initial_state,
|
¤t_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
let action_group = gio::SimpleActionGroup::new();
|
let action_group = gio::SimpleActionGroup::new();
|
||||||
action_group.add_action(&layout_action);
|
action_group.add_action(&layout_action);
|
||||||
|
|
||||||
|
menu.insert_action_group("popup", Some(&action_group));
|
||||||
|
layout_action
|
||||||
|
});
|
||||||
|
|
||||||
menu.insert_action_group("popup", Some(&action_group));
|
|
||||||
menu.bind_model(Some(&model), Some("popup"));
|
menu.bind_model(Some(&model), Some("popup"));
|
||||||
|
|
||||||
menu.connect_closed(move |_menu| {
|
menu.connect_closed(move |_menu| {
|
||||||
let state = match layout_action.get_state() {
|
if let Some(layout_action) = &action {
|
||||||
Some(v) => {
|
let state = match layout_action.get_state() {
|
||||||
let s = v.get::<String>().or_else(|| {
|
Some(v) => {
|
||||||
eprintln!("Variant is not string: {:?}", v);
|
let s = v.get::<String>().or_else(|| {
|
||||||
|
eprintln!("Variant is not string: {:?}", v);
|
||||||
|
None
|
||||||
|
});
|
||||||
|
// FIXME: the `get_state` docs call for unrefing,
|
||||||
|
// but the function is nowhere to be found
|
||||||
|
// glib::Variant::unref(v);
|
||||||
|
s
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
eprintln!("No variant selected");
|
||||||
None
|
None
|
||||||
});
|
},
|
||||||
// FIXME: the `get_state` docs call for unrefing,
|
};
|
||||||
// but the function is nowhere to be found
|
set_layout("xkb".into(), state.unwrap_or("us".into()));
|
||||||
// glib::Variant::unref(v);
|
}
|
||||||
s
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
eprintln!("No variant selected");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
};
|
|
||||||
set_layout("xkb".into(), state.unwrap_or("us".into()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
menu.popup();
|
menu.popup();
|
||||||
|
|||||||
Reference in New Issue
Block a user