diff --git a/src/components/record_table.rs b/src/components/record_table.rs index ebdf1ba..dea2800 100644 --- a/src/components/record_table.rs +++ b/src/components/record_table.rs @@ -26,7 +26,7 @@ pub struct RecordTableComponent { impl RecordTableComponent { pub fn new(key_config: KeyConfig) -> Self { Self { - filter: TableFilterComponent::default(), + filter: TableFilterComponent::new(key_config.clone()), table: TableComponent::new(key_config.clone()), focus: Focus::Table, key_config, @@ -61,11 +61,11 @@ impl DrawableComponent for RecordTableComponent { .constraints(vec![Constraint::Length(3), Constraint::Length(5)]) .split(area); - self.filter - .draw(f, layout[0], focused && matches!(self.focus, Focus::Filter))?; - self.table .draw(f, layout[1], focused && matches!(self.focus, Focus::Table))?; + + self.filter + .draw(f, layout[0], focused && matches!(self.focus, Focus::Filter))?; Ok(()) } } diff --git a/src/components/table_filter.rs b/src/components/table_filter.rs index 001038c..7b88e1f 100644 --- a/src/components/table_filter.rs +++ b/src/components/table_filter.rs @@ -1,5 +1,9 @@ -use super::{compute_character_width, Component, DrawableComponent, EventState}; +use super::{ + compute_character_width, CompletionComponent, Component, DrawableComponent, EventState, + MovableComponent, +}; use crate::components::command::CommandInfo; +use crate::config::KeyConfig; use crate::event::Key; use anyhow::Result; use database_tree::Table; @@ -14,24 +18,26 @@ use tui::{ use unicode_width::UnicodeWidthStr; pub struct TableFilterComponent { + key_config: KeyConfig, pub table: Option, pub input: Vec, input_idx: usize, input_cursor_position: u16, + completion: CompletionComponent, } -impl Default for TableFilterComponent { - fn default() -> Self { +impl TableFilterComponent { + pub fn new(key_config: KeyConfig) -> Self { Self { + key_config: key_config.clone(), table: None, input: Vec::new(), input_idx: 0, input_cursor_position: 0, + completion: CompletionComponent::new(key_config, ""), } } -} -impl TableFilterComponent { pub fn input_str(&self) -> String { self.input.iter().collect() } @@ -42,6 +48,21 @@ impl TableFilterComponent { self.input_idx = 0; self.input_cursor_position = 0; } + + fn update_completion(&mut self) { + let input = &self + .input + .iter() + .enumerate() + .filter(|(i, _)| i <= &self.input_idx) + .map(|(_, i)| i) + .collect::() + .split(" ") + .map(|i| i.to_string()) + .collect::>(); + self.completion + .update(input.last().unwrap_or(&String::new())); + } } impl DrawableComponent for TableFilterComponent { @@ -69,6 +90,23 @@ impl DrawableComponent for TableFilterComponent { }) .block(Block::default().borders(Borders::ALL)); f.render_widget(query, area); + + if focused { + self.completion.draw( + f, + area, + false, + (self + .table + .as_ref() + .map_or(String::new(), |table| table.name.to_string()) + .width() as u16 + + 2) + .saturating_add(self.input_cursor_position), + 0, + )?; + }; + if focused { f.set_cursor( (area.x @@ -91,21 +129,40 @@ impl Component for TableFilterComponent { fn event(&mut self, key: Key) -> Result { let input_str: String = self.input.iter().collect(); + + // apply comletion candidates + if key == self.key_config.enter { + if let Some(candidate) = self.completion.string_to_be_completed() { + let mut input = self.input.iter().collect::(); + input.insert_str(self.input_idx, candidate.as_str()); + self.input = input.chars().collect(); + self.input_idx += candidate.len(); + self.input_cursor_position += candidate + .chars() + .map(|c| compute_character_width(c)) + .sum::(); + self.update_completion(); + return Ok(EventState::Consumed); + } + } + match key { Key::Char(c) => { self.input.insert(self.input_idx, c); self.input_idx += 1; self.input_cursor_position += compute_character_width(c); + self.update_completion(); - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } Key::Delete | Key::Backspace => { if input_str.width() > 0 && !self.input.is_empty() && self.input_idx > 0 { let last_c = self.input.remove(self.input_idx - 1); self.input_idx -= 1; self.input_cursor_position -= compute_character_width(last_c); + self.update_completion(); } - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } Key::Left => { if !self.input.is_empty() && self.input_idx > 0 { @@ -114,14 +171,14 @@ impl Component for TableFilterComponent { .input_cursor_position .saturating_sub(compute_character_width(self.input[self.input_idx])); } - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } Key::Ctrl('a') => { if !self.input.is_empty() && self.input_idx > 0 { self.input_idx = 0; self.input_cursor_position = 0 } - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } Key::Right => { if self.input_idx < self.input.len() { @@ -129,17 +186,16 @@ impl Component for TableFilterComponent { self.input_idx += 1; self.input_cursor_position += compute_character_width(next_c); } - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } Key::Ctrl('e') => { if self.input_idx < self.input.len() { self.input_idx = self.input.len(); self.input_cursor_position = self.input_str().width() as u16; } - return Ok(EventState::Consumed); + Ok(EventState::Consumed) } - _ => (), + key => self.completion.event(key), } - Ok(EventState::NotConsumed) } }