Merge branch 'enoji' into 'master'
Enoji: implemented mechanism See merge request Librem5/squeekboard!276
This commit is contained in:
		
							
								
								
									
										16
									
								
								data/keyboards/emoji.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								data/keyboards/emoji.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
---
 | 
			
		||||
outlines:
 | 
			
		||||
    default: { width: 52, height: 52 }
 | 
			
		||||
    altline: { width: 52, height: 52 }
 | 
			
		||||
 | 
			
		||||
views:
 | 
			
		||||
    base:
 | 
			
		||||
        - "😀 😁 😅 😂 😊 😇 🙃"
 | 
			
		||||
        - "😍 😘 😋 😜 😎 🥳 😔"
 | 
			
		||||
        - "😢 😭 😡 😱 🤔 😬 🙄"
 | 
			
		||||
        - "preferences 🤨 🤓 😴 🤢 🤮 😈"
 | 
			
		||||
buttons:
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "keyboard-mode-symbolic"
 | 
			
		||||
@ -131,14 +131,17 @@ static void drag(EekGtkKeyboard *self,
 | 
			
		||||
{
 | 
			
		||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
			
		||||
    squeek_layout_drag(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
			
		||||
                       x, y, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
			
		||||
                       x, y, eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                       priv->keyboard->manager, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void release(EekGtkKeyboard *self, guint32 time)
 | 
			
		||||
{
 | 
			
		||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
			
		||||
 | 
			
		||||
    squeek_layout_release(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
			
		||||
    squeek_layout_release(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
			
		||||
                          eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                          priv->keyboard->manager, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,8 @@ struct _EekboardContextServicePrivate {
 | 
			
		||||
    LevelKeyboard *keyboard; // currently used keyboard
 | 
			
		||||
    GHashTable *keyboard_hash; // a table of available keyboards, per layout
 | 
			
		||||
 | 
			
		||||
    char *overlay;
 | 
			
		||||
 | 
			
		||||
    GSettings *settings;
 | 
			
		||||
    uint32_t hint;
 | 
			
		||||
    uint32_t purpose;
 | 
			
		||||
@ -214,15 +216,17 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
 | 
			
		||||
{
 | 
			
		||||
    g_autofree gchar *keyboard_type = NULL;
 | 
			
		||||
    g_autofree gchar *keyboard_layout = NULL;
 | 
			
		||||
    settings_get_layout(context->priv->settings, &keyboard_type, &keyboard_layout);
 | 
			
		||||
 | 
			
		||||
    if (!keyboard_type) {
 | 
			
		||||
        keyboard_type = g_strdup("us");
 | 
			
		||||
    if (context->priv->overlay) {
 | 
			
		||||
        keyboard_layout = g_strdup(context->priv->overlay);
 | 
			
		||||
    } else {
 | 
			
		||||
        g_autofree gchar *keyboard_type = NULL;
 | 
			
		||||
        settings_get_layout(context->priv->settings,
 | 
			
		||||
                            &keyboard_type, &keyboard_layout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!keyboard_layout) {
 | 
			
		||||
        keyboard_layout = g_strdup("undefined");
 | 
			
		||||
        keyboard_layout = g_strdup("us");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
 | 
			
		||||
@ -262,6 +266,8 @@ settings_handle_layout_changed(GSettings *s,
 | 
			
		||||
    (void)keys;
 | 
			
		||||
    (void)n_keys;
 | 
			
		||||
    EekboardContextService *context = user_data;
 | 
			
		||||
    free(context->priv->overlay);
 | 
			
		||||
    context->priv->overlay = NULL;
 | 
			
		||||
    update_layout_and_type(context);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
@ -391,6 +397,8 @@ eekboard_context_service_init (EekboardContextService *self)
 | 
			
		||||
        g_warning ("Could not connect to gsettings updates, layout"
 | 
			
		||||
                   " changing unavailable");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->priv->overlay = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -463,6 +471,7 @@ eekboard_context_service_destroy (EekboardContextService *context)
 | 
			
		||||
    if (context->priv->enabled) {
 | 
			
		||||
        eekboard_context_service_disable (context);
 | 
			
		||||
    }
 | 
			
		||||
    free(context->priv->overlay);
 | 
			
		||||
    g_signal_emit (context, signals[DESTROYED], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -498,3 +507,14 @@ void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
 | 
			
		||||
        update_layout_and_type(context);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
 | 
			
		||||
    context->priv->overlay = strdup(name);
 | 
			
		||||
    update_layout_and_type(context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
eekboard_context_service_get_overlay(EekboardContextService *context) {
 | 
			
		||||
    return context->priv->overlay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ void squeek_layout_free(struct squeek_layout*);
 | 
			
		||||
void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
			
		||||
                           struct transformation widget_to_layout,
 | 
			
		||||
                           uint32_t timestamp,
 | 
			
		||||
                           EekboardContextService *manager,
 | 
			
		||||
                           EekGtkKeyboard *ui_keyboard);
 | 
			
		||||
void squeek_layout_release_all_only(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp);
 | 
			
		||||
void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
			
		||||
@ -45,7 +46,8 @@ void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyb
 | 
			
		||||
void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
			
		||||
                        double x_widget, double y_widget,
 | 
			
		||||
                        struct transformation widget_to_layout,
 | 
			
		||||
                        uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
 | 
			
		||||
                        uint32_t timestamp, EekboardContextService *manager,
 | 
			
		||||
                        EekGtkKeyboard *ui_keyboard);
 | 
			
		||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t     *cr);
 | 
			
		||||
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t     *cr);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ use std::vec::Vec;
 | 
			
		||||
use ::action::Action;
 | 
			
		||||
use ::drawing;
 | 
			
		||||
use ::keyboard::{ KeyState, PressType };
 | 
			
		||||
use ::manager;
 | 
			
		||||
use ::submission::{ Timestamp, VirtualKeyboard };
 | 
			
		||||
use ::util::find_max_double;
 | 
			
		||||
 | 
			
		||||
@ -258,6 +259,7 @@ pub mod c {
 | 
			
		||||
            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
			
		||||
            widget_to_layout: Transformation,
 | 
			
		||||
            time: u32,
 | 
			
		||||
            manager: manager::c::Manager,
 | 
			
		||||
            ui_keyboard: EekGtkKeyboard,
 | 
			
		||||
        ) {
 | 
			
		||||
            let time = Timestamp(time);
 | 
			
		||||
@ -273,6 +275,7 @@ pub mod c {
 | 
			
		||||
                    &widget_to_layout,
 | 
			
		||||
                    time,
 | 
			
		||||
                    ui_keyboard,
 | 
			
		||||
                    manager,
 | 
			
		||||
                    key,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
@ -344,6 +347,7 @@ pub mod c {
 | 
			
		||||
            x_widget: f64, y_widget: f64,
 | 
			
		||||
            widget_to_layout: Transformation,
 | 
			
		||||
            time: u32,
 | 
			
		||||
            manager: manager::c::Manager,
 | 
			
		||||
            ui_keyboard: EekGtkKeyboard,
 | 
			
		||||
        ) {
 | 
			
		||||
            let time = Timestamp(time);
 | 
			
		||||
@ -378,6 +382,7 @@ pub mod c {
 | 
			
		||||
                            &widget_to_layout,
 | 
			
		||||
                            time,
 | 
			
		||||
                            ui_keyboard,
 | 
			
		||||
                            manager,
 | 
			
		||||
                            key,
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
@ -395,6 +400,7 @@ pub mod c {
 | 
			
		||||
                        &widget_to_layout,
 | 
			
		||||
                        time,
 | 
			
		||||
                        ui_keyboard,
 | 
			
		||||
                        manager,
 | 
			
		||||
                        key,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
@ -853,6 +859,7 @@ mod seat {
 | 
			
		||||
        widget_to_layout: &c::Transformation,
 | 
			
		||||
        time: Timestamp,
 | 
			
		||||
        ui_keyboard: c::EekGtkKeyboard,
 | 
			
		||||
        manager: manager::c::Manager,
 | 
			
		||||
        key: &Rc<RefCell<KeyState>>,
 | 
			
		||||
    ) {
 | 
			
		||||
        layout.release_key(virtual_keyboard, &mut key.clone(), time);
 | 
			
		||||
@ -874,7 +881,8 @@ mod seat {
 | 
			
		||||
                };
 | 
			
		||||
                ::popover::show(
 | 
			
		||||
                    ui_keyboard,
 | 
			
		||||
                    widget_to_layout.reverse_bounds(bounds)
 | 
			
		||||
                    widget_to_layout.reverse_bounds(bounds),
 | 
			
		||||
                    manager,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ mod layout;
 | 
			
		||||
mod locale;
 | 
			
		||||
mod locale_config;
 | 
			
		||||
mod logging;
 | 
			
		||||
mod manager;
 | 
			
		||||
mod outputs;
 | 
			
		||||
mod popover;
 | 
			
		||||
mod resources;
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,9 @@ mod c {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub struct Translation<'a>(pub &'a str);
 | 
			
		||||
 | 
			
		||||
fn cstring_safe(s: &str) -> CString {
 | 
			
		||||
    CString::new(s)
 | 
			
		||||
        .unwrap_or(CString::new("").unwrap())
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										34
									
								
								src/manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/manager.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
/*! Procedures relating to the management of the switching of layouts */
 | 
			
		||||
use ::util;
 | 
			
		||||
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use std::os::raw::{c_char, c_void};
 | 
			
		||||
 | 
			
		||||
    /// EekboardContextService*
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct Manager(*const c_void);
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn eekboard_context_service_set_overlay(
 | 
			
		||||
            manager: Manager,
 | 
			
		||||
            name: *const c_char,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        pub fn eekboard_context_service_get_overlay(
 | 
			
		||||
            manager: Manager,
 | 
			
		||||
        ) -> *const c_char;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the overlay name.
 | 
			
		||||
/// The result lifetime is "as long as the C copy lives"
 | 
			
		||||
pub fn get_overlay(manager: c::Manager) -> Option<String> {
 | 
			
		||||
    let raw_str = unsafe {
 | 
			
		||||
        c::eekboard_context_service_get_overlay(manager)
 | 
			
		||||
    };
 | 
			
		||||
    // this string is generated from Rust, should never be invalid
 | 
			
		||||
    util::c::as_str(&raw_str).unwrap()
 | 
			
		||||
        .map(String::from)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								src/popover.rs
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								src/popover.rs
									
									
									
									
									
								
							@ -2,9 +2,11 @@
 | 
			
		||||
 | 
			
		||||
use gio;
 | 
			
		||||
use gtk;
 | 
			
		||||
use ::layout::c::EekGtkKeyboard;
 | 
			
		||||
use ::locale::compare_current_locale;
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
use ::layout::c::{ Bounds, EekGtkKeyboard };
 | 
			
		||||
use ::locale::{ Translation, compare_current_locale };
 | 
			
		||||
use ::locale_config::system_locale;
 | 
			
		||||
use ::manager;
 | 
			
		||||
use ::resources;
 | 
			
		||||
 | 
			
		||||
use gio::ActionMapExt;
 | 
			
		||||
@ -92,7 +94,7 @@ mod variants {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
 | 
			
		||||
fn make_menu_builder(inputs: Vec<(&str, Translation)>) -> gtk::Builder {
 | 
			
		||||
    let mut xml: Vec<u8> = Vec::new();
 | 
			
		||||
    writeln!(
 | 
			
		||||
        xml,
 | 
			
		||||
@ -101,7 +103,7 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
 | 
			
		||||
  <menu id=\"app-menu\">
 | 
			
		||||
    <section>"
 | 
			
		||||
    ).unwrap();
 | 
			
		||||
    for (input_name, human_name) in inputs {
 | 
			
		||||
    for (input_name, translation) in inputs {
 | 
			
		||||
        writeln!(
 | 
			
		||||
            xml,
 | 
			
		||||
            "
 | 
			
		||||
@ -110,7 +112,7 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
 | 
			
		||||
            <attribute name=\"action\">layout</attribute>
 | 
			
		||||
            <attribute name=\"target\">{}</attribute>
 | 
			
		||||
        </item>",
 | 
			
		||||
            human_name,
 | 
			
		||||
            translation.0,
 | 
			
		||||
            input_name,
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
    }
 | 
			
		||||
@ -141,16 +143,79 @@ fn set_layout(kind: String, name: String) {
 | 
			
		||||
    settings.apply();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
			
		||||
/// A reference to what the user wants to see
 | 
			
		||||
#[derive(PartialEq, Clone, Debug)]
 | 
			
		||||
enum LayoutId {
 | 
			
		||||
    /// Affects the layout in system settings
 | 
			
		||||
    System {
 | 
			
		||||
        kind: String,
 | 
			
		||||
        name: String,
 | 
			
		||||
    },
 | 
			
		||||
    /// Only affects what this input method presents
 | 
			
		||||
    Local(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LayoutId {
 | 
			
		||||
    fn get_name(&self) -> &str {
 | 
			
		||||
        match &self {
 | 
			
		||||
            LayoutId::System { kind: _, name } => name.as_str(),
 | 
			
		||||
            LayoutId::Local(name) => name.as_str(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn set_visible_layout(
 | 
			
		||||
    manager: manager::c::Manager,
 | 
			
		||||
    layout_id: LayoutId,
 | 
			
		||||
) {
 | 
			
		||||
    match layout_id {
 | 
			
		||||
        LayoutId::System { kind, name } => set_layout(kind, name),
 | 
			
		||||
        LayoutId::Local(name) => {
 | 
			
		||||
            let name = CString::new(name.as_str()).unwrap();
 | 
			
		||||
            let name_ptr = name.as_ptr();
 | 
			
		||||
            unsafe {
 | 
			
		||||
                manager::c::eekboard_context_service_set_overlay(
 | 
			
		||||
                    manager,
 | 
			
		||||
                    name_ptr,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Takes into account first any overlays, then system layouts from the list
 | 
			
		||||
fn get_current_layout(
 | 
			
		||||
    manager: manager::c::Manager,
 | 
			
		||||
    system_layouts: &Vec<LayoutId>,
 | 
			
		||||
) -> Option<LayoutId> {
 | 
			
		||||
    match manager::get_overlay(manager) {
 | 
			
		||||
        Some(name) => Some(LayoutId::Local(name)),
 | 
			
		||||
        None => system_layouts.get(0).map(LayoutId::clone),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn show(
 | 
			
		||||
    window: EekGtkKeyboard,
 | 
			
		||||
    position: Bounds,
 | 
			
		||||
    manager: manager::c::Manager,
 | 
			
		||||
) {
 | 
			
		||||
    unsafe { gtk::set_initialized() };
 | 
			
		||||
    let window = unsafe { gtk::Widget::from_glib_none(window.0) };
 | 
			
		||||
 | 
			
		||||
    let overlay_layouts = resources::get_overlays().into_iter()
 | 
			
		||||
        .map(|name| LayoutId::Local(name.to_string()));
 | 
			
		||||
 | 
			
		||||
    let settings = gio::Settings::new("org.gnome.desktop.input-sources");
 | 
			
		||||
    let inputs = settings.get_value("sources").unwrap();
 | 
			
		||||
    let inputs = variants::get_tuples(inputs);
 | 
			
		||||
    
 | 
			
		||||
    let input_names: Vec<&str> = inputs.iter()
 | 
			
		||||
        .map(|(_kind, name)| name.as_str())
 | 
			
		||||
    let system_layouts: Vec<LayoutId> = inputs.into_iter()
 | 
			
		||||
        .map(|(kind, name)| LayoutId::System { kind, name })
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    let all_layouts: Vec<LayoutId> = system_layouts.clone()
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .chain(overlay_layouts)
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    let translations = system_locale()
 | 
			
		||||
@ -162,26 +227,56 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
			
		||||
        )
 | 
			
		||||
        .and_then(|lang| resources::get_layout_names(lang.as_str()));
 | 
			
		||||
 | 
			
		||||
    // sorted collection of human and machine names
 | 
			
		||||
    let mut human_names: Vec<(&str, &str)> = match translations {
 | 
			
		||||
    let translated_names = all_layouts.iter()
 | 
			
		||||
        .map(LayoutId::get_name);
 | 
			
		||||
    let translated_names: Vec<Translation> = match translations {
 | 
			
		||||
        Some(translations) => {
 | 
			
		||||
            input_names.iter()
 | 
			
		||||
                .map(|name| (*name, *translations.get(name).unwrap_or(name)))
 | 
			
		||||
            translated_names
 | 
			
		||||
                .map(move |name| {
 | 
			
		||||
                    translations.get(name)
 | 
			
		||||
                        .map(|translation| translation.clone())
 | 
			
		||||
                        .unwrap_or(Translation(name))
 | 
			
		||||
                })
 | 
			
		||||
                .collect()
 | 
			
		||||
        },
 | 
			
		||||
        // display bare codes
 | 
			
		||||
        None => {
 | 
			
		||||
            input_names.iter()
 | 
			
		||||
                .map(|n| (*n, *n)) // turns &&str into &str
 | 
			
		||||
            translated_names.map(|name| Translation(name))
 | 
			
		||||
                .collect()
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    human_names.sort_unstable_by(|(_, human_label_a), (_, human_label_b)| {
 | 
			
		||||
        compare_current_locale(human_label_a, human_label_b)
 | 
			
		||||
    // sorted collection of human and machine names
 | 
			
		||||
    let mut human_names: Vec<(Translation, LayoutId)> = translated_names
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .zip(all_layouts.clone().into_iter())
 | 
			
		||||
        .collect();
 | 
			
		||||
 | 
			
		||||
    human_names.sort_unstable_by(|(tr_a, _), (tr_b, _)| {
 | 
			
		||||
        compare_current_locale(tr_a.0, tr_b.0)
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let builder = make_menu_builder(human_names);
 | 
			
		||||
    // GVariant doesn't natively support `enum`s,
 | 
			
		||||
    // so the `choices` vector will serve as a lookup table.
 | 
			
		||||
    let choices_with_translations: Vec<(String, (Translation, LayoutId))>
 | 
			
		||||
        = human_names.into_iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
                .map(|(i, human_entry)| {(
 | 
			
		||||
                    format!("{}_{}", i, human_entry.1.get_name()),
 | 
			
		||||
                    human_entry,
 | 
			
		||||
                )}).collect();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
@ -195,16 +290,21 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
			
		||||
        height: position.width.floor() as i32,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if let Some(current_name) = input_names.get(0) {
 | 
			
		||||
        let current_name = current_name.to_variant();
 | 
			
		||||
    if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
 | 
			
		||||
        let current_name_variant = choices.iter()
 | 
			
		||||
            .find(
 | 
			
		||||
                |(_id, layout)| layout == ¤t_layout
 | 
			
		||||
            ).unwrap()
 | 
			
		||||
            .0.to_variant();
 | 
			
		||||
 | 
			
		||||
        let layout_action = gio::SimpleAction::new_stateful(
 | 
			
		||||
            "layout",
 | 
			
		||||
            Some(current_name.type_()),
 | 
			
		||||
            ¤t_name,
 | 
			
		||||
            Some(current_name_variant.type_()),
 | 
			
		||||
            ¤t_name_variant,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        layout_action.connect_change_state(|_action, state| {
 | 
			
		||||
        let menu_inner = menu.clone();
 | 
			
		||||
        layout_action.connect_change_state(move |_action, state| {
 | 
			
		||||
            match state {
 | 
			
		||||
                Some(v) => {
 | 
			
		||||
                    v.get::<String>()
 | 
			
		||||
@ -212,10 +312,20 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
			
		||||
                            eprintln!("Variant is not string: {:?}", v);
 | 
			
		||||
                            None
 | 
			
		||||
                        })
 | 
			
		||||
                        .map(|state| set_layout("xkb".into(), state));
 | 
			
		||||
                        .map(|state| {
 | 
			
		||||
                            let (_id, layout) = choices.iter()
 | 
			
		||||
                                .find(
 | 
			
		||||
                                    |choices| state == choices.0
 | 
			
		||||
                                ).unwrap();
 | 
			
		||||
                            set_visible_layout(
 | 
			
		||||
                                manager,
 | 
			
		||||
                                layout.clone(),
 | 
			
		||||
                            )
 | 
			
		||||
                        });
 | 
			
		||||
                },
 | 
			
		||||
                None => eprintln!("No variant selected"),
 | 
			
		||||
            };
 | 
			
		||||
            menu_inner.popdown();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let action_group = gio::SimpleActionGroup::new();
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use ::locale::Translation;
 | 
			
		||||
 | 
			
		||||
use std::iter::FromIterator;
 | 
			
		||||
 | 
			
		||||
@ -23,6 +24,8 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
 | 
			
		||||
    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
			
		||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
			
		||||
    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
			
		||||
    // Overlays
 | 
			
		||||
    ("emoji", include_str!("../data/keyboards/emoji.yaml")),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
pub fn get_keyboard(needle: &str) -> Option<&'static str> {
 | 
			
		||||
@ -39,6 +42,18 @@ pub fn get_keyboard(needle: &str) -> Option<&'static str> {
 | 
			
		||||
        })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const OVERLAY_NAMES: &[*const str] = &[
 | 
			
		||||
    "emoji"
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
pub fn get_overlays() -> Vec<&'static str> {
 | 
			
		||||
    OVERLAY_NAMES.iter()
 | 
			
		||||
        .map(|name| {
 | 
			
		||||
            let name: *const str = *name;
 | 
			
		||||
            unsafe { &*name }
 | 
			
		||||
        }).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Translations of the layout identifier strings
 | 
			
		||||
const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
			
		||||
    ("de-DE", include_str!("../data/langs/de-DE.txt")),
 | 
			
		||||
@ -49,7 +64,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
pub fn get_layout_names(lang: &str)
 | 
			
		||||
    -> Option<HashMap<&'static str, &'static str>>
 | 
			
		||||
    -> Option<HashMap<&'static str, Translation<'static>>>
 | 
			
		||||
{
 | 
			
		||||
    let translations = LAYOUT_NAMES.iter()
 | 
			
		||||
        .find(|(name, _data)| {
 | 
			
		||||
@ -63,7 +78,7 @@ pub fn get_layout_names(lang: &str)
 | 
			
		||||
    translations.map(make_mapping)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_line(line: &str) -> Option<(&str, &str)> {
 | 
			
		||||
fn parse_line(line: &str) -> Option<(&str, Translation)> {
 | 
			
		||||
    let comment = line.trim().starts_with("#");
 | 
			
		||||
    if comment {
 | 
			
		||||
        None
 | 
			
		||||
@ -71,11 +86,11 @@ fn parse_line(line: &str) -> Option<(&str, &str)> {
 | 
			
		||||
        let mut iter = line.splitn(2, " ");
 | 
			
		||||
        let name = iter.next().unwrap();
 | 
			
		||||
        // will skip empty and unfinished lines
 | 
			
		||||
        iter.next().map(|tr| (name, tr.trim()))
 | 
			
		||||
        iter.next().map(|tr| (name, Translation(tr.trim())))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn make_mapping(data: &str) -> HashMap<&str, &str> {
 | 
			
		||||
fn make_mapping(data: &str) -> HashMap<&str, Translation> {
 | 
			
		||||
    HashMap::from_iter(
 | 
			
		||||
        data.split("\n")
 | 
			
		||||
            .filter_map(parse_line)
 | 
			
		||||
@ -86,10 +101,17 @@ fn make_mapping(data: &str) -> HashMap<&str, &str> {
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn check_overlays_present() {
 | 
			
		||||
        for name in get_overlays() {
 | 
			
		||||
            assert!(get_keyboard(name).is_some());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn mapping_line() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Some(("name", "translation")),
 | 
			
		||||
            Some(("name", Translation("translation"))),
 | 
			
		||||
            parse_line("name translation")
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ pub mod c {
 | 
			
		||||
    
 | 
			
		||||
    use std::borrow::ToOwned;
 | 
			
		||||
    
 | 
			
		||||
    // The lifetime on input limits the existence of the result
 | 
			
		||||
    pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
 | 
			
		||||
        if s.is_null() {
 | 
			
		||||
            Ok(None)
 | 
			
		||||
 | 
			
		||||
@ -58,6 +58,8 @@ foreach layout : [
 | 
			
		||||
    'no',
 | 
			
		||||
    'number',
 | 
			
		||||
    'se',
 | 
			
		||||
    
 | 
			
		||||
    'emoji',
 | 
			
		||||
]
 | 
			
		||||
    test(
 | 
			
		||||
        'test_layout_' + layout,
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user