widgets: select AutoCompleteEntry on Enter

jmap-eventsource
Manos Pitsidianakis 2020-10-24 14:32:02 +03:00
parent 7b1ab389fa
commit 560771b32a
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
3 changed files with 102 additions and 40 deletions

View File

@ -458,8 +458,7 @@ fn run_app(opt: Opt) -> Result<()> {
}, },
UIMode::Insert => { UIMode::Insert => {
match k { match k {
Key::Char('\n') | Key::Esc => { Key::Esc => {
state.mode = UIMode::Normal;
state.rcv_event(UIEvent::ChangeMode(UIMode::Normal)); state.rcv_event(UIEvent::ChangeMode(UIMode::Normal));
state.redraw(); state.redraw();
}, },

View File

@ -500,7 +500,16 @@ impl Component for StatusBar {
}; };
} }
UIEvent::CmdInput(Key::Char('\t')) => { 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 mut utext = UText::new(suggestion);
let len = utext.as_str().len(); let len = utext.as_str().len();
utext.set_cursor(len); utext.set_cursor(len);

View File

@ -147,19 +147,35 @@ impl Component for Field {
None, None,
); );
} }
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { 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;
}
}
}
match *event { 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) => { UIEvent::InsertInput(Key::Up) => {
if let Text(_, Some((_, auto_complete))) = self { if let Text(_, Some((_, auto_complete))) = self {
auto_complete.dec_cursor(); auto_complete.dec_cursor();
@ -838,26 +854,60 @@ impl Component for AutoComplete {
if self.entries.is_empty() { if self.entries.is_empty() {
return; return;
}; };
let upper_left = upper_left!(area);
self.dirty = false; 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(); let (width, height) = self.content.size();
clear_area(grid, area, crate::conf::value(context, "theme_default"));
copy_area( copy_area(
grid, grid,
&self.content, &self.content,
area, (upper_left, pos_dec(bottom_right, (x_offset, 0))),
((0, 0), (width.saturating_sub(1), height.saturating_sub(1))), (
(0, top_idx),
(width.saturating_sub(1), height.saturating_sub(1)),
),
); );
/* Highlight cursor */ /* Highlight cursor */
change_colors( if self.cursor > 0 {
grid, change_colors(
( grid,
pos_inc(upper_left, (0, self.cursor)), (
pos_inc(upper_left, (width.saturating_sub(1), self.cursor)), pos_inc(upper_left, (0, (self.cursor - 1) % rows)),
), (
Color::Default, std::cmp::min(
Color::Byte(246), 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); context.dirty_areas.push_back(area);
} }
fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool {
@ -922,7 +972,7 @@ impl AutoComplete {
&mut content, &mut content,
Color::Byte(23), Color::Byte(23),
Color::Byte(7), Color::Byte(7),
Attr::DEFAULT, Attr::ITALICS,
((x + 2, i), (width - 1, i)), ((x + 2, i), (width - 1, i)),
None, None,
); );
@ -943,7 +993,7 @@ impl AutoComplete {
} }
pub fn inc_cursor(&mut self) { 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.cursor += 1;
self.set_dirty(true); self.set_dirty(true);
} }
@ -958,15 +1008,15 @@ impl AutoComplete {
} }
pub fn set_cursor(&mut self, val: usize) { pub fn set_cursor(&mut self, val: usize) {
debug_assert!(val < self.entries.len()); debug_assert!(val <= self.entries.len());
self.cursor = val; self.cursor = val;
} }
pub fn get_suggestion(&mut self) -> Option<String> { pub fn get_suggestion(&mut self) -> Option<String> {
if self.entries.is_empty() { if self.entries.is_empty() || self.cursor == 0 {
return None; return None;
} }
let ret = self.entries.remove(self.cursor); let ret = self.entries.remove(self.cursor - 1);
self.entries.clear(); self.entries.clear();
self.cursor = 0; self.cursor = 0;
self.content.empty(); self.content.empty();
@ -1007,13 +1057,17 @@ impl ScrollBar {
} }
clear_area(grid, area, crate::conf::value(context, "theme_default")); 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 visible_ratio: f64 = (std::cmp::min(visible_rows, length) as f64) / (length as f64);
let scrollbar_height = std::cmp::max((visible_ratio * (height as f32)) as usize, 1); 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 { if self.show_arrows {
height -= 3; height -= 3;
} }
let scrollbar_offset = { 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 { if scrollbar_height + temp > height {
height.saturating_sub(scrollbar_height) height.saturating_sub(scrollbar_height)
} else { } else {
@ -1030,7 +1084,7 @@ impl ScrollBar {
} }
upper_left = pos_inc(upper_left, (0, scrollbar_offset)); 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); grid[upper_left].set_bg(crate::conf::value(context, "widgets.options.highlighted").bg);
upper_left = pos_inc(upper_left, (0, 1)); upper_left = pos_inc(upper_left, (0, 1));
} }
@ -1060,13 +1114,13 @@ impl ScrollBar {
} }
clear_area(grid, area, crate::conf::value(context, "theme_default")); 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 visible_ratio: f64 = (std::cmp::min(visible_cols, length) as f64) / (length as f64);
let scrollbar_width = std::cmp::max((visible_ratio * (width as f32)) as usize, 1); let scrollbar_width = std::cmp::max((visible_ratio * (width as f64)) as usize, 1);
if self.show_arrows { if self.show_arrows {
width -= 3; width -= 3;
} }
let scrollbar_offset = { 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 { if scrollbar_width + temp > width {
width.saturating_sub(scrollbar_width) width.saturating_sub(scrollbar_width)
} else { } else {