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.
This commit is contained in:
213
src/layout.rs
213
src/layout.rs
@ -238,16 +238,20 @@ pub mod c {
|
|||||||
|
|
||||||
// The list must be copied,
|
// The list must be copied,
|
||||||
// because it will be mutated in the loop
|
// because it will be mutated in the loop
|
||||||
for key in layout.state.pressed_keys.clone() {
|
for button in layout.state.pressed_buttons.clone() {
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) {
|
||||||
seat::handle_release_key(
|
seat::handle_release_key(
|
||||||
layout,
|
layout,
|
||||||
&mut submission,
|
&mut submission,
|
||||||
Some(&ui_backend),
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
Some((&popover_state, app_state.clone())),
|
Some((&popover_state, app_state.clone())),
|
||||||
key,
|
&key,
|
||||||
);
|
button,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
drawing::queue_redraw(ui_keyboard);
|
drawing::queue_redraw(ui_keyboard);
|
||||||
}
|
}
|
||||||
@ -265,16 +269,21 @@ pub mod c {
|
|||||||
let mut submission = submission.borrow_mut();
|
let mut submission = submission.borrow_mut();
|
||||||
// The list must be copied,
|
// The list must be copied,
|
||||||
// because it will be mutated in the loop
|
// because it will be mutated in the loop
|
||||||
for key in layout.state.pressed_keys.clone() {
|
for button in layout.state.pressed_buttons.clone() {
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
if let Some(key) = layout.shape.get_key(&button) {
|
||||||
seat::handle_release_key(
|
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||||
layout,
|
seat::handle_release_key(
|
||||||
&mut submission,
|
layout,
|
||||||
None, // don't update UI
|
&mut submission,
|
||||||
Timestamp(time),
|
None, // don't update UI
|
||||||
None, // don't switch layouts
|
Timestamp(time),
|
||||||
&mut key.clone(),
|
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)
|
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(
|
seat::handle_press_key(
|
||||||
layout,
|
layout,
|
||||||
&mut submission,
|
&mut submission,
|
||||||
Timestamp(time),
|
Timestamp(time),
|
||||||
&state,
|
&state,
|
||||||
|
button,
|
||||||
);
|
);
|
||||||
// maybe TODO: draw on the display buffer here
|
// maybe TODO: draw on the display buffer here
|
||||||
drawing::queue_redraw(ui_keyboard);
|
drawing::queue_redraw(ui_keyboard);
|
||||||
@ -344,39 +359,48 @@ pub mod c {
|
|||||||
Point { x: x_widget, y: y_widget }
|
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 button_info = {
|
||||||
let place = layout.find_button_by_position(point);
|
let place = layout.find_button_by_position(point);
|
||||||
place.map(|place| {(
|
place.map(|(place, index)| {(
|
||||||
place.button.state.clone(),
|
place.button.state.clone(),
|
||||||
place.button.clone(),
|
index,
|
||||||
place.offset,
|
|
||||||
)})
|
)})
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((state, _button, _view_position)) = button_info {
|
if let Some((state, (row, position_in_row))) = button_info {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for wrapped_key in pressed {
|
for button in pressed {
|
||||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) {
|
||||||
if Rc::ptr_eq(&state, &wrapped_key.0) {
|
if Rc::ptr_eq(&state, &key) {
|
||||||
found = true;
|
found = true;
|
||||||
|
} else {
|
||||||
|
seat::handle_release_key(
|
||||||
|
layout,
|
||||||
|
&mut submission,
|
||||||
|
Some(&ui_backend),
|
||||||
|
time,
|
||||||
|
Some((&popover_state, app_state.clone())),
|
||||||
|
&key,
|
||||||
|
button,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
seat::handle_release_key(
|
log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button);
|
||||||
layout,
|
|
||||||
&mut submission,
|
|
||||||
Some(&ui_backend),
|
|
||||||
time,
|
|
||||||
Some((&popover_state, app_state.clone())),
|
|
||||||
key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
|
let button = ButtonPosition {
|
||||||
|
view: layout.state.current_view.clone(),
|
||||||
|
row,
|
||||||
|
position_in_row,
|
||||||
|
};
|
||||||
seat::handle_press_key(
|
seat::handle_press_key(
|
||||||
layout,
|
layout,
|
||||||
&mut submission,
|
&mut submission,
|
||||||
time,
|
time,
|
||||||
&state,
|
&state,
|
||||||
|
button,
|
||||||
);
|
);
|
||||||
// maybe TODO: draw on the display buffer here
|
// maybe TODO: draw on the display buffer here
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -384,16 +408,20 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for wrapped_key in pressed {
|
for button in pressed {
|
||||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
if let Some(key) = layout.shape.get_key(&button).map(|k| k.clone()) {
|
||||||
seat::handle_release_key(
|
seat::handle_release_key(
|
||||||
layout,
|
layout,
|
||||||
&mut submission,
|
&mut submission,
|
||||||
Some(&ui_backend),
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
Some((&popover_state, app_state.clone())),
|
Some((&popover_state, app_state.clone())),
|
||||||
key,
|
&key,
|
||||||
);
|
button,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log_print!(logging::Level::Bug, "Failed to find button at position {:?}", button);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawing::queue_redraw(ui_keyboard);
|
drawing::queue_redraw(ui_keyboard);
|
||||||
@ -509,8 +537,9 @@ impl Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the first button that covers the specified point
|
/// Finds the first button that covers the specified point
|
||||||
/// relative to row's position's origin
|
/// relative to row's position's origin.
|
||||||
fn find_button_by_position(&self, x: f64) -> &(f64, Box<Button>)
|
/// Returns its index too.
|
||||||
|
fn find_button_by_position(&self, x: f64) -> (&(f64, Box<Button>), usize)
|
||||||
{
|
{
|
||||||
// Buttons are sorted so we can use a binary search to find the clicked
|
// Buttons are sorted so we can use a binary search to find the clicked
|
||||||
// button. Note this doesn't check whether the point is actually within
|
// button. Note this doesn't check whether the point is actually within
|
||||||
@ -523,7 +552,7 @@ impl Row {
|
|||||||
let index = result.unwrap_or_else(|r| r);
|
let index = result.unwrap_or_else(|r| r);
|
||||||
let index = if index > 0 { index - 1 } else { 0 };
|
let index = if index > 0 { index - 1 } else { 0 };
|
||||||
|
|
||||||
&self.buttons[index]
|
(&self.buttons[index], index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,9 +603,10 @@ impl View {
|
|||||||
View { rows, size: Size { width, height } }
|
View { rows, size: Size { width, height } }
|
||||||
}
|
}
|
||||||
/// Finds the first button that covers the specified point
|
/// Finds the first button that covers the specified point
|
||||||
/// relative to view's position's origin
|
/// relative to view's position's origin.
|
||||||
|
/// Returns also (row, column) index.
|
||||||
fn find_button_by_position(&self, point: c::Point)
|
fn find_button_by_position(&self, point: c::Point)
|
||||||
-> Option<ButtonPlace>
|
-> Option<(ButtonPlace, (usize, usize))>
|
||||||
{
|
{
|
||||||
// Only test bounds of the view here, letting rows/column search extend
|
// Only test bounds of the view here, letting rows/column search extend
|
||||||
// to the edges of these bounds.
|
// to the edges of these bounds.
|
||||||
@ -599,12 +629,16 @@ impl View {
|
|||||||
let index = if index > 0 { index - 1 } else { 0 };
|
let index = if index > 0 { index - 1 } else { 0 };
|
||||||
|
|
||||||
let row = &self.rows[index];
|
let row = &self.rows[index];
|
||||||
let button = row.1.find_button_by_position(point.x - row.0.x);
|
let ((button_pos, button), button_index)
|
||||||
|
= row.1.find_button_by_position(point.x - row.0.x);
|
||||||
|
|
||||||
Some(ButtonPlace {
|
Some((
|
||||||
button: &button.1,
|
ButtonPlace {
|
||||||
offset: &row.0 + c::Point { x: button.0, y: 0.0 },
|
button: &button,
|
||||||
})
|
offset: &row.0 + c::Point { x: *button_pos, y: 0.0 },
|
||||||
|
},
|
||||||
|
(index, button_index),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> Size {
|
pub fn get_size(&self) -> Size {
|
||||||
@ -662,6 +696,19 @@ pub struct Layout {
|
|||||||
pub shape: LayoutData,
|
pub shape: LayoutData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Button position for the pressed buttons list
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct ButtonPosition {
|
||||||
|
// There's only ever going to be a handul of pressed buttons,
|
||||||
|
// so as inefficient as String is, it won't make a difference.
|
||||||
|
// In the worst case, views can turn into indices in the description.
|
||||||
|
view: String,
|
||||||
|
/// Index to the view's row.
|
||||||
|
row: usize,
|
||||||
|
/// Index to the row's button.
|
||||||
|
position_in_row: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 struct LayoutState {
|
||||||
pub current_view: String,
|
pub current_view: String,
|
||||||
@ -677,12 +724,13 @@ pub struct LayoutState {
|
|||||||
// through all buttons of the current view anyway.
|
// through all buttons of the current view anyway.
|
||||||
// When the list tracks actual location,
|
// When the list tracks actual location,
|
||||||
// it becomes possible to place popovers and other UI accurately.
|
// it becomes possible to place popovers and other UI accurately.
|
||||||
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
|
pub pressed_buttons: Vec<ButtonPosition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder structure for picking up layout data from storage
|
/// A builder structure for picking up layout data from storage
|
||||||
pub struct LayoutParseData {
|
pub struct LayoutParseData {
|
||||||
/// Point is the offset within layout
|
/// Point is the offset within the panel
|
||||||
|
/// (transformed to layout's coordinate space).
|
||||||
pub views: HashMap<String, (c::Point, View)>,
|
pub views: HashMap<String, (c::Point, View)>,
|
||||||
/// xkb keymaps applicable to the contained keys
|
/// xkb keymaps applicable to the contained keys
|
||||||
pub keymaps: Vec<CString>,
|
pub keymaps: Vec<CString>,
|
||||||
@ -717,6 +765,13 @@ impl fmt::Display for NoSuchView {
|
|||||||
|
|
||||||
|
|
||||||
impl LayoutData {
|
impl LayoutData {
|
||||||
|
fn get_key(&self, button: &ButtonPosition) -> Option<&Rc<RefCell<KeyState>>> {
|
||||||
|
let (_, view) = self.views.get(&button.view)?;
|
||||||
|
let (_, row) = view.rows.get(button.row)?;
|
||||||
|
let (_, key) = row.buttons.get(button.position_in_row)?;
|
||||||
|
Some(&key.state)
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates size without margins
|
/// Calculates size without margins
|
||||||
fn calculate_inner_size(&self) -> Size {
|
fn calculate_inner_size(&self) -> Size {
|
||||||
View::calculate_super_size(
|
View::calculate_super_size(
|
||||||
@ -779,7 +834,7 @@ impl Layout {
|
|||||||
state: LayoutState {
|
state: LayoutState {
|
||||||
current_view: "base".to_owned(),
|
current_view: "base".to_owned(),
|
||||||
view_latched: LatchedState::Not,
|
view_latched: LatchedState::Not,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_buttons: Vec::new(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -808,7 +863,8 @@ impl Layout {
|
|||||||
&self.state.view_latched
|
&self.state.view_latched
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_button_by_position(&self, point: c::Point) -> Option<ButtonPlace> {
|
/// Returns index too
|
||||||
|
fn find_button_by_position(&self, point: c::Point) -> Option<(ButtonPlace, (usize, usize))> {
|
||||||
let (offset, layout) = self.get_current_view_position();
|
let (offset, layout) = self.get_current_view_position();
|
||||||
layout.find_button_by_position(point - offset)
|
layout.find_button_by_position(point - offset)
|
||||||
}
|
}
|
||||||
@ -1020,12 +1076,17 @@ mod seat {
|
|||||||
submission: &mut Submission,
|
submission: &mut Submission,
|
||||||
time: Timestamp,
|
time: Timestamp,
|
||||||
rckey: &Rc<RefCell<KeyState>>,
|
rckey: &Rc<RefCell<KeyState>>,
|
||||||
|
button: ButtonPosition,
|
||||||
) {
|
) {
|
||||||
if !layout.state.pressed_keys.insert(::util::Pointer(rckey.clone())) {
|
let find = layout.state.pressed_buttons.iter()
|
||||||
|
.position(|b| b == &button);
|
||||||
|
if let Some(_) = find {
|
||||||
log_print!(
|
log_print!(
|
||||||
logging::Level::Bug,
|
logging::Level::Bug,
|
||||||
"Key {:?} was already pressed", rckey,
|
"Button {:?} was already pressed", button,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
layout.state.pressed_buttons.push(button);
|
||||||
}
|
}
|
||||||
let key: KeyState = {
|
let key: KeyState = {
|
||||||
RefCell::borrow(rckey).clone()
|
RefCell::borrow(rckey).clone()
|
||||||
@ -1072,6 +1133,7 @@ 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,
|
||||||
) {
|
) {
|
||||||
let key: KeyState = {
|
let key: KeyState = {
|
||||||
RefCell::borrow(rckey).clone()
|
RefCell::borrow(rckey).clone()
|
||||||
@ -1135,7 +1197,12 @@ mod seat {
|
|||||||
|
|
||||||
let pointer = ::util::Pointer(rckey.clone());
|
let pointer = ::util::Pointer(rckey.clone());
|
||||||
// Apply state changes
|
// Apply state changes
|
||||||
layout.state.pressed_keys.remove(&pointer);
|
let pos = layout.state.pressed_buttons.iter().position(|b| b == &button);
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
layout.state.pressed_buttons.remove(pos);
|
||||||
|
} else {
|
||||||
|
log_print!(logging::Level::Bug, "No button to remove from pressed list: {:?}", button);
|
||||||
|
}
|
||||||
// Commit activated button state changes
|
// Commit activated button state changes
|
||||||
RefCell::replace(rckey, key);
|
RefCell::replace(rckey, key);
|
||||||
}
|
}
|
||||||
@ -1240,7 +1307,7 @@ mod test {
|
|||||||
state: LayoutState {
|
state: LayoutState {
|
||||||
current_view: "base".into(),
|
current_view: "base".into(),
|
||||||
view_latched: LatchedState::Not,
|
view_latched: LatchedState::Not,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_buttons: Vec::new(),
|
||||||
},
|
},
|
||||||
shape: LayoutData {
|
shape: LayoutData {
|
||||||
keymaps: Vec::new(),
|
keymaps: Vec::new(),
|
||||||
@ -1320,7 +1387,7 @@ mod test {
|
|||||||
state: LayoutState {
|
state: LayoutState {
|
||||||
current_view: "base".into(),
|
current_view: "base".into(),
|
||||||
view_latched: LatchedState::Not,
|
view_latched: LatchedState::Not,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_buttons: Vec::new(),
|
||||||
},
|
},
|
||||||
shape: LayoutData {
|
shape: LayoutData {
|
||||||
keymaps: Vec::new(),
|
keymaps: Vec::new(),
|
||||||
@ -1391,7 +1458,7 @@ mod test {
|
|||||||
state: LayoutState {
|
state: LayoutState {
|
||||||
current_view: "base".into(),
|
current_view: "base".into(),
|
||||||
view_latched: LatchedState::Not,
|
view_latched: LatchedState::Not,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_buttons: Vec::new(),
|
||||||
},
|
},
|
||||||
shape: LayoutData {
|
shape: LayoutData {
|
||||||
keymaps: Vec::new(),
|
keymaps: Vec::new(),
|
||||||
@ -1465,19 +1532,19 @@ mod test {
|
|||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
|
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
|
||||||
.unwrap().button.name.to_str().unwrap() == "A"
|
.unwrap().0.button.name.to_str().unwrap() == "A"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
view.find_button_by_position(c::Point { x: 14.99, y: 5.0 })
|
view.find_button_by_position(c::Point { x: 14.99, y: 5.0 })
|
||||||
.unwrap().button.name.to_str().unwrap() == "A"
|
.unwrap().0.button.name.to_str().unwrap() == "A"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
view.find_button_by_position(c::Point { x: 15.01, y: 5.0 })
|
view.find_button_by_position(c::Point { x: 15.01, y: 5.0 })
|
||||||
.unwrap().button.name.to_str().unwrap() == "B"
|
.unwrap().0.button.name.to_str().unwrap() == "B"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
view.find_button_by_position(c::Point { x: 25.0, y: 5.0 })
|
view.find_button_by_position(c::Point { x: 25.0, y: 5.0 })
|
||||||
.unwrap().button.name.to_str().unwrap() == "B"
|
.unwrap().0.button.name.to_str().unwrap() == "B"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user