popover: Install emoji layout
In order to do that, an additional piece of state (layout switcher) was exposed to the event handlers, a separation between squeekboard-only and system layouts was introduced, along with a Translation structure to prevent mixing up strings.
This commit is contained in:
		
							
								
								
									
										15
									
								
								data/keyboards/emoji.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								data/keyboards/emoji.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					outlines:
 | 
				
			||||||
 | 
					    default: { width: 37.46341, height: 52 }
 | 
				
			||||||
 | 
					    altline: { width: 48.39024, 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);
 | 
					    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
				
			||||||
    squeek_layout_drag(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
					    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)
 | 
					static void release(EekGtkKeyboard *self, guint32 time)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
					    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
 | 
					static gboolean
 | 
				
			||||||
 | 
				
			|||||||
@ -71,6 +71,8 @@ struct _EekboardContextServicePrivate {
 | 
				
			|||||||
    LevelKeyboard *keyboard; // currently used keyboard
 | 
					    LevelKeyboard *keyboard; // currently used keyboard
 | 
				
			||||||
    GHashTable *keyboard_hash; // a table of available keyboards, per layout
 | 
					    GHashTable *keyboard_hash; // a table of available keyboards, per layout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char *overlay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GSettings *settings;
 | 
					    GSettings *settings;
 | 
				
			||||||
    uint32_t hint;
 | 
					    uint32_t hint;
 | 
				
			||||||
    uint32_t purpose;
 | 
					    uint32_t purpose;
 | 
				
			||||||
@ -214,15 +216,17 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
				
			|||||||
void
 | 
					void
 | 
				
			||||||
eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
 | 
					eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    g_autofree gchar *keyboard_type = NULL;
 | 
					 | 
				
			||||||
    g_autofree gchar *keyboard_layout = NULL;
 | 
					    g_autofree gchar *keyboard_layout = NULL;
 | 
				
			||||||
    settings_get_layout(context->priv->settings, &keyboard_type, &keyboard_layout);
 | 
					    if (context->priv->overlay) {
 | 
				
			||||||
 | 
					        keyboard_layout = g_strdup(context->priv->overlay);
 | 
				
			||||||
    if (!keyboard_type) {
 | 
					    } else {
 | 
				
			||||||
        keyboard_type = g_strdup("us");
 | 
					        g_autofree gchar *keyboard_type = NULL;
 | 
				
			||||||
 | 
					        settings_get_layout(context->priv->settings,
 | 
				
			||||||
 | 
					                            &keyboard_type, &keyboard_layout);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!keyboard_layout) {
 | 
					    if (!keyboard_layout) {
 | 
				
			||||||
        keyboard_layout = g_strdup("undefined");
 | 
					        keyboard_layout = g_strdup("us");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
 | 
					    EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
 | 
				
			||||||
@ -262,6 +266,8 @@ settings_handle_layout_changed(GSettings *s,
 | 
				
			|||||||
    (void)keys;
 | 
					    (void)keys;
 | 
				
			||||||
    (void)n_keys;
 | 
					    (void)n_keys;
 | 
				
			||||||
    EekboardContextService *context = user_data;
 | 
					    EekboardContextService *context = user_data;
 | 
				
			||||||
 | 
					    free(context->priv->overlay);
 | 
				
			||||||
 | 
					    context->priv->overlay = NULL;
 | 
				
			||||||
    update_layout_and_type(context);
 | 
					    update_layout_and_type(context);
 | 
				
			||||||
    return TRUE;
 | 
					    return TRUE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -391,6 +397,8 @@ eekboard_context_service_init (EekboardContextService *self)
 | 
				
			|||||||
        g_warning ("Could not connect to gsettings updates, layout"
 | 
					        g_warning ("Could not connect to gsettings updates, layout"
 | 
				
			||||||
                   " changing unavailable");
 | 
					                   " changing unavailable");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->priv->overlay = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -463,6 +471,7 @@ eekboard_context_service_destroy (EekboardContextService *context)
 | 
				
			|||||||
    if (context->priv->enabled) {
 | 
					    if (context->priv->enabled) {
 | 
				
			||||||
        eekboard_context_service_disable (context);
 | 
					        eekboard_context_service_disable (context);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    free(context->priv->overlay);
 | 
				
			||||||
    g_signal_emit (context, signals[DESTROYED], 0);
 | 
					    g_signal_emit (context, signals[DESTROYED], 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -498,3 +507,9 @@ void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
 | 
				
			|||||||
        update_layout_and_type(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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
					void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
				
			||||||
                           struct transformation widget_to_layout,
 | 
					                           struct transformation widget_to_layout,
 | 
				
			||||||
                           uint32_t timestamp,
 | 
					                           uint32_t timestamp,
 | 
				
			||||||
 | 
					                           EekboardContextService *manager,
 | 
				
			||||||
                           EekGtkKeyboard *ui_keyboard);
 | 
					                           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_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,
 | 
					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,
 | 
					void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
				
			||||||
                        double x_widget, double y_widget,
 | 
					                        double x_widget, double y_widget,
 | 
				
			||||||
                        struct transformation widget_to_layout,
 | 
					                        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_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);
 | 
					void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t     *cr);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@ use std::vec::Vec;
 | 
				
			|||||||
use ::action::Action;
 | 
					use ::action::Action;
 | 
				
			||||||
use ::drawing;
 | 
					use ::drawing;
 | 
				
			||||||
use ::keyboard::{ KeyState, PressType };
 | 
					use ::keyboard::{ KeyState, PressType };
 | 
				
			||||||
 | 
					use ::manager;
 | 
				
			||||||
use ::submission::{ Timestamp, VirtualKeyboard };
 | 
					use ::submission::{ Timestamp, VirtualKeyboard };
 | 
				
			||||||
use ::util::find_max_double;
 | 
					use ::util::find_max_double;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -258,6 +259,7 @@ pub mod c {
 | 
				
			|||||||
            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
				
			||||||
            widget_to_layout: Transformation,
 | 
					            widget_to_layout: Transformation,
 | 
				
			||||||
            time: u32,
 | 
					            time: u32,
 | 
				
			||||||
 | 
					            manager: manager::c::Manager,
 | 
				
			||||||
            ui_keyboard: EekGtkKeyboard,
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            let time = Timestamp(time);
 | 
					            let time = Timestamp(time);
 | 
				
			||||||
@ -273,6 +275,7 @@ pub mod c {
 | 
				
			|||||||
                    &widget_to_layout,
 | 
					                    &widget_to_layout,
 | 
				
			||||||
                    time,
 | 
					                    time,
 | 
				
			||||||
                    ui_keyboard,
 | 
					                    ui_keyboard,
 | 
				
			||||||
 | 
					                    manager,
 | 
				
			||||||
                    key,
 | 
					                    key,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -344,6 +347,7 @@ pub mod c {
 | 
				
			|||||||
            x_widget: f64, y_widget: f64,
 | 
					            x_widget: f64, y_widget: f64,
 | 
				
			||||||
            widget_to_layout: Transformation,
 | 
					            widget_to_layout: Transformation,
 | 
				
			||||||
            time: u32,
 | 
					            time: u32,
 | 
				
			||||||
 | 
					            manager: manager::c::Manager,
 | 
				
			||||||
            ui_keyboard: EekGtkKeyboard,
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            let time = Timestamp(time);
 | 
					            let time = Timestamp(time);
 | 
				
			||||||
@ -378,6 +382,7 @@ pub mod c {
 | 
				
			|||||||
                            &widget_to_layout,
 | 
					                            &widget_to_layout,
 | 
				
			||||||
                            time,
 | 
					                            time,
 | 
				
			||||||
                            ui_keyboard,
 | 
					                            ui_keyboard,
 | 
				
			||||||
 | 
					                            manager,
 | 
				
			||||||
                            key,
 | 
					                            key,
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -395,6 +400,7 @@ pub mod c {
 | 
				
			|||||||
                        &widget_to_layout,
 | 
					                        &widget_to_layout,
 | 
				
			||||||
                        time,
 | 
					                        time,
 | 
				
			||||||
                        ui_keyboard,
 | 
					                        ui_keyboard,
 | 
				
			||||||
 | 
					                        manager,
 | 
				
			||||||
                        key,
 | 
					                        key,
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -853,6 +859,7 @@ mod seat {
 | 
				
			|||||||
        widget_to_layout: &c::Transformation,
 | 
					        widget_to_layout: &c::Transformation,
 | 
				
			||||||
        time: Timestamp,
 | 
					        time: Timestamp,
 | 
				
			||||||
        ui_keyboard: c::EekGtkKeyboard,
 | 
					        ui_keyboard: c::EekGtkKeyboard,
 | 
				
			||||||
 | 
					        manager: manager::c::Manager,
 | 
				
			||||||
        key: &Rc<RefCell<KeyState>>,
 | 
					        key: &Rc<RefCell<KeyState>>,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        layout.release_key(virtual_keyboard, &mut key.clone(), time);
 | 
					        layout.release_key(virtual_keyboard, &mut key.clone(), time);
 | 
				
			||||||
@ -874,7 +881,8 @@ mod seat {
 | 
				
			|||||||
                };
 | 
					                };
 | 
				
			||||||
                ::popover::show(
 | 
					                ::popover::show(
 | 
				
			||||||
                    ui_keyboard,
 | 
					                    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;
 | 
				
			||||||
mod locale_config;
 | 
					mod locale_config;
 | 
				
			||||||
mod logging;
 | 
					mod logging;
 | 
				
			||||||
 | 
					mod manager;
 | 
				
			||||||
mod outputs;
 | 
					mod outputs;
 | 
				
			||||||
mod popover;
 | 
					mod popover;
 | 
				
			||||||
mod resources;
 | 
					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 {
 | 
					fn cstring_safe(s: &str) -> CString {
 | 
				
			||||||
    CString::new(s)
 | 
					    CString::new(s)
 | 
				
			||||||
        .unwrap_or(CString::new("").unwrap())
 | 
					        .unwrap_or(CString::new("").unwrap())
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								src/manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/manager.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					/*! Procedures relating to the management of the switching of layouts */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										161
									
								
								src/popover.rs
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								src/popover.rs
									
									
									
									
									
								
							@ -2,9 +2,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use gio;
 | 
					use gio;
 | 
				
			||||||
use gtk;
 | 
					use gtk;
 | 
				
			||||||
use ::layout::c::EekGtkKeyboard;
 | 
					use std::ffi::CString;
 | 
				
			||||||
use ::locale::compare_current_locale;
 | 
					use ::layout::c::{ Bounds, EekGtkKeyboard };
 | 
				
			||||||
 | 
					use ::locale::{ Translation, compare_current_locale };
 | 
				
			||||||
use ::locale_config::system_locale;
 | 
					use ::locale_config::system_locale;
 | 
				
			||||||
 | 
					use ::manager;
 | 
				
			||||||
use ::resources;
 | 
					use ::resources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use gio::ActionMapExt;
 | 
					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();
 | 
					    let mut xml: Vec<u8> = Vec::new();
 | 
				
			||||||
    writeln!(
 | 
					    writeln!(
 | 
				
			||||||
        xml,
 | 
					        xml,
 | 
				
			||||||
@ -101,7 +103,7 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
 | 
				
			|||||||
  <menu id=\"app-menu\">
 | 
					  <menu id=\"app-menu\">
 | 
				
			||||||
    <section>"
 | 
					    <section>"
 | 
				
			||||||
    ).unwrap();
 | 
					    ).unwrap();
 | 
				
			||||||
    for (input_name, human_name) in inputs {
 | 
					    for (input_name, translation) in inputs {
 | 
				
			||||||
        writeln!(
 | 
					        writeln!(
 | 
				
			||||||
            xml,
 | 
					            xml,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
@ -110,7 +112,7 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
 | 
				
			|||||||
            <attribute name=\"action\">layout</attribute>
 | 
					            <attribute name=\"action\">layout</attribute>
 | 
				
			||||||
            <attribute name=\"target\">{}</attribute>
 | 
					            <attribute name=\"target\">{}</attribute>
 | 
				
			||||||
        </item>",
 | 
					        </item>",
 | 
				
			||||||
            human_name,
 | 
					            translation.0,
 | 
				
			||||||
            input_name,
 | 
					            input_name,
 | 
				
			||||||
        ).unwrap();
 | 
					        ).unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -141,16 +143,68 @@ fn set_layout(kind: String, name: String) {
 | 
				
			|||||||
    settings.apply();
 | 
					    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(&'static str),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl LayoutId {
 | 
				
			||||||
 | 
					    fn get_name(&self) -> &str {
 | 
				
			||||||
 | 
					        match &self {
 | 
				
			||||||
 | 
					            LayoutId::System { kind: _, name } => name.as_str(),
 | 
				
			||||||
 | 
					            LayoutId::Local(name) => name,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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).unwrap();
 | 
				
			||||||
 | 
					            let name_ptr = name.as_ptr();
 | 
				
			||||||
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                manager::c::eekboard_context_service_set_overlay(
 | 
				
			||||||
 | 
					                    manager,
 | 
				
			||||||
 | 
					                    name_ptr,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn show(
 | 
				
			||||||
 | 
					    window: EekGtkKeyboard,
 | 
				
			||||||
 | 
					    position: Bounds,
 | 
				
			||||||
 | 
					    manager: manager::c::Manager,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
    unsafe { gtk::set_initialized() };
 | 
					    unsafe { gtk::set_initialized() };
 | 
				
			||||||
    let window = unsafe { gtk::Widget::from_glib_none(window.0) };
 | 
					    let window = unsafe { gtk::Widget::from_glib_none(window.0) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let overlay_layouts = resources::get_overlays().into_iter()
 | 
				
			||||||
 | 
					        .map(|name| LayoutId::Local(name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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);
 | 
					    let inputs = variants::get_tuples(inputs);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let input_names: Vec<&str> = inputs.iter()
 | 
					    let system_layouts: Vec<LayoutId> = inputs.into_iter()
 | 
				
			||||||
        .map(|(_kind, name)| name.as_str())
 | 
					        .map(|(kind, name)| LayoutId::System { kind, name })
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let all_layouts: Vec<LayoutId> = system_layouts.clone()
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        .chain(overlay_layouts)
 | 
				
			||||||
        .collect();
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let translations = system_locale()
 | 
					    let translations = system_locale()
 | 
				
			||||||
@ -162,26 +216,56 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        .and_then(|lang| resources::get_layout_names(lang.as_str()));
 | 
					        .and_then(|lang| resources::get_layout_names(lang.as_str()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // sorted collection of human and machine names
 | 
					    let use_codes: Box<dyn FnMut(&str) -> Translation>
 | 
				
			||||||
    let mut human_names: Vec<(&str, &str)> = match translations {
 | 
					        = Box::new(|name| Translation(name));
 | 
				
			||||||
        Some(translations) => {
 | 
					 | 
				
			||||||
            input_names.iter()
 | 
					 | 
				
			||||||
                .map(|name| (*name, *translations.get(name).unwrap_or(name)))
 | 
					 | 
				
			||||||
                .collect()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        // display bare codes
 | 
					 | 
				
			||||||
        None => {
 | 
					 | 
				
			||||||
            input_names.iter()
 | 
					 | 
				
			||||||
                .map(|n| (*n, *n)) // turns &&str into &str
 | 
					 | 
				
			||||||
                .collect()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    human_names.sort_unstable_by(|(_, human_label_a), (_, human_label_b)| {
 | 
					    let translated_names = all_layouts.iter()
 | 
				
			||||||
        compare_current_locale(human_label_a, human_label_b)
 | 
					        .map(LayoutId::get_name)
 | 
				
			||||||
 | 
					        // use a different closure depending on whether we have translations
 | 
				
			||||||
 | 
					        .map(match translations {
 | 
				
			||||||
 | 
					            Some(translations) => {
 | 
				
			||||||
 | 
					                let use_translations: Box<dyn FnMut(&str) -> Translation>
 | 
				
			||||||
 | 
					                     = Box::new(move |name| {
 | 
				
			||||||
 | 
					                        translations.get(name)
 | 
				
			||||||
 | 
					                            .map(|translation| translation.clone())
 | 
				
			||||||
 | 
					                            .unwrap_or(Translation(name))
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                use_translations
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            None => use_codes,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // sorted collection of human and machine names
 | 
				
			||||||
 | 
					    let mut human_names: Vec<(Translation, LayoutId)> = translated_names
 | 
				
			||||||
 | 
					        .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
 | 
					    // Much more debuggable to populate the model & menu
 | 
				
			||||||
    // from a string representation
 | 
					    // from a string representation
 | 
				
			||||||
    // than add items imperatively
 | 
					    // than add items imperatively
 | 
				
			||||||
@ -195,16 +279,21 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
				
			|||||||
        height: position.width.floor() as i32,
 | 
					        height: position.width.floor() as i32,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(current_name) = input_names.get(0) {
 | 
					    if let Some(current_layout) = system_layouts.get(0) {
 | 
				
			||||||
        let current_name = current_name.to_variant();
 | 
					        let current_name_variant = choices.iter()
 | 
				
			||||||
 | 
					            .find(
 | 
				
			||||||
 | 
					                |(_id, layout)| layout == current_layout
 | 
				
			||||||
 | 
					            ).unwrap()
 | 
				
			||||||
 | 
					            .0.to_variant();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let layout_action = gio::SimpleAction::new_stateful(
 | 
					        let layout_action = gio::SimpleAction::new_stateful(
 | 
				
			||||||
            "layout",
 | 
					            "layout",
 | 
				
			||||||
            Some(current_name.type_()),
 | 
					            Some(current_name_variant.type_()),
 | 
				
			||||||
            ¤t_name,
 | 
					            ¤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 {
 | 
					            match state {
 | 
				
			||||||
                Some(v) => {
 | 
					                Some(v) => {
 | 
				
			||||||
                    v.get::<String>()
 | 
					                    v.get::<String>()
 | 
				
			||||||
@ -212,10 +301,20 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
 | 
				
			|||||||
                            eprintln!("Variant is not string: {:?}", v);
 | 
					                            eprintln!("Variant is not string: {:?}", v);
 | 
				
			||||||
                            None
 | 
					                            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"),
 | 
					                None => eprintln!("No variant selected"),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					            menu_inner.popdown();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let action_group = gio::SimpleActionGroup::new();
 | 
					        let action_group = gio::SimpleActionGroup::new();
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use ::locale::Translation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::iter::FromIterator;
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,6 +24,8 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
 | 
				
			|||||||
    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
					    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
				
			||||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
					    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
				
			||||||
    ("se", include_str!("../data/keyboards/se.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> {
 | 
					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
 | 
					/// Translations of the layout identifier strings
 | 
				
			||||||
const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
					const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
				
			||||||
    ("de-DE", include_str!("../data/langs/de-DE.txt")),
 | 
					    ("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)
 | 
					pub fn get_layout_names(lang: &str)
 | 
				
			||||||
    -> Option<HashMap<&'static str, &'static str>>
 | 
					    -> Option<HashMap<&'static str, Translation<'static>>>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    let translations = LAYOUT_NAMES.iter()
 | 
					    let translations = LAYOUT_NAMES.iter()
 | 
				
			||||||
        .find(|(name, _data)| {
 | 
					        .find(|(name, _data)| {
 | 
				
			||||||
@ -63,7 +78,7 @@ pub fn get_layout_names(lang: &str)
 | 
				
			|||||||
    translations.map(make_mapping)
 | 
					    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("#");
 | 
					    let comment = line.trim().starts_with("#");
 | 
				
			||||||
    if comment {
 | 
					    if comment {
 | 
				
			||||||
        None
 | 
					        None
 | 
				
			||||||
@ -71,11 +86,11 @@ fn parse_line(line: &str) -> Option<(&str, &str)> {
 | 
				
			|||||||
        let mut iter = line.splitn(2, " ");
 | 
					        let mut iter = line.splitn(2, " ");
 | 
				
			||||||
        let name = iter.next().unwrap();
 | 
					        let name = iter.next().unwrap();
 | 
				
			||||||
        // will skip empty and unfinished lines
 | 
					        // 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(
 | 
					    HashMap::from_iter(
 | 
				
			||||||
        data.split("\n")
 | 
					        data.split("\n")
 | 
				
			||||||
            .filter_map(parse_line)
 | 
					            .filter_map(parse_line)
 | 
				
			||||||
@ -86,10 +101,17 @@ fn make_mapping(data: &str) -> HashMap<&str, &str> {
 | 
				
			|||||||
mod test {
 | 
					mod test {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn check_overlays_present() {
 | 
				
			||||||
 | 
					        for name in get_overlays() {
 | 
				
			||||||
 | 
					            assert!(get_keyboard(name).is_some());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn mapping_line() {
 | 
					    fn mapping_line() {
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            Some(("name", "translation")),
 | 
					            Some(("name", Translation("translation"))),
 | 
				
			||||||
            parse_line("name translation")
 | 
					            parse_line("name translation")
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -58,6 +58,8 @@ foreach layout : [
 | 
				
			|||||||
    'no',
 | 
					    'no',
 | 
				
			||||||
    'number',
 | 
					    'number',
 | 
				
			||||||
    'se',
 | 
					    'se',
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    'emoji',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
    test(
 | 
					    test(
 | 
				
			||||||
        'test_layout_' + layout,
 | 
					        'test_layout_' + layout,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user