From 560771b32acf4d76e2f0ff91cf4ba233519d161d Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 24 Oct 2020 14:32:02 +0300 Subject: [PATCH] widgets: select AutoCompleteEntry on Enter --- src/bin.rs | 3 +- src/components/utilities.rs | 11 ++- src/components/utilities/widgets.rs | 128 ++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 40 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 457fe5ab..b4c827d5 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -458,8 +458,7 @@ fn run_app(opt: Opt) -> Result<()> { }, UIMode::Insert => { match k { - Key::Char('\n') | Key::Esc => { - state.mode = UIMode::Normal; + Key::Esc => { state.rcv_event(UIEvent::ChangeMode(UIMode::Normal)); state.redraw(); }, diff --git a/src/components/utilities.rs b/src/components/utilities.rs index 72727381..990641e7 100644 --- a/src/components/utilities.rs +++ b/src/components/utilities.rs @@ -500,7 +500,16 @@ impl Component for StatusBar { }; } UIEvent::CmdInput(Key::Char('\t')) => { - if let Some(suggestion) = self.auto_complete.get_suggestion() { + if let Some(suggestion) = self.auto_complete.get_suggestion().or_else(|| { + if self.auto_complete.cursor() == 0 { + self.auto_complete + .suggestions() + .last() + .map(|e| e.entry.clone()) + } else { + None + } + }) { let mut utext = UText::new(suggestion); let len = utext.as_str().len(); utext.set_cursor(len); diff --git a/src/components/utilities/widgets.rs b/src/components/utilities/widgets.rs index 02dbf9af..132f19fd 100644 --- a/src/components/utilities/widgets.rs +++ b/src/components/utilities/widgets.rs @@ -147,19 +147,35 @@ impl Component for Field { None, ); } - fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { - if let Text(ref mut s, Some((_, auto_complete))) = self { - if let UIEvent::InsertInput(Key::Char('\t')) = event { - if let Some(suggestion) = auto_complete.get_suggestion() { - *s = UText::new(suggestion); - let len = s.as_str().len(); - s.set_cursor(len); - return true; - } - } - } - + fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { match *event { + UIEvent::InsertInput(Key::Char('\t')) => { + if let Text(ref mut s, Some((_, auto_complete))) = self { + if let Some(suggestion) = auto_complete.get_suggestion() { + *s = UText::new(suggestion); + let len = s.as_str().len(); + s.set_cursor(len); + return true; + } + } + if let Text(ref mut s, _) = self { + s.insert_char(' '); + } + return true; + } + UIEvent::InsertInput(Key::Char('\n')) => { + if let Text(ref mut s, Some((_, auto_complete))) = self { + if let Some(suggestion) = auto_complete.get_suggestion() { + *s = UText::new(suggestion); + let len = s.as_str().len(); + s.set_cursor(len); + } + } + context + .replies + .push_back(UIEvent::ChangeMode(UIMode::Normal)); + return true; + } UIEvent::InsertInput(Key::Up) => { if let Text(_, Some((_, auto_complete))) = self { auto_complete.dec_cursor(); @@ -838,26 +854,60 @@ impl Component for AutoComplete { if self.entries.is_empty() { return; }; - - let upper_left = upper_left!(area); self.dirty = false; + + let (upper_left, bottom_right) = area; + let rows = get_y(bottom_right) - get_y(upper_left); + if rows == 0 { + return; + } + let page_no = (self.cursor.saturating_sub(1)).wrapping_div(rows); + let top_idx = page_no * rows; + let x_offset = if rows < self.entries.len() { 1 } else { 0 }; + let (width, height) = self.content.size(); + clear_area(grid, area, crate::conf::value(context, "theme_default")); copy_area( grid, &self.content, - area, - ((0, 0), (width.saturating_sub(1), height.saturating_sub(1))), + (upper_left, pos_dec(bottom_right, (x_offset, 0))), + ( + (0, top_idx), + (width.saturating_sub(1), height.saturating_sub(1)), + ), ); /* Highlight cursor */ - change_colors( - grid, - ( - pos_inc(upper_left, (0, self.cursor)), - pos_inc(upper_left, (width.saturating_sub(1), self.cursor)), - ), - Color::Default, - Color::Byte(246), - ); + if self.cursor > 0 { + change_colors( + grid, + ( + pos_inc(upper_left, (0, (self.cursor - 1) % rows)), + ( + std::cmp::min( + get_x(upper_left) + width.saturating_sub(1), + get_x(bottom_right), + ) + .saturating_sub(x_offset), + get_y(pos_inc(upper_left, (0, (self.cursor - 1) % rows))), + ), + ), + Color::Default, + Color::Byte(246), + ); + } + if rows < self.entries.len() { + ScrollBar { show_arrows: false }.draw( + grid, + ( + set_y(pos_dec(bottom_right, (x_offset, 0)), get_y(upper_left)), + pos_dec(bottom_right, (x_offset, 0)), + ), + context, + self.cursor.saturating_sub(1), + rows, + self.entries.len(), + ); + } context.dirty_areas.push_back(area); } fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool { @@ -922,7 +972,7 @@ impl AutoComplete { &mut content, Color::Byte(23), Color::Byte(7), - Attr::DEFAULT, + Attr::ITALICS, ((x + 2, i), (width - 1, i)), None, ); @@ -943,7 +993,7 @@ impl AutoComplete { } pub fn inc_cursor(&mut self) { - if self.cursor < self.entries.len().saturating_sub(1) { + if self.cursor < self.entries.len() { self.cursor += 1; self.set_dirty(true); } @@ -958,15 +1008,15 @@ impl AutoComplete { } pub fn set_cursor(&mut self, val: usize) { - debug_assert!(val < self.entries.len()); + debug_assert!(val <= self.entries.len()); self.cursor = val; } pub fn get_suggestion(&mut self) -> Option { - if self.entries.is_empty() { + if self.entries.is_empty() || self.cursor == 0 { return None; } - let ret = self.entries.remove(self.cursor); + let ret = self.entries.remove(self.cursor - 1); self.entries.clear(); self.cursor = 0; self.content.empty(); @@ -1007,13 +1057,17 @@ impl ScrollBar { } clear_area(grid, area, crate::conf::value(context, "theme_default")); - let visible_ratio: f32 = (std::cmp::min(visible_rows, length) as f32) / (length as f32); - let scrollbar_height = std::cmp::max((visible_ratio * (height as f32)) as usize, 1); + let visible_ratio: f64 = (std::cmp::min(visible_rows, length) as f64) / (length as f64); + let scrollbar_height = std::cmp::min( + height.saturating_sub(1), + std::cmp::max((visible_ratio * (height as f64)) as usize, 1), + ); if self.show_arrows { height -= 3; } + let scrollbar_offset = { - let temp = (((pos as f32) / (length as f32)) * (height as f32)) as usize; + let temp = (((pos as f64) / (length as f64)) * (height as f64)) as usize; if scrollbar_height + temp > height { height.saturating_sub(scrollbar_height) } else { @@ -1030,7 +1084,7 @@ impl ScrollBar { } upper_left = pos_inc(upper_left, (0, scrollbar_offset)); - for _ in 0..=scrollbar_height { + for _ in 0..scrollbar_height { grid[upper_left].set_bg(crate::conf::value(context, "widgets.options.highlighted").bg); upper_left = pos_inc(upper_left, (0, 1)); } @@ -1060,13 +1114,13 @@ impl ScrollBar { } clear_area(grid, area, crate::conf::value(context, "theme_default")); - let visible_ratio: f32 = (std::cmp::min(visible_cols, length) as f32) / (length as f32); - let scrollbar_width = std::cmp::max((visible_ratio * (width as f32)) as usize, 1); + let visible_ratio: f64 = (std::cmp::min(visible_cols, length) as f64) / (length as f64); + let scrollbar_width = std::cmp::max((visible_ratio * (width as f64)) as usize, 1); if self.show_arrows { width -= 3; } let scrollbar_offset = { - let temp = (((pos as f32) / (length as f32)) * (width as f32)) as usize; + let temp = (((pos as f64) / (length as f64)) * (width as f64)) as usize; if scrollbar_width + temp > width { width.saturating_sub(scrollbar_width) } else {