From a2fcdd8e9d487e0cff9471cb830701ffef50251b Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 4 Jul 2023 09:32:49 -0700 Subject: [PATCH 1/5] Add typing history --- src/widget/input.rs | 64 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/widget/input.rs b/src/widget/input.rs index b7b064a1..7eaac7a2 100644 --- a/src/widget/input.rs +++ b/src/widget/input.rs @@ -1,3 +1,5 @@ +use std::collections::VecDeque; + use data::{input, Buffer, Command}; pub use iced::widget::text_input::{focus, move_cursor_to_end}; use iced::widget::{component, container, row, text, text_input, Component}; @@ -39,6 +41,8 @@ pub enum Event { Input(String), Send, Tab, + Up, + Down, } pub struct Input<'a, Message> { @@ -53,6 +57,8 @@ pub struct State { input: String, error: Option, completion: Completion, + history: VecDeque, + selected_history: Option, } impl<'a, Message> Component for Input<'a, Message> @@ -65,7 +71,11 @@ where fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option { match event { Event::Input(input) => { + // Reset error state state.error = None; + // Reset selected history + state.selected_history = None; + state.input = input; state.completion.process(&state.input); @@ -75,6 +85,8 @@ where Event::Send => { // Reset error state state.error = None; + // Reset selected history + state.selected_history = None; if let Some(command) = state.completion.select() { state.input = command; @@ -91,8 +103,8 @@ where } }; - // Clear message, we parsed it succesfully - state.input = String::new(); + // Clear message and add it to history + state.history.push_front(std::mem::take(&mut state.input)); Some((self.on_submit)(input)) } else { @@ -103,6 +115,40 @@ where state.completion.tab(); None } + Event::Up => { + if !state.history.is_empty() { + if let Some(index) = state.selected_history.as_mut() { + *index = (*index + 1).min(state.history.len() - 1); + } else { + state.selected_history = Some(0); + } + + state.input = state + .history + .get(state.selected_history.unwrap()) + .unwrap() + .clone(); + + return Some(self.on_completion.clone()); + } + + None + } + Event::Down => { + if let Some(index) = state.selected_history.as_mut() { + if *index == 0 { + state.selected_history = None; + state.input.clear(); + } else { + *index -= 1; + state.input = state.history.get(*index).unwrap().clone(); + } + + return Some(self.on_completion.clone()); + } + + None + } } } @@ -121,6 +167,7 @@ where .style(style) .into(); + // Add tab support if selecting a completion let input = if state.completion.is_selecting() { key_press( text_input, @@ -132,6 +179,19 @@ where text_input }; + // Add up / down support for history cycling + let input = key_press( + key_press( + input, + key_press::KeyCode::Up, + key_press::Modifiers::default(), + Event::Up, + ), + key_press::KeyCode::Down, + key_press::Modifiers::default(), + Event::Down, + ); + let overlay = state .error .as_ref() From bc7d8579e4761071a3de2dde711c60535888971a Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 4 Jul 2023 10:34:06 -0700 Subject: [PATCH 2/5] Reset input state when replacing pane buffer --- src/buffer.rs | 9 +++++++++ src/buffer/channel.rs | 4 ++++ src/buffer/input_view.rs | 4 ++++ src/buffer/query.rs | 4 ++++ src/buffer/server.rs | 4 ++++ src/screen/dashboard.rs | 19 ++++++++++++++++++- src/widget/input.rs | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/buffer.rs b/src/buffer.rs index 158b0aab..e72ea2b6 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -146,6 +146,15 @@ impl Buffer { } } + pub fn reset(&self) -> Command { + match self { + Buffer::Empty => Command::none(), + Buffer::Channel(channel) => channel.reset().map(Message::Channel), + Buffer::Server(server) => server.reset().map(Message::Server), + Buffer::Query(query) => query.reset().map(Message::Query), + } + } + pub fn scroll_to_start(&mut self) -> Command { match self { Buffer::Empty => Command::none(), diff --git a/src/buffer/channel.rs b/src/buffer/channel.rs index 5a606a26..42cf7349 100644 --- a/src/buffer/channel.rs +++ b/src/buffer/channel.rs @@ -182,6 +182,10 @@ impl Channel { pub fn focus(&self) -> Command { self.input_view.focus().map(Message::InputView) } + + pub fn reset(&self) -> Command { + self.input_view.reset().map(Message::InputView) + } } mod nick_list { diff --git a/src/buffer/input_view.rs b/src/buffer/input_view.rs index 24a14716..ff9bc8fc 100644 --- a/src/buffer/input_view.rs +++ b/src/buffer/input_view.rs @@ -62,4 +62,8 @@ impl State { pub fn focus(&self) -> Command { input::focus(self.input_id.clone()) } + + pub fn reset(&self) -> Command { + input::reset(self.input_id.clone()) + } } diff --git a/src/buffer/query.rs b/src/buffer/query.rs index 94cbc0cd..9f818e74 100644 --- a/src/buffer/query.rs +++ b/src/buffer/query.rs @@ -148,4 +148,8 @@ impl Query { pub fn focus(&self) -> Command { self.input_view.focus().map(Message::InputView) } + + pub fn reset(&self) -> Command { + self.input_view.reset().map(Message::InputView) + } } diff --git a/src/buffer/server.rs b/src/buffer/server.rs index 666c7316..6c394650 100644 --- a/src/buffer/server.rs +++ b/src/buffer/server.rs @@ -104,4 +104,8 @@ impl Server { pub fn focus(&self) -> Command { self.input_view.focus().map(Message::InputView) } + + pub fn reset(&self) -> Command { + self.input_view.reset().map(Message::InputView) + } } diff --git a/src/screen/dashboard.rs b/src/screen/dashboard.rs index 51c967d9..6e0d16ba 100644 --- a/src/screen/dashboard.rs +++ b/src/screen/dashboard.rs @@ -178,7 +178,10 @@ impl Dashboard { if let Some(state) = self.panes.get_mut(&pane) { state.buffer = Buffer::from(kind); self.last_changed = Some(Instant::now()); - return self.focus_pane(pane); + return Command::batch(vec![ + self.reset_pane(pane), + self.focus_pane(pane), + ]); } } side_menu::Event::Close(pane) => { @@ -526,6 +529,20 @@ impl Dashboard { } } + fn reset_pane(&mut self, pane: pane_grid::Pane) -> Command { + self.panes + .iter() + .find_map(|(p, state)| { + (*p == pane).then(|| { + state + .buffer + .reset() + .map(move |message| Message::Pane(pane::Message::Buffer(pane, message))) + }) + }) + .unwrap_or(Command::none()) + } + pub fn track(&mut self) -> Command { let resources = self .panes diff --git a/src/widget/input.rs b/src/widget/input.rs index 7eaac7a2..f4cf6464 100644 --- a/src/widget/input.rs +++ b/src/widget/input.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use data::{input, Buffer, Command}; +use iced::advanced::widget::{self, Operation}; pub use iced::widget::text_input::{focus, move_cursor_to_end}; use iced::widget::{component, container, row, text, text_input, Component}; @@ -201,6 +202,10 @@ where anchored_overlay(input, overlay) } + + fn operate(&self, state: &mut State, operation: &mut dyn widget::Operation) { + operation.custom(state, Some(&self.id.clone().into())); + } } fn error<'a, Message: 'a>(error: impl ToString) -> Element<'a, Message> { @@ -219,3 +224,30 @@ where component(input) } } + +pub fn reset(id: impl Into) -> iced::Command { + struct Reset { + id: widget::Id, + } + + impl Operation for Reset { + fn container( + &mut self, + _id: Option<&widget::Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + + fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&widget::Id>) { + if Some(&self.id) == id { + dbg!("asdfasdf"); + if let Some(state) = state.downcast_mut::() { + *state = State::default(); + } + } + } + } + + iced::Command::widget(Reset { id: id.into() }) +} From d28b0a8d703057813b1c446a225fdc84d29cc506 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 4 Jul 2023 10:39:53 -0700 Subject: [PATCH 3/5] Reset completion state when cycling history --- src/widget/input.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/widget/input.rs b/src/widget/input.rs index f4cf6464..cd9c0557 100644 --- a/src/widget/input.rs +++ b/src/widget/input.rs @@ -117,6 +117,8 @@ where None } Event::Up => { + state.completion.reset(); + if !state.history.is_empty() { if let Some(index) = state.selected_history.as_mut() { *index = (*index + 1).min(state.history.len() - 1); @@ -129,6 +131,7 @@ where .get(state.selected_history.unwrap()) .unwrap() .clone(); + state.completion.process(&state.input); return Some(self.on_completion.clone()); } @@ -136,6 +139,8 @@ where None } Event::Down => { + state.completion.reset(); + if let Some(index) = state.selected_history.as_mut() { if *index == 0 { state.selected_history = None; @@ -143,6 +148,7 @@ where } else { *index -= 1; state.input = state.history.get(*index).unwrap().clone(); + state.completion.process(&state.input); } return Some(self.on_completion.clone()); From e96fdfd48d5e1196b1eb8a5856bf3e6aa495dea7 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 4 Jul 2023 10:58:09 -0700 Subject: [PATCH 4/5] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 736c17ea..be54b312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Added: - New configuration option `dashboard.sidebar_default_action` allows controlling the pane behaviour when selecting channels in the sidebar - Support for RAW command - Messages from other users containing your nickname are now highlighted using the `info` colour +- Previously sent messages can be accessed per buffer in the text input with up / down arrows Changed: From 64905a2778fb6c06baa17a32ed2a6461d7eed263 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Tue, 4 Jul 2023 13:52:18 -0700 Subject: [PATCH 5/5] Truncate to last 100 history entries --- src/widget/input.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widget/input.rs b/src/widget/input.rs index cd9c0557..7c607eb4 100644 --- a/src/widget/input.rs +++ b/src/widget/input.rs @@ -11,6 +11,8 @@ use crate::theme; mod completion; +pub const HISTORY_LENGTH: usize = 100; + pub type Id = text_input::Id; pub fn input<'a, Message>( @@ -106,6 +108,7 @@ where // Clear message and add it to history state.history.push_front(std::mem::take(&mut state.input)); + state.history.truncate(HISTORY_LENGTH); Some((self.on_submit)(input)) } else {