diff --git a/src/app.rs b/src/app.rs index 9acb846..ede61e0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -117,10 +117,6 @@ impl App { } pub async fn event(&mut self, key: Key) -> anyhow::Result<()> { - if self.tab.event(key)?.is_consumed() { - return Ok(()); - } - if let Key::Esc = key { if self.error.error.is_some() { self.error.error = None; @@ -228,7 +224,7 @@ impl App { if self.record_table.filter.input.is_empty() { None } else { - Some(self.record_table.filter.input.to_string()) + Some(self.record_table.filter.input_str()) }, ) .await?; @@ -259,7 +255,7 @@ impl App { if self.record_table.filter.input.is_empty() { None } else { - Some(self.record_table.filter.input.to_string()) + Some(self.record_table.filter.input_str()) }, ) .await?; @@ -291,10 +287,13 @@ impl App { Ok(EventState::NotConsumed) } - pub fn move_focus(&mut self, key: Key) { + pub fn move_focus(&mut self, key: Key) -> anyhow::Result<()> { if let Key::Char('c') = key { self.focus = Focus::ConnectionList; - return; + return Ok(()); + } + if self.tab.event(key)?.is_consumed() { + return Ok(()); } match self.focus { Focus::ConnectionList => { @@ -311,5 +310,6 @@ impl App { _ => (), }, } + Ok(()) } } diff --git a/src/components/databases.rs b/src/components/databases.rs index 5e92f95..5a3f128 100644 --- a/src/components/databases.rs +++ b/src/components/databases.rs @@ -228,7 +228,12 @@ impl Component for DatabasesComponent { return Ok(EventState::Consumed); } Key::Char(c) if matches!(self.focus_block, FocusBlock::Filter) => { - self.input.push(c); + self.input.insert( + self.input + .width() + .saturating_sub(self.input_cursor_x as usize), + c, + ); self.filterd_tree = Some(self.tree.filter(self.input.clone())); return Ok(EventState::Consumed); } diff --git a/src/components/record_table.rs b/src/components/record_table.rs index 9538677..3e30304 100644 --- a/src/components/record_table.rs +++ b/src/components/record_table.rs @@ -40,6 +40,10 @@ impl RecordTableComponent { pub fn update(&mut self, rows: Vec>, headers: Vec) { self.table.rows = rows; self.table.headers = headers; + if !self.table.rows.is_empty() { + self.table.state.select(None); + self.table.state.select(Some(0)); + } } pub fn reset(&mut self) { diff --git a/src/components/table_filter.rs b/src/components/table_filter.rs index 9833e12..0b528ad 100644 --- a/src/components/table_filter.rs +++ b/src/components/table_filter.rs @@ -1,6 +1,7 @@ use super::{Component, DrawableComponent, EventState}; use crate::event::Key; use anyhow::Result; +use std::convert::TryInto; use tui::{ backend::Backend, layout::Rect, @@ -9,36 +10,42 @@ use tui::{ widgets::{Block, Borders, Paragraph}, Frame, }; -use unicode_width::UnicodeWidthStr; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; pub struct TableFilterComponent { pub table: Option, - pub input: String, - pub input_cursor_x: u16, + pub input: Vec, + pub input_idx: usize, + pub input_cursor_position: u16, } impl Default for TableFilterComponent { fn default() -> Self { Self { table: None, - input: String::new(), - input_cursor_x: 0, + input: Vec::new(), + input_idx: 0, + input_cursor_position: 0, } } } impl TableFilterComponent { - pub fn increment_input_cursor_x(&mut self) { - if self.input_cursor_x > 0 { - self.input_cursor_x -= 1; + pub fn increment_input_idx(&mut self) { + if self.input_idx > 0 { + self.input_idx -= 1; } } - pub fn decrement_input_cursor_x(&mut self) { - if self.input_cursor_x < self.input.width() as u16 { - self.input_cursor_x += 1; + pub fn decrement_input_idx(&mut self) { + if self.input_idx < self.input.iter().collect::().width() { + self.input_idx += 1; } } + + pub fn input_str(&self) -> String { + self.input.iter().collect() + } } impl DrawableComponent for TableFilterComponent { @@ -53,9 +60,9 @@ impl DrawableComponent for TableFilterComponent { Span::from(format!( " {}", if focused || !self.input.is_empty() { - self.input.as_ref() + self.input.iter().collect::() } else { - "Enter a SQL expression in WHERE clause" + "Enter a SQL expression in WHERE clause".to_string() } )), ])) @@ -69,15 +76,13 @@ impl DrawableComponent for TableFilterComponent { if focused { f.set_cursor( (area.x - + self.input.width() as u16 - + 1 - + self + + (1 + self .table .as_ref() .map_or(String::new(), |table| table.to_string()) - .width() as u16 - + 1) - .saturating_sub(self.input_cursor_x), + .width() + + 1) as u16) + .saturating_add(self.input_cursor_position), area.y + 1, ) } @@ -87,34 +92,62 @@ impl DrawableComponent for TableFilterComponent { impl Component for TableFilterComponent { fn event(&mut self, key: Key) -> Result { + let input_str: String = self.input.iter().collect(); match key { Key::Char(c) => { - self.input.push(c); + self.input.insert(self.input_idx, c); + self.input_idx += 1; + self.input_cursor_position += compute_character_width(c); + return Ok(EventState::Consumed); } Key::Delete | Key::Backspace => { - if self.input.width() > 0 { - if self.input_cursor_x == 0 { - self.input.pop(); - return Ok(EventState::Consumed); - } - if self.input.width() - self.input_cursor_x as usize > 0 { - self.input - .remove(self.input.width() - self.input_cursor_x as usize); + if input_str.width() > 0 { + if !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); } - return Ok(EventState::Consumed); } + return Ok(EventState::Consumed); } Key::Left => { - self.decrement_input_cursor_x(); + if !self.input.is_empty() && self.input_idx > 0 { + self.input_idx -= 1; + self.input_cursor_position = self + .input_cursor_position + .saturating_sub(compute_character_width(self.input[self.input_idx])); + } + return 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); } Key::Right => { - self.increment_input_cursor_x(); + if self.input_idx < self.input.len() { + let next_c = self.input[self.input_idx]; + self.input_idx += 1; + self.input_cursor_position += compute_character_width(next_c); + } return 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); + } + _ => println!("{}", key), } Ok(EventState::NotConsumed) } } + +fn compute_character_width(c: char) -> u16 { + UnicodeWidthChar::width(c).unwrap().try_into().unwrap() +} diff --git a/src/event/key.rs b/src/event/key.rs index ba4979f..48238d3 100644 --- a/src/event/key.rs +++ b/src/event/key.rs @@ -195,10 +195,6 @@ impl From for Key { code: event::KeyCode::Char(c), modifiers: event::KeyModifiers::CONTROL, } => Key::Ctrl(c), - event::KeyEvent { - code: event::KeyCode::Char(c), - modifiers: event::KeyModifiers::SHIFT, - } => Key::Shift(c), event::KeyEvent { code: event::KeyCode::Char(c),