diff --git a/data/common.css b/data/common.css new file mode 100644 index 00000000..0acd83b4 --- /dev/null +++ b/data/common.css @@ -0,0 +1,6 @@ +/* Theme independent style */ + +sq_view.pin sq_button { + border-radius: 0px; + margin: 1px 1px 1px 1px; +} diff --git a/data/keyboards/pin/us.yaml b/data/keyboards/pin/us.yaml new file mode 100644 index 00000000..85467af7 --- /dev/null +++ b/data/keyboards/pin/us.yaml @@ -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" + diff --git a/data/squeekboard.gresources.xml b/data/squeekboard.gresources.xml index 8e98c977..795d22b7 100644 --- a/data/squeekboard.gresources.xml +++ b/data/squeekboard.gresources.xml @@ -1,6 +1,7 @@ + common.css style.css style-Adwaita:dark.css popup.ui diff --git a/data/style-Adwaita:dark.css b/data/style-Adwaita:dark.css index 6b52750f..8a89a4d4 100644 --- a/data/style-Adwaita:dark.css +++ b/data/style-Adwaita:dark.css @@ -58,3 +58,5 @@ sq_button.small { background: #1c71d8; border-color: #3584e4; } + +@import url("resource:///sm/puri/squeekboard/common.css"); diff --git a/data/style.css b/data/style.css index 40b8ffa6..3feec5c7 100644 --- a/data/style.css +++ b/data/style.css @@ -61,3 +61,5 @@ sq_button.small { background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/ border-color: @borders; /*#3584e4;*/ } + +@import url("resource:///sm/puri/squeekboard/common.css"); diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 1794eaa3..e29041aa 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -315,6 +315,7 @@ eek_renderer_new (LevelKeyboard *keyboard, renderer_init(renderer); renderer->pcontext = pcontext; g_object_ref (renderer->pcontext); + const char *purpose_class = "normal"; /* Create a style context for the layout */ 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) { 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()); renderer->button_context = gtk_style_context_new (); gtk_style_context_set_path(renderer->button_context, path); diff --git a/src/data/loading.rs b/src/data/loading.rs index 8f362a67..30cbcc7f 100644 --- a/src/data/loading.rs +++ b/src/data/loading.rs @@ -61,7 +61,7 @@ pub mod c { }; 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)) } } @@ -168,9 +168,9 @@ fn get_directory_string( None => match content_purpose { ContentPurpose::Number => Special("number"), ContentPurpose::Digits => Special("number"), - ContentPurpose::Pin => Special("number"), ContentPurpose::Phone => Special("number"), ContentPurpose::Terminal => Special("terminal"), + ContentPurpose::Pin => Special("pin"), _ => Default, }, Some(overlay) => Special(overlay), diff --git a/src/imservice.rs b/src/imservice.rs index ce6a036e..5c30da6c 100644 --- a/src/imservice.rs +++ b/src/imservice.rs @@ -233,7 +233,7 @@ bitflags!{ /// use rs::imservice::ContentPurpose; /// assert_eq!(ContentPurpose::Alpha as u32, 1); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub enum ContentPurpose { Normal = 0, Alpha = 1, diff --git a/src/layout.h b/src/layout.h index 0ed8cd09..e50eac8f 100644 --- a/src/layout.h +++ b/src/layout.h @@ -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); 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_release(struct squeek_layout *layout, diff --git a/src/layout.rs b/src/layout.rs index 810bdf18..14719dd0 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,17 +1,17 @@ /*! * Layout-related data. - * + * * The `View` contains `Row`s and each `Row` contains `Button`s. * They carry data relevant to their positioning only, * except the Button, which also carries some data * about its appearance and function. - * + * * The layout is determined bottom-up, by measuring `Button` sizes, * deriving `Row` sizes from them, and then centering them within the `View`. - * + * * That makes the `View` position immutable, * and therefore different than the other positions. - * + * * Note that it might be a better idea * to make `View` position depend on its contents, * and let the renderer scale and center it within the widget. @@ -32,6 +32,8 @@ use ::manager; use ::submission::{ Submission, SubmitData, Timestamp }; use ::util::find_max_double; +use ::imservice::ContentPurpose; + // Traits use std::borrow::Borrow; use ::logging::Warn; @@ -64,7 +66,7 @@ pub mod c { pub x: f64, pub y: f64, } - + impl Add for Point { type Output = Self; fn add(self, other: Self) -> Self { @@ -81,7 +83,7 @@ pub mod c { } } } - + impl Sub<&Point> for Point { type Output = Point; fn sub(self, other: &Point) -> Point { @@ -152,14 +154,14 @@ pub mod c { } } } - + // This is constructed only in C, no need for warnings #[allow(dead_code)] #[repr(transparent)] pub struct LevelKeyboard(*const c_void); // 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. /// The origin of the transformation is the point inside the margins. #[no_mangle] @@ -183,6 +185,13 @@ pub mod c { 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] pub extern "C" fn squeek_layout_free(layout: *mut Layout) { @@ -271,7 +280,7 @@ pub mod c { let state = layout.find_button_by_position(point) .map(|place| place.button.state.clone()); - + if let Some(state) = state { seat::handle_press_key( layout, @@ -311,7 +320,7 @@ pub mod c { let point = ui_backend.widget_to_layout.forward( Point { x: x_widget, y: y_widget } ); - + let pressed = layout.pressed_keys.clone(); let button_info = { let place = layout.find_button_by_position(point); @@ -370,11 +379,11 @@ pub mod c { #[cfg(test)] mod test { use super::*; - + fn near(a: f64, b: f64) -> bool { (a - b).abs() < ((a + b) * 0.001f64).abs() } - + #[test] fn transform_back() { let transform = Transformation { @@ -413,7 +422,7 @@ pub enum Label { /// The graphical representation of a button #[derive(Clone, Debug)] pub struct Button { - /// ID string, e.g. for CSS + /// ID string, e.g. for CSS pub name: CString, /// Label to display to the user pub label: Label, @@ -573,11 +582,11 @@ impl View { offset: &row.0 + c::Point { x: button.0, y: 0.0 }, }) } - + pub fn get_size(&self) -> Size { self.size.clone() } - + /// Returns positioned rows, with appropriate x offsets (centered) pub fn get_rows(&self) -> &Vec<(c::Point, Row)> { &self.rows @@ -627,6 +636,7 @@ pub enum LatchedState { pub struct Layout { pub margins: Margins, pub kind: ArrangementKind, + pub purpose: ContentPurpose, pub current_view: String, // If current view is latched, @@ -676,7 +686,7 @@ impl fmt::Display for NoSuchView { // The usage of &mut on Rc> doesn't mean anything special. // Cloning could also be used. impl Layout { - pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout { + pub fn new(data: LayoutData, kind: ArrangementKind, purpose: ContentPurpose) -> Layout { Layout { kind, current_view: "base".to_owned(), @@ -685,6 +695,7 @@ impl Layout { keymaps: data.keymaps, pressed_keys: HashSet::new(), margins: data.margins, + purpose, } } @@ -731,7 +742,7 @@ impl Layout { ), } } - + pub fn calculate_transformation( &self, available: Size, @@ -770,7 +781,7 @@ impl Layout { } } } - + fn apply_view_transition( &mut self, action: &Action, @@ -812,7 +823,7 @@ impl Layout { /// /// Although the state is not defined at the keys /// (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 fn process_action_for_view<'a>( @@ -906,7 +917,7 @@ mod procedures { }) }).collect() } - + #[cfg(test)] mod test { use super::*; @@ -1113,7 +1124,7 @@ mod test { state: state, }) } - + #[test] fn latch_lock_unlock() { let action = Action::LockView { @@ -1152,7 +1163,7 @@ mod test { latches: true, looks_locked_from: vec![], }; - + let submit = Action::Erase; let view = View::new(vec![( @@ -1194,7 +1205,8 @@ mod test { "base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view), }, - }; + purpose: ContentPurpose::Normal, + }; // Basic cycle layout.apply_view_transition(&switch); @@ -1220,14 +1232,14 @@ mod test { latches: true, looks_locked_from: vec![], }; - + let unswitch = Action::LockView { lock: "locked".into(), unlock: "unlocked".into(), latches: false, looks_locked_from: vec![], }; - + let submit = Action::Erase; let view = View::new(vec![( @@ -1270,7 +1282,8 @@ mod test { "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view), }, - }; + purpose: ContentPurpose::Normal, + }; layout.apply_view_transition(&switch); assert_eq!(&layout.current_view, "locked"); @@ -1286,14 +1299,14 @@ mod test { latches: true, looks_locked_from: vec![], }; - + let switch_again = Action::LockView { lock: "ĄĘ".into(), unlock: "locked".into(), latches: true, looks_locked_from: vec![], }; - + let submit = Action::Erase; let view = View::new(vec![( @@ -1336,7 +1349,8 @@ mod test { "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()), "ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view), }, - }; + purpose: ContentPurpose::Normal, + }; // Latch twice, then Ąto-unlatch across 2 levels layout.apply_view_transition(&switch); @@ -1436,6 +1450,7 @@ mod test { views: hashmap! { String::new() => (c::Point { x: 0.0, y: 0.0 }, view), }, + purpose: ContentPurpose::Normal, }; assert_eq!( layout.calculate_inner_size(), diff --git a/src/resources.rs b/src/resources.rs index 7a048985..cfc3c3f8 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -93,6 +93,7 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[ // Others ("number/us", include_str!("../data/keyboards/number/us.yaml")), + ("pin/us", include_str!("../data/keyboards/pin/us.yaml")), // Terminal ("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")), diff --git a/tests/meson.build b/tests/meson.build index 6db349f5..d43fe9a8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -100,6 +100,7 @@ foreach layout : [ # Block: Not languages. 'emoji/us', 'number/us', + 'pin/us', ] extra = [] if layout.startswith('emoji/')