From c017a773ea5631d777ee8fae1fb0eab74279d820 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Fri, 30 Sep 2022 17:12:00 +0000 Subject: [PATCH 1/9] layout: Gather state-related stuff together --- src/drawing.rs | 2 +- src/layout.rs | 123 ++++++++++++++++++++++++++++--------------------- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/src/drawing.rs b/src/drawing.rs index 6ccf925e..7d62ed48 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -91,7 +91,7 @@ mod c { &state.action, &active_modifiers, layout.get_view_latched(), - &layout.current_view, + &layout.state.current_view, ); if state.pressed == keyboard::PressType::Pressed || locked != LockedStyle::Free diff --git a/src/layout.rs b/src/layout.rs index b472c373..6c20a3f1 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -238,7 +238,7 @@ pub mod c { // The list must be copied, // because it will be mutated in the loop - for key in layout.pressed_keys.clone() { + for key in layout.state.pressed_keys.clone() { let key: &Rc> = key.borrow(); seat::handle_release_key( layout, @@ -265,7 +265,7 @@ pub mod c { let mut submission = submission.borrow_mut(); // The list must be copied, // because it will be mutated in the loop - for key in layout.pressed_keys.clone() { + for key in layout.state.pressed_keys.clone() { let key: &Rc> = key.borrow(); seat::handle_release_key( layout, @@ -344,7 +344,7 @@ pub mod c { Point { x: x_widget, y: y_widget } ); - let pressed = layout.pressed_keys.clone(); + let pressed = layout.state.pressed_keys.clone(); let button_info = { let place = layout.find_button_by_position(point); place.map(|place| {( @@ -658,15 +658,11 @@ pub enum LatchedState { // Arrangement (views) + details (keymap) + State (keys) /// State of the UI, contains the backend as well pub struct Layout { + pub state: LayoutState, + pub margins: Margins, pub kind: ArrangementKind, pub purpose: ContentPurpose, - pub current_view: String, - - // If current view is latched, - // clicking any button that emits an action (erase, submit, set modifier) - // will cause lock buttons to unlatch. - view_latched: LatchedState, // Views own the actual buttons which have state // Maybe they should own UI only, @@ -677,7 +673,16 @@ pub struct Layout { // Non-UI stuff /// xkb keymaps applicable to the contained keys. Unchangeable pub keymaps: Vec, - // Changeable state +} + +/// Changeable state that can't be derived from the definition of the layout +pub struct LayoutState { + pub current_view: String, + + // If current view is latched, + // clicking any button that emits an action (erase, submit, set modifier) + // will cause lock buttons to unlatch. + view_latched: LatchedState, // a Vec would be enough, but who cares, this will be small & fast enough // TODO: turn those into per-input point *_buttons to track dragging. // The renderer doesn't need the list of pressed keys any more, @@ -713,28 +718,30 @@ impl Layout { pub fn new(data: LayoutData, kind: ArrangementKind, purpose: ContentPurpose) -> Layout { Layout { kind, - current_view: "base".to_owned(), - view_latched: LatchedState::Not, views: data.views, keymaps: data.keymaps, - pressed_keys: HashSet::new(), margins: data.margins, purpose, + state: LayoutState { + current_view: "base".to_owned(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, } } pub fn get_current_view_position(&self) -> &(c::Point, View) { &self.views - .get(&self.current_view).expect("Selected nonexistent view") + .get(&self.state.current_view).expect("Selected nonexistent view") } pub fn get_current_view(&self) -> &View { - &self.views.get(&self.current_view).expect("Selected nonexistent view").1 + &self.views.get(&self.state.current_view).expect("Selected nonexistent view").1 } fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { if self.views.contains_key(&view) { - self.current_view = view; + self.state.current_view = view; Ok(()) } else { Err(NoSuchView) @@ -744,7 +751,7 @@ impl Layout { // Layout is passed around mutably, // so better keep the field away from direct access. pub fn get_view_latched(&self) -> &LatchedState { - &self.view_latched + &self.state.view_latched } /// Calculates size without margins @@ -816,8 +823,8 @@ impl Layout { ) { let (transition, new_latched) = Layout::process_action_for_view( action, - &self.current_view, - &self.view_latched, + &self.state.current_view, + &self.state.view_latched, ); match transition { @@ -826,15 +833,15 @@ impl Layout { ViewTransition::NoChange => {}, }; - self.view_latched = new_latched; + self.state.view_latched = new_latched; } /// Unlatch all latched keys, /// so that the new view is the one before first press. fn unstick_locks(&mut self) { - if let LatchedState::FromView(name) = self.view_latched.clone() { + if let LatchedState::FromView(name) = self.state.view_latched.clone() { match self.set_view(name.clone()) { - Ok(_) => { self.view_latched = LatchedState::Not; } + Ok(_) => { self.state.view_latched = LatchedState::Not; } Err(e) => log_print!( logging::Level::Bug, "Bad view {}, can't unlatch ({:?})", @@ -1004,7 +1011,7 @@ mod seat { time: Timestamp, rckey: &Rc>, ) { - if !layout.pressed_keys.insert(::util::Pointer(rckey.clone())) { + if !layout.state.pressed_keys.insert(::util::Pointer(rckey.clone())) { log_print!( logging::Level::Bug, "Key {:?} was already pressed", rckey, @@ -1118,7 +1125,7 @@ mod seat { let pointer = ::util::Pointer(rckey.clone()); // Apply state changes - layout.pressed_keys.remove(&pointer); + layout.state.pressed_keys.remove(&pointer); // Commit activated button state changes RefCell::replace(rckey, key); } @@ -1220,11 +1227,13 @@ mod test { )]); let mut layout = Layout { - current_view: "base".into(), - view_latched: LatchedState::Not, + state: LayoutState { + current_view: "base".into(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, keymaps: Vec::new(), kind: ArrangementKind::Base, - pressed_keys: HashSet::new(), margins: Margins { top: 0.0, left: 0.0, @@ -1243,18 +1252,18 @@ mod test { // Basic cycle layout.apply_view_transition(&switch); - assert_eq!(&layout.current_view, "locked"); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&switch); - assert_eq!(&layout.current_view, "locked"); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&submit); - assert_eq!(&layout.current_view, "locked"); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&switch); - assert_eq!(&layout.current_view, "base"); + assert_eq!(&layout.state.current_view, "base"); layout.apply_view_transition(&switch); // Unlatch - assert_eq!(&layout.current_view, "locked"); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&submit); - assert_eq!(&layout.current_view, "base"); + assert_eq!(&layout.state.current_view, "base"); } #[test] @@ -1296,11 +1305,13 @@ mod test { )]); let mut layout = Layout { - current_view: "base".into(), - view_latched: LatchedState::Not, + state: LayoutState { + current_view: "base".into(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, keymaps: Vec::new(), kind: ArrangementKind::Base, - pressed_keys: HashSet::new(), margins: Margins { top: 0.0, left: 0.0, @@ -1319,9 +1330,9 @@ mod test { }; layout.apply_view_transition(&switch); - assert_eq!(&layout.current_view, "locked"); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&unswitch); - assert_eq!(&layout.current_view, "unlocked"); + assert_eq!(&layout.state.current_view, "unlocked"); } #[test] @@ -1363,11 +1374,13 @@ mod test { )]); let mut layout = Layout { - current_view: "base".into(), - view_latched: LatchedState::Not, + state: LayoutState { + current_view: "base".into(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, keymaps: Vec::new(), kind: ArrangementKind::Base, - pressed_keys: HashSet::new(), margins: Margins { top: 0.0, left: 0.0, @@ -1387,14 +1400,14 @@ mod test { // Latch twice, then Ąto-unlatch across 2 levels layout.apply_view_transition(&switch); - println!("{:?}", layout.view_latched); - assert_eq!(&layout.current_view, "locked"); + println!("{:?}", layout.state.view_latched); + assert_eq!(&layout.state.current_view, "locked"); layout.apply_view_transition(&switch_again); - println!("{:?}", layout.view_latched); - assert_eq!(&layout.current_view, "ĄĘ"); + println!("{:?}", layout.state.view_latched); + assert_eq!(&layout.state.current_view, "ĄĘ"); layout.apply_view_transition(&submit); - println!("{:?}", layout.view_latched); - assert_eq!(&layout.current_view, "base"); + println!("{:?}", layout.state.view_latched); + assert_eq!(&layout.state.current_view, "base"); } #[test] @@ -1468,11 +1481,13 @@ mod test { ), ]); let layout = Layout { - current_view: String::new(), - view_latched: LatchedState::Not, + state: LayoutState { + current_view: String::new(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, keymaps: Vec::new(), kind: ArrangementKind::Base, - pressed_keys: HashSet::new(), // Lots of bottom margin margins: Margins { top: 0.0, @@ -1521,11 +1536,13 @@ mod test { ), ]); let layout = Layout { - current_view: String::new(), - view_latched: LatchedState::Not, + state: LayoutState { + current_view: String::new(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, keymaps: Vec::new(), kind: ArrangementKind::Base, - pressed_keys: HashSet::new(), margins: Margins { top: 0.0, left: 0.0, From 649f67d31954d14e5640b75d5449b58983b6e4e0 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Fri, 30 Sep 2022 17:42:00 +0000 Subject: [PATCH 2/9] layout: Split out static data This will make it possible later to cache this data or compare for best size selection without hassle. --- src/data/loading.rs | 4 +- src/data/parsing.rs | 4 +- src/layout.rs | 254 +++++++++++++++++++++++--------------------- src/submission.rs | 4 +- 4 files changed, 136 insertions(+), 130 deletions(-) diff --git a/src/data/loading.rs b/src/data/loading.rs index b173477a..cd2ad3a2 100644 --- a/src/data/loading.rs +++ b/src/data/loading.rs @@ -192,7 +192,7 @@ fn iter_layout_sources( } fn load_layout_data(source: DataSource) - -> Result<::layout::LayoutData, LoadError> + -> Result<::layout::LayoutParseData, LoadError> { let handler = logging::Print {}; match source { @@ -217,7 +217,7 @@ fn load_layout_data_with_fallback( kind: ArrangementKind, purpose: ContentPurpose, overlay: Option<&str>, -) -> (ArrangementKind, layout::LayoutData) { +) -> (ArrangementKind, layout::LayoutParseData) { // Build the path to the right keyboard layout subdirectory let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR") diff --git a/src/data/parsing.rs b/src/data/parsing.rs index 820362cc..4e113433 100644 --- a/src/data/parsing.rs +++ b/src/data/parsing.rs @@ -157,7 +157,7 @@ impl Layout { } pub fn build(self, mut warning_handler: H) - -> (Result<::layout::LayoutData, FormattingError>, H) + -> (Result<::layout::LayoutParseData, FormattingError>, H) { let button_names = self.views.values() .flat_map(|rows| { @@ -279,7 +279,7 @@ impl Layout { }; ( - Ok(::layout::LayoutData { + Ok(layout::LayoutParseData { views: views, keymaps: keymaps.into_iter().map(|keymap_str| CString::new(keymap_str) diff --git a/src/layout.rs b/src/layout.rs index 6c20a3f1..a68a480e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -182,7 +182,7 @@ pub mod c { allocation_height: f64, ) -> Transformation { let layout = unsafe { &*layout }; - layout.calculate_transformation(Size { + layout.shape.calculate_transformation(Size { width: allocation_width, height: allocation_height, }) @@ -192,14 +192,14 @@ pub mod c { pub extern "C" fn squeek_layout_get_kind(layout: *const Layout) -> u32 { let layout = unsafe { &*layout }; - layout.kind.clone() as u32 + layout.shape.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 + layout.shape.purpose.clone() as u32 } #[no_mangle] @@ -654,28 +654,15 @@ pub enum LatchedState { Not, } -// TODO: split into sth like -// Arrangement (views) + details (keymap) + State (keys) -/// State of the UI, contains the backend as well +/// Associates the state of a layout with its definition. +/// Contains everything necessary to present this layout to the user +/// and to determine its reactions to inputs. pub struct Layout { pub state: LayoutState, - - pub margins: Margins, - pub kind: ArrangementKind, - pub purpose: ContentPurpose, - - // Views own the actual buttons which have state - // Maybe they should own UI only, - // and keys should be owned by a dedicated non-UI-State? - /// Point is the offset within the layout - pub views: HashMap, - - // Non-UI stuff - /// xkb keymaps applicable to the contained keys. Unchangeable - pub keymaps: Vec, + pub shape: LayoutData, } -/// Changeable state that can't be derived from the definition of the layout +/// Changeable state that can't be derived from the definition of the layout. pub struct LayoutState { pub current_view: String, @@ -694,13 +681,31 @@ pub struct LayoutState { } /// A builder structure for picking up layout data from storage -pub struct LayoutData { +pub struct LayoutParseData { /// Point is the offset within layout pub views: HashMap, + /// xkb keymaps applicable to the contained keys pub keymaps: Vec, pub margins: Margins, } +/// Static, cacheable information for the layout +pub struct LayoutData { + pub margins: Margins, + pub kind: ArrangementKind, + pub purpose: ContentPurpose, + + // Views own the actual buttons which have state + // Maybe they should own UI only, + // and keys should be owned by a dedicated non-UI-State? + /// Point is the offset within the layout + pub views: HashMap, + + // Non-UI stuff + /// xkb keymaps applicable to the contained keys. Unchangeable + pub keymaps: Vec, +} + #[derive(Debug)] struct NoSuchView; @@ -710,50 +715,8 @@ impl fmt::Display for NoSuchView { } } -// Unfortunately, changes are not atomic due to mutability :( -// An error will not be recoverable -// 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, purpose: ContentPurpose) -> Layout { - Layout { - kind, - views: data.views, - keymaps: data.keymaps, - margins: data.margins, - purpose, - state: LayoutState { - current_view: "base".to_owned(), - view_latched: LatchedState::Not, - pressed_keys: HashSet::new(), - }, - } - } - - pub fn get_current_view_position(&self) -> &(c::Point, View) { - &self.views - .get(&self.state.current_view).expect("Selected nonexistent view") - } - - pub fn get_current_view(&self) -> &View { - &self.views.get(&self.state.current_view).expect("Selected nonexistent view").1 - } - - fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { - if self.views.contains_key(&view) { - self.state.current_view = view; - Ok(()) - } else { - Err(NoSuchView) - } - } - - // Layout is passed around mutably, - // so better keep the field away from direct access. - pub fn get_view_latched(&self) -> &LatchedState { - &self.state.view_latched - } +impl LayoutData { /// Calculates size without margins fn calculate_inner_size(&self) -> Size { View::calculate_super_size( @@ -797,6 +760,53 @@ impl Layout { scale_y: 1.0, }) } +} + +// Unfortunately, changes are not atomic due to mutability :( +// An error will not be recoverable +// The usage of &mut on Rc> doesn't mean anything special. +// Cloning could also be used. +impl Layout { + pub fn new(data: LayoutParseData, kind: ArrangementKind, purpose: ContentPurpose) -> Layout { + Layout { + shape: LayoutData { + kind, + views: data.views, + keymaps: data.keymaps, + margins: data.margins, + purpose, + }, + state: LayoutState { + current_view: "base".to_owned(), + view_latched: LatchedState::Not, + pressed_keys: HashSet::new(), + }, + } + } + + pub fn get_current_view_position(&self) -> &(c::Point, View) { + &self.shape.views + .get(&self.state.current_view).expect("Selected nonexistent view") + } + + pub fn get_current_view(&self) -> &View { + &self.shape.views.get(&self.state.current_view).expect("Selected nonexistent view").1 + } + + fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { + if self.shape.views.contains_key(&view) { + self.state.current_view = view; + Ok(()) + } else { + Err(NoSuchView) + } + } + + // Layout is passed around mutably, + // so better keep the field away from direct access. + pub fn get_view_latched(&self) -> &LatchedState { + &self.state.view_latched + } fn find_button_by_position(&self, point: c::Point) -> Option { let (offset, layout) = self.get_current_view_position(); @@ -1232,22 +1242,24 @@ mod test { view_latched: LatchedState::Not, pressed_keys: HashSet::new(), }, - keymaps: Vec::new(), - kind: ArrangementKind::Base, - margins: Margins { - top: 0.0, - left: 0.0, - right: 0.0, - bottom: 0.0, + shape: LayoutData { + keymaps: Vec::new(), + kind: ArrangementKind::Base, + margins: Margins { + top: 0.0, + left: 0.0, + right: 0.0, + bottom: 0.0, + }, + views: hashmap! { + // Both can use the same structure. + // Switching doesn't depend on the view shape + // as long as the switching button is present. + "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, }, - views: hashmap! { - // Both can use the same structure. - // Switching doesn't depend on the view shape - // as long as the switching button is present. - "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 @@ -1310,23 +1322,25 @@ mod test { view_latched: LatchedState::Not, pressed_keys: HashSet::new(), }, - keymaps: Vec::new(), - kind: ArrangementKind::Base, - margins: Margins { - top: 0.0, - left: 0.0, - right: 0.0, - bottom: 0.0, + shape: LayoutData { + keymaps: Vec::new(), + kind: ArrangementKind::Base, + margins: Margins { + top: 0.0, + left: 0.0, + right: 0.0, + bottom: 0.0, + }, + views: hashmap! { + // Both can use the same structure. + // Switching doesn't depend on the view shape + // as long as the switching button is present. + "base".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), + }, + purpose: ContentPurpose::Normal, }, - views: hashmap! { - // Both can use the same structure. - // Switching doesn't depend on the view shape - // as long as the switching button is present. - "base".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), - }, - purpose: ContentPurpose::Normal, }; layout.apply_view_transition(&switch); @@ -1379,23 +1393,25 @@ mod test { view_latched: LatchedState::Not, pressed_keys: HashSet::new(), }, - keymaps: Vec::new(), - kind: ArrangementKind::Base, - margins: Margins { - top: 0.0, - left: 0.0, - right: 0.0, - bottom: 0.0, + shape: LayoutData { + keymaps: Vec::new(), + kind: ArrangementKind::Base, + margins: Margins { + top: 0.0, + left: 0.0, + right: 0.0, + bottom: 0.0, + }, + views: hashmap! { + // All can use the same structure. + // Switching doesn't depend on the view shape + // as long as the switching button is present. + "base".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), + }, + purpose: ContentPurpose::Normal, }, - views: hashmap! { - // All can use the same structure. - // Switching doesn't depend on the view shape - // as long as the switching button is present. - "base".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), - }, - purpose: ContentPurpose::Normal, }; // Latch twice, then Ąto-unlatch across 2 levels @@ -1480,12 +1496,7 @@ mod test { )]), ), ]); - let layout = Layout { - state: LayoutState { - current_view: String::new(), - view_latched: LatchedState::Not, - pressed_keys: HashSet::new(), - }, + let layout = LayoutData { keymaps: Vec::new(), kind: ArrangementKind::Base, // Lots of bottom margin @@ -1535,12 +1546,7 @@ mod test { )]), ), ]); - let layout = Layout { - state: LayoutState { - current_view: String::new(), - view_latched: LatchedState::Not, - pressed_keys: HashSet::new(), - }, + let layout = LayoutData { keymaps: Vec::new(), kind: ArrangementKind::Base, margins: Margins { diff --git a/src/submission.rs b/src/submission.rs index 39da68f1..40ddae7e 100644 --- a/src/submission.rs +++ b/src/submission.rs @@ -52,7 +52,7 @@ pub mod c { let submission = submission.clone_ref(); let mut submission = submission.borrow_mut(); let layout = unsafe { &*layout }; - submission.use_layout(layout, Timestamp(time)); + submission.use_layout(&layout.shape, Timestamp(time)); } #[no_mangle] @@ -303,7 +303,7 @@ impl Submission { } } - pub fn use_layout(&mut self, layout: &layout::Layout, time: Timestamp) { + pub fn use_layout(&mut self, layout: &layout::LayoutData, time: Timestamp) { self.keymap_fds = layout.keymaps.iter() .map(|keymap_str| vkeyboard::c::KeyMap::from_cstr( keymap_str.as_c_str() From 5e12666b8fd4a3e7d93d26000c00eaea00820273 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Sat, 1 Oct 2022 11:11:30 +0000 Subject: [PATCH 3/9] layout: Pressed buttons are now a list of indices This makes it possible to copy layout state wholesale, as well as to drop shared state of keys. --- src/layout.rs | 213 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 73 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index a68a480e..dc41d5c0 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -238,16 +238,20 @@ pub mod c { // The list must be copied, // because it will be mutated in the loop - for key in layout.state.pressed_keys.clone() { - let key: &Rc> = key.borrow(); - seat::handle_release_key( - layout, - &mut submission, - Some(&ui_backend), - time, - Some((&popover_state, app_state.clone())), - key, - ); + for button in layout.state.pressed_buttons.clone() { + if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) { + seat::handle_release_key( + layout, + &mut submission, + Some(&ui_backend), + time, + Some((&popover_state, app_state.clone())), + &key, + button, + ); + } else { + log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button); + } } drawing::queue_redraw(ui_keyboard); } @@ -265,16 +269,21 @@ pub mod c { let mut submission = submission.borrow_mut(); // The list must be copied, // because it will be mutated in the loop - for key in layout.state.pressed_keys.clone() { - let key: &Rc> = key.borrow(); - seat::handle_release_key( - layout, - &mut submission, - None, // don't update UI - Timestamp(time), - None, // don't switch layouts - &mut key.clone(), - ); + for button in layout.state.pressed_buttons.clone() { + if let Some(key) = layout.shape.get_key(&button) { + let key: &Rc> = key.borrow(); + seat::handle_release_key( + layout, + &mut submission, + None, // don't update UI + Timestamp(time), + None, // don't switch layouts + &mut key.clone(), + button, + ); + } else { + log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button); + } } } @@ -296,14 +305,20 @@ pub mod c { ); let state = layout.find_button_by_position(point) - .map(|place| place.button.state.clone()); + .map(|(place, index)| (place.button.state.clone(), index)); - if let Some(state) = state { + if let Some((state, (row, position_in_row))) = state { + let button = ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }; seat::handle_press_key( layout, &mut submission, Timestamp(time), &state, + button, ); // maybe TODO: draw on the display buffer here drawing::queue_redraw(ui_keyboard); @@ -344,39 +359,48 @@ pub mod c { Point { x: x_widget, y: y_widget } ); - let pressed = layout.state.pressed_keys.clone(); + let pressed = layout.state.pressed_buttons.clone(); let button_info = { let place = layout.find_button_by_position(point); - place.map(|place| {( + place.map(|(place, index)| {( place.button.state.clone(), - place.button.clone(), - place.offset, + index, )}) }; - if let Some((state, _button, _view_position)) = button_info { + if let Some((state, (row, position_in_row))) = button_info { let mut found = false; - for wrapped_key in pressed { - let key: &Rc> = wrapped_key.borrow(); - if Rc::ptr_eq(&state, &wrapped_key.0) { - found = true; + for button in pressed { + if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) { + if Rc::ptr_eq(&state, &key) { + found = true; + } else { + seat::handle_release_key( + layout, + &mut submission, + Some(&ui_backend), + time, + Some((&popover_state, app_state.clone())), + &key, + button, + ); + } } else { - seat::handle_release_key( - layout, - &mut submission, - Some(&ui_backend), - time, - Some((&popover_state, app_state.clone())), - key, - ); + log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button); } } if !found { + let button = ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }; seat::handle_press_key( layout, &mut submission, time, &state, + button, ); // maybe TODO: draw on the display buffer here unsafe { @@ -384,16 +408,20 @@ pub mod c { } } } else { - for wrapped_key in pressed { - let key: &Rc> = wrapped_key.borrow(); - seat::handle_release_key( - layout, - &mut submission, - Some(&ui_backend), - time, - Some((&popover_state, app_state.clone())), - key, - ); + for button in pressed { + if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) { + seat::handle_release_key( + layout, + &mut submission, + Some(&ui_backend), + time, + Some((&popover_state, app_state.clone())), + &key, + button, + ); + } else { + log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button); + } } } drawing::queue_redraw(ui_keyboard); @@ -509,8 +537,9 @@ impl Row { } /// Finds the first button that covers the specified point - /// relative to row's position's origin - fn find_button_by_position(&self, x: f64) -> &(f64, Box