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