layout: Separate button info from state

Splitting out state into a dedicated place lets keep all the immutable metadata in one place, and all state in another.
This commit is contained in:
Dorota Czaplejewicz
2022-10-01 13:37:35 +00:00
parent fc4fa63cc6
commit 74c5ef4a51
6 changed files with 92 additions and 100 deletions

View File

@ -17,13 +17,12 @@ use xkbcommon::xkb;
use super::{ Error, LoadError }; use super::{ Error, LoadError };
use ::action; use ::action;
use ::keyboard::{ use crate::keyboard::{
KeyState, PressType, Key, KeyState, PressType,
generate_keymaps, generate_keycodes, KeyCode, FormattingError generate_keymaps, generate_keycodes, KeyCode, FormattingError
}; };
use ::layout; use ::layout;
use ::logging; use ::logging;
use ::util::hash_map_map;
use ::resources; use ::resources;
// traits, derives // traits, derives
@ -183,7 +182,7 @@ impl Layout {
extract_symbol_names(&button_actions) extract_symbol_names(&button_actions)
); );
let button_states = HashMap::<String, KeyState>::from_iter( let button_states = HashMap::<String, Key>::from_iter(
button_actions.into_iter().map(|(name, action)| { button_actions.into_iter().map(|(name, action)| {
let keycodes = match &action { let keycodes = match &action {
::action::Action::Submit { text: _, keys } => { ::action::Action::Submit { text: _, keys } => {
@ -208,8 +207,7 @@ impl Layout {
}; };
( (
name.into(), name.into(),
KeyState { Key {
pressed: PressType::Released,
keycodes, keycodes,
action, action,
} }
@ -222,13 +220,7 @@ impl Layout {
Ok(v) => v, Ok(v) => v,
}; };
let button_states_cache = hash_map_map( let button_states_cache = button_states;
button_states,
|name, state| {(
name,
Rc::new(RefCell::new(state))
)}
);
let views: Vec<_> = self.views.iter() let views: Vec<_> = self.views.iter()
.map(|(name, view)| { .map(|(name, view)| {
@ -461,7 +453,7 @@ fn create_button<H: logging::Handler>(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
outlines: &HashMap<String, Outline>, outlines: &HashMap<String, Outline>,
name: &str, name: &str,
state: Rc<RefCell<KeyState>>, data: Key,
warning_handler: &mut H, warning_handler: &mut H,
) -> ::layout::Button { ) -> ::layout::Button {
let cname = CString::new(name.clone()) let cname = CString::new(name.clone())
@ -523,7 +515,11 @@ fn create_button<H: logging::Handler>(
height: outline.height, height: outline.height,
}, },
label: label, label: label,
state: state, action: data.action,
keycodes: data.keycodes,
state: Rc::new(
RefCell::new(KeyState { pressed: PressType::Released })
),
} }
} }
@ -677,7 +673,6 @@ mod tests {
out.views["base"].1 out.views["base"].1
.get_rows()[0].1 .get_rows()[0].1
.get_buttons()[0].1 .get_buttons()[0].1
.state.borrow()
.keycodes.len(), .keycodes.len(),
2 2
); );
@ -694,7 +689,6 @@ mod tests {
out.views["base"].1 out.views["base"].1
.get_rows()[0].1 .get_rows()[0].1
.get_buttons()[0].1 .get_buttons()[0].1
.state.borrow()
.keycodes.len(), .keycodes.len(),
1 1
); );

View File

@ -88,7 +88,7 @@ mod c {
let state = RefCell::borrow(&button.state).clone(); let state = RefCell::borrow(&button.state).clone();
let locked = LockedStyle::from_action( let locked = LockedStyle::from_action(
&state.action, &button.action,
&active_modifiers, &active_modifiers,
layout.get_view_latched(), layout.get_view_latched(),
&layout.state.current_view, &layout.state.current_view,

View File

@ -1,6 +1,8 @@
/*! State of the emulated keyboard and keys. /*! State of the emulated keyboard and keys.
* Regards the keyboard as if it was composed of switches. */ * Regards the keyboard as if it was composed of switches. */
use crate::action::Action;
use crate::util;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
@ -10,9 +12,6 @@ use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action;
use ::util;
// Traits // Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -24,7 +23,7 @@ pub enum PressType {
} }
/// The extended, unambiguous layout-keycode /// The extended, unambiguous layout-keycode
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct KeyCode { pub struct KeyCode {
pub code: u32, pub code: u32,
pub keymap_idx: usize, pub keymap_idx: usize,
@ -53,15 +52,19 @@ bitflags!{
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct KeyStateId(*const KeyState); pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct KeyState { pub struct Key {
pub pressed: PressType,
/// A cache of raw keycodes derived from Action::Submit given a keymap /// A cache of raw keycodes derived from Action::Submit given a keymap
pub keycodes: Vec<KeyCode>, pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
pub action: Action, pub action: Action,
} }
#[derive(Debug, Clone)]
pub struct KeyState {
pub pressed: PressType,
}
impl KeyState { impl KeyState {
#[must_use] #[must_use]
pub fn into_released(self) -> KeyState { pub fn into_released(self) -> KeyState {

View File

@ -29,7 +29,7 @@ use crate::action::Action;
use crate::actors; use crate::actors;
use crate::drawing; use crate::drawing;
use crate::float_ord::FloatOrd; use crate::float_ord::FloatOrd;
use crate::keyboard::KeyState; use crate::keyboard::{KeyState, KeyCode};
use crate::logging; use crate::logging;
use crate::popover; use crate::popover;
use crate::receiver; use crate::receiver;
@ -466,7 +466,7 @@ pub enum Label {
IconName(CString), IconName(CString),
} }
/// The graphical representation of a button /// The definition of an interactive button
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Button { pub struct Button {
/// ID string, e.g. for CSS /// ID string, e.g. for CSS
@ -476,7 +476,12 @@ pub struct Button {
pub size: Size, pub size: Size,
/// The name of the visual class applied /// The name of the visual class applied
pub outline_name: CString, pub outline_name: CString,
/// current state, shared with other buttons // action-related stuff
/// A cache of raw keycodes derived from Action::Submit given a keymap
pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released
pub action: Action,
/// Current state, shared with other buttons
pub state: Rc<RefCell<KeyState>>, pub state: Rc<RefCell<KeyState>>,
} }
@ -489,7 +494,7 @@ impl Button {
} }
} }
/// The graphical representation of a row of buttons /// The representation of a row of buttons
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Row { pub struct Row {
/// Buttons together with their offset from the left relative to the row. /// Buttons together with their offset from the left relative to the row.
@ -758,10 +763,14 @@ impl fmt::Display for NoSuchView {
impl LayoutData { impl LayoutData {
fn get_key(&self, button: &ButtonPosition) -> Option<&Rc<RefCell<KeyState>>> { fn get_key(&self, button: &ButtonPosition) -> Option<&Rc<RefCell<KeyState>>> {
self.get_button(button).map(|button| &button.state)
}
fn get_button(&self, button: &ButtonPosition) -> Option<&Button> {
let (_, view) = self.views.get(&button.view)?; let (_, view) = self.views.get(&button.view)?;
let (_, row) = view.rows.get(button.row)?; let (_, row) = view.rows.get(button.row)?;
let (_, key) = row.buttons.get(button.position_in_row)?; let (_, key) = row.buttons.get(button.position_in_row)?;
Some(&key.state) Some(key)
} }
/// Calculates size without margins /// Calculates size without margins
@ -1029,7 +1038,10 @@ mod procedures {
let state = make_state(); let state = make_state();
let state_clone = state.clone(); let state_clone = state.clone();
let button = make_button_with_state("1".into(), state); let button = Box::new(Button {
state,
..make_button("1".into())
});
let button_ptr = as_ptr(&button); let button_ptr = as_ptr(&button);
let row = Row::new(vec!((0.1, button))); let row = Row::new(vec!((0.1, button)));
@ -1078,12 +1090,13 @@ mod seat {
"Button {:?} was already pressed", button, "Button {:?} was already pressed", button,
); );
} else { } else {
layout.state.pressed_buttons.push(button); layout.state.pressed_buttons.push(button.clone());
} }
let key: KeyState = { let key: KeyState = {
RefCell::borrow(rckey).clone() RefCell::borrow(rckey).clone()
}; };
let action = key.action.clone(); let button = layout.shape.get_button(&button).unwrap();
let action = button.action.clone();
match action { match action {
Action::Submit { Action::Submit {
text: Some(text), text: Some(text),
@ -1091,7 +1104,7 @@ mod seat {
} => submission.handle_press( } => submission.handle_press(
KeyState::get_id(rckey), KeyState::get_id(rckey),
SubmitData::Text(&text), SubmitData::Text(&text),
&key.keycodes, &button.keycodes,
time, time,
), ),
Action::Submit { Action::Submit {
@ -1100,13 +1113,13 @@ mod seat {
} => submission.handle_press( } => submission.handle_press(
KeyState::get_id(rckey), KeyState::get_id(rckey),
SubmitData::Keycodes, SubmitData::Keycodes,
&key.keycodes, &button.keycodes,
time, time,
), ),
Action::Erase => submission.handle_press( Action::Erase => submission.handle_press(
KeyState::get_id(rckey), KeyState::get_id(rckey),
SubmitData::Erase, SubmitData::Erase,
&key.keycodes, &button.keycodes,
time, time,
), ),
_ => {}, _ => {},
@ -1125,12 +1138,13 @@ mod seat {
// and passed always. // and passed always.
manager: Option<(&actors::popover::State, receiver::State)>, manager: Option<(&actors::popover::State, receiver::State)>,
rckey: &Rc<RefCell<KeyState>>, rckey: &Rc<RefCell<KeyState>>,
button: ButtonPosition, button_pos: ButtonPosition,
) { ) {
let key: KeyState = { let key: KeyState = {
RefCell::borrow(rckey).clone() RefCell::borrow(rckey).clone()
}; };
let action = key.action.clone(); let button = layout.shape.get_button(&button_pos).unwrap();
let action = button.action.clone();
layout.apply_view_transition(&action); layout.apply_view_transition(&action);
@ -1188,11 +1202,12 @@ mod seat {
}; };
// Apply state changes // Apply state changes
let pos = layout.state.pressed_buttons.iter().position(|b| b == &button); let pos = layout.state.pressed_buttons.iter()
.position(|b| b == &button_pos);
if let Some(pos) = pos { if let Some(pos) = pos {
layout.state.pressed_buttons.remove(pos); layout.state.pressed_buttons.remove(pos);
} else { } else {
log_print!(logging::Level::Bug, "No button to remove from pressed list: {:?}", button); log_print!(logging::Level::Bug, "No button to remove from pressed list: {:?}", button_pos);
} }
// Commit activated button state changes // Commit activated button state changes
RefCell::replace(rckey, key); RefCell::replace(rckey, key);
@ -1204,33 +1219,26 @@ mod test {
use super::*; use super::*;
use std::ffi::CString; use std::ffi::CString;
use ::keyboard::PressType; use crate::keyboard::{PressType, KeyState};
pub fn make_state_with_action(action: Action) pub fn make_state() -> Rc<RefCell<KeyState>> {
-> Rc<RefCell<::keyboard::KeyState>> Rc::new(RefCell::new(KeyState {
{
Rc::new(RefCell::new(::keyboard::KeyState {
pressed: PressType::Released, pressed: PressType::Released,
keycodes: Vec::new(),
action,
})) }))
} }
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> { pub fn make_button(
make_state_with_action(Action::SetView("default".into()))
}
pub fn make_button_with_state(
name: String, name: String,
state: Rc<RefCell<::keyboard::KeyState>>, ) -> Button {
) -> Box<Button> { Button {
Box::new(Button {
name: CString::new(name.clone()).unwrap(), name: CString::new(name.clone()).unwrap(),
size: Size { width: 0f64, height: 0f64 }, size: Size { width: 0f64, height: 0f64 },
outline_name: CString::new("test").unwrap(), outline_name: CString::new("test").unwrap(),
label: Label::Text(CString::new(name).unwrap()), label: Label::Text(CString::new(name).unwrap()),
state: state, action: Action::SetView("default".into()),
}) keycodes: Vec::new(),
state: make_state(),
}
} }
#[test] #[test]
@ -1279,17 +1287,17 @@ mod test {
Row::new(vec![ Row::new(vec![
( (
0.0, 0.0,
make_button_with_state( Box::new(Button {
"switch".into(), action: switch.clone(),
make_state_with_action(switch.clone()) ..make_button("switch".into())
), }),
), ),
( (
1.0, 1.0,
make_button_with_state( Box::new(Button {
"submit".into(), action: submit.clone(),
make_state_with_action(submit.clone()) ..make_button("submit".into())
), }),
), ),
]), ]),
)]); )]);
@ -1359,17 +1367,17 @@ mod test {
Row::new(vec![ Row::new(vec![
( (
0.0, 0.0,
make_button_with_state( Box::new(Button {
"switch".into(), action: switch.clone(),
make_state_with_action(switch.clone()) ..make_button("switch".into())
), }),
), ),
( (
1.0, 1.0,
make_button_with_state( Box::new(Button {
"submit".into(), action: submit.clone(),
make_state_with_action(submit.clone()) ..make_button("submit".into())
), }),
), ),
]), ]),
)]); )]);
@ -1430,17 +1438,17 @@ mod test {
Row::new(vec![ Row::new(vec![
( (
0.0, 0.0,
make_button_with_state( Box::new(Button {
"switch".into(), action: switch.clone(),
make_state_with_action(switch.clone()) ..make_button("switch".into())
), }),
), ),
( (
1.0, 1.0,
make_button_with_state( Box::new(Button {
"submit".into(), action: submit.clone(),
make_state_with_action(submit.clone()) ..make_button("submit".into())
), }),
), ),
]), ]),
)]); )]);
@ -1496,14 +1504,14 @@ mod test {
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 5.0, height: 10.0 }, size: Size { width: 5.0, height: 10.0 },
..*make_button_with_state("A".into(), make_state()) ..make_button("A".into())
}), }),
), ),
( (
5.0, 5.0,
Box::new(Button { Box::new(Button {
size: Size { width: 5.0, height: 10.0 }, size: Size { width: 5.0, height: 10.0 },
..*make_button_with_state("B".into(), make_state()) ..make_button("B".into())
}), }),
), ),
]), ]),
@ -1515,7 +1523,7 @@ mod test {
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 30.0, height: 10.0 }, size: Size { width: 30.0, height: 10.0 },
..*make_button_with_state("bar".into(), make_state()) ..make_button("bar".into())
}), }),
), ),
]), ]),
@ -1549,7 +1557,7 @@ mod test {
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 1.0, height: 1.0 }, size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state()) ..make_button("foo".into())
}), }),
)]), )]),
), ),
@ -1599,7 +1607,7 @@ mod test {
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 1.0, height: 1.0 }, size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state()) ..make_button("foo".into())
}), }),
)]), )]),
), ),

