layout: Make press handling more transparent
In adition to that, an attempt at multiple locked levels of keys was added.
This commit is contained in:
@ -6,8 +6,8 @@ use std::ffi::CString;
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct KeySym(pub String);
|
pub struct KeySym(pub String);
|
||||||
|
|
||||||
/// Use to switch layouts
|
/// Name of a set of buttons displayed at one time within the layout
|
||||||
type Level = String;
|
type View = String;
|
||||||
|
|
||||||
/// Use to send modified keypresses
|
/// Use to send modified keypresses
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -20,12 +20,12 @@ pub enum Modifier {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Switch to this view
|
/// Switch to this view
|
||||||
SetLevel(Level),
|
SetView(View),
|
||||||
/// Switch to a view and latch
|
/// Switch to a view and latch
|
||||||
LockLevel {
|
LockView {
|
||||||
lock: Level,
|
lock: View,
|
||||||
/// When unlocked by pressing it or emitting a key
|
/// When unlocked by pressing it or emitting a key
|
||||||
unlock: Level,
|
unlock: View,
|
||||||
},
|
},
|
||||||
/// Set this modifier TODO: release?
|
/// Set this modifier TODO: release?
|
||||||
SetModifier(Modifier),
|
SetModifier(Modifier),
|
||||||
|
|||||||
@ -504,7 +504,7 @@ fn create_action<H: WarningHandler>(
|
|||||||
match submission {
|
match submission {
|
||||||
SubmitData::Action(
|
SubmitData::Action(
|
||||||
Action::SetView(view_name)
|
Action::SetView(view_name)
|
||||||
) => ::action::Action::SetLevel(
|
) => ::action::Action::SetView(
|
||||||
filter_view_name(
|
filter_view_name(
|
||||||
name, view_name.clone(), &view_names,
|
name, view_name.clone(), &view_names,
|
||||||
warning_handler,
|
warning_handler,
|
||||||
@ -512,7 +512,7 @@ fn create_action<H: WarningHandler>(
|
|||||||
),
|
),
|
||||||
SubmitData::Action(Action::Locking {
|
SubmitData::Action(Action::Locking {
|
||||||
lock_view, unlock_view
|
lock_view, unlock_view
|
||||||
}) => ::action::Action::LockLevel {
|
}) => ::action::Action::LockView {
|
||||||
lock: filter_view_name(
|
lock: filter_view_name(
|
||||||
name,
|
name,
|
||||||
lock_view.clone(),
|
lock_view.clone(),
|
||||||
|
|||||||
@ -55,15 +55,27 @@ pub struct KeyState {
|
|||||||
|
|
||||||
impl KeyState {
|
impl KeyState {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn activate(self) -> KeyState {
|
pub fn into_activated(self) -> KeyState {
|
||||||
match self.action {
|
match self.action {
|
||||||
Action::LockLevel { lock: _, unlock: _ } => KeyState {
|
Action::LockView { lock: _, unlock: _ } => KeyState {
|
||||||
locked: self.locked ^ true,
|
locked: self.locked ^ true,
|
||||||
..self
|
..self
|
||||||
},
|
},
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_switched(self) -> KeyState {
|
||||||
|
use self::PressType::*;
|
||||||
|
KeyState {
|
||||||
|
pressed: match self.pressed {
|
||||||
|
Released => Pressed,
|
||||||
|
Pressed => Released,
|
||||||
|
},
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
||||||
|
|||||||
357
src/layout.rs
357
src/layout.rs
@ -20,6 +20,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{ HashMap, HashSet };
|
use std::collections::{ HashMap, HashSet };
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ use ::float_ord::FloatOrd;
|
|||||||
use ::keyboard::{ KeyState, PressType };
|
use ::keyboard::{ KeyState, PressType };
|
||||||
use ::submission::{ Timestamp, VirtualKeyboard };
|
use ::submission::{ Timestamp, VirtualKeyboard };
|
||||||
use ::util;
|
use ::util;
|
||||||
|
use ::util::Stack;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
@ -193,10 +195,7 @@ pub mod c {
|
|||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
|
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
|
||||||
let layout = unsafe { &*layout };
|
let layout = unsafe { &*layout };
|
||||||
let view_name = layout.current_view.clone();
|
layout.get_current_view().as_ref() as *const View
|
||||||
layout.views.get(&view_name)
|
|
||||||
.expect("Current view doesn't exist")
|
|
||||||
.as_ref() as *const View
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -356,21 +355,25 @@ pub mod c {
|
|||||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||||
// 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
|
||||||
|
let ui_backend = UIBackend {
|
||||||
|
widget_to_layout,
|
||||||
|
keyboard: ui_keyboard,
|
||||||
|
};
|
||||||
let keys = layout.get_pressed_keys();
|
let keys = layout.get_pressed_keys();
|
||||||
for key in keys {
|
for key in keys {
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||||
ui::release_key(
|
seat::release_key(
|
||||||
layout,
|
layout,
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
&widget_to_layout,
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
ui_keyboard,
|
key,
|
||||||
key
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Release all buittons but don't redraw
|
/// Release all buttons but don't redraw.
|
||||||
|
/// Use for situations where the UI is gone, e.g. unmapped.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_layout_release_all_only(
|
fn squeek_layout_release_all_only(
|
||||||
@ -378,15 +381,18 @@ pub mod c {
|
|||||||
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
|
||||||
time: u32,
|
time: u32,
|
||||||
) {
|
) {
|
||||||
let layout = unsafe { &mut *layout };
|
let mut layout = unsafe { &mut *layout };
|
||||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||||
|
let time = Timestamp(time);
|
||||||
|
|
||||||
for key in layout.get_pressed_keys() {
|
for key in layout.get_pressed_keys() {
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||||
layout.release_key(
|
seat::release_key(
|
||||||
|
&mut layout,
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
|
None, // don't update anything UI related
|
||||||
|
time,
|
||||||
&mut key.clone(),
|
&mut key.clone(),
|
||||||
Timestamp(time)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,6 +456,11 @@ pub mod c {
|
|||||||
Point { x: x_widget, y: y_widget }
|
Point { x: x_widget, y: y_widget }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let ui_backend = UIBackend {
|
||||||
|
keyboard: ui_keyboard,
|
||||||
|
widget_to_layout,
|
||||||
|
};
|
||||||
|
|
||||||
let pressed = layout.get_pressed_keys();
|
let pressed = layout.get_pressed_keys();
|
||||||
let state_place = {
|
let state_place = {
|
||||||
let view = layout.get_current_view();
|
let view = layout.get_current_view();
|
||||||
@ -466,12 +477,11 @@ pub mod c {
|
|||||||
if Rc::ptr_eq(&state, &key) {
|
if Rc::ptr_eq(&state, &key) {
|
||||||
found = true;
|
found = true;
|
||||||
} else {
|
} else {
|
||||||
ui::release_key(
|
seat::release_key(
|
||||||
layout,
|
layout,
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
&widget_to_layout,
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
ui_keyboard,
|
|
||||||
&key,
|
&key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -482,12 +492,11 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for key in pressed {
|
for key in pressed {
|
||||||
ui::release_key(
|
seat::release_key(
|
||||||
layout,
|
layout,
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
&widget_to_layout,
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
ui_keyboard,
|
|
||||||
&key,
|
&key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -740,12 +749,32 @@ pub enum ArrangementKind {
|
|||||||
Wide = 1,
|
Wide = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ViewLockKind {
|
||||||
|
/// Lock gets released the next time something is submitted
|
||||||
|
OneShot,
|
||||||
|
/// Lock persists until the lock button is clicked again
|
||||||
|
Persisting,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ViewLock {
|
||||||
|
/// Name of the view
|
||||||
|
view: String,
|
||||||
|
/// ID of the lock, for unlocking.
|
||||||
|
/// Ideally unique within the layout
|
||||||
|
id: String,
|
||||||
|
kind: ViewLockKind,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: split into sth like
|
// TODO: split into sth like
|
||||||
// Arrangement (views) + details (keymap) + State (keys)
|
// Arrangement (views) + details (keymap) + State (keys)
|
||||||
/// State of the UI, contains the backend as well
|
/// State of the UI, contains the backend as well
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub kind: ArrangementKind,
|
pub kind: ArrangementKind,
|
||||||
pub current_view: String,
|
/// The view after unlocking all locking view locks.
|
||||||
|
pub base_view: String,
|
||||||
|
/// A stack of views that are locked, later locks with higher indices.
|
||||||
|
/// Setting a view directly discards all.
|
||||||
|
pub locked_views: Stack<ViewLock>,
|
||||||
// Views own the actual buttons which have state
|
// Views own the actual buttons which have state
|
||||||
// Maybe they should own UI only,
|
// Maybe they should own UI only,
|
||||||
// and keys should be owned by a dedicated non-UI-State?
|
// and keys should be owned by a dedicated non-UI-State?
|
||||||
@ -764,8 +793,23 @@ pub struct LayoutData {
|
|||||||
pub keymap_str: CString,
|
pub keymap_str: CString,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct NoSuchView;
|
struct NoSuchView;
|
||||||
|
|
||||||
|
impl fmt::Display for NoSuchView {
|
||||||
|
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(out, "No such view")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NoSuchLock;
|
||||||
|
|
||||||
|
impl fmt::Display for NoSuchLock {
|
||||||
|
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(out, "No such lock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unfortunately, changes are not atomic due to mutability :(
|
// Unfortunately, changes are not atomic due to mutability :(
|
||||||
// An error will not be recoverable
|
// An error will not be recoverable
|
||||||
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
|
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
|
||||||
@ -774,13 +818,18 @@ impl Layout {
|
|||||||
pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout {
|
pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout {
|
||||||
Layout {
|
Layout {
|
||||||
kind,
|
kind,
|
||||||
current_view: "base".to_owned(),
|
base_view: "base".to_owned(),
|
||||||
|
locked_views: Vec::new(),
|
||||||
views: data.views,
|
views: data.views,
|
||||||
keymap_str: data.keymap_str,
|
keymap_str: data.keymap_str,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_view(&self) -> &Box<View> {
|
fn get_current_view(&self) -> &Box<View> {
|
||||||
self.views.get(&self.current_view).expect("Selected nonexistent view")
|
let view_name = self.locked_views.iter().next_back()
|
||||||
|
.map(|lock| &lock.view)
|
||||||
|
.unwrap_or(&self.base_view);
|
||||||
|
self.views.get(view_name).expect("Selected nonexistent view")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all keys matching filter, without duplicates
|
/// Returns all keys matching filter, without duplicates
|
||||||
@ -824,33 +873,54 @@ impl Layout {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Top oneshot locks can be all removed when a button is pressed
|
||||||
|
fn get_top_oneshot_locks_idx(&self) -> usize {
|
||||||
|
let last_persisting_idx = self.locked_views.iter()
|
||||||
|
.rposition(|lock| match lock.kind {
|
||||||
|
ViewLockKind::Persisting => true,
|
||||||
|
ViewLockKind::OneShot => false,
|
||||||
|
});
|
||||||
|
match last_persisting_idx {
|
||||||
|
// don't remove the last persisting item
|
||||||
|
Some(idx) => idx + 1,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock_view(&mut self, view: String, id: String)
|
||||||
|
-> Result<(), NoSuchView>
|
||||||
|
{
|
||||||
|
if self.views.contains_key(&view) {
|
||||||
|
self.locked_views.push(ViewLock {
|
||||||
|
view,
|
||||||
|
id,
|
||||||
|
kind: ViewLockKind::OneShot,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(NoSuchView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock_view(&mut self, id: String) -> Result<(), NoSuchLock> {
|
||||||
|
let lock_idx = self.locked_views.iter()
|
||||||
|
// find the first occurrence: locking the same key multiple times
|
||||||
|
// sounds like not something we want to do
|
||||||
|
.position(|lock| lock.id == id);
|
||||||
|
lock_idx.map(|idx| self.locked_views.truncate(idx))
|
||||||
|
.ok_or(NoSuchLock)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
||||||
if self.views.contains_key(&view) {
|
if self.views.contains_key(&view) {
|
||||||
self.current_view = view;
|
self.base_view = view;
|
||||||
|
self.locked_views = Vec::new();
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(NoSuchView)
|
Err(NoSuchView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_key(
|
|
||||||
&mut self,
|
|
||||||
virtual_keyboard: &VirtualKeyboard,
|
|
||||||
mut key: &mut Rc<RefCell<KeyState>>,
|
|
||||||
time: Timestamp,
|
|
||||||
) {
|
|
||||||
{
|
|
||||||
let mut bkey = key.borrow_mut();
|
|
||||||
virtual_keyboard.switch(
|
|
||||||
&bkey.keycodes,
|
|
||||||
PressType::Released,
|
|
||||||
time,
|
|
||||||
);
|
|
||||||
bkey.pressed = PressType::Released;
|
|
||||||
}
|
|
||||||
self.set_level_from_press(&mut key);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn press_key(
|
fn press_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
virtual_keyboard: &VirtualKeyboard,
|
virtual_keyboard: &VirtualKeyboard,
|
||||||
@ -865,47 +935,6 @@ impl Layout {
|
|||||||
);
|
);
|
||||||
bkey.pressed = PressType::Pressed;
|
bkey.pressed = PressType::Pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
|
||||||
fn activate(layout: &mut Layout, key: &Rc<RefCell<KeyState>>) {
|
|
||||||
let updated = {
|
|
||||||
let keyref = RefCell::borrow(key);
|
|
||||||
keyref.clone().activate()
|
|
||||||
};
|
|
||||||
RefCell::replace(key, updated.clone());
|
|
||||||
layout.set_state_from_press(updated.action.clone(), updated.locked);
|
|
||||||
};
|
|
||||||
|
|
||||||
// unlock all
|
|
||||||
let keys = self.get_locked_keys();
|
|
||||||
for key in &keys {
|
|
||||||
activate(self, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't handle the same key twice, but handle it at least once,
|
|
||||||
// because its press is the reason we're here
|
|
||||||
if let None = keys.iter().find(|k| Rc::ptr_eq(k, &key)) {
|
|
||||||
activate(self, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_state_from_press(&mut self, action: Action, locked: bool) {
|
|
||||||
let view_name = match action {
|
|
||||||
Action::SetLevel(name) => {
|
|
||||||
Some(name.clone())
|
|
||||||
},
|
|
||||||
Action::LockLevel { lock, unlock } => {
|
|
||||||
Some(if locked { lock } else { unlock }.clone())
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(view_name) = view_name {
|
|
||||||
if let Err(_e) = self.set_view(view_name.clone()) {
|
|
||||||
eprintln!("No such view: {}, ignoring switch", view_name)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod procedures {
|
mod procedures {
|
||||||
@ -949,8 +978,7 @@ mod procedures {
|
|||||||
key: &Rc<RefCell<KeyState>>,
|
key: &Rc<RefCell<KeyState>>,
|
||||||
ui_keyboard: c::EekGtkKeyboard,
|
ui_keyboard: c::EekGtkKeyboard,
|
||||||
) {
|
) {
|
||||||
let paths = ::layout::procedures::find_key_paths(&view, key);
|
for (_row, button) in find_key_paths(&view, key) {
|
||||||
for (_row, button) in paths {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
c::procedures::eek_gtk_on_button_released(
|
c::procedures::eek_gtk_on_button_released(
|
||||||
button.as_ref() as *const Button,
|
button.as_ref() as *const Button,
|
||||||
@ -1037,45 +1065,150 @@ mod procedures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UIBackend {
|
||||||
|
widget_to_layout: c::procedures::Transformation,
|
||||||
|
keyboard: c::EekGtkKeyboard,
|
||||||
|
}
|
||||||
|
|
||||||
/// Top level UI procedures
|
/// Top level UI procedures
|
||||||
mod ui {
|
mod seat {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// Only use from release_key for when the view switch is ignored
|
||||||
|
fn set_all_unlocked(layout: &mut Layout) {
|
||||||
|
for key in layout.get_locked_keys() {
|
||||||
|
let mut key = RefCell::borrow_mut(&key);
|
||||||
|
match &key.action {
|
||||||
|
Action::LockView { lock: _, unlock: _ } => {},
|
||||||
|
a => eprintln!(
|
||||||
|
"BUG: action {:?} was found inside locked keys",
|
||||||
|
a
|
||||||
|
),
|
||||||
|
};
|
||||||
|
key.locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only use from release_key. Changes state of other keys only.
|
||||||
|
fn handle_set_view(layout: &mut Layout, view_name: String) {
|
||||||
|
set_all_unlocked(layout);
|
||||||
|
layout.set_view(view_name.clone())
|
||||||
|
.map_err(|e|
|
||||||
|
eprintln!("Bad view {} ({}), ignoring", view_name, e)
|
||||||
|
).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_unstick_locks(layout: &mut Layout) {
|
||||||
|
let oneshot_idx = layout.get_top_oneshot_locks_idx();
|
||||||
|
let (_permalocks, onetimes) = layout.locked_views.split_at(oneshot_idx);
|
||||||
|
// Convert into a hashmap for easier finding of elements
|
||||||
|
let onetime_ids = HashSet::<&String>::from_iter(
|
||||||
|
onetimes.into_iter().map(|lock| &lock.id)
|
||||||
|
);
|
||||||
|
for key in layout.get_locked_keys() {
|
||||||
|
let mut key = RefCell::borrow_mut(&key);
|
||||||
|
match &key.action {
|
||||||
|
Action::LockView { lock: _, unlock: id } => {
|
||||||
|
if onetime_ids.contains(id) {
|
||||||
|
key.locked = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
a => eprintln!(
|
||||||
|
"BUG: action {:?} was found inside locked keys",
|
||||||
|
a
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
layout.locked_views.truncate(oneshot_idx);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: turn into release_button
|
// TODO: turn into release_button
|
||||||
pub fn release_key(
|
pub fn release_key(
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
virtual_keyboard: &VirtualKeyboard,
|
virtual_keyboard: &VirtualKeyboard,
|
||||||
widget_to_layout: &c::procedures::Transformation,
|
ui: Option<&UIBackend>,
|
||||||
time: Timestamp,
|
time: Timestamp,
|
||||||
ui_keyboard: c::EekGtkKeyboard,
|
rckey: &Rc<RefCell<KeyState>>,
|
||||||
key: &Rc<RefCell<KeyState>>,
|
|
||||||
) {
|
) {
|
||||||
layout.release_key(virtual_keyboard, &mut key.clone(), time);
|
let key: KeyState = {
|
||||||
|
RefCell::borrow(rckey).clone()
|
||||||
|
};
|
||||||
|
let action = key.action.clone();
|
||||||
|
|
||||||
let view = layout.get_current_view();
|
// update
|
||||||
let action = RefCell::borrow(key).action.clone();
|
let key = key.into_switched();
|
||||||
if let Action::ShowPreferences = action {
|
let key = match action {
|
||||||
let paths = ::layout::procedures::find_key_paths(
|
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
|
||||||
view, key
|
_ => key,
|
||||||
);
|
};
|
||||||
// getting first item will cause mispositioning
|
|
||||||
// with more than one button with the same key
|
// process changes
|
||||||
// on the keyboard
|
match action {
|
||||||
if let Some((row, button)) = paths.get(0) {
|
Action::Submit { text: _, keys: _ } => {
|
||||||
let bounds = ::layout::procedures::get_button_bounds(
|
handle_unstick_locks(layout);
|
||||||
view, row, button
|
virtual_keyboard.switch(
|
||||||
).unwrap_or_else(|| {
|
&key.keycodes,
|
||||||
eprintln!("BUG: Clicked button has no position?");
|
PressType::Released,
|
||||||
c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
|
time,
|
||||||
});
|
|
||||||
::popover::show(
|
|
||||||
ui_keyboard,
|
|
||||||
widget_to_layout.reverse_bounds(bounds)
|
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
Action::SetView(view) => {
|
||||||
|
handle_set_view(layout, view)
|
||||||
procedures::release_ui_buttons(view, key, ui_keyboard);
|
},
|
||||||
|
Action::LockView { lock, unlock } => {
|
||||||
|
// The button that triggered this will be in the right state
|
||||||
|
// due to commit at the end.
|
||||||
|
set_all_unlocked(layout);
|
||||||
|
|
||||||
|
match key.locked {
|
||||||
|
true => {
|
||||||
|
layout.lock_view(lock.clone(), unlock).map_err(|e|
|
||||||
|
eprintln!("Bad view {} ({}), ignoring", lock, e)
|
||||||
|
).ok();
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
layout.unlock_view(unlock.clone()).map_err(|e| {
|
||||||
|
eprintln!(
|
||||||
|
"BUG: Bad id {} ({}), resetting locks",
|
||||||
|
unlock, e
|
||||||
|
);
|
||||||
|
layout.set_view("base".into()).unwrap()
|
||||||
|
}).ok();
|
||||||
|
},
|
||||||
|
// TODO: stuck => stick_view(lock, unlock)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action::ShowPreferences => if let Some(ui) = &ui {
|
||||||
|
let view = layout.get_current_view();
|
||||||
|
let paths = ::layout::procedures::find_key_paths(
|
||||||
|
view, &rckey
|
||||||
|
);
|
||||||
|
// Getting first item will cause mispositioning
|
||||||
|
// with more than one button with the same key
|
||||||
|
// on the keyboard.
|
||||||
|
if let Some((row, button)) = paths.get(0) {
|
||||||
|
let bounds = ::layout::procedures::get_button_bounds(
|
||||||
|
view, row, button
|
||||||
|
).unwrap_or_else(|| {
|
||||||
|
eprintln!("BUG: Clicked button has no position?");
|
||||||
|
c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
|
||||||
|
});
|
||||||
|
::popover::show(
|
||||||
|
ui.keyboard,
|
||||||
|
ui.widget_to_layout.reverse_bounds(bounds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action::SetModifier(_) => eprintln!("Modifiers unsupported"),
|
||||||
|
};
|
||||||
|
//layout.release_key(virtual_keyboard, &mut key.clone(), time);
|
||||||
|
|
||||||
|
if let Some(ui) = ui {
|
||||||
|
let view = layout.get_current_view();
|
||||||
|
procedures::release_ui_buttons(view, &rckey, ui.keyboard);
|
||||||
|
};
|
||||||
|
// Commits activated button state changes
|
||||||
|
RefCell::replace(rckey, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1090,7 +1223,7 @@ mod test {
|
|||||||
pressed: PressType::Released,
|
pressed: PressType::Released,
|
||||||
locked: false,
|
locked: false,
|
||||||
keycodes: Vec::new(),
|
keycodes: Vec::new(),
|
||||||
action: Action::SetLevel("default".into()),
|
action: Action::SetView("default".into()),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -198,6 +198,9 @@ pub trait WarningHandler {
|
|||||||
fn handle(&mut self, warning: &str);
|
fn handle(&mut self, warning: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: only allow indexing, push, peek, and pop
|
||||||
|
pub type Stack<T> = Vec<T>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user