diff options
| author | Aram Drevekenin <aram@poor.dev> | 2024-08-16 14:00:01 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-16 14:00:01 +0200 |
| commit | 056537d3eef06cddc60dc13b715a41766bb1fe9d (patch) | |
| tree | ff7e107e992e4c98ac60ac88da7215cb0beb668a /default-plugins | |
| parent | docs(changelog): CommandPaneReRun plugin event (diff) | |
| download | sl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.tar.gz sl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.tar.xz sl0ppij-056537d3eef06cddc60dc13b715a41766bb1fe9d.zip | |
feat(ux): first run setup-wizard (#3556)
* separate saved/runtime structure, kind of working
* serializing config
* work
* work
* save config through the configuration screen
* work
* startup wizard
* style(code): cleanups
* fix(session): reload config from disk when switching sessions
* style(fmt): rustfmt
* fix(config): propagate cli config options to screen
* style(fmt): rustfmt
Diffstat (limited to 'default-plugins')
| -rw-r--r-- | default-plugins/configuration/src/main.rs | 385 | ||||
| -rw-r--r-- | default-plugins/fixture-plugin-for-tests/src/main.rs | 2 |
2 files changed, 317 insertions, 70 deletions
diff --git a/default-plugins/configuration/src/main.rs b/default-plugins/configuration/src/main.rs index db0219c5..bcdc4d63 100644 --- a/default-plugins/configuration/src/main.rs +++ b/default-plugins/configuration/src/main.rs @@ -2,6 +2,8 @@ use zellij_tile::prelude::*; use std::collections::{BTreeMap, BTreeSet}; +static UI_SIZE: usize = 15; + struct State { userspace_configuration: BTreeMap<String, String>, selected_index: Option<usize>, @@ -16,6 +18,9 @@ struct State { preset_color_index: usize, primary_leader_key_color_index: usize, secondary_leader_key_color_index: usize, + notification: Option<String>, + is_setup_wizard: bool, + ui_size: usize, } impl Default for State { @@ -43,6 +48,9 @@ impl Default for State { secondary_leader_key_color_index: 0, mode_color_index: 2, preset_color_index: 1, + notification: None, + is_setup_wizard: false, + ui_size: UI_SIZE, } } } @@ -51,6 +59,10 @@ register_plugin!(State); impl ZellijPlugin for State { fn load(&mut self, configuration: BTreeMap<String, String>) { + self.is_setup_wizard = configuration + .get("is_setup_wizard") + .map(|v| v == "true") + .unwrap_or(false); self.userspace_configuration = configuration; // we need the ReadApplicationState permission to receive the ModeUpdate and TabUpdate // events @@ -64,12 +76,19 @@ impl ZellijPlugin for State { PermissionType::ChangeApplicationState, ]); subscribe(&[ - EventType::ModeUpdate, - EventType::TabUpdate, - EventType::Key, - EventType::Timer, EventType::PermissionRequestResult, + EventType::Key, + EventType::FailedToWriteConfigToDisk, ]); + if self.is_setup_wizard { + self.ui_size = 18; + self.selected_index = Some(0); + let own_plugin_id = get_plugin_ids().plugin_id; + rename_plugin_pane(own_plugin_id, "First Run Setup Wizard (Step 1/1)"); + resize_focused_pane(Resize::Increase); + resize_focused_pane(Resize::Increase); + resize_focused_pane(Resize::Increase); + } } fn update(&mut self, event: Event) -> bool { let mut should_render = false; @@ -80,10 +99,26 @@ impl ZellijPlugin for State { Event::Key(key) => { if self.remapping_leaders { should_render = self.handle_remapping_screen_key(key); + } else if self.is_setup_wizard { + should_render = self.handle_setup_wizard_key(key); } else { should_render = self.handle_main_screen_key(key); } }, + Event::FailedToWriteConfigToDisk(config_file_path) => { + match config_file_path { + Some(failed_path) => { + self.notification = Some(format!( + "Failed to write configuration file: {}", + failed_path + )); + }, + None => { + self.notification = Some(format!("Failed to write configuration file.")); + }, + } + should_render = true; + }, _ => (), }; should_render @@ -91,6 +126,8 @@ impl ZellijPlugin for State { fn render(&mut self, rows: usize, cols: usize) { if self.remapping_leaders { self.render_remapping_leaders_screen(rows, cols); + } else if self.is_setup_wizard { + self.render_setup_wizard_screen(rows, cols); } else { self.render_main_screen(rows, cols); } @@ -185,7 +222,10 @@ impl State { } fn handle_main_screen_key(&mut self, key: KeyWithModifier) -> bool { let mut should_render = false; - if key.bare_key == BareKey::Down && key.has_no_modifiers() { + if self.notification.is_some() { + self.notification = None; + should_render = true; + } else if key.bare_key == BareKey::Down && key.has_no_modifiers() { if self.selected_index.is_none() { self.selected_index = Some(0); } else if self.selected_index < Some(1) { @@ -205,45 +245,70 @@ impl State { should_render = true; } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() { if let Some(selected) = self.selected_index.take() { - if selected == 0 { - // TODO: these should be part of a "transaction" when they are - // implemented - reconfigure(default_keybinds( - self.primary_modifier - .iter() - .map(|m| m.to_string()) - .collect::<Vec<_>>() - .join(" "), - self.secondary_modifier - .iter() - .map(|m| m.to_string()) - .collect::<Vec<_>>() - .join(" "), - )); - switch_to_input_mode(&InputMode::Normal); - } else if selected == 1 { - // TODO: these should be part of a "transaction" when they are - // implemented - reconfigure(unlock_first_keybinds( - self.primary_modifier - .iter() - .map(|m| m.to_string()) - .collect::<Vec<_>>() - .join(" "), - self.secondary_modifier - .iter() - .map(|m| m.to_string()) - .collect::<Vec<_>>() - .join(" "), - )); - switch_to_input_mode(&InputMode::Locked); - } + let write_to_disk = false; + self.reconfigure(selected, write_to_disk); + self.notification = Some("Configuration applied to current session.".to_owned()); + should_render = true; + } else { + self.selected_index = Some(0); + should_render = true; + } + } else if key.bare_key == BareKey::Char(' ') && key.has_no_modifiers() { + if let Some(selected) = self.selected_index.take() { + let write_to_disk = true; + self.reconfigure(selected, write_to_disk); + self.notification = Some("Configuration applied and saved to disk.".to_owned()); should_render = true; } } else if key.bare_key == BareKey::Char('l') && key.has_no_modifiers() { self.remapping_leaders = true; should_render = true; - } else if key.bare_key == BareKey::Esc && key.has_no_modifiers() { + } else if (key.bare_key == BareKey::Esc && key.has_no_modifiers()) + || key.is_key_with_ctrl_modifier(BareKey::Char('c')) + { + close_self(); + should_render = true; + } + should_render + } + fn handle_setup_wizard_key(&mut self, key: KeyWithModifier) -> bool { + let mut should_render = false; + if self.notification.is_some() { + self.notification = None; + should_render = true; + } else if key.bare_key == BareKey::Down && key.has_no_modifiers() { + if self.selected_index.is_none() { + self.selected_index = Some(0); + } else if self.selected_index < Some(1) { + self.selected_index = Some(1); + } else { + self.selected_index = None; + } + should_render = true; + } else if key.bare_key == BareKey::Up && key.has_no_modifiers() { + if self.selected_index.is_none() { + self.selected_index = Some(1); + } else if self.selected_index == Some(1) { + self.selected_index = Some(0); + } else { + self.selected_index = None; + } + should_render = true; + } else if key.bare_key == BareKey::Enter && key.has_no_modifiers() { + if let Some(selected) = self.selected_index.take() { + let write_to_disk = true; + self.reconfigure(selected, write_to_disk); + close_self(); + } else { + self.selected_index = Some(0); + should_render = true; + } + } else if key.bare_key == BareKey::Char('l') && key.has_no_modifiers() { + self.remapping_leaders = true; + should_render = true; + } else if (key.bare_key == BareKey::Esc && key.has_no_modifiers()) + || key.is_key_with_ctrl_modifier(BareKey::Char('c')) + { close_self(); should_render = true; } @@ -412,7 +477,7 @@ impl State { print_text_with_coordinates( Text::new(title_text).color_range(2, ..), left_padding, - rows.saturating_sub(15) / 2, + rows.saturating_sub(self.ui_size) / 2, None, None, ); @@ -426,7 +491,91 @@ impl State { print_text_with_coordinates( Text::new(title_text).color_range(2, ..), left_padding, - rows.saturating_sub(15) / 2, + rows.saturating_sub(self.ui_size) / 2, + None, + None, + ); + } + } + fn render_setup_wizard_title(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) { + let widths = self.main_screen_widths(primary_modifier_key_text); + if cols >= widths.0 { + let title_text_1 = "Hi there! How would you like to interact with Zellij?"; + let title_text_2 = "Not sure? Press <ENTER> to choose Default."; + let title_text_3 = "Everything can always be changed later."; + let title_text_4 = "Tips appear on screen - you don't need to remember anything."; + let left_padding = cols.saturating_sub(widths.0) / 2; + let first_row_coords = (rows.saturating_sub(self.ui_size) / 2).saturating_sub(1); + print_text_with_coordinates( + Text::new(title_text_1).color_range(2, ..), + left_padding, + first_row_coords, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_2) + .color_range(0, ..10) + .color_range(2, 16..23) + .color_range(self.preset_color_index, 34..41), + left_padding, + first_row_coords + 2, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_3), + left_padding, + first_row_coords + 4, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_4), + left_padding, + first_row_coords + 5, + None, + None, + ); + } else { + let title_text_1 = "Hi there! Which do you prefer?"; + let title_text_2 = "Not sure? Press <ENTER>"; + let title_text_3 = "Can be changed later. Tips appear"; + let title_text_4 = "on screen - no need to remember"; + let left_padding = if cols >= widths.1 { + cols.saturating_sub(widths.1) / 2 + } else { + cols.saturating_sub(widths.2) / 2 + }; + let first_row_coords = (rows.saturating_sub(self.ui_size) / 2).saturating_sub(1); + print_text_with_coordinates( + Text::new(title_text_1).color_range(2, ..), + left_padding, + first_row_coords, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_2) + .color_range(0, ..10) + .color_range(2, 16..23) + .color_range(self.preset_color_index, 40..49), + left_padding, + first_row_coords + 2, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_3), + left_padding, + first_row_coords + 4, + None, + None, + ); + print_text_with_coordinates( + Text::new(title_text_4), + left_padding, + first_row_coords + 5, None, None, ); @@ -455,7 +604,7 @@ impl State { ) .indent(1), NestedListItem::new(format!( - "{} t - to enter TAB mode.", + "{} t - to enter TAB mode", primary_modifier_key_text )) .color_range( @@ -488,7 +637,7 @@ impl State { primary_modifier_key_text_len + 14..primary_modifier_key_text_len + 18, ), NestedListItem::new(format!( - "{} t - to enter TAB mode.", + "{} t - to enter TAB mode", primary_modifier_key_text )) .indent(1) @@ -517,7 +666,7 @@ impl State { primary_modifier_key_text_len + 5..primary_modifier_key_text_len + 10, ) .indent(1), - NestedListItem::new(format!("{} t - TAB mode.", primary_modifier_key_text)) + NestedListItem::new(format!("{} t - TAB mode", primary_modifier_key_text)) .color_range( self.primary_leader_key_color_index, ..primary_modifier_key_text_len + 3, @@ -536,9 +685,9 @@ impl State { } let left_padding = cols.saturating_sub(max_width) / 2; let top_coordinates = if rows > 14 { - (rows.saturating_sub(15) / 2) + 2 + (rows.saturating_sub(self.ui_size) / 2) + 2 } else { - (rows.saturating_sub(15) / 2) + 1 + (rows.saturating_sub(self.ui_size) / 2) + 1 }; print_nested_list_with_coordinates( list_items, @@ -578,7 +727,7 @@ impl State { primary_modifier_key_text_len + 16..primary_modifier_key_text_len + 21, ), NestedListItem::new(format!( - "{} g + t to enter TAB mode.", + "{} g + t to enter TAB mode", primary_modifier_key_text )) .indent(1) @@ -623,7 +772,7 @@ impl State { ) .indent(1), NestedListItem::new(format!( - "{} g + t to enter TAB mode.", + "{} g + t to enter TAB mode", primary_modifier_key_text )) .color_range( @@ -664,7 +813,7 @@ impl State { primary_modifier_key_text_len + 7..primary_modifier_key_text_len + 11, ) .indent(1), - NestedListItem::new(format!("{} g + t TAB mode.", primary_modifier_key_text)) + NestedListItem::new(format!("{} g + t TAB mode", primary_modifier_key_text)) .color_range( self.primary_leader_key_color_index, ..primary_modifier_key_text_len + 3, @@ -687,9 +836,9 @@ impl State { } let left_padding = cols.saturating_sub(max_width) / 2; let top_coordinates = if rows > 14 { - (rows.saturating_sub(15) / 2) + 7 + (rows.saturating_sub(self.ui_size) / 2) + 7 } else { - (rows.saturating_sub(15) / 2) + 5 + (rows.saturating_sub(self.ui_size) / 2) + 5 }; print_nested_list_with_coordinates( list_items, @@ -710,14 +859,14 @@ impl State { let primary_modifier_key_text_len = primary_modifier_key_text.chars().count(); let secondary_modifier_key_text_len = secondary_modifier_key_text.chars().count(); let top_coordinates = if rows > 14 { - (rows.saturating_sub(15) / 2) + 12 + (rows.saturating_sub(self.ui_size) / 2) + 12 } else { - (rows.saturating_sub(15) / 2) + 9 + (rows.saturating_sub(self.ui_size) / 2) + 9 }; if cols >= widths.0 { let leader_key_text = format!( - "Leader keys: {} - modes, {} - quicknav and shortcuts.", + "Leader keys: {} - modes, {} - quicknav and shortcuts", primary_modifier_key_text, secondary_modifier_key_text ); let left_padding = cols.saturating_sub(widths.0) / 2; @@ -767,12 +916,12 @@ impl State { ) }; } - fn render_warning_if_needed(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) { + fn render_info_line(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) { let widths = self.main_screen_widths(primary_modifier_key_text); let top_coordinates = if rows > 14 { - (rows.saturating_sub(15) / 2) + 14 + (rows.saturating_sub(self.ui_size) / 2) + 14 } else { - (rows.saturating_sub(15) / 2) + 10 + (rows.saturating_sub(self.ui_size) / 2) + 10 }; let left_padding = if cols >= widths.0 { cols.saturating_sub(widths.0) / 2 @@ -781,7 +930,15 @@ impl State { } else { cols.saturating_sub(widths.2) / 2 }; - if let Some(warning_text) = self.warning_text(cols) { + if let Some(notification) = &self.notification { + print_text_with_coordinates( + Text::new(notification).color_range(3, ..), + left_padding, + top_coordinates, + None, + None, + ); + } else if let Some(warning_text) = self.warning_text(cols) { print_text_with_coordinates( Text::new(warning_text).color_range(3, ..), left_padding, @@ -791,24 +948,56 @@ impl State { ); } } - fn render_help_text_main(&self, rows: usize, cols: usize, primary_modifier_key_text: &str) { - let widths = self.main_screen_widths(primary_modifier_key_text); - if cols >= widths.0 { - let help_text = "Help: <↓↑/ENTER> - navigate/select, <l> - leaders, <ESC> - close"; + fn render_help_text_main(&self, rows: usize, cols: usize) { + let full_help_text = "Help: <↓↑> - navigate, <ENTER> - apply, <SPACE> - apply & save, <l> - leaders, <ESC> - close"; + let short_help_text = "Help: <↓↑> / <ENTER> / <SPACE> / <l> / <ESC>"; + if cols >= full_help_text.chars().count() { print_text_with_coordinates( - Text::new(help_text) - .color_range(2, 6..16) - .color_range(2, 36..39) - .color_range(2, 51..56), + Text::new(full_help_text) + .color_range(2, 6..10) + .color_range(2, 23..30) + .color_range(2, 40..47) + .color_range(2, 64..67) + .color_range(2, 79..84), 0, rows, None, None, ); } else { - let help_text = "Help: <↓↑> / <ENTER> / <l> / <ESC>"; print_text_with_coordinates( - Text::new(help_text) + Text::new(short_help_text) + .color_range(2, 6..10) + .color_range(2, 13..20) + .color_range(2, 23..30) + .color_range(2, 33..36) + .color_range(2, 39..44), + 0, + rows, + None, + None, + ); + } + } + fn render_help_text_setup_wizard(&self, rows: usize, cols: usize) { + let full_help_text = + "Help: <↓↑> - navigate, <ENTER> - apply & save, <l> - change leaders, <ESC> - close"; + let short_help_text = "Help: <↓↑> / <ENTER> / <l> / <ESC>"; + if cols >= full_help_text.chars().count() { + print_text_with_coordinates( + Text::new(full_help_text) + .color_range(2, 6..10) + .color_range(2, 23..30) + .color_range(2, 47..50) + .color_range(2, 69..74), + 0, + rows, + None, + None, + ); + } else { + print_text_with_coordinates( + Text::new(short_help_text) .color_range(2, 6..10) .color_range(2, 13..20) .color_range(2, 23..26) @@ -892,8 +1081,23 @@ impl State { &primary_modifier_key_text, &secondary_modifier_key_text, ); - self.render_warning_if_needed(rows, cols, &primary_modifier_key_text); - self.render_help_text_main(rows, cols, &primary_modifier_key_text); + self.render_info_line(rows, cols, &primary_modifier_key_text); + self.render_help_text_main(rows, cols); + } + fn render_setup_wizard_screen(&mut self, rows: usize, cols: usize) { + let primary_modifier_key_text = self.primary_modifier_text(); + let secondary_modifier_key_text = self.secondary_modifier_text(); + self.render_setup_wizard_title(rows, cols, &primary_modifier_key_text); + self.render_first_bulletin(rows + 8, cols, &primary_modifier_key_text); + self.render_second_bulletin(rows + 8, cols, &primary_modifier_key_text); + self.render_leader_keys_indication( + rows + 8, + cols, + &primary_modifier_key_text, + &secondary_modifier_key_text, + ); + self.render_info_line(rows + 8, cols, &primary_modifier_key_text); + self.render_help_text_setup_wizard(rows + 8, cols); } fn warning_text(&self, max_width: usize) -> Option<String> { if self.needs_kitty_support() { @@ -921,6 +1125,47 @@ impl State { || self.primary_modifier.contains(&KeyModifier::Super) || self.secondary_modifier.contains(&KeyModifier::Super) } + fn reconfigure(&self, selected: usize, write_to_disk: bool) { + if selected == 0 { + // TODO: these should be part of a "transaction" when they are + // implemented + reconfigure( + default_keybinds( + self.primary_modifier + .iter() + .map(|m| m.to_string()) + .collect::<Vec<_>>() + .join(" "), + self.secondary_modifier + .iter() + .map(|m| m.to_string()) + .collect::<Vec<_>>() + .join(" "), + ), + write_to_disk, + ); + switch_to_input_mode(&InputMode::Normal); + } else if selected == 1 { + // TODO: these should be part of a "transaction" when they are + // implemented + reconfigure( + unlock_first_keybinds( + self.primary_modifier + .iter() + .map(|m| m.to_string()) + .collect::<Vec<_>>() + .join(" "), + self.secondary_modifier + .iter() + .map(|m| m.to_string()) + .collect::<Vec<_>>() + .join(" "), + ), + write_to_disk, + ); + switch_to_input_mode(&InputMode::Locked); + } + } } fn unlock_first_keybinds(primary_modifier: String, secondary_modifier: String) -> String { diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs index 52b2d7e0..b0915f29 100644 --- a/default-plugins/fixture-plugin-for-tests/src/main.rs +++ b/default-plugins/fixture-plugin-for-tests/src/main.rs @@ -332,6 +332,7 @@ impl ZellijPlugin for State { ); }, BareKey::Char('0') if key.has_modifiers(&[KeyModifier::Ctrl]) => { + let write_to_disk = true; reconfigure( " keybinds { @@ -341,6 +342,7 @@ impl ZellijPlugin for State { } " .to_owned(), + write_to_disk, ); }, BareKey::Char('a') if key.has_modifiers(&[KeyModifier::Alt]) => { |
