From 649f67d31954d14e5640b75d5449b58983b6e4e0 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Fri, 30 Sep 2022 17:42:00 +0000 Subject: [PATCH] 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()