From 14d05eae9b390da87d42d80fe0f76118b6cbfb23 Mon Sep 17 00:00:00 2001 From: kosay Date: Mon, 22 May 2023 00:07:23 +0900 Subject: [PATCH 01/13] refactor(tui_wrapper/util): fn -> trait --- src/tui_wrapper/util.rs | 69 ++++++++++++++++++++++++++ src/ui/widget/complex/single_select.rs | 4 +- 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/tui_wrapper/util.rs diff --git a/src/tui_wrapper/util.rs b/src/tui_wrapper/util.rs new file mode 100644 index 00000000..44171cfc --- /dev/null +++ b/src/tui_wrapper/util.rs @@ -0,0 +1,69 @@ +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent}; +use tui::layout::{Constraint, Direction, Layout, Rect}; + +pub fn key_event_to_code(key: KeyEvent) -> KeyCode { + use KeyCode::*; + + match key.code { + Char('p') if key.modifiers == KeyModifiers::CONTROL => Up, + Char('n') if key.modifiers == KeyModifiers::CONTROL => Down, + + Char('b') if key.modifiers == KeyModifiers::CONTROL => Left, + Char('f') if key.modifiers == KeyModifiers::CONTROL => Right, + + Char('u') if key.modifiers == KeyModifiers::CONTROL => PageUp, + Char('d') if key.modifiers == KeyModifiers::CONTROL => PageDown, + + Char('h') if key.modifiers == KeyModifiers::CONTROL => Delete, + Backspace => Delete, + + Char('a') if key.modifiers == KeyModifiers::CONTROL => Home, + Char('e') if key.modifiers == KeyModifiers::CONTROL => End, + + Char('[') if key.modifiers == KeyModifiers::CONTROL => Esc, + + _ => key.code, + } +} + +pub trait MousePosition { + fn position(&self) -> (u16, u16); +} + +impl MousePosition for MouseEvent { + fn position(&self) -> (u16, u16) { + (self.column, self.row) + } +} + +pub trait RectContainsPoint { + fn contains_point(&self, point: (u16, u16)) -> bool; +} + +impl RectContainsPoint for Rect { + fn contains_point(&self, (x, y): (u16, u16)) -> bool { + (self.left() <= x && x < self.right()) && (self.top() <= y && y < self.bottom()) + } +} + +pub fn child_window_chunk(width_rate: u16, height_rate: u16, chunk: Rect) -> Rect { + let w = width_rate; + let h = height_rate; + let chunk = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - h) / 2), + Constraint::Percentage(h), + Constraint::Percentage((100 - h) / 2), + ]) + .split(chunk); + + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - w) / 2), + Constraint::Percentage(w), + Constraint::Percentage((100 - w) / 2), + ]) + .split(chunk[1])[1] +} diff --git a/src/ui/widget/complex/single_select.rs b/src/ui/widget/complex/single_select.rs index f528e1ae..015f90c3 100644 --- a/src/ui/widget/complex/single_select.rs +++ b/src/ui/widget/complex/single_select.rs @@ -19,7 +19,7 @@ use crate::{ event::UserEvent, ui::{ event::{EventResult, InnerCallback}, - util::RectContainsPoint, + util::{MousePosition, RectContainsPoint}, widget::*, Window, }, @@ -268,7 +268,7 @@ impl WidgetTrait for SingleSelect<'_> { } fn on_mouse_event(&mut self, ev: MouseEvent) -> EventResult { - let pos = (ev.column, ev.row); + let pos = ev.position(); let chunks = &self.inner_chunks; From ad9f9c5ef8196d9536862cb1954255c28f499176 Mon Sep 17 00:00:00 2001 From: kosay Date: Mon, 22 May 2023 00:15:40 +0900 Subject: [PATCH 02/13] Revert "refactor(tui_wrapper/util): fn -> trait" This reverts commit f855dff268b314fd1206b19482321ce9f620c498. --- src/tui_wrapper/util.rs | 24 +++++++----------------- src/ui/widget/complex/single_select.rs | 4 ++-- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/tui_wrapper/util.rs b/src/tui_wrapper/util.rs index 44171cfc..61414cf5 100644 --- a/src/tui_wrapper/util.rs +++ b/src/tui_wrapper/util.rs @@ -25,25 +25,15 @@ pub fn key_event_to_code(key: KeyEvent) -> KeyCode { _ => key.code, } } - -pub trait MousePosition { - fn position(&self) -> (u16, u16); -} - -impl MousePosition for MouseEvent { - fn position(&self) -> (u16, u16) { - (self.column, self.row) - } +#[inline] +pub fn mouse_pos(ev: MouseEvent) -> (u16, u16) { + (ev.column, ev.row) } -pub trait RectContainsPoint { - fn contains_point(&self, point: (u16, u16)) -> bool; -} - -impl RectContainsPoint for Rect { - fn contains_point(&self, (x, y): (u16, u16)) -> bool { - (self.left() <= x && x < self.right()) && (self.top() <= y && y < self.bottom()) - } +#[inline] +pub fn contains(chunk: Rect, point: (u16, u16)) -> bool { + let (px, py) = point; + (chunk.left() <= px && px <= chunk.right()) && (chunk.top() <= py && py <= chunk.bottom()) } pub fn child_window_chunk(width_rate: u16, height_rate: u16, chunk: Rect) -> Rect { diff --git a/src/ui/widget/complex/single_select.rs b/src/ui/widget/complex/single_select.rs index 015f90c3..f528e1ae 100644 --- a/src/ui/widget/complex/single_select.rs +++ b/src/ui/widget/complex/single_select.rs @@ -19,7 +19,7 @@ use crate::{ event::UserEvent, ui::{ event::{EventResult, InnerCallback}, - util::{MousePosition, RectContainsPoint}, + util::RectContainsPoint, widget::*, Window, }, @@ -268,7 +268,7 @@ impl WidgetTrait for SingleSelect<'_> { } fn on_mouse_event(&mut self, ev: MouseEvent) -> EventResult { - let pos = ev.position(); + let pos = (ev.column, ev.row); let chunks = &self.inner_chunks; From b568a3614a21b48b17f4feee9a33c2843e504968 Mon Sep 17 00:00:00 2001 From: kosay Date: Fri, 16 Jun 2023 01:59:53 +0900 Subject: [PATCH 03/13] chore: remove src/tui_wrapper/util.rs --- src/tui_wrapper/util.rs | 59 ----------------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 src/tui_wrapper/util.rs diff --git a/src/tui_wrapper/util.rs b/src/tui_wrapper/util.rs deleted file mode 100644 index 61414cf5..00000000 --- a/src/tui_wrapper/util.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent}; -use tui::layout::{Constraint, Direction, Layout, Rect}; - -pub fn key_event_to_code(key: KeyEvent) -> KeyCode { - use KeyCode::*; - - match key.code { - Char('p') if key.modifiers == KeyModifiers::CONTROL => Up, - Char('n') if key.modifiers == KeyModifiers::CONTROL => Down, - - Char('b') if key.modifiers == KeyModifiers::CONTROL => Left, - Char('f') if key.modifiers == KeyModifiers::CONTROL => Right, - - Char('u') if key.modifiers == KeyModifiers::CONTROL => PageUp, - Char('d') if key.modifiers == KeyModifiers::CONTROL => PageDown, - - Char('h') if key.modifiers == KeyModifiers::CONTROL => Delete, - Backspace => Delete, - - Char('a') if key.modifiers == KeyModifiers::CONTROL => Home, - Char('e') if key.modifiers == KeyModifiers::CONTROL => End, - - Char('[') if key.modifiers == KeyModifiers::CONTROL => Esc, - - _ => key.code, - } -} -#[inline] -pub fn mouse_pos(ev: MouseEvent) -> (u16, u16) { - (ev.column, ev.row) -} - -#[inline] -pub fn contains(chunk: Rect, point: (u16, u16)) -> bool { - let (px, py) = point; - (chunk.left() <= px && px <= chunk.right()) && (chunk.top() <= py && py <= chunk.bottom()) -} - -pub fn child_window_chunk(width_rate: u16, height_rate: u16, chunk: Rect) -> Rect { - let w = width_rate; - let h = height_rate; - let chunk = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Percentage((100 - h) / 2), - Constraint::Percentage(h), - Constraint::Percentage((100 - h) / 2), - ]) - .split(chunk); - - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage((100 - w) / 2), - Constraint::Percentage(w), - Constraint::Percentage((100 - w) / 2), - ]) - .split(chunk[1])[1] -} From 3c98e357791c37b9b3b38699cfaf22b8a9df77b9 Mon Sep 17 00:00:00 2001 From: kosay Date: Fri, 16 Jun 2023 10:24:35 +0900 Subject: [PATCH 04/13] wip: --- src/ui/tab.rs | 48 +++++++++++++++++++++--- src/ui/widget.rs | 2 +- src/ui/widget/complex/input.rs | 2 +- src/ui/widget/complex/multiple_select.rs | 10 +++-- src/ui/widget/complex/single_select.rs | 6 +-- src/ui/widget/config.rs | 6 ++- src/ui/widget/list.rs | 4 +- src/ui/widget/table.rs | 14 +++---- src/ui/widget/text.rs | 10 ++--- src/ui/window.rs | 25 +----------- src/window/config.rs | 4 +- src/window/event.rs | 4 +- src/window/list.rs | 4 +- src/window/network.rs | 4 +- src/window/pod.rs | 4 +- src/window/yaml.rs | 4 +- 16 files changed, 87 insertions(+), 64 deletions(-) diff --git a/src/ui/tab.rs b/src/ui/tab.rs index 7b6570dd..d3bbbc43 100644 --- a/src/ui/tab.rs +++ b/src/ui/tab.rs @@ -1,5 +1,10 @@ -use super::widget::*; +use super::{ + event::EventResult, + util::{MousePosition, RectContainsPoint}, + widget::*, +}; +use crossterm::event::{MouseButton, MouseEvent, MouseEventKind}; use ratatui::{ backend::Backend, layout::{Constraint, Layout, Rect}, @@ -34,6 +39,7 @@ pub struct Tab<'a> { layout: Layout, active_widget_index: usize, activatable_widget_indices: Vec, + mouse_over_widget_index: Option, } impl<'a> Tab<'a> { @@ -59,6 +65,7 @@ impl<'a> Tab<'a> { layout, activatable_widget_indices, active_widget_index: 0, + mouse_over_widget_index: None, } } @@ -151,6 +158,34 @@ impl<'a> Tab<'a> { } }) } + + pub fn on_mouse_event(&mut self, ev: MouseEvent) -> EventResult { + let pos = ev.position(); + + let active_widget_id = self.active_widget_id().to_string(); + + let Some((index, id)) = self + .as_mut_widgets() + .iter_mut() + .enumerate() + .find(|(_, w)| w.chunk().contains_point(pos)) + .map(|(i, w)| (i, w.id().to_string()) ) else { return EventResult::Ignore }; + + match ev.kind { + MouseEventKind::Down(MouseButton::Left) => { + if id != active_widget_id { + self.activate_widget_by_id(&id); + + return EventResult::Ignore; + } + } + _ => { + self.mouse_over_widget_index = Some(index); + } + } + + self.active_widget_mut().on_mouse_event(ev) + } } impl Tab<'_> { @@ -158,9 +193,12 @@ impl Tab<'_> { where B: Backend, { - self.widgets - .iter_mut() - .enumerate() - .for_each(|(i, w)| w.widget.render(f, i == self.active_widget_index)); + self.widgets.iter_mut().enumerate().for_each(|(i, w)| { + w.widget.render( + f, + i == self.active_widget_index, + self.mouse_over_widget_index.is_some_and(|idx| idx == i), + ) + }); } } diff --git a/src/ui/widget.rs b/src/ui/widget.rs index 30c3e864..d5a37a17 100644 --- a/src/ui/widget.rs +++ b/src/ui/widget.rs @@ -202,7 +202,7 @@ pub trait WidgetTrait { #[enum_dispatch] pub trait RenderTrait { - fn render(&mut self, f: &mut Frame, is_active: bool); + fn render(&mut self, f: &mut Frame, is_active: bool, is_mouse_over: bool); } #[enum_dispatch(WidgetTrait, RenderTrait)] diff --git a/src/ui/widget/complex/input.rs b/src/ui/widget/complex/input.rs index 8c595524..1da51788 100644 --- a/src/ui/widget/complex/input.rs +++ b/src/ui/widget/complex/input.rs @@ -98,7 +98,7 @@ impl InputForm { } fn block(&self, selected: bool) -> Block<'static> { - self.widget_config.render_block(selected) + self.widget_config.render_block(selected, false) } pub fn render(&mut self, f: &mut Frame, selected: bool) { diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index 048dca99..4a3c4648 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -271,11 +271,13 @@ impl<'a> SelectForm<'a> { .alignment(Alignment::Center) .block(Block::default()); - self.list_widget.render(f, self.active_form_index == 0); + self.list_widget + .render(f, self.active_form_index == 0, false); f.render_widget(arrow, chunks[1]); - self.selected_widget.render(f, self.active_form_index == 1); + self.selected_widget + .render(f, self.active_form_index == 1, false); } fn update_layout(&mut self, chunk: Rect) { @@ -626,12 +628,12 @@ impl Default for MultipleSelect<'_> { } impl RenderTrait for MultipleSelect<'_> { - fn render(&mut self, f: &mut Frame, selected: bool) { + fn render(&mut self, f: &mut Frame, selected: bool, is_mouse_over: bool) { let block = if let Some(block_injection) = &self.block_injection { (block_injection)(&*self, selected) } else { self.widget_config - .render_block(self.can_activate() && selected) + .render_block(self.can_activate() && selected, is_mouse_over) }; let inner_chunk = block.inner(self.chunk); diff --git a/src/ui/widget/complex/single_select.rs b/src/ui/widget/complex/single_select.rs index f528e1ae..7f40d4aa 100644 --- a/src/ui/widget/complex/single_select.rs +++ b/src/ui/widget/complex/single_select.rs @@ -38,7 +38,7 @@ struct SelectForm<'a> { impl<'a> SelectForm<'a> { fn render(&mut self, f: &mut Frame) { - self.list_widget.render(f, true); + self.list_widget.render(f, true, false); } fn filter_items(&self, items: &BTreeSet) -> Vec { @@ -324,7 +324,7 @@ impl WidgetTrait for SingleSelect<'_> { } impl RenderTrait for SingleSelect<'_> { - fn render(&mut self, f: &mut Frame<'_, B>, selected: bool) + fn render(&mut self, f: &mut Frame<'_, B>, selected: bool, is_mouse_over: bool) where B: Backend, { @@ -332,7 +332,7 @@ impl RenderTrait for SingleSelect<'_> { (block_injection)(&*self, selected) } else { self.widget_config - .render_block(self.can_activate() && selected) + .render_block(self.can_activate() && selected, is_mouse_over) }; f.render_widget(block, self.chunk); diff --git a/src/ui/widget/config.rs b/src/ui/widget/config.rs index 0d279aec..21c6a470 100644 --- a/src/ui/widget/config.rs +++ b/src/ui/widget/config.rs @@ -126,10 +126,14 @@ impl WidgetConfig { /// /// Active: ─ + Title ─── (BOLD) /// Inactive: ─── Title ─── (DarkGray: title is Raw) - pub fn render_block(&self, is_active: bool) -> Block<'static> { + pub fn render_block(&self, is_active: bool, is_mouse_over: bool) -> Block<'static> { let block = if self.can_activate { if is_active { self.block.clone() + } else if is_mouse_over { + self.block + .clone() + .border_style(Style::default().fg(Color::Gray)) } else { self.block .clone() diff --git a/src/ui/widget/list.rs b/src/ui/widget/list.rs index b67d4244..f59e7d45 100644 --- a/src/ui/widget/list.rs +++ b/src/ui/widget/list.rs @@ -385,12 +385,12 @@ impl<'a> List<'a> { } impl RenderTrait for List<'_> { - fn render(&mut self, f: &mut Frame, is_active: bool) { + fn render(&mut self, f: &mut Frame, is_active: bool, is_mouse_over: bool) { let block = if let Some(block_injection) = &self.block_injection { (block_injection)(&*self, is_active) } else { self.widget_config - .render_block(self.can_activate() && is_active) + .render_block(self.can_activate() && is_active, is_mouse_over) }; f.render_stateful_widget(self.widget(block), self.chunk, &mut self.state); diff --git a/src/ui/widget/table.rs b/src/ui/widget/table.rs index bbd346fe..4b47174b 100644 --- a/src/ui/widget/table.rs +++ b/src/ui/widget/table.rs @@ -600,7 +600,7 @@ impl<'a> Table<'a> { } impl RenderTrait for Table<'_> { - fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool) + fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool, is_mouse_over: bool) where B: Backend, { @@ -620,7 +620,7 @@ impl RenderTrait for Table<'_> { } } - let block = widget_config.render_block(self.can_activate() && is_active); + let block = widget_config.render_block(self.can_activate() && is_active, is_mouse_over); let constraints = constraints(self.items.digits()); @@ -907,7 +907,7 @@ mod tests { terminal .draw(|f| { - table.render(f, true); + table.render(f, true, false); }) .unwrap(); @@ -946,7 +946,7 @@ mod tests { terminal .draw(|f| { - table.render(f, true); + table.render(f, true, false); }) .unwrap(); @@ -971,7 +971,7 @@ mod tests { terminal .draw(|f| { - table.render(f, true); + table.render(f, true, false); }) .unwrap(); @@ -995,7 +995,7 @@ mod tests { terminal .draw(|f| { - table.render(f, false); + table.render(f, false, false); }) .unwrap(); @@ -1009,7 +1009,7 @@ mod tests { terminal .draw(|f| { - table.render(f, false); + table.render(f, false, false); }) .unwrap(); diff --git a/src/ui/widget/text.rs b/src/ui/widget/text.rs index 8388acba..847cff54 100644 --- a/src/ui/widget/text.rs +++ b/src/ui/widget/text.rs @@ -39,7 +39,7 @@ use super::{ RenderTrait, SelectedItem, WidgetTrait, }; -type RenderBlockInjection = Rc Block<'static>>; +type RenderBlockInjection = Rc Block<'static>>; mod highlight_content { @@ -271,7 +271,7 @@ impl TextBuilder { pub fn block_injection(mut self, block_injection: F) -> Self where - F: Fn(&Text, bool) -> Block<'static> + 'static, + F: Fn(&Text, bool, bool) -> Block<'static> + 'static, { self.block_injection = Some(Rc::new(block_injection)); self @@ -831,15 +831,15 @@ impl WidgetTrait for Text { } impl RenderTrait for Text { - fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool) + fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool, is_mouse_over: bool) where B: Backend, { let block = if let Some(block_injection) = &self.block_injection { - (block_injection)(&*self, self.can_activate() && is_active) + (block_injection)(&*self, self.can_activate() && is_active, is_mouse_over) } else { self.widget_config - .render_block(self.can_activate() && is_active) + .render_block(self.can_activate() && is_active, is_mouse_over) }; let wrapped_lines = self.item.wrapped_lines(); diff --git a/src/ui/window.rs b/src/ui/window.rs index 95e7b17c..0c8a3559 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -391,7 +391,7 @@ impl<'a> Window<'a> { if let Some(id) = &self.open_popup_id { if let Some(popup) = self.popups.iter_mut().find(|p| p.id() == id) { f.render_widget(Clear, child_window_chunk(80, 80, self.chunk)); - popup.render(f, true); + popup.render(f, true, false); } } } @@ -480,8 +480,6 @@ impl Window<'_> { } let pos = (ev.column, ev.row); - let active_widget_id = self.active_widget_id().to_string(); - let mut activate_widget_id = None; let result = match self.area_kind_by_cursor_position(pos) { AreaKind::Tab => { @@ -492,22 +490,7 @@ impl Window<'_> { AreaKind::Widgets => { self.mouse_over_tab_index = None; - if let Some(w) = self - .active_tab_mut() - .as_mut_widgets() - .iter_mut() - .find(|w| w.chunk().contains_point(pos)) - { - activate_widget_id = if w.id() != active_widget_id { - Some(w.id().to_string()) - } else { - None - }; - - w.on_mouse_event(ev) - } else { - EventResult::Ignore - } + self.active_tab_mut().on_mouse_event(ev) } AreaKind::OutSide => { self.mouse_over_tab_index = None; @@ -516,10 +499,6 @@ impl Window<'_> { } }; - if let Some(id) = activate_widget_id { - self.activate_widget_by_id(&id); - } - result } diff --git a/src/window/config.rs b/src/window/config.rs index bc18bed8..494b64e7 100644 --- a/src/window/config.rs +++ b/src/window/config.rs @@ -126,14 +126,14 @@ impl<'a> ConfigTabBuilder<'a> { .id(view_id::tab_config_widget_raw_data) .widget_config(&WidgetConfig::builder().title("Raw Data").build()) .wrap() - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.title_mut() = format!("Raw Data [{}/{}]", index, size).into(); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }); if let Some(cb) = self.clipboard { diff --git a/src/window/event.rs b/src/window/event.rs index c6036f66..107e523a 100644 --- a/src/window/event.rs +++ b/src/window/event.rs @@ -38,14 +38,14 @@ impl<'a> EventsTabBuilder<'a> { .widget_config(&WidgetConfig::builder().title("Event").build()) .wrap() .follow() - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.append_title_mut() = Some(format!(" [{}/{}]", index, size).into()); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }); if let Some(cb) = self.clipboard { diff --git a/src/window/list.rs b/src/window/list.rs index 14ed2715..a65ba65a 100644 --- a/src/window/list.rs +++ b/src/window/list.rs @@ -61,14 +61,14 @@ impl<'a> ListTabBuilder<'a> { let builder = Text::builder() .id(view_id::tab_list_widget_list) .widget_config(&WidgetConfig::builder().title("List").build()) - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.append_title_mut() = Some(format!(" [{}/{}]", index, size).into()); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }) .action('f', open_subwin); diff --git a/src/window/network.rs b/src/window/network.rs index 76f66062..d4dd1b6b 100644 --- a/src/window/network.rs +++ b/src/window/network.rs @@ -131,14 +131,14 @@ impl<'a> NetworkTabBuilder<'a> { let builder = Text::builder() .id(view_id::tab_network_widget_description) .widget_config(&WidgetConfig::builder().title("Description").build()) - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.title_mut() = format!("Description [{}/{}]", index, size).into(); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }); if let Some(cb) = self.clipboard { diff --git a/src/window/pod.rs b/src/window/pod.rs index a4e30fae..ad643fb7 100644 --- a/src/window/pod.rs +++ b/src/window/pod.rs @@ -123,14 +123,14 @@ impl<'a> PodTabBuilder<'a> { .widget_config(&WidgetConfig::builder().title("Log").build()) .wrap() .follow() - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.title_mut() = format!("Log [{}/{}]", index, size).into(); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }) .action(UserEvent::from(KeyCode::Enter), add_newline); diff --git a/src/window/yaml.rs b/src/window/yaml.rs index 2ce7dbc4..0827edd2 100644 --- a/src/window/yaml.rs +++ b/src/window/yaml.rs @@ -62,14 +62,14 @@ impl<'a> YamlTabBuilder<'a> { let builder = Text::builder() .id(view_id::tab_yaml_widget_yaml) .widget_config(&WidgetConfig::builder().title("Yaml").build()) - .block_injection(|text: &Text, is_active: bool| { + .block_injection(|text: &Text, is_active: bool, is_mouse_over: bool| { let (index, size) = text.state(); let mut config = text.widget_config().clone(); *config.append_title_mut() = Some(format!(" [{}/{}]", index, size).into()); - config.render_block(text.can_activate() && is_active) + config.render_block(text.can_activate() && is_active, is_mouse_over) }) .action('f', open_subwin) .wrap(); From f8f2288eb51851ee6ed102600002e9ca50e53cff Mon Sep 17 00:00:00 2001 From: kosay Date: Sat, 17 Jun 2023 14:57:56 +0900 Subject: [PATCH 05/13] refactor(ui): Refactor rendering logic to use "is_active" instead of "selected" as the argument for indicating widget's active state --- src/ui/widget/complex/input.rs | 14 +++++++------- src/ui/widget/complex/multiple_select.rs | 12 ++++++------ src/ui/widget/complex/single_select.rs | 6 +++--- src/ui/widget/table/filter_form.rs | 4 ++-- src/ui/widget/text.rs | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ui/widget/complex/input.rs b/src/ui/widget/complex/input.rs index 1da51788..588eae14 100644 --- a/src/ui/widget/complex/input.rs +++ b/src/ui/widget/complex/input.rs @@ -97,13 +97,13 @@ impl InputForm { } } - fn block(&self, selected: bool) -> Block<'static> { - self.widget_config.render_block(selected, false) + fn block(&self, is_active: bool) -> Block<'static> { + self.widget_config.render_block(is_active, false) } - pub fn render(&mut self, f: &mut Frame, selected: bool) { - let spans = self.render_content(selected); - let block = self.block(selected); + pub fn render(&mut self, f: &mut Frame, is_active: bool) { + let spans = self.render_content(is_active); + let block = self.block(is_active); let chunk = self.chunk; let widget = Paragraph::new(spans).block(block); @@ -111,8 +111,8 @@ impl InputForm { f.render_widget(widget, chunk); } - pub fn render_content(&mut self, selected: bool) -> Line<'static> { - if selected { + pub fn render_content(&mut self, is_active: bool) -> Line<'static> { + if is_active { self.cursor.update_tick(); } else { self.cursor.mode = Mode::Hide diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index 4a3c4648..c0c706bc 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -83,11 +83,11 @@ mod inner { self.items.values_mut().for_each(|v| *v = false); } - fn filter_items(items: &BTreeMap, selected: bool) -> Vec { + fn filter_items(items: &BTreeMap, is_active: bool) -> Vec { items .iter() .filter_map(|(k, v)| { - if *v == selected { + if *v == is_active { Some(k.clone()) } else { None @@ -628,12 +628,12 @@ impl Default for MultipleSelect<'_> { } impl RenderTrait for MultipleSelect<'_> { - fn render(&mut self, f: &mut Frame, selected: bool, is_mouse_over: bool) { + fn render(&mut self, f: &mut Frame, is_active: bool, is_mouse_over: bool) { let block = if let Some(block_injection) = &self.block_injection { - (block_injection)(&*self, selected) + (block_injection)(&*self, is_active) } else { self.widget_config - .render_block(self.can_activate() && selected, is_mouse_over) + .render_block(self.can_activate() && is_active, is_mouse_over) }; let inner_chunk = block.inner(self.chunk); @@ -647,7 +647,7 @@ impl RenderTrait for MultipleSelect<'_> { Paragraph::new(format!("[{}/{}]", status.0, status.1)), self.layout.split(inner_chunk)[LAYOUT_INDEX_FOR_STATUS], ); - self.selected_widget.render(f, selected); + self.selected_widget.render(f, is_active); } } diff --git a/src/ui/widget/complex/single_select.rs b/src/ui/widget/complex/single_select.rs index 7f40d4aa..58f467c2 100644 --- a/src/ui/widget/complex/single_select.rs +++ b/src/ui/widget/complex/single_select.rs @@ -324,15 +324,15 @@ impl WidgetTrait for SingleSelect<'_> { } impl RenderTrait for SingleSelect<'_> { - fn render(&mut self, f: &mut Frame<'_, B>, selected: bool, is_mouse_over: bool) + fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool, is_mouse_over: bool) where B: Backend, { let block = if let Some(block_injection) = &self.block_injection { - (block_injection)(&*self, selected) + (block_injection)(&*self, is_active) } else { self.widget_config - .render_block(self.can_activate() && selected, is_mouse_over) + .render_block(self.can_activate() && is_active, is_mouse_over) }; f.render_widget(block, self.chunk); diff --git a/src/ui/widget/table/filter_form.rs b/src/ui/widget/table/filter_form.rs index 53bd1971..166e4591 100644 --- a/src/ui/widget/table/filter_form.rs +++ b/src/ui/widget/table/filter_form.rs @@ -39,13 +39,13 @@ impl FilterForm { self.input_widget.on_key_event(ev) } - pub fn render(&mut self, f: &mut Frame<'_, B>, selected: bool) + pub fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool) where B: Backend, { let header = "FILTER: "; - let content = self.input_widget.render_content(selected); + let content = self.input_widget.render_content(is_active); let content_width = self.chunk.width.saturating_sub(8); diff --git a/src/ui/widget/text.rs b/src/ui/widget/text.rs index 847cff54..f0d198e0 100644 --- a/src/ui/widget/text.rs +++ b/src/ui/widget/text.rs @@ -144,13 +144,13 @@ impl SearchForm { self.input_widget.on_key_event(ev) } - fn render(&mut self, f: &mut Frame<'_, B>, selected: bool, status: (usize, usize)) + fn render(&mut self, f: &mut Frame<'_, B>, is_active: bool, status: (usize, usize)) where B: Backend, { let header = "Search: "; - let content = self.input_widget.render_content(selected); + let content = self.input_widget.render_content(is_active); let status = format!(" [{}/{}]", status.0, status.1); From 8c350992f5cab0bab4b1b3e34dd669d661e2380b Mon Sep 17 00:00:00 2001 From: kosay Date: Sat, 17 Jun 2023 23:06:59 +0900 Subject: [PATCH 06/13] chore: remove shell.nix --- shell.nix | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 shell.nix diff --git a/shell.nix b/shell.nix deleted file mode 100644 index df3deb1f..00000000 --- a/shell.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ pkgs ? import { } }: - -pkgs.mkShell { - buildInputs = [ - pkgs.openssl - pkgs.pkg-config - ]; -} From 99f7cad6c918eec8ec7fe21c743c1c870554121a Mon Sep 17 00:00:00 2001 From: kosay Date: Mon, 19 Jun 2023 20:05:51 +0900 Subject: [PATCH 07/13] feat(ui): Remove mouse-over highlight when a key uinput event occurs --- src/ui/tab.rs | 6 ++++++ src/ui/window.rs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/ui/tab.rs b/src/ui/tab.rs index d3bbbc43..3a9bf344 100644 --- a/src/ui/tab.rs +++ b/src/ui/tab.rs @@ -98,11 +98,15 @@ impl<'a> Tab<'a> { } pub fn activate_next_widget(&mut self) { + self.mouse_over_widget_index = None; + self.active_widget_index = (self.active_widget_index + 1) % self.activatable_widget_indices.len(); } pub fn activate_prev_widget(&mut self) { + self.mouse_over_widget_index = None; + let activatable_widget_len = self.activatable_widget_indices.len(); self.active_widget_index = @@ -135,6 +139,8 @@ impl<'a> Tab<'a> { .enumerate() .find(|(_, w)| w.widget.id() == id) { + self.mouse_over_widget_index = None; + self.active_widget_index = index; } } diff --git a/src/ui/window.rs b/src/ui/window.rs index 0c8a3559..385d75df 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -427,6 +427,8 @@ impl Window<'_> { } pub fn on_key_event(&mut self, ev: KeyEvent) -> EventResult { + self.mouse_over_tab_index = None; + if let Some(id) = &self.open_popup_id { if let Some(popup) = self.popups.iter_mut().find(|w| w.id() == id) { return popup.on_key_event(ev); From 2b0b480a0de0b7b3c3435d9fc8d12c69b6d67018 Mon Sep 17 00:00:00 2001 From: kosay Date: Mon, 19 Jun 2023 20:23:32 +0900 Subject: [PATCH 08/13] feat(ui/multiple_select): implment mouse-over highlight --- src/ui/widget/complex/multiple_select.rs | 67 ++++++++++++++++++------ 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index c0c706bc..53c50e7f 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -7,7 +7,7 @@ use crate::ui::{ Window, }; -use crossterm::event::{KeyCode, KeyEvent, MouseEvent}; +use crossterm::event::{KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind}; use derivative::*; @@ -189,6 +189,9 @@ mod inner { use inner::SelectItems; +const LIST_FORM_ID: usize = 0; +const SELECTED_FORM_ID: usize = 1; + #[derive(Derivative)] #[derivative(Debug)] struct SelectForm<'a> { @@ -198,6 +201,7 @@ struct SelectForm<'a> { selected_widget: List<'a>, chunk: Rect, active_form_index: usize, + mouse_over_widget_index: Option, direction: Direction, #[derivative(Debug = "ignore")] matcher: SkimMatcherV2, @@ -212,6 +216,7 @@ impl Default for SelectForm<'_> { selected_widget: List::default(), chunk: Rect::default(), active_form_index: 0, + mouse_over_widget_index: None, matcher: SkimMatcherV2::default(), direction: Direction::Vertical, } @@ -271,13 +276,19 @@ impl<'a> SelectForm<'a> { .alignment(Alignment::Center) .block(Block::default()); - self.list_widget - .render(f, self.active_form_index == 0, false); + self.list_widget.render( + f, + self.active_form_index == LIST_FORM_ID, + self.mouse_over_widget_index == Some(LIST_FORM_ID), + ); f.render_widget(arrow, chunks[1]); - self.selected_widget - .render(f, self.active_form_index == 1, false); + self.selected_widget.render( + f, + self.active_form_index == 1, + self.mouse_over_widget_index == Some(SELECTED_FORM_ID), + ); } fn update_layout(&mut self, chunk: Rect) { @@ -339,7 +350,7 @@ impl<'a> SelectForm<'a> { } fn active_form(&mut self) -> &List<'a> { - if self.active_form_index == 0 { + if self.active_form_index == LIST_FORM_ID { &self.list_widget } else { &self.selected_widget @@ -347,7 +358,7 @@ impl<'a> SelectForm<'a> { } fn active_form_mut(&mut self) -> &mut List<'a> { - if self.active_form_index == 0 { + if self.active_form_index == LIST_FORM_ID { &mut self.list_widget } else { &mut self.selected_widget @@ -356,22 +367,26 @@ impl<'a> SelectForm<'a> { #[allow(dead_code)] fn inactive_form_mut(&mut self) -> &mut List<'a> { - if self.active_form_index == 1 { - &mut self.list_widget - } else { + if self.active_form_index == LIST_FORM_ID { &mut self.selected_widget + } else { + &mut self.list_widget } } fn toggle_active_form(&mut self) { - if self.active_form_index == 0 { - self.active_form_index = 1 + self.mouse_over_widget_index = None; + + if self.active_form_index == LIST_FORM_ID { + self.active_form_index = SELECTED_FORM_ID } else { - self.active_form_index = 0 + self.active_form_index = LIST_FORM_ID } } fn activate_form_by_index(&mut self, index: usize) { + self.mouse_over_widget_index = None; + self.active_form_index = index; } @@ -461,17 +476,35 @@ impl<'a> SelectForm<'a> { let (chunks, _) = self.chunks_and_arrow(); if chunks[0].contains_point(pos) { - self.activate_form_by_index(0); - self.list_widget.on_mouse_event(ev) + if let MouseEventKind::Down(MouseButton::Left) = ev.kind { + if self.active_form_index != LIST_FORM_ID { + self.activate_form_by_index(LIST_FORM_ID); + return EventResult::Ignore; + } + } else { + self.mouse_over_widget_index = Some(LIST_FORM_ID); + } + + self.active_form_mut().on_mouse_event(ev) } else if chunks[2].contains_point(pos) { - self.activate_form_by_index(1); - self.selected_widget.on_mouse_event(ev) + if let MouseEventKind::Down(MouseButton::Left) = ev.kind { + if self.active_form_index != SELECTED_FORM_ID { + self.activate_form_by_index(SELECTED_FORM_ID); + return EventResult::Ignore; + } + } else { + self.mouse_over_widget_index = Some(SELECTED_FORM_ID); + } + + self.active_form_mut().on_mouse_event(ev) } else { EventResult::Nop } } fn on_key_event(&mut self, ev: KeyEvent) -> EventResult { + self.mouse_over_widget_index = None; + self.active_form_mut().on_key_event(ev) } } From 7bdfb9bc122cfb246072a724d2179645fa7c5656 Mon Sep 17 00:00:00 2001 From: kosay Date: Tue, 20 Jun 2023 00:56:18 +0900 Subject: [PATCH 09/13] refactor(ui/multiple_select): remove unnecessary argument of render method --- src/ui/widget/complex/multiple_select.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index 53c50e7f..daea1b1c 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -266,7 +266,7 @@ impl<'a> SelectForm<'a> { } } - fn render(&mut self, f: &mut Frame, _: bool) { + fn render(&mut self, f: &mut Frame) { let (chunks, arrow) = self.chunks_and_arrow(); let arrow = Paragraph::new(Span::styled( @@ -680,7 +680,7 @@ impl RenderTrait for MultipleSelect<'_> { Paragraph::new(format!("[{}/{}]", status.0, status.1)), self.layout.split(inner_chunk)[LAYOUT_INDEX_FOR_STATUS], ); - self.selected_widget.render(f, is_active); + self.selected_widget.render(f); } } From 4bdc504c87c5608918a8afe148dcd91efbf91346 Mon Sep 17 00:00:00 2001 From: kosay Date: Wed, 21 Jun 2023 00:35:01 +0900 Subject: [PATCH 10/13] feat: Enable capturing focus events. --- src/main.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5e1419a2..7bbc39be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use crossbeam::channel::{bounded, Receiver, Sender}; use crossterm::{ cursor::Show, - event::{DisableMouseCapture, EnableMouseCapture}, + event::{DisableFocusChange, DisableMouseCapture, EnableFocusChange, EnableMouseCapture}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; @@ -36,8 +36,13 @@ use ratatui::{backend::CrosstermBackend, Terminal, TerminalOptions, Viewport}; macro_rules! enable_raw_mode { () => { enable_raw_mode().expect("failed to enable raw mode"); - execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture) - .expect("failed to enable raw mode"); + execute!( + io::stdout(), + EnterAlternateScreen, + EnableMouseCapture, + EnableFocusChange + ) + .expect("failed to enable raw mode"); }; } @@ -47,6 +52,7 @@ macro_rules! disable_raw_mode { io::stdout(), LeaveAlternateScreen, DisableMouseCapture, + DisableFocusChange, Show ) .expect("failed to restore terminal"); From 5d4edba8652e4b6a9575c40dd1492600dcf88c82 Mon Sep 17 00:00:00 2001 From: kosay Date: Wed, 21 Jun 2023 00:56:24 +0900 Subject: [PATCH 11/13] feat(ui): Clear mouse-over highlight on `FocusLost` and `FocusGained` events --- src/ui/tab.rs | 10 +++++--- src/ui/widget/complex/multiple_select.rs | 14 ++++++++--- src/ui/window.rs | 32 ++++++++++++++++++++---- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/ui/tab.rs b/src/ui/tab.rs index 3a9bf344..7437fff0 100644 --- a/src/ui/tab.rs +++ b/src/ui/tab.rs @@ -98,14 +98,14 @@ impl<'a> Tab<'a> { } pub fn activate_next_widget(&mut self) { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); self.active_widget_index = (self.active_widget_index + 1) % self.activatable_widget_indices.len(); } pub fn activate_prev_widget(&mut self) { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); let activatable_widget_len = self.activatable_widget_indices.len(); @@ -139,12 +139,16 @@ impl<'a> Tab<'a> { .enumerate() .find(|(_, w)| w.widget.id() == id) { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); self.active_widget_index = index; } } + pub fn clear_mouse_over(&mut self) { + self.mouse_over_widget_index = None; + } + pub fn find_widget(&self, id: &str) -> Option<&Widget<'a>> { self.widgets.iter().find_map(|w| { if w.widget.id() == id { diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index daea1b1c..e4b648c8 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -375,7 +375,7 @@ impl<'a> SelectForm<'a> { } fn toggle_active_form(&mut self) { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); if self.active_form_index == LIST_FORM_ID { self.active_form_index = SELECTED_FORM_ID @@ -385,7 +385,7 @@ impl<'a> SelectForm<'a> { } fn activate_form_by_index(&mut self, index: usize) { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); self.active_form_index = index; } @@ -503,10 +503,14 @@ impl<'a> SelectForm<'a> { } fn on_key_event(&mut self, ev: KeyEvent) -> EventResult { - self.mouse_over_widget_index = None; + self.clear_mouse_over(); self.active_form_mut().on_key_event(ev) } + + fn clear_mouse_over(&mut self) { + self.mouse_over_widget_index = None; + } } type RenderBlockInjection = Rc Block<'static>>; @@ -722,6 +726,10 @@ impl<'a> MultipleSelect<'a> { pub fn select_all(&mut self) { self.selected_widget.select_all(); } + + pub fn clear_mouse_over(&mut self) { + self.selected_widget.clear_mouse_over(); + } } impl WidgetTrait for MultipleSelect<'_> { diff --git a/src/ui/window.rs b/src/ui/window.rs index 385d75df..d4d0e08b 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -347,6 +347,10 @@ impl<'a> Window<'a> { pub fn activate_widget_by_id(&mut self, id: &str) { self.active_tab_mut().activate_widget_by_id(id) } + + pub fn clear_mouse_over(&mut self) { + self.mouse_over_tab_index = None; + } } // Render @@ -416,18 +420,36 @@ impl Window<'_> { UserEvent::Key(ev) => self.on_key_event(ev), UserEvent::Mouse(ev) => self.on_mouse_event(ev), UserEvent::FocusLost => { - self.mouse_over_tab_index = None; + self.clear_mouse_over(); + self.active_tab_mut().clear_mouse_over(); + + if let Some(id) = &self.open_popup_id { + if let Some(Widget::MultipleSelect(w)) = + self.popups.iter_mut().find(|w| w.id() == id) + { + w.clear_mouse_over(); + } + } EventResult::Nop } UserEvent::FocusGained => { - self.mouse_over_tab_index = None; + self.clear_mouse_over(); + self.active_tab_mut().clear_mouse_over(); + if let Some(id) = &self.open_popup_id { + if let Some(Widget::MultipleSelect(w)) = + self.popups.iter_mut().find(|w| w.id() == id) + { + w.clear_mouse_over(); + } + } + EventResult::Nop } } } pub fn on_key_event(&mut self, ev: KeyEvent) -> EventResult { - self.mouse_over_tab_index = None; + self.clear_mouse_over(); if let Some(id) = &self.open_popup_id { if let Some(popup) = self.popups.iter_mut().find(|w| w.id() == id) { @@ -490,12 +512,12 @@ impl Window<'_> { EventResult::Nop } AreaKind::Widgets => { - self.mouse_over_tab_index = None; + self.clear_mouse_over(); self.active_tab_mut().on_mouse_event(ev) } AreaKind::OutSide => { - self.mouse_over_tab_index = None; + self.clear_mouse_over(); EventResult::Ignore } From b8dda875a8c57a76f2c7cad07542d018c0546964 Mon Sep 17 00:00:00 2001 From: kosay Date: Sun, 25 Jun 2023 16:18:05 +0900 Subject: [PATCH 12/13] feat(ui): Activate the clicked widget and process the click event on the activated widget. --- src/ui/tab.rs | 5 ++-- src/ui/widget/complex/multiple_select.rs | 30 ++++++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/ui/tab.rs b/src/ui/tab.rs index 7437fff0..c80e4122 100644 --- a/src/ui/tab.rs +++ b/src/ui/tab.rs @@ -185,13 +185,12 @@ impl<'a> Tab<'a> { MouseEventKind::Down(MouseButton::Left) => { if id != active_widget_id { self.activate_widget_by_id(&id); - - return EventResult::Ignore; } } - _ => { + MouseEventKind::Moved => { self.mouse_over_widget_index = Some(index); } + _ => {} } self.active_widget_mut().on_mouse_event(ev) diff --git a/src/ui/widget/complex/multiple_select.rs b/src/ui/widget/complex/multiple_select.rs index e4b648c8..1b891310 100644 --- a/src/ui/widget/complex/multiple_select.rs +++ b/src/ui/widget/complex/multiple_select.rs @@ -476,24 +476,30 @@ impl<'a> SelectForm<'a> { let (chunks, _) = self.chunks_and_arrow(); if chunks[0].contains_point(pos) { - if let MouseEventKind::Down(MouseButton::Left) = ev.kind { - if self.active_form_index != LIST_FORM_ID { - self.activate_form_by_index(LIST_FORM_ID); - return EventResult::Ignore; + match ev.kind { + MouseEventKind::Down(MouseButton::Left) => { + if self.active_form_index != LIST_FORM_ID { + self.activate_form_by_index(LIST_FORM_ID); + } + } + MouseEventKind::Moved => { + self.mouse_over_widget_index = Some(LIST_FORM_ID); } - } else { - self.mouse_over_widget_index = Some(LIST_FORM_ID); + _ => {} } self.active_form_mut().on_mouse_event(ev) } else if chunks[2].contains_point(pos) { - if let MouseEventKind::Down(MouseButton::Left) = ev.kind { - if self.active_form_index != SELECTED_FORM_ID { - self.activate_form_by_index(SELECTED_FORM_ID); - return EventResult::Ignore; + match ev.kind { + MouseEventKind::Down(MouseButton::Left) => { + if self.active_form_index != SELECTED_FORM_ID { + self.activate_form_by_index(SELECTED_FORM_ID); + } + } + MouseEventKind::Moved => { + self.mouse_over_widget_index = Some(SELECTED_FORM_ID); } - } else { - self.mouse_over_widget_index = Some(SELECTED_FORM_ID); + _ => {} } self.active_form_mut().on_mouse_event(ev) From 5c69c10094e697747c6da2de2ad40e2692be4f37 Mon Sep 17 00:00:00 2001 From: kosay Date: Sun, 25 Jun 2023 16:47:30 +0900 Subject: [PATCH 13/13] feat(ui): Remove mouse hover highlight when key input events occur. --- src/ui/window.rs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/ui/window.rs b/src/ui/window.rs index d4d0e08b..4dca3e38 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -350,6 +350,14 @@ impl<'a> Window<'a> { pub fn clear_mouse_over(&mut self) { self.mouse_over_tab_index = None; + + self.active_tab_mut().clear_mouse_over(); + + if let Some(id) = &self.open_popup_id { + if let Some(Widget::MultipleSelect(w)) = self.popups.iter_mut().find(|w| w.id() == id) { + w.clear_mouse_over(); + } + } } } @@ -421,27 +429,10 @@ impl Window<'_> { UserEvent::Mouse(ev) => self.on_mouse_event(ev), UserEvent::FocusLost => { self.clear_mouse_over(); - self.active_tab_mut().clear_mouse_over(); - - if let Some(id) = &self.open_popup_id { - if let Some(Widget::MultipleSelect(w)) = - self.popups.iter_mut().find(|w| w.id() == id) - { - w.clear_mouse_over(); - } - } EventResult::Nop } UserEvent::FocusGained => { self.clear_mouse_over(); - self.active_tab_mut().clear_mouse_over(); - if let Some(id) = &self.open_popup_id { - if let Some(Widget::MultipleSelect(w)) = - self.popups.iter_mut().find(|w| w.id() == id) - { - w.clear_mouse_over(); - } - } EventResult::Nop }