use std::collections::BTreeSet; use zellij_tile::prelude::*; use crate::ui_components::{back_to_presets, info_line}; use crate::{POSSIBLE_MODIFIERS, WIDTH_BREAKPOINTS}; #[derive(Debug)] pub struct RebindLeadersScreen { selected_primary_key_index: usize, selected_secondary_key_index: usize, main_leader_selected: bool, rebinding_main_leader: bool, browsing_primary_modifier: bool, browsing_secondary_modifier: bool, main_leader: Option, primary_modifier: BTreeSet, secondary_modifier: BTreeSet, latest_mode_info: Option, notification: Option, is_rebinding_for_presets: bool, ui_is_dirty: bool, } impl Default for RebindLeadersScreen { fn default() -> Self { let mut primary_modifier = BTreeSet::new(); primary_modifier.insert(KeyModifier::Ctrl); let mut secondary_modifier = BTreeSet::new(); secondary_modifier.insert(KeyModifier::Alt); RebindLeadersScreen { selected_primary_key_index: 0, selected_secondary_key_index: 0, main_leader_selected: false, rebinding_main_leader: false, browsing_primary_modifier: false, browsing_secondary_modifier: false, main_leader: None, primary_modifier, secondary_modifier, latest_mode_info: None, notification: None, is_rebinding_for_presets: false, ui_is_dirty: false, } } } impl RebindLeadersScreen { pub fn with_rebinding_for_presets(mut self) -> Self { self.is_rebinding_for_presets = true; self } pub fn with_mode_info(mut self, latest_mode_info: Option) -> Self { self.latest_mode_info = latest_mode_info; self.hard_reset_ui_state(); self } pub fn update_mode_info(&mut self, mode_info: ModeInfo) { self.latest_mode_info = Some(mode_info); if !self.ui_is_dirty { self.set_main_leader_from_keybindings(); self.set_primary_and_secondary_modifiers_from_keybindings(); } } pub fn primary_and_secondary_modifiers( &self, ) -> (BTreeSet, BTreeSet) { ( self.primary_modifier.clone(), self.secondary_modifier.clone(), ) } fn set_main_leader_from_keybindings(&mut self) { self.main_leader = self.latest_mode_info.as_ref().and_then(|mode_info| { mode_info .keybinds .iter() .find_map(|m| { if m.0 == InputMode::Locked { Some(m.1.clone()) } else { None } }) .and_then(|k| { k.into_iter().find_map(|(k, a)| { if a == &[actions::Action::SwitchToMode(InputMode::Normal)] { Some(k) } else { None } }) }) }); } fn set_primary_and_secondary_modifiers_from_keybindings(&mut self) { let mut primary_modifier = self.latest_mode_info.as_ref().and_then(|mode_info| { mode_info .keybinds .iter() .find_map(|m| { if m.0 == InputMode::Locked { Some(m.1.clone()) } else { None } }) .and_then(|k| { k.into_iter().find_map(|(k, a)| { if a == &[actions::Action::SwitchToMode(InputMode::Normal)] { Some(k.key_modifiers.clone()) } else { None } }) }) }); let mut secondary_modifier = self.latest_mode_info.as_ref().and_then(|mode_info| { let base_mode = mode_info.base_mode.unwrap_or(InputMode::Normal); mode_info .keybinds .iter() .find_map(|m| { if m.0 == base_mode { Some(m.1.clone()) } else { None } }) .and_then(|k| { k.into_iter().find_map(|(k, a)| { if a == &[actions::Action::NewPane(None, None, false)] { Some(k.key_modifiers.clone()) } else { None } }) }) }); if let Some(primary_modifier) = primary_modifier.take() { self.primary_modifier = primary_modifier; } if let Some(secondary_modifier) = secondary_modifier.take() { self.secondary_modifier = secondary_modifier; } } pub fn set_unlock_toggle_selected(&mut self) { self.main_leader_selected = true; self.selected_primary_key_index = 0; self.selected_secondary_key_index = 0; self.rebinding_main_leader = false; self.browsing_primary_modifier = false; self.browsing_secondary_modifier = false; } pub fn set_primary_modifier_selected(&mut self) { self.browsing_primary_modifier = true; self.main_leader_selected = false; self.selected_primary_key_index = 0; self.selected_secondary_key_index = 0; self.rebinding_main_leader = false; self.browsing_secondary_modifier = false; } pub fn set_secondary_modifier_selected(&mut self) { self.browsing_secondary_modifier = true; self.browsing_primary_modifier = false; self.main_leader_selected = false; self.selected_primary_key_index = 0; self.selected_secondary_key_index = 0; self.rebinding_main_leader = false; } pub fn set_rebinding_unlock_toggle(&mut self) { self.rebinding_main_leader = true; self.browsing_secondary_modifier = false; self.browsing_primary_modifier = false; self.main_leader_selected = false; self.selected_primary_key_index = 0; self.selected_secondary_key_index = 0; } pub fn move_secondary_index_down(&mut self) { if self.selected_secondary_key_index < POSSIBLE_MODIFIERS.len().saturating_sub(1) { self.selected_secondary_key_index += 1; } else { self.set_unlock_toggle_selected(); } } pub fn move_secondary_index_up(&mut self) { if self.selected_secondary_key_index > 0 { self.selected_secondary_key_index -= 1; } else { self.set_unlock_toggle_selected(); } } pub fn move_selection_for_default_preset(&mut self, key: &KeyWithModifier) { if self.browsing_primary_modifier { if key.bare_key == BareKey::Left && key.has_no_modifiers() { self.browsing_primary_modifier = false; self.browsing_secondary_modifier = true; self.selected_secondary_key_index = self.selected_primary_key_index; } else if key.bare_key == BareKey::Right && key.has_no_modifiers() { self.browsing_primary_modifier = false; self.browsing_secondary_modifier = true; self.selected_secondary_key_index = self.selected_primary_key_index; } else if key.bare_key == BareKey::Down && key.has_no_modifiers() { if self.selected_primary_key_index < POSSIBLE_MODIFIERS.len().saturating_sub(1) { self.selected_primary_key_index += 1; } } else if key.bare_key == BareKey::Up && key.has_no_modifiers() { if self.selected_primary_key_index > 0 { self.selected_primary_key_index -= 1; } } } else if self.browsing_secondary_modifier { if key.bare_key == BareKey::Left && key.has_no_modifiers() { self.browsing_secondary_modifier = false; self.browsing_primary_modifier = true; self.selected_primary_key_index = self.selected_secondary_key_index; } else if key.bare_key == BareKey::Right && key.has_no_modifiers() { self.browsing_secondary_modifier = false; self.browsing_primary_modifier = true; self.selected_primary_key_index = self.selected_secondary_key_index; } else if key.bare_key == BareKey::Down && key.has_no_modifiers() { if self.selected_secondary_key_index < POSSIBLE_MODIFIERS.len().saturating_sub(1) { self.selected_secondary_key_index += 1; } } else if key.bare_key == BareKey::Up && key.has_no_modifiers() { if self.selected_secondary_key_index > 0 { self.selected_secondary_key_index -= 1; } } } else { self.set_primary_modifier_selected(); } } pub fn move_selection_for_unlock_first(&mut self, key: &KeyWithModifier) { if self.browsing_secondary_modifier { if (key.bare_key == BareKey::Left || key.bare_key == BareKey::Right) && key.has_no_modifiers() { self.set_unlock_toggle_selected(); } else if key.bare_key == BareKey::Down && key.has_no_modifiers() { self.move_secondary_index_down(); } else if key.bare_key == BareKey::Up && key.has_no_modifiers() { self.move_secondary_index_up(); } } else if self.main_leader_selected { if (key.bare_key == BareKey::Down || key.bare_key == BareKey::Up || key.bare_key == BareKey::Right || key.bare_key == BareKey::Left) && key.has_no_modifiers() { self.set_secondary_modifier_selected(); } } else { self.set_unlock_toggle_selected(); } } pub fn render( &mut self, rows: usize, cols: usize, ui_size: usize, notification: &Option, ) { if self.is_rebinding_for_presets { back_to_presets(); } let notification = notification.clone().or_else(|| self.notification.clone()); if self.currently_in_unlock_first() { self.render_unlock_first(rows, cols); } else { self.render_default_preset(rows, cols); } let warning_text = self.warning_text(cols); info_line(rows, cols, ui_size, ¬ification, &warning_text, None); } fn render_unlock_first(&mut self, rows: usize, cols: usize) { self.render_screen_title_unlock_first(rows, cols); self.render_unlock_toggle(rows, cols); self.render_secondary_modifier_selector(rows, cols); self.render_help_text(rows, cols); } fn render_default_preset(&mut self, rows: usize, cols: usize) { self.render_screen_title_default_preset(rows, cols); self.render_primary_modifier_selector(rows, cols); self.render_secondary_modifier_selector(rows, cols); self.render_help_text(rows, cols); } fn render_primary_modifier_selector(&self, rows: usize, cols: usize) { let screen_width = if cols >= WIDTH_BREAKPOINTS.0 { WIDTH_BREAKPOINTS.0 } else { WIDTH_BREAKPOINTS.1 }; let base_x = cols.saturating_sub(screen_width) / 2; let base_y = rows.saturating_sub(7) / 2; let primary_modifier_key_text = self.primary_modifier_text(); let (primary_modifier_text, primary_modifier_start_position) = if cols >= WIDTH_BREAKPOINTS.0 { (format!("Primary: {}", primary_modifier_key_text), 9) } else { (format!("{}", primary_modifier_key_text), 0) }; print_text_with_coordinates( Text::new(primary_modifier_text).color_range(3, primary_modifier_start_position..), base_x, base_y + 4, None, None, ); print_nested_list_with_coordinates( POSSIBLE_MODIFIERS .iter() .enumerate() .map(|(i, m)| { let item = if self.primary_modifier.contains(m) { NestedListItem::new(m.to_string()).color_range(3, ..) } else { NestedListItem::new(m.to_string()) }; if self.browsing_primary_modifier && self.selected_primary_key_index == i { item.selected() } else { item } }) .collect(), base_x, base_y + 5, Some(screen_width / 2), None, ); } pub fn render_screen_title_unlock_first(&self, rows: usize, cols: usize) { let screen_width = if cols >= WIDTH_BREAKPOINTS.0 { WIDTH_BREAKPOINTS.0 } else { WIDTH_BREAKPOINTS.1 }; let leader_keys_text = if cols >= WIDTH_BREAKPOINTS.0 { "Rebind leader keys (Non-Colliding preset)" } else if cols >= WIDTH_BREAKPOINTS.1 { "Rebind leader keys (Non-Colliding)" } else { "Rebind leader keys" }; let base_x = cols.saturating_sub(screen_width) / 2; let base_y = rows.saturating_sub(10) / 2; let explanation_text_1 = if cols >= WIDTH_BREAKPOINTS.0 { "Unlock toggle - used to expose the other modes (eg. PANE, TAB)" } else if cols >= WIDTH_BREAKPOINTS.1 { "Unlock toggle - expose other modes" } else { "" }; let explanation_text_2 = if cols >= WIDTH_BREAKPOINTS.0 { "Secondary modifier - prefixes common actions (eg. New Pane)" } else if cols >= WIDTH_BREAKPOINTS.1 { "Secondary modifier - common actions" } else { "" }; print_text_with_coordinates( Text::new(leader_keys_text).color_range(2, ..), base_x, base_y, None, None, ); print_text_with_coordinates( Text::new(explanation_text_1).color_range(1, ..=12), base_x, base_y + 2, None, None, ); print_text_with_coordinates( Text::new(explanation_text_2).color_range(1, ..=17), base_x, base_y + 3, None, None, ); } fn render_screen_title_default_preset(&self, rows: usize, cols: usize) { let screen_width = if cols >= WIDTH_BREAKPOINTS.0 { WIDTH_BREAKPOINTS.0 } else { WIDTH_BREAKPOINTS.1 }; let leader_keys_text = if cols >= WIDTH_BREAKPOINTS.0 { "Rebind leader keys (Default preset)" } else { "Rebind leader keys" }; let base_x = cols.saturating_sub(screen_width) / 2; let base_y = rows.saturating_sub(10) / 2; let explanation_text_1 = if cols >= WIDTH_BREAKPOINTS.0 { "Primary - the modifier used to switch modes (eg. PANE, TAB)" } else if cols >= WIDTH_BREAKPOINTS.1 { "Primary - used to switch modes" } else { "" }; let explanation_text_2 = if cols >= WIDTH_BREAKPOINTS.0 { "Secondary - the modifier used for common actions (eg. New Pane)" } else if cols >= WIDTH_BREAKPOINTS.1 { "Secondary - common actions" } else { "" }; print_text_with_coordinates( Text::new(leader_keys_text).color_range(2, ..), base_x, base_y, None, None, ); print_text_with_coordinates( Text::new(explanation_text_1).color_range(1, ..=6), base_x, base_y + 2, None, None, ); print_text_with_coordinates( Text::new(explanation_text_2).color_range(1, ..=8), base_x, base_y + 3, None, None, ); } fn render_unlock_toggle(&self, rows: usize, cols: usize) { let screen_width = if cols >= WIDTH_BREAKPOINTS.0 { WIDTH_BREAKPOINTS.0 } else { WIDTH_BREAKPOINTS.1 }; let base_x = cols.saturating_sub(screen_width) / 2; let base_y = rows.saturating_sub(10) / 2; if let Some(main_leader_key_text) = self.main_leader_text() { let main_leader_key_text = if self.rebinding_main_leader { "...".to_owned() } else { main_leader_key_text }; let (primary_modifier_text, primary_modifier_start_position) = if cols >= WIDTH_BREAKPOINTS.0 { (format!("Unlock Toggle: {}", main_leader_key_text), 15) } else { (format!("{}", main_leader_key_text), 0) }; let mut primary_modifier = Text::new(primary_modifier_text).color_range(3, primary_modifier_start_position..); if self.main_leader_selected { primary_modifier = primary_modifier.selected(); } print_text_with_coordinates(primary_modifier, base_x, base_y + 5, None, None); if self.rebinding_main_leader { let first_bulletin = "[Enter new key] eg."; let second_bulletin = "\"Ctrl g\", \"Alt g\","; let third_bulletin = "\"Alt ESC\", \"Ctrl SPACE\""; print_nested_list_with_coordinates( vec![ NestedListItem::new(first_bulletin).color_range(3, ..=14), NestedListItem::new(second_bulletin), NestedListItem::new(third_bulletin), ], base_x, base_y + 6, None, None, ); } } } fn main_leader_text(&self) -> Option { self.main_leader.as_ref().map(|m| format!("{}", m)) } fn render_secondary_modifier_selector(&mut self, rows: usize, cols: usize) { let screen_width = if cols >= WIDTH_BREAKPOINTS.0 { WIDTH_BREAKPOINTS.0 } else { WIDTH_BREAKPOINTS.1 }; let base_x = cols.saturating_sub(screen_width) / 2; let base_y = rows.saturating_sub(10) / 2; let secondary_modifier_key_text = self.secondary_modifier_text(); let (secondary_modifier_text, secondary_modifier_start_position) = if cols >= WIDTH_BREAKPOINTS.0 { if self.currently_in_unlock_first() { ( format!("Secondary Modifier: {}", secondary_modifier_key_text), 20, ) } else { (format!("Secondary: {}", secondary_modifier_key_text), 11) } } else { (format!("{}", secondary_modifier_key_text), 0) }; let secondary_modifier_menu_x_coords = base_x + (screen_width / 2); print_text_with_coordinates( Text::new(secondary_modifier_text).color_range(0, secondary_modifier_start_position..), secondary_modifier_menu_x_coords, base_y + 5, None, None, ); print_nested_list_with_coordinates( POSSIBLE_MODIFIERS .iter() .enumerate() .map(|(i, m)| { let item = if self.secondary_modifier.contains(m) { NestedListItem::new(m.to_string()).color_range(0, ..) } else { NestedListItem::new(m.to_string()) }; if self.browsing_secondary_modifier && self.selected_secondary_key_index == i { item.selected() } else { item } }) .collect(), secondary_modifier_menu_x_coords, base_y + 6, Some(screen_width / 2), None, ); } fn primary_modifier_text(&self) -> String { if self.primary_modifier.is_empty() { "".to_owned() } else { self.primary_modifier .iter() .map(|m| m.to_string()) .collect::>() .join("-") } } fn secondary_modifier_text(&self) -> String { if self.secondary_modifier.is_empty() { "".to_owned() } else { self.secondary_modifier .iter() .map(|m| m.to_string()) .collect::>() .join("-") } } fn render_help_text(&self, rows: usize, cols: usize) { if self.is_rebinding_for_presets { return self.render_help_text_for_presets_rebinding(rows, cols); } let help_text_long = "Help: <←↓↑→> - navigate, - select, - apply, - save, - reset, - close"; let help_text_medium = "Help: <←↓↑→/SPACE> - navigate/select, - apply/save, - reset, - close"; let help_text_short = "Help: <←↓↑→>// select/ save/ reset/"; let help_text_minimum = "<←↓↑→>/////"; if cols >= help_text_long.chars().count() { print_text_with_coordinates( Text::new(help_text_long) .color_range(2, 6..=12) .color_range(2, 25..=31) .color_range(2, 43..=49) .color_range(2, 60..=67) .color_range(2, 77..=84) .color_range(2, 95..=99), 0, rows, None, None, ); } else if cols >= help_text_medium.chars().count() { print_text_with_coordinates( Text::new(help_text_medium) .color_range(2, 6..=17) .color_range(2, 38..=48) .color_range(2, 64..=72) .color_range(2, 82..=86), 0, rows, None, None, ); } else if cols >= help_text_short.chars().count() { print_text_with_coordinates( Text::new(help_text_short) .color_range(2, 6..=11) .color_range(2, 13..=19) .color_range(2, 21..=27) .color_range(2, 36..=40) .color_range(2, 47..=54) .color_range(2, 62..=66), 0, rows, None, None, ); } else { print_text_with_coordinates( Text::new(help_text_minimum) .color_range(2, ..=5) .color_range(2, 7..=13) .color_range(2, 15..=21) .color_range(2, 23..=27) .color_range(2, 29..=36) .color_range(2, 38..=42), 0, rows, None, None, ); } } fn render_help_text_for_presets_rebinding(&self, rows: usize, cols: usize) { let help_text_long = "Help: <←↓↑→> - navigate, - select, - apply to presets in previous screen"; let help_text_medium = "Help: <←↓↑→> - navigate, - select, - apply"; let help_text_short = "<←↓↑→/SPACE> - navigate/select, - apply"; let help_text_minimum = "<←↓↑→>//"; if cols >= help_text_long.chars().count() { print_text_with_coordinates( Text::new(help_text_long) .color_range(2, 6..=12) .color_range(2, 25..=31) .color_range(2, 43..=49), 0, rows, None, None, ); } else if cols >= help_text_medium.chars().count() { print_text_with_coordinates( Text::new(help_text_medium) .color_range(2, 6..=12) .color_range(2, 25..=31) .color_range(2, 43..=49), 0, rows, None, None, ); } else if cols >= help_text_short.chars().count() { print_text_with_coordinates( Text::new(help_text_short) .color_range(2, 1..=4) .color_range(2, 6..=10) .color_range(2, 32..=38), 0, rows, None, None, ); } else { print_text_with_coordinates( Text::new(help_text_minimum) .color_range(2, ..=5) .color_range(2, 7..=13) .color_range(2, 15..=21), 0, rows, None, None, ); } } pub fn handle_key(&mut self, key: KeyWithModifier) -> bool { if let Some(notification) = self.notification.take() { drop(notification); true } else if self.currently_in_unlock_first() { self.handle_unlock_first_key(key) } else { self.handle_default_preset_key(key) } } fn currently_in_unlock_first(&self) -> bool { if self.is_rebinding_for_presets { false } else { self.latest_mode_info .as_ref() .map(|m| m.base_mode == Some(InputMode::Locked)) .unwrap_or(false) } } fn handle_default_preset_key(&mut self, key: KeyWithModifier) -> bool { let should_render = true; if key.bare_key == BareKey::Insert && key.has_no_modifiers() && !self.is_rebinding_for_presets { let write_to_disk = true; self.rebind_keys(write_to_disk); self.hard_reset_ui_state(); } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() { let write_to_disk = false; self.rebind_keys(write_to_disk); self.hard_reset_ui_state(); } else if key.is_key_with_ctrl_modifier(BareKey::Char('c')) { self.hard_reset_ui_state(); } else if key.bare_key == BareKey::Esc && key.has_no_modifiers() { close_self(); } else if key.bare_key == BareKey::Char(' ') && key.has_no_modifiers() { if self.browsing_primary_modifier { let selected_primary_key_index = self.selected_primary_key_index; self.toggle_primary_modifier(selected_primary_key_index); } else if self.browsing_secondary_modifier { let selected_secondary_key_index = self.selected_secondary_key_index; self.toggle_secondary_modifier(selected_secondary_key_index); } } else if (key.bare_key == BareKey::Left || key.bare_key == BareKey::Right || key.bare_key == BareKey::Up || key.bare_key == BareKey::Down) && key.has_no_modifiers() { self.move_selection_for_default_preset(&key); } else if self.rebinding_main_leader { self.soft_reset_ui_state(); self.main_leader = Some(key.clone()); self.ui_is_dirty = true; } should_render } fn toggle_secondary_modifier(&mut self, secondary_modifier_index: usize) { if let Some(selected_modifier) = POSSIBLE_MODIFIERS.get(secondary_modifier_index) { if self.secondary_modifier.contains(selected_modifier) { self.secondary_modifier.remove(selected_modifier); } else { self.secondary_modifier.insert(*selected_modifier); } self.ui_is_dirty = true; } } fn toggle_primary_modifier(&mut self, primary_modifier_index: usize) { if let Some(selected_modifier) = POSSIBLE_MODIFIERS.get(primary_modifier_index) { if self.primary_modifier.contains(selected_modifier) { self.primary_modifier.remove(selected_modifier); } else { self.primary_modifier.insert(*selected_modifier); } self.ui_is_dirty = true; } } fn rebind_keys(&mut self, write_to_disk: bool) { let mut keys_to_unbind = vec![]; let mut keys_to_bind = vec![]; if self.currently_in_unlock_first() { if let Some(unlock_key) = &self.main_leader { self.bind_unlock_key(&mut keys_to_unbind, &mut keys_to_bind, unlock_key); } self.bind_all_secondary_actions(&mut keys_to_unbind, &mut keys_to_bind); } else { self.bind_all_secondary_actions(&mut keys_to_unbind, &mut keys_to_bind); self.bind_all_primary_actions(&mut keys_to_unbind, &mut keys_to_bind); } if write_to_disk { self.notification = Some("Configuration applied and saved to disk.".to_owned()); } else { self.notification = Some("Configuration applied to current session.".to_owned()); } rebind_keys(keys_to_unbind, keys_to_bind, write_to_disk); } fn bind_all_primary_actions( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, ) { self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Locked, KeyWithModifier::new_with_modifiers(BareKey::Char('g'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Pane, KeyWithModifier::new_with_modifiers(BareKey::Char('p'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Tab, KeyWithModifier::new_with_modifiers(BareKey::Char('t'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Resize, KeyWithModifier::new_with_modifiers(BareKey::Char('n'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Move, KeyWithModifier::new_with_modifiers(BareKey::Char('h'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Scroll, KeyWithModifier::new_with_modifiers(BareKey::Char('s'), self.primary_modifier.clone()), ); self.bind_primary_switch_to_mode_action( keys_to_unbind, keys_to_bind, InputMode::Session, KeyWithModifier::new_with_modifiers(BareKey::Char('o'), self.primary_modifier.clone()), ); self.bind_quit_action( keys_to_unbind, keys_to_bind, KeyWithModifier::new_with_modifiers(BareKey::Char('q'), self.primary_modifier.clone()), ); } fn bind_quit_action( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, new_key: KeyWithModifier, ) { let all_relevant_modes = vec![ InputMode::Normal, InputMode::Pane, InputMode::Tab, InputMode::Resize, InputMode::Move, InputMode::Search, InputMode::Scroll, InputMode::Session, ]; for mode in &all_relevant_modes { for current_keybind in self.get_current_keybinds(*mode, &[actions::Action::Quit]) { keys_to_unbind.push((*mode, current_keybind)); } keys_to_bind.push((*mode, new_key.clone(), vec![actions::Action::Quit])); } } fn get_current_keybinds( &self, in_mode: InputMode, actions: &[actions::Action], ) -> Vec { self.latest_mode_info .as_ref() .and_then(|m_i| { m_i.keybinds .iter() .find_map(|m| if m.0 == in_mode { Some(&m.1) } else { None }) }) .map(|k| { k.into_iter() .filter_map(|(k, a)| if a == actions { Some(k.clone()) } else { None }) .collect::>() }) .unwrap_or_else(Default::default) } fn bind_primary_switch_to_mode_action( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, target_mode: InputMode, new_key: KeyWithModifier, ) { let all_relevant_modes = vec![ InputMode::Locked, InputMode::Normal, InputMode::Pane, InputMode::Tab, InputMode::Resize, InputMode::Move, InputMode::Search, InputMode::Scroll, InputMode::Session, ]; for mode in &all_relevant_modes { if mode == &target_mode { for current_keybind in self.get_current_keybinds( *mode, &[actions::Action::SwitchToMode(InputMode::Normal)], ) { if current_keybind.bare_key != BareKey::Enter && current_keybind.bare_key != BareKey::Esc { keys_to_unbind.push((*mode, current_keybind)); } } } else { for current_keybind in self.get_current_keybinds(*mode, &[actions::Action::SwitchToMode(target_mode)]) { keys_to_unbind.push((*mode, current_keybind)); } } } for mode in &all_relevant_modes { if mode == &target_mode { keys_to_bind.push(( *mode, new_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Normal)], )); } else if mode != &InputMode::Locked { keys_to_bind.push(( *mode, new_key.clone(), vec![actions::Action::SwitchToMode(target_mode)], )); } } } fn bind_all_secondary_actions( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, ) { self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::NewPane(None, None, false)], KeyWithModifier::new_with_modifiers( BareKey::Char('n'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::ToggleFloatingPanes], KeyWithModifier::new_with_modifiers( BareKey::Char('f'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveTab(Direction::Left)], KeyWithModifier::new_with_modifiers( BareKey::Char('i'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveTab(Direction::Right)], KeyWithModifier::new_with_modifiers( BareKey::Char('o'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocusOrTab(Direction::Left)], KeyWithModifier::new_with_modifiers( BareKey::Char('h'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocusOrTab(Direction::Left)], KeyWithModifier::new_with_modifiers(BareKey::Left, self.secondary_modifier.clone()), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocusOrTab(Direction::Right)], KeyWithModifier::new_with_modifiers( BareKey::Char('l'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocusOrTab(Direction::Right)], KeyWithModifier::new_with_modifiers(BareKey::Right, self.secondary_modifier.clone()), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocus(Direction::Down)], KeyWithModifier::new_with_modifiers( BareKey::Char('j'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocus(Direction::Down)], KeyWithModifier::new_with_modifiers(BareKey::Down, self.secondary_modifier.clone()), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocus(Direction::Up)], KeyWithModifier::new_with_modifiers( BareKey::Char('k'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::MoveFocus(Direction::Up)], KeyWithModifier::new_with_modifiers(BareKey::Up, self.secondary_modifier.clone()), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::Resize(Resize::Increase, None)], KeyWithModifier::new_with_modifiers( BareKey::Char('+'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::Resize(Resize::Increase, None)], KeyWithModifier::new_with_modifiers( BareKey::Char('='), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::Resize(Resize::Decrease, None)], KeyWithModifier::new_with_modifiers( BareKey::Char('-'), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::PreviousSwapLayout], KeyWithModifier::new_with_modifiers( BareKey::Char('['), self.secondary_modifier.clone(), ), ); self.bind_actions( keys_to_unbind, keys_to_bind, &[actions::Action::NextSwapLayout], KeyWithModifier::new_with_modifiers( BareKey::Char(']'), self.secondary_modifier.clone(), ), ); } fn bind_actions( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, actions: &[actions::Action], key: KeyWithModifier, ) { for current_keybind in self.get_current_keybinds(InputMode::Normal, actions) { keys_to_unbind.push((InputMode::Normal, current_keybind)); } for current_keybind in self.get_current_keybinds(InputMode::Locked, actions) { keys_to_unbind.push((InputMode::Locked, current_keybind)); } keys_to_bind.push((InputMode::Normal, key.clone(), actions.to_vec())); keys_to_bind.push((InputMode::Locked, key, actions.to_vec())); } fn bind_unlock_key( &self, keys_to_unbind: &mut Vec<(InputMode, KeyWithModifier)>, keys_to_bind: &mut Vec<(InputMode, KeyWithModifier, Vec)>, unlock_key: &KeyWithModifier, ) { if let Some(previous_unlock_key) = self.get_current_keybind( InputMode::Locked, &[actions::Action::SwitchToMode(InputMode::Normal)], ) { keys_to_unbind.push((InputMode::Locked, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Normal, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Pane, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Tab, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Resize, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Move, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Search, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Scroll, previous_unlock_key.clone())); keys_to_unbind.push((InputMode::Session, previous_unlock_key.clone())); } keys_to_bind.push(( InputMode::Locked, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Normal)], )); keys_to_bind.push(( InputMode::Normal, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Pane, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Tab, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Resize, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Move, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Search, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Scroll, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); keys_to_bind.push(( InputMode::Session, unlock_key.clone(), vec![actions::Action::SwitchToMode(InputMode::Locked)], )); } fn get_current_keybind( &self, in_mode: InputMode, actions: &[actions::Action], ) -> Option { self.latest_mode_info .as_ref() .and_then(|m_i| { m_i.keybinds .iter() .find_map(|m| if m.0 == in_mode { Some(&m.1) } else { None }) }) .and_then(|k| { k.into_iter() .find_map(|(k, a)| if a == actions { Some(k) } else { None }) }) .cloned() } fn soft_reset_ui_state(&mut self) { let mut latest_mode_info = self.latest_mode_info.take(); let notification = self.notification.take(); let primary_modifier = self.primary_modifier.clone(); let secondary_modifier = self.secondary_modifier.clone(); let main_leader = self.main_leader.clone(); let ui_is_dirty = self.ui_is_dirty; let is_rebinding_for_presets = self.is_rebinding_for_presets; *self = Default::default(); if let Some(latest_mode_info) = latest_mode_info.take() { self.update_mode_info(latest_mode_info); } self.notification = notification; self.primary_modifier = primary_modifier; self.secondary_modifier = secondary_modifier; self.main_leader = main_leader; self.ui_is_dirty = ui_is_dirty; self.is_rebinding_for_presets = is_rebinding_for_presets; } fn hard_reset_ui_state(&mut self) { let mut latest_mode_info = self.latest_mode_info.take(); let notification = self.notification.take(); let is_rebinding_for_presets = self.is_rebinding_for_presets; *self = Default::default(); if let Some(latest_mode_info) = latest_mode_info.take() { self.update_mode_info(latest_mode_info); } self.notification = notification; self.is_rebinding_for_presets = is_rebinding_for_presets; } fn handle_unlock_first_key(&mut self, key: KeyWithModifier) -> bool { if key.bare_key == BareKey::Insert && key.has_no_modifiers() && !self.is_rebinding_for_presets { let write_to_disk = true; self.rebind_keys(write_to_disk); self.hard_reset_ui_state(); } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() { let write_to_disk = false; self.rebind_keys(write_to_disk); self.hard_reset_ui_state(); } else if key.is_key_with_ctrl_modifier(BareKey::Char('c')) { self.hard_reset_ui_state(); } else if key.bare_key == BareKey::Esc && key.has_no_modifiers() { if self.rebinding_main_leader { self.soft_reset_ui_state(); } else { close_self(); } } else if key.bare_key == BareKey::Char(' ') && key.has_no_modifiers() { if self.main_leader_selected { self.set_rebinding_unlock_toggle(); } else if self.browsing_secondary_modifier { let selected_secondary_key_index = self.selected_secondary_key_index; self.toggle_secondary_modifier(selected_secondary_key_index); } } else if (key.bare_key == BareKey::Left || key.bare_key == BareKey::Right || key.bare_key == BareKey::Up || key.bare_key == BareKey::Down) && key.has_no_modifiers() { self.move_selection_for_unlock_first(&key); } else if self.rebinding_main_leader { self.soft_reset_ui_state(); self.main_leader = Some(key.clone()); self.ui_is_dirty = true; } true } fn warning_text(&self, max_width: usize) -> Option { if self.needs_kitty_support() { if max_width >= 38 { Some(String::from("Warning: requires supporting terminal.")) } else { Some(String::from("Requires supporting terminal")) } } else if self.primary_modifier.is_empty() && self.secondary_modifier.is_empty() { if max_width >= 49 { Some(String::from( "Warning: no leaders defined. UI will be disabled.", )) } else { Some(String::from("No leaders. UI will be unusable.")) } } else { None } } fn needs_kitty_support(&self) -> bool { self.primary_modifier.len() > 1 || self.secondary_modifier.len() > 1 || self.primary_modifier.contains(&KeyModifier::Super) || self.secondary_modifier.contains(&KeyModifier::Super) } }