layout: Split out static data
This will make it possible later to cache this data or compare for best size selection without hassle.
This commit is contained in:
		@ -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")
 | 
			
		||||
 | 
			
		||||
@ -157,7 +157,7 @@ impl Layout {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn build<H: logging::Handler>(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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										254
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								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<String, (c::Point, View)>,
 | 
			
		||||
 | 
			
		||||
    // Non-UI stuff
 | 
			
		||||
    /// xkb keymaps applicable to the contained keys. Unchangeable
 | 
			
		||||
    pub keymaps: Vec<CString>,
 | 
			
		||||
    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<String, (c::Point, View)>,
 | 
			
		||||
    /// xkb keymaps applicable to the contained keys
 | 
			
		||||
    pub keymaps: Vec<CString>,
 | 
			
		||||
    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<String, (c::Point, View)>,
 | 
			
		||||
 | 
			
		||||
    // Non-UI stuff
 | 
			
		||||
    /// xkb keymaps applicable to the contained keys. Unchangeable
 | 
			
		||||
    pub keymaps: Vec<CString>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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<RefCell<KeyState>> 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<RefCell<KeyState>> 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<ButtonPlace> {
 | 
			
		||||
        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 {
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user