aboutsummaryrefslogtreecommitdiffstats
path: root/default-plugins
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-08-16 14:00:01 +0200
committerGitHub <noreply@github.com>2024-08-16 14:00:01 +0200
commit056537d3eef06cddc60dc13b715a41766bb1fe9d (patch)
treeff7e107e992e4c98ac60ac88da7215cb0beb668a /default-plugins
parentdocs(changelog): CommandPaneReRun plugin event (diff)
downloadsl0ppij-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.rs385
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs2
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]) => {