View File

@ -109,8 +109,7 @@ fn check_layout(layout: Layout, allow_missing_return: bool) {
for (_pos, view) in layout.views.values() { for (_pos, view) in layout.views.values() {
for (_y, row) in view.get_rows() { for (_y, row) in view.get_rows() {
for (_x, button) in row.get_buttons() { for (_x, button) in row.get_buttons() {
let keystate = button.state.borrow(); for keycode in &button.keycodes {
for keycode in &keystate.keycodes {
match xkb_states[keycode.keymap_idx].key_get_one_sym(keycode.code) { match xkb_states[keycode.keymap_idx].key_get_one_sym(keycode.code) {
xkb::KEY_NoSymbol => { xkb::KEY_NoSymbol => {
eprintln!( eprintln!(

View File

@ -1,12 +1,10 @@
/*! Assorted helpers */ /*! Assorted helpers */
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use ::float_ord::FloatOrd; use ::float_ord::FloatOrd;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::hash::{ Hash, Hasher }; use std::hash::{ Hash, Hasher };
use std::iter::FromIterator;
use std::ops::Mul; use std::ops::Mul;
pub mod c { pub mod c {
@ -138,16 +136,6 @@ pub trait CloneOwned {
fn clone_owned(&self) -> Self::Owned; fn clone_owned(&self) -> Self::Owned;
} }
pub fn hash_map_map<K, V, F, K1, V1>(map: HashMap<K, V>, mut f: F)
-> HashMap<K1, V1>
where F: FnMut(K, V) -> (K1, V1),
K1: std::cmp::Eq + std::hash::Hash
{
HashMap::from_iter(
map.into_iter().map(|(key, value)| f(key, value))
)
}
pub fn find_max_double<T, I, F>(iterator: I, get: F) pub fn find_max_double<T, I, F>(iterator: I, get: F)
-> f64 -> f64
where I: Iterator<Item=T>, where I: Iterator<Item=T>,