popover: Move Emoji and Terminal to ui file

This allows them to be translated via po so we don't have to maintain
the translation list by hand or care about empty translation files.

The only downside is that the "overlay layouts" in OVERLAY_NAMES need to
match the ones in the ui file but since a missing one is a clean crash
when selected there's little potential for subtle breackage. We could
even ensure consistency by comparing the two at run time but they change
only slowly this is not much of an issue (compared to how e.g. new
languages can be added now).
This commit is contained in:
Guido Günther
2021-12-08 14:18:14 +01:00
parent e776bf005e
commit 9b7e065b0f
2 changed files with 43 additions and 91 deletions

View File

@ -20,10 +20,9 @@ use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
use gtk::BuilderExtManual;
use gtk::prelude::*;
use gtk::PopoverExt;
use gtk::WidgetExt;
use std::io::Write;
use ::logging::Warn;
mod c {
@ -111,46 +110,6 @@ mod variants {
}
}
fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
let mut xml: Vec<u8> = Vec::new();
writeln!(
xml,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<interface>
<menu id=\"app-menu\">
<section>"
).unwrap();
for (input_name, translation) in inputs {
writeln!(
xml,
"
<item>
<attribute name=\"label\" translatable=\"yes\">{}</attribute>
<attribute name=\"action\">layout</attribute>
<attribute name=\"target\">{}</attribute>
</item>",
translation.0,
input_name,
).unwrap();
}
writeln!(
xml,
"
</section>
<section>
<item>
<attribute name=\"label\" translatable=\"yes\">Keyboard Settings</attribute>
<attribute name=\"action\">settings</attribute>
</item>
</section>
</menu>
</interface>"
).unwrap();
gtk::Builder::new_from_string(
&String::from_utf8(xml).expect("Bad menu definition")
)
}
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
@ -350,12 +309,12 @@ pub fn show(
.chain(overlay_layouts)
.collect();
let translated_names = translate_layout_names(&all_layouts);
// sorted collection of human and machine names
let translated_names = translate_layout_names(&system_layouts);
// sorted collection of language layouts
let mut human_names: Vec<(OwnedTranslation, LayoutId)> = translated_names
.into_iter()
.zip(all_layouts.clone().into_iter())
.zip(system_layouts.clone().into_iter())
.collect();
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| {
@ -367,32 +326,14 @@ pub fn show(
}
});
// GVariant doesn't natively support `enum`s,
// so the `choices` vector will serve as a lookup table.
let choices_with_translations: Vec<(String, (OwnedTranslation, LayoutId))>
= human_names.into_iter()
.enumerate()
.map(|(i, human_entry)| {(
format!("{}_{}", i, human_entry.1.get_name()),
human_entry,
)}).collect();
let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popup.ui");
let model: gio::Menu = builder.get_object("app-menu").unwrap();
let builder = make_menu_builder(
choices_with_translations.iter()
.map(|(id, (translation, _))| (id.as_str(), (*translation).clone()))
.collect()
);
let choices: Vec<(String, LayoutId)>
= choices_with_translations.into_iter()
.map(|(id, (_tr, layout))| (id, layout))
.collect();
// Much more debuggable to populate the model & menu
// from a string representation
// than add items imperatively
let model: gio::MenuModel = builder.get_object("app-menu").unwrap();
for (tr, l) in human_names.iter().rev() {
let detailed_action = format!("layout::{}", l.get_name());
let item = gio::MenuItem::new(Some(&tr.0), Some(detailed_action.as_str()));
model.prepend_item (&item);
}
let menu = gtk::Popover::new_from_model(Some(&window), &model);
menu.set_pointing_to(&gtk::Rectangle {
@ -403,32 +344,36 @@ pub fn show(
});
menu.set_constrain_to(gtk::PopoverConstraint::None);
let action_group = gio::SimpleActionGroup::new();
if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
let current_name_variant = choices.iter()
let current_layout_name = all_layouts.iter()
.find(
|(_id, layout)| layout == &current_layout
|l| l.get_name() == current_layout.get_name()
).unwrap()
.0.to_variant();
.get_name();
log_print!(logging::Level::Debug, "Current Layout {}", current_layout_name);
let layout_action = gio::SimpleAction::new_stateful(
"layout",
Some(current_name_variant.type_()),
&current_name_variant,
Some(current_layout_name.to_variant().type_()),
&current_layout_name.to_variant()
);
let menu_inner = menu.clone();
layout_action.connect_change_state(move |_action, state| {
match state {
Some(v) => {
log_print!(logging::Level::Debug, "Selected layout {}", v);
v.get::<String>()
.or_print(
logging::Problem::Bug,
&format!("Variant is not string: {:?}", v)
)
.map(|state| {
let (_id, layout) = choices.iter()
let layout = all_layouts.iter()
.find(
|choices| state == choices.0
|choices| state == choices.get_name()
).unwrap();
set_visible_layout(
manager,
@ -443,20 +388,18 @@ pub fn show(
};
menu_inner.popdown();
});
let settings_action = gio::SimpleAction::new("settings", None);
settings_action.connect_activate(move |_, _| {
let s = CString::new("region").unwrap();
unsafe { c::popover_open_settings_panel(s.as_ptr()) };
});
let action_group = gio::SimpleActionGroup::new();
action_group.add_action(&layout_action);
action_group.add_action(&settings_action);
menu.insert_action_group("popup", Some(&action_group));
};
let settings_action = gio::SimpleAction::new("settings", None);
settings_action.connect_activate(move |_, _| {
let s = CString::new("region").unwrap();
unsafe { c::popover_open_settings_panel(s.as_ptr()) };
});
action_group.add_action(&settings_action);
menu.insert_action_group("popup", Some(&action_group));
menu.bind_model(Some(&model), Some("popup"));
menu.popup();
}