summaryrefslogtreecommitdiffstats
path: root/default-plugins/strider/src/search/controls_line.rs
diff options
context:
space:
mode:
Diffstat (limited to 'default-plugins/strider/src/search/controls_line.rs')
-rw-r--r--default-plugins/strider/src/search/controls_line.rs353
1 files changed, 353 insertions, 0 deletions
diff --git a/default-plugins/strider/src/search/controls_line.rs b/default-plugins/strider/src/search/controls_line.rs
new file mode 100644
index 000000000..a75715ef1
--- /dev/null
+++ b/default-plugins/strider/src/search/controls_line.rs
@@ -0,0 +1,353 @@
+use crate::search::search_state::SearchType;
+use crate::search::ui::{
+ arrow, bold, color_line_to_end, dot, styled_text, BLACK, GRAY_DARK, GRAY_LIGHT, RED, WHITE,
+};
+
+#[derive(Default)]
+pub struct ControlsLine {
+ controls: Vec<Control>,
+ scanning_indication: Option<Vec<&'static str>>,
+ animation_offset: u8,
+}
+
+impl ControlsLine {
+ pub fn new(controls: Vec<Control>, scanning_indication: Option<Vec<&'static str>>) -> Self {
+ ControlsLine {
+ controls,
+ scanning_indication,
+ ..Default::default()
+ }
+ }
+ pub fn with_animation_offset(mut self, animation_offset: u8) -> Self {
+ self.animation_offset = animation_offset;
+ self
+ }
+ pub fn render(&self, max_width: usize, show_controls: bool) -> String {
+ if show_controls {
+ self.render_controls(max_width)
+ } else {
+ self.render_empty_line(max_width)
+ }
+ }
+ pub fn render_controls(&self, max_width: usize) -> String {
+ let loading_animation =
+ LoadingAnimation::new(&self.scanning_indication, self.animation_offset);
+ let full_length = loading_animation.full_len()
+ + self.controls.iter().map(|c| c.full_len()).sum::<usize>();
+ let mid_length =
+ loading_animation.mid_len() + self.controls.iter().map(|c| c.mid_len()).sum::<usize>();
+ let short_length = loading_animation.short_len()
+ + self.controls.iter().map(|c| c.short_len()).sum::<usize>();
+ if max_width >= full_length {
+ let mut to_render = String::new();
+ for control in &self.controls {
+ to_render.push_str(&control.render_full_length());
+ }
+ to_render.push_str(&self.render_padding(max_width.saturating_sub(full_length)));
+ to_render.push_str(&loading_animation.render_full_length());
+ to_render
+ } else if max_width >= mid_length {
+ let mut to_render = String::new();
+ for control in &self.controls {
+ to_render.push_str(&control.render_mid_length());
+ }
+ to_render.push_str(&self.render_padding(max_width.saturating_sub(mid_length)));
+ to_render.push_str(&loading_animation.render_mid_length());
+ to_render
+ } else if max_width >= short_length {
+ let mut to_render = String::new();
+ for control in &self.controls {
+ to_render.push_str(&control.render_short_length());
+ }
+ to_render.push_str(&self.render_padding(max_width.saturating_sub(short_length)));
+ to_render.push_str(&loading_animation.render_short_length());
+ to_render
+ } else {
+ format!("")
+ }
+ }
+ pub fn render_empty_line(&self, max_width: usize) -> String {
+ let loading_animation =
+ LoadingAnimation::new(&self.scanning_indication, self.animation_offset);
+ let mut to_render = String::new();
+ if max_width >= loading_animation.full_len() {
+ to_render.push_str(
+ &self.render_padding(max_width.saturating_sub(loading_animation.full_len())),
+ );
+ to_render.push_str(&loading_animation.render_full_length());
+ } else if max_width >= loading_animation.mid_len() {
+ to_render.push_str(
+ &self.render_padding(max_width.saturating_sub(loading_animation.mid_len())),
+ );
+ to_render.push_str(&loading_animation.render_mid_length());
+ } else if max_width >= loading_animation.short_len() {
+ to_render.push_str(
+ &self.render_padding(max_width.saturating_sub(loading_animation.short_len())),
+ );
+ to_render.push_str(&loading_animation.render_short_length());
+ }
+ to_render
+ }
+ fn render_padding(&self, padding: usize) -> String {
+ // TODO: color whole line
+ format!("{}\u{1b}[{}C", color_line_to_end(GRAY_LIGHT), padding)
+ }
+}
+
+pub struct Control {
+ key: &'static str,
+ options: Vec<&'static str>,
+ option_index: (usize, usize), // eg. 1 out of 2 (1, 2)
+ keycode_background_color: u8,
+ keycode_foreground_color: u8,
+ control_text_background_color: u8,
+ control_text_foreground_color: u8,
+ active_dot_color: u8,
+}
+
+impl Default for Control {
+ fn default() -> Self {
+ Control {
+ key: "",
+ options: vec![],
+ option_index: (0, 0),
+ keycode_background_color: GRAY_LIGHT,
+ keycode_foreground_color: WHITE,
+ control_text_background_color: GRAY_DARK,
+ control_text_foreground_color: BLACK,
+ active_dot_color: RED,
+ }
+ }
+}
+
+impl Control {
+ pub fn new(
+ key: &'static str,
+ options: Vec<&'static str>,
+ option_index: (usize, usize),
+ ) -> Self {
+ Control {
+ key,
+ options,
+ option_index,
+ ..Default::default()
+ }
+ }
+ pub fn new_floating_control(key: &'static str, should_open_floating: bool) -> Self {
+ if should_open_floating {
+ Control::new(key, vec!["OPEN FLOATING", "FLOATING", "F"], (2, 2))
+ } else {
+ Control::new(key, vec!["OPEN TILED", "TILED", "T"], (1, 2))
+ }
+ }
+ pub fn new_filter_control(key: &'static str, search_filter: &SearchType) -> Self {
+ match search_filter {
+ SearchType::NamesAndContents => Control::new(
+ key,
+ vec!["FILE NAMES AND CONTENTS", "NAMES + CONTENTS", "N+C"],
+ (1, 3),
+ ),
+ SearchType::Names => Control::new(key, vec!["FILE NAMES", "NAMES", "N"], (2, 3)),
+ SearchType::Contents => {
+ Control::new(key, vec!["FILE CONTENTS", "CONTENTS", "C"], (3, 3))
+ },
+ }
+ }
+ pub fn short_len(&self) -> usize {
+ let short_text = self
+ .options
+ .get(2)
+ .or_else(|| self.options.get(1))
+ .or_else(|| self.options.get(0))
+ .unwrap_or(&"");
+ short_text.chars().count() + self.key.chars().count() + self.option_index.1 + 7
+ // 7 for all the spaces and decorations
+ }
+ pub fn mid_len(&self) -> usize {
+ let mid_text = self
+ .options
+ .get(1)
+ .or_else(|| self.options.get(0))
+ .unwrap_or(&"");
+ mid_text.chars().count() + self.key.chars().count() + self.option_index.1 + 7
+ // 7 for all the spaces and decorations
+ }
+ pub fn full_len(&self) -> usize {
+ let full_text = self.options.get(0).unwrap_or(&"");
+ full_text.chars().count() + self.key.chars().count() + self.option_index.1 + 7
+ // 7 for all the spaces and decorations
+ }
+ pub fn render_short_length(&self) -> String {
+ let short_text = self
+ .options
+ .get(2)
+ .or_else(|| self.options.get(1))
+ .or_else(|| self.options.get(0))
+ .unwrap_or(&"");
+ self.render(short_text)
+ }
+ pub fn render_mid_length(&self) -> String {
+ let mid_text = self
+ .options
+ .get(1)
+ .or_else(|| self.options.get(0))
+ .unwrap_or(&"");
+ self.render(mid_text)
+ }
+ pub fn render_full_length(&self) -> String {
+ let full_text = self.options.get(0).unwrap_or(&"");
+ self.render(full_text)
+ }
+ fn render(&self, text: &str) -> String {
+ format!(
+ "{}{}{}{}{}{}",
+ self.render_keycode(&format!(" {} ", self.key)),
+ arrow(
+ self.keycode_background_color,
+ self.control_text_background_color
+ ),
+ self.render_selection_dots(),
+ self.render_control_text(&format!("{} ", text)),
+ arrow(
+ self.control_text_background_color,
+ self.keycode_background_color
+ ),
+ color_line_to_end(self.keycode_background_color),
+ )
+ }
+ fn render_keycode(&self, text: &str) -> String {
+ styled_text(
+ self.keycode_foreground_color,
+ self.keycode_background_color,
+ &bold(text),
+ )
+ }
+ fn render_control_text(&self, text: &str) -> String {
+ styled_text(
+ self.control_text_foreground_color,
+ self.control_text_background_color,
+ &bold(text),
+ )
+ }
+ fn render_selection_dots(&self) -> String {
+ let mut selection_dots = String::from(" ");
+ for i in 1..=self.option_index.1 {
+ if i == self.option_index.0 {
+ selection_dots.push_str(&dot(
+ self.active_dot_color,
+ self.control_text_background_color,
+ ));
+ } else {
+ selection_dots.push_str(&dot(
+ self.control_text_foreground_color,
+ self.control_text_background_color,
+ ));
+ }
+ }
+ selection_dots.push_str(" ");
+ selection_dots
+ }
+}
+
+struct LoadingAnimation {
+ scanning_indication: Option<Vec<&'static str>>,
+ animation_offset: u8,
+ background_color: u8,
+ foreground_color: u8,
+}
+impl LoadingAnimation {
+ pub fn new(scanning_indication: &Option<Vec<&'static str>>, animation_offset: u8) -> Self {
+ LoadingAnimation {
+ scanning_indication: scanning_indication.clone(),
+ animation_offset,
+ background_color: GRAY_LIGHT,
+ foreground_color: WHITE,
+ }
+ }
+ pub fn full_len(&self) -> usize {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| scanning_indication.get(0))
+ .map(|s| s.chars().count() + 3) // 3 for animation dots
+ .unwrap_or(0)
+ }
+ pub fn mid_len(&self) -> usize {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| {
+ scanning_indication
+ .get(1)
+ .or_else(|| scanning_indication.get(0))
+ })
+ .map(|s| s.chars().count() + 3) // 3 for animation dots
+ .unwrap_or(0)
+ }
+ pub fn short_len(&self) -> usize {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| {
+ scanning_indication
+ .get(2)
+ .or_else(|| scanning_indication.get(1))
+ .or_else(|| scanning_indication.get(0))
+ })
+ .map(|s| s.chars().count() + 3) // 3 for animation dots
+ .unwrap_or(0)
+ }
+ pub fn render_full_length(&self) -> String {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| scanning_indication.get(0))
+ .map(|s| {
+ styled_text(
+ self.foreground_color,
+ self.background_color,
+ &bold(&(s.to_string() + &self.animation_dots())),
+ )
+ })
+ .unwrap_or_else(String::new)
+ }
+ pub fn render_mid_length(&self) -> String {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| {
+ scanning_indication
+ .get(1)
+ .or_else(|| scanning_indication.get(0))
+ })
+ .map(|s| {
+ styled_text(
+ self.background_color,
+ self.foreground_color,
+ &bold(&(s.to_string() + &self.animation_dots())),
+ )
+ })
+ .unwrap_or_else(String::new)
+ }
+ pub fn render_short_length(&self) -> String {
+ self.scanning_indication
+ .as_ref()
+ .and_then(|scanning_indication| {
+ scanning_indication
+ .get(2)
+ .or_else(|| scanning_indication.get(1))
+ .or_else(|| scanning_indication.get(0))
+ })
+ .map(|s| {
+ styled_text(
+ self.background_color,
+ self.foreground_color,
+ &bold(&(s.to_string() + &self.animation_dots())),
+ )
+ })
+ .unwrap_or_else(String::new)
+ }
+ fn animation_dots(&self) -> String {
+ let mut to_render = String::from("");
+ let dot_count = self.animation_offset % 4;
+ for _ in 0..dot_count {
+ to_render.push('.');
+ }
+ to_render
+ }
+}