Merge branch 'pin-keyboard' into 'master'

Use special pin keyboard

See merge request World/Phosh/squeekboard!492
This commit is contained in:
dcz
2021-12-05 18:23:35 +00:00
12 changed files with 131 additions and 32 deletions

6
data/common.css Normal file
View File

@ -0,0 +1,6 @@
/* Theme independent style */
sq_view.pin sq_button {
border-radius: 0px;
margin: 1px 1px 1px 1px;
}

View File

@ -0,0 +1,20 @@
---
margins: { top: 4, side: 0, bottom: 4 }
outlines:
default: { width: 120, height: 52 }
views:
base:
- "1 2 3"
- "4 5 6"
- "7 8 9"
- "BackSpace 0 Return"
buttons:
BackSpace:
icon: "edit-clear-symbolic"
action: erase
Return:
icon: "key-enter"
keysym: "Return"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<gresources> <gresources>
<gresource prefix="/sm/puri/squeekboard"> <gresource prefix="/sm/puri/squeekboard">
<file compressed="true">common.css</file>
<file compressed="true">style.css</file> <file compressed="true">style.css</file>
<file compressed="true">style-Adwaita:dark.css</file> <file compressed="true">style-Adwaita:dark.css</file>
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file> <file compressed="true" preprocess="xml-stripblanks">popup.ui</file>

View File

@ -58,3 +58,5 @@ sq_button.small {
background: #1c71d8; background: #1c71d8;
border-color: #3584e4; border-color: #3584e4;
} }
@import url("resource:///sm/puri/squeekboard/common.css");

View File

@ -61,3 +61,5 @@ sq_button.small {
background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/ background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/
border-color: @borders; /*#3584e4;*/ border-color: @borders; /*#3584e4;*/
} }
@import url("resource:///sm/puri/squeekboard/common.css");

View File

@ -315,6 +315,7 @@ eek_renderer_new (LevelKeyboard *keyboard,
renderer_init(renderer); renderer_init(renderer);
renderer->pcontext = pcontext; renderer->pcontext = pcontext;
g_object_ref (renderer->pcontext); g_object_ref (renderer->pcontext);
const char *purpose_class = "normal";
/* Create a style context for the layout */ /* Create a style context for the layout */
GtkWidgetPath *path = gtk_widget_path_new(); GtkWidgetPath *path = gtk_widget_path_new();
@ -336,6 +337,55 @@ eek_renderer_new (LevelKeyboard *keyboard,
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) { if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_widget_path_iter_add_class(path, -1, "wide"); gtk_widget_path_iter_add_class(path, -1, "wide");
} }
/* Add style classes based on purpose */
switch (squeek_layout_get_purpose (keyboard->layout)) {
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL:
purpose_class = "normal";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA:
purpose_class = "alpha";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS:
purpose_class = "digits";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
purpose_class = "number";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
purpose_class = "phone";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL:
purpose_class = "url";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL:
purpose_class = "email";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME:
purpose_class = "name";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD:
purpose_class = "password";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN:
purpose_class = "pin";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE:
purpose_class = "date";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME:
purpose_class = "time";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME:
purpose_class = "datetime";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
purpose_class = "terminal";
break;
default:
g_warning ("Unknown input purpose %d", squeek_layout_get_purpose(keyboard->layout));
}
gtk_widget_path_iter_add_class(path, -1, purpose_class);
gtk_widget_path_append_type(path, button_type()); gtk_widget_path_append_type(path, button_type());
renderer->button_context = gtk_style_context_new (); renderer->button_context = gtk_style_context_new ();
gtk_style_context_set_path(renderer->button_context, path); gtk_style_context_set_path(renderer->button_context, path);

View File

@ -61,7 +61,7 @@ pub mod c {
}; };
let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str); let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
let layout = ::layout::Layout::new(layout, kind); let layout = ::layout::Layout::new(layout, kind, variant);
Box::into_raw(Box::new(layout)) Box::into_raw(Box::new(layout))
} }
} }
@ -168,9 +168,9 @@ fn get_directory_string(
None => match content_purpose { None => match content_purpose {
ContentPurpose::Number => Special("number"), ContentPurpose::Number => Special("number"),
ContentPurpose::Digits => Special("number"), ContentPurpose::Digits => Special("number"),
ContentPurpose::Pin => Special("number"),
ContentPurpose::Phone => Special("number"), ContentPurpose::Phone => Special("number"),
ContentPurpose::Terminal => Special("terminal"), ContentPurpose::Terminal => Special("terminal"),
ContentPurpose::Pin => Special("pin"),
_ => Default, _ => Default,
}, },
Some(overlay) => Special(overlay), Some(overlay) => Special(overlay),

View File

@ -233,7 +233,7 @@ bitflags!{
/// use rs::imservice::ContentPurpose; /// use rs::imservice::ContentPurpose;
/// assert_eq!(ContentPurpose::Alpha as u32, 1); /// assert_eq!(ContentPurpose::Alpha as u32, 1);
/// ``` /// ```
#[derive(Debug, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ContentPurpose { pub enum ContentPurpose {
Normal = 0, Normal = 0,
Alpha = 1, Alpha = 1,

View File

@ -33,6 +33,7 @@ struct transformation squeek_layout_calculate_transformation(
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type, uint32_t variant_type, const char *overlay_name); struct squeek_layout *squeek_load_layout(const char *name, uint32_t type, uint32_t variant_type, const char *overlay_name);
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *); enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
uint32_t squeek_layout_get_purpose(const struct squeek_layout *);
void squeek_layout_free(struct squeek_layout*); void squeek_layout_free(struct squeek_layout*);
void squeek_layout_release(struct squeek_layout *layout, void squeek_layout_release(struct squeek_layout *layout,

View File

@ -1,17 +1,17 @@
/*! /*!
* Layout-related data. * Layout-related data.
* *
* The `View` contains `Row`s and each `Row` contains `Button`s. * The `View` contains `Row`s and each `Row` contains `Button`s.
* They carry data relevant to their positioning only, * They carry data relevant to their positioning only,
* except the Button, which also carries some data * except the Button, which also carries some data
* about its appearance and function. * about its appearance and function.
* *
* The layout is determined bottom-up, by measuring `Button` sizes, * The layout is determined bottom-up, by measuring `Button` sizes,
* deriving `Row` sizes from them, and then centering them within the `View`. * deriving `Row` sizes from them, and then centering them within the `View`.
* *
* That makes the `View` position immutable, * That makes the `View` position immutable,
* and therefore different than the other positions. * and therefore different than the other positions.
* *
* Note that it might be a better idea * Note that it might be a better idea
* to make `View` position depend on its contents, * to make `View` position depend on its contents,
* and let the renderer scale and center it within the widget. * and let the renderer scale and center it within the widget.
@ -32,6 +32,8 @@ use ::manager;
use ::submission::{ Submission, SubmitData, Timestamp }; use ::submission::{ Submission, SubmitData, Timestamp };
use ::util::find_max_double; use ::util::find_max_double;
use ::imservice::ContentPurpose;
// Traits // Traits
use std::borrow::Borrow; use std::borrow::Borrow;
use ::logging::Warn; use ::logging::Warn;
@ -64,7 +66,7 @@ pub mod c {
pub x: f64, pub x: f64,
pub y: f64, pub y: f64,
} }
impl Add for Point { impl Add for Point {
type Output = Self; type Output = Self;
fn add(self, other: Self) -> Self { fn add(self, other: Self) -> Self {
@ -81,7 +83,7 @@ pub mod c {
} }
} }
} }
impl Sub<&Point> for Point { impl Sub<&Point> for Point {
type Output = Point; type Output = Point;
fn sub(self, other: &Point) -> Point { fn sub(self, other: &Point) -> Point {
@ -152,14 +154,14 @@ pub mod c {
} }
} }
} }
// This is constructed only in C, no need for warnings // This is constructed only in C, no need for warnings
#[allow(dead_code)] #[allow(dead_code)]
#[repr(transparent)] #[repr(transparent)]
pub struct LevelKeyboard(*const c_void); pub struct LevelKeyboard(*const c_void);
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
/// Positions the layout contents within the available space. /// Positions the layout contents within the available space.
/// The origin of the transformation is the point inside the margins. /// The origin of the transformation is the point inside the margins.
#[no_mangle] #[no_mangle]
@ -183,6 +185,13 @@ pub mod c {
layout.kind.clone() as u32 layout.kind.clone() as u32
} }
#[no_mangle]
pub extern "C"
fn squeek_layout_get_purpose(layout: *const Layout) -> u32 {
let layout = unsafe { &*layout };
layout.purpose.clone() as u32
}
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_free(layout: *mut Layout) { fn squeek_layout_free(layout: *mut Layout) {
@ -271,7 +280,7 @@ pub mod c {
let state = layout.find_button_by_position(point) let state = layout.find_button_by_position(point)
.map(|place| place.button.state.clone()); .map(|place| place.button.state.clone());
if let Some(state) = state { if let Some(state) = state {
seat::handle_press_key( seat::handle_press_key(
layout, layout,
@ -311,7 +320,7 @@ pub mod c {
let point = ui_backend.widget_to_layout.forward( let point = ui_backend.widget_to_layout.forward(
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
let pressed = layout.pressed_keys.clone(); let pressed = layout.pressed_keys.clone();
let button_info = { let button_info = {
let place = layout.find_button_by_position(point); let place = layout.find_button_by_position(point);
@ -370,11 +379,11 @@ pub mod c {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
fn near(a: f64, b: f64) -> bool { fn near(a: f64, b: f64) -> bool {
(a - b).abs() < ((a + b) * 0.001f64).abs() (a - b).abs() < ((a + b) * 0.001f64).abs()
} }
#[test] #[test]
fn transform_back() { fn transform_back() {
let transform = Transformation { let transform = Transformation {
@ -413,7 +422,7 @@ pub enum Label {
/// The graphical representation of a button /// The graphical representation of a button
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Button { pub struct Button {
/// ID string, e.g. for CSS /// ID string, e.g. for CSS
pub name: CString, pub name: CString,
/// Label to display to the user /// Label to display to the user
pub label: Label, pub label: Label,
@ -573,11 +582,11 @@ impl View {
offset: &row.0 + c::Point { x: button.0, y: 0.0 }, offset: &row.0 + c::Point { x: button.0, y: 0.0 },
}) })
} }
pub fn get_size(&self) -> Size { pub fn get_size(&self) -> Size {
self.size.clone() self.size.clone()
} }
/// Returns positioned rows, with appropriate x offsets (centered) /// Returns positioned rows, with appropriate x offsets (centered)
pub fn get_rows(&self) -> &Vec<(c::Point, Row)> { pub fn get_rows(&self) -> &Vec<(c::Point, Row)> {
&self.rows &self.rows
@ -627,6 +636,7 @@ pub enum LatchedState {
pub struct Layout { pub struct Layout {
pub margins: Margins, pub margins: Margins,
pub kind: ArrangementKind, pub kind: ArrangementKind,
pub purpose: ContentPurpose,
pub current_view: String, pub current_view: String,
// If current view is latched, // If current view is latched,
@ -676,7 +686,7 @@ impl fmt::Display for NoSuchView {
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special. // The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
// Cloning could also be used. // Cloning could also be used.
impl Layout { impl Layout {
pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout { pub fn new(data: LayoutData, kind: ArrangementKind, purpose: ContentPurpose) -> Layout {
Layout { Layout {
kind, kind,
current_view: "base".to_owned(), current_view: "base".to_owned(),
@ -685,6 +695,7 @@ impl Layout {
keymaps: data.keymaps, keymaps: data.keymaps,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
margins: data.margins, margins: data.margins,
purpose,
} }
} }
@ -731,7 +742,7 @@ impl Layout {
), ),
} }
} }
pub fn calculate_transformation( pub fn calculate_transformation(
&self, &self,
available: Size, available: Size,
@ -770,7 +781,7 @@ impl Layout {
} }
} }
} }
fn apply_view_transition( fn apply_view_transition(
&mut self, &mut self,
action: &Action, action: &Action,
@ -812,7 +823,7 @@ impl Layout {
/// ///
/// Although the state is not defined at the keys /// Although the state is not defined at the keys
/// (it's in the relationship between view and action), /// (it's in the relationship between view and action),
/// keys go through the following stages when clicked repeatedly: /// keys go through the following stages when clicked repeatedly:
/// unlocked+unlatched -> locked+latched -> locked+unlatched /// unlocked+unlatched -> locked+latched -> locked+unlatched
/// -> unlocked+unlatched /// -> unlocked+unlatched
fn process_action_for_view<'a>( fn process_action_for_view<'a>(
@ -906,7 +917,7 @@ mod procedures {
}) })
}).collect() }).collect()
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -1113,7 +1124,7 @@ mod test {
state: state, state: state,
}) })
} }
#[test] #[test]
fn latch_lock_unlock() { fn latch_lock_unlock() {
let action = Action::LockView { let action = Action::LockView {
@ -1152,7 +1163,7 @@ mod test {
latches: true, latches: true,
looks_locked_from: vec![], looks_locked_from: vec![],
}; };
let submit = Action::Erase; let submit = Action::Erase;
let view = View::new(vec![( let view = View::new(vec![(
@ -1194,7 +1205,8 @@ mod test {
"base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view), "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
}, },
}; purpose: ContentPurpose::Normal,
};
// Basic cycle // Basic cycle
layout.apply_view_transition(&switch); layout.apply_view_transition(&switch);
@ -1220,14 +1232,14 @@ mod test {
latches: true, latches: true,
looks_locked_from: vec![], looks_locked_from: vec![],
}; };
let unswitch = Action::LockView { let unswitch = Action::LockView {
lock: "locked".into(), lock: "locked".into(),
unlock: "unlocked".into(), unlock: "unlocked".into(),
latches: false, latches: false,
looks_locked_from: vec![], looks_locked_from: vec![],
}; };
let submit = Action::Erase; let submit = Action::Erase;
let view = View::new(vec![( let view = View::new(vec![(
@ -1270,7 +1282,8 @@ mod test {
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view), "unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
}, },
}; purpose: ContentPurpose::Normal,
};
layout.apply_view_transition(&switch); layout.apply_view_transition(&switch);
assert_eq!(&layout.current_view, "locked"); assert_eq!(&layout.current_view, "locked");
@ -1286,14 +1299,14 @@ mod test {
latches: true, latches: true,
looks_locked_from: vec![], looks_locked_from: vec![],
}; };
let switch_again = Action::LockView { let switch_again = Action::LockView {
lock: "ĄĘ".into(), lock: "ĄĘ".into(),
unlock: "locked".into(), unlock: "locked".into(),
latches: true, latches: true,
looks_locked_from: vec![], looks_locked_from: vec![],
}; };
let submit = Action::Erase; let submit = Action::Erase;
let view = View::new(vec![( let view = View::new(vec![(
@ -1336,7 +1349,8 @@ mod test {
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view), "ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view),
}, },
}; purpose: ContentPurpose::Normal,
};
// Latch twice, then Ąto-unlatch across 2 levels // Latch twice, then Ąto-unlatch across 2 levels
layout.apply_view_transition(&switch); layout.apply_view_transition(&switch);
@ -1436,6 +1450,7 @@ mod test {
views: hashmap! { views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view), String::new() => (c::Point { x: 0.0, y: 0.0 }, view),
}, },
purpose: ContentPurpose::Normal,
}; };
assert_eq!( assert_eq!(
layout.calculate_inner_size(), layout.calculate_inner_size(),

View File

@ -93,6 +93,7 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
// Others // Others
("number/us", include_str!("../data/keyboards/number/us.yaml")), ("number/us", include_str!("../data/keyboards/number/us.yaml")),
("pin/us", include_str!("../data/keyboards/pin/us.yaml")),
// Terminal // Terminal
("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")), ("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")),

View File

@ -100,6 +100,7 @@ foreach layout : [
# Block: Not languages. # Block: Not languages.
'emoji/us', 'emoji/us',
'number/us', 'number/us',
'pin/us',
] ]
extra = [] extra = []
if layout.startswith('emoji/') if layout.startswith('emoji/')