diff --git a/src/action.rs b/src/action.rs index be42fe59..862dcbf1 100644 --- a/src/action.rs +++ b/src/action.rs @@ -29,6 +29,9 @@ pub enum Action { lock: View, /// When unlocked by pressing it or emitting a key unlock: View, + /// Whether key has a latched state + /// that pops when another key is pressed. + latches: bool, }, /// Hold this modifier for as long as the button is pressed ApplyModifier(Modifier), @@ -48,14 +51,14 @@ pub enum Action { impl Action { pub fn is_locked(&self, view_name: &str) -> bool { match self { - Action::LockView { lock, unlock: _ } => lock == view_name, + Action::LockView { lock, unlock: _, latches: _ } => lock == view_name, _ => false, } } pub fn is_active(&self, view_name: &str) -> bool { match self { Action::SetView(view) => view == view_name, - Action::LockView { lock, unlock: _ } => lock == view_name, + Action::LockView { lock, unlock: _, latches: _ } => lock == view_name, _ => false, } } diff --git a/src/data.rs b/src/data.rs index ab0c4d5a..60e86db6 100644 --- a/src/data.rs +++ b/src/data.rs @@ -266,7 +266,11 @@ struct ButtonMeta { #[serde(deny_unknown_fields)] enum Action { #[serde(rename="locking")] - Locking { lock_view: String, unlock_view: String }, + Locking { + lock_view: String, + unlock_view: String, + pops: Option, + }, #[serde(rename="set_view")] SetView(String), #[serde(rename="show_prefs")] @@ -577,7 +581,8 @@ fn create_action( ) ), SubmitData::Action(Action::Locking { - lock_view, unlock_view + lock_view, unlock_view, + pops, }) => ::action::Action::LockView { lock: filter_view_name( name, @@ -591,6 +596,7 @@ fn create_action( &view_names, warning_handler, ), + latches: pops.unwrap_or(true), }, SubmitData::Action( Action::ShowPrefs diff --git a/src/layout.rs b/src/layout.rs index 20984523..7ca0adb5 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -830,14 +830,14 @@ impl Layout { fn process_action_for_view<'a>( action: &'a Action, current_view: &str, - latch: &LatchedState, + latched: &LatchedState, ) -> (ViewTransition<'a>, LatchedState) { match action { Action::Submit { text: _, keys: _ } | Action::Erase | Action::ApplyModifier(_) => { - let t = match latch { + let t = match latched { LatchedState::FromView(_) => ViewTransition::UnlatchAll, LatchedState::Not => ViewTransition::NoChange, }; @@ -847,29 +847,32 @@ impl Layout { ViewTransition::ChangeTo(view), LatchedState::Not, ), - Action::LockView { lock, unlock } => { + Action::LockView { lock, unlock, latches } => { use self::ViewTransition as VT; let locked = action.is_locked(current_view); - match (locked, latch) { + match (locked, latched, latches) { // Was unlocked, now make locked but latched. - (false, LatchedState::Not) => ( + (false, LatchedState::Not, true) => ( VT::ChangeTo(lock), LatchedState::FromView(current_view.into()), ), // Layout is latched for reason other than this button. - (false, LatchedState::FromView(view)) => ( + (false, LatchedState::FromView(view), true) => ( VT::ChangeTo(lock), LatchedState::FromView(view.clone()), ), // Was latched, now only locked. - (true, LatchedState::FromView(_)) + (true, LatchedState::FromView(_), true) => (VT::NoChange, LatchedState::Not), + // Was unlocked, can't latch so now make fully locked. + (false, _, false) + => (VT::ChangeTo(lock), LatchedState::Not), // Was locked, now make unlocked. - (true, LatchedState::Not) + (true, _, _) => (VT::ChangeTo(unlock), LatchedState::Not), } }, - _ => (ViewTransition::NoChange, latch.clone()), + _ => (ViewTransition::NoChange, latched.clone()), } } } @@ -1128,6 +1131,7 @@ mod test { let action = Action::LockView { lock: "lock".into(), unlock: "unlock".into(), + latches: true, }; assert_eq!( @@ -1156,6 +1160,7 @@ mod test { let switch = Action::LockView { lock: "locked".into(), unlock: "base".into(), + latches: true, }; let submit = Action::Erase; @@ -1217,16 +1222,82 @@ mod test { assert_eq!(&layout.current_view, "base"); } + #[test] + fn reverse_unlatch_layout() { + let switch = Action::LockView { + lock: "locked".into(), + unlock: "base".into(), + latches: true, + }; + + let unswitch = Action::LockView { + lock: "locked".into(), + unlock: "unlocked".into(), + latches: false, + }; + + let submit = Action::Erase; + + let view = View::new(vec![( + 0.0, + Row::new(vec![ + ( + 0.0, + make_button_with_state( + "switch".into(), + make_state_with_action(switch.clone()) + ), + ), + ( + 1.0, + make_button_with_state( + "submit".into(), + make_state_with_action(submit.clone()) + ), + ), + ]), + )]); + + let mut layout = Layout { + current_view: "base".into(), + view_latched: LatchedState::Not, + keymaps: Vec::new(), + kind: ArrangementKind::Base, + pressed_keys: HashSet::new(), + 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), + }, + }; + + layout.apply_view_transition(&switch); + assert_eq!(&layout.current_view, "locked"); + layout.apply_view_transition(&unswitch); + assert_eq!(&layout.current_view, "unlocked"); + } + #[test] fn latch_twopop_layout() { let switch = Action::LockView { lock: "locked".into(), unlock: "base".into(), + latches: true, }; let switch_again = Action::LockView { lock: "ĄĘ".into(), unlock: "locked".into(), + latches: true, }; let submit = Action::Erase;