From a63e67b8d9f818804ff6ad777511cf00542fc116 Mon Sep 17 00:00:00 2001 From: tommy Date: Fri, 12 Jul 2019 19:20:38 -0400 Subject: [PATCH 1/4] Basic chat window history functionality --- voxygen/src/hud/chat.rs | 66 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index fcdcbb470a..4c376d4bf1 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -82,7 +82,9 @@ impl<'a> Chat<'a> { pub struct State { messages: VecDeque, input: String, - + history: VecDeque, + history_pos: usize, + history_max: usize, ids: Ids, } @@ -100,6 +102,9 @@ impl<'a> Widget for Chat<'a> { State { input: "".to_owned(), messages: VecDeque::new(), + history: VecDeque::new(), + history_pos: 0, + history_max: 32, ids: Ids::new(id_gen), } } @@ -266,8 +271,65 @@ impl<'a> Widget for Chat<'a> { }) { let msg = state.input.clone(); - state.update(|s| s.input.clear()); + state.update(|s| { + s.input.clear(); + // update the history + s.history.push_front(msg.clone()); + s.history_pos = 0; + while s.history.len() > s.history_max { + s.history.pop_back(); + } + }); Some(Event::SendMessage(msg)) + } + // If up is pressed, use history + else if ui + .widget_input(state.ids.input) + .presses() + .key() + .any(|key_press| match key_press.key { + Key::Up => true, + _ => false, + }) + { + state.update(|s| { + if !s.history.is_empty() { + if s.history_pos < s.history.len() { + s.history_pos += 1; + if let Some(string) = s.history.get(s.history_pos - 1) { + s.input.clear(); + s.input.push_str(string); + } + } + } + }); + None + } + // If down is pressed, use history + else if ui + .widget_input(state.ids.input) + .presses() + .key() + .any(|key_press| match key_press.key { + Key::Down => true, + _ => false, + }) + { + state.update(|s| { + if !s.history.is_empty() { + if s.history_pos > 1 { + s.history_pos -= 1; + s.input.clear(); + if let Some(string) = s.history.get(s.history_pos - 1) { + s.input.push_str(string); + } + } else { + s.history_pos = 0; + s.input.clear(); + } + } + }); + None } else { None } From 12029d7e7ffb50af9164b3bbed6b099f7ed6b9f7 Mon Sep 17 00:00:00 2001 From: tommy Date: Mon, 15 Jul 2019 13:04:48 -0400 Subject: [PATCH 2/4] Added SetText event, rewritten based on comments --- voxygen/src/hud/chat.rs | 108 ++++++++++++++++++++-------------------- voxygen/src/hud/mod.rs | 11 ++++ 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 4c376d4bf1..f9f9188c36 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -82,15 +82,19 @@ impl<'a> Chat<'a> { pub struct State { messages: VecDeque, input: String, + ids: Ids, history: VecDeque, + // Index into the history Vec, history_pos == 0 is history not in use + // otherwise index is history_pos -1 history_pos: usize, history_max: usize, - ids: Ids, + history_command: Option, } pub enum Event { SendMessage(String), Focus(Id), + SetText(String), } impl<'a> Widget for Chat<'a> { @@ -105,6 +109,7 @@ impl<'a> Widget for Chat<'a> { history: VecDeque::new(), history_pos: 0, history_max: 32, + history_command: None, ids: Ids::new(id_gen), } } @@ -256,80 +261,73 @@ impl<'a> Widget for Chat<'a> { } } + // Take a key from the input queue + let keys = ui.widget_input(state.ids.input).presses().key().take(1); + // If the chat widget is focused, return a focus event to pass the focus to the input box. if keyboard_capturer == Some(id) { Some(Event::Focus(state.ids.input)) } // If enter is pressed and the input box is not empty, send the current message. - else if ui - .widget_input(state.ids.input) - .presses() - .key() - .any(|key_press| match key_press.key { - Key::Return if !state.input.is_empty() => true, - _ => false, - }) - { + else if keys.clone().any(|key_press| match key_press.key { + Key::Return if !state.input.is_empty() => true, + _ => false, + }) { let msg = state.input.clone(); state.update(|s| { s.input.clear(); - // update the history + // Update the history s.history.push_front(msg.clone()); s.history_pos = 0; - while s.history.len() > s.history_max { - s.history.pop_back(); - } + s.history.truncate(s.history_max); }); Some(Event::SendMessage(msg)) } // If up is pressed, use history - else if ui - .widget_input(state.ids.input) - .presses() - .key() - .any(|key_press| match key_press.key { - Key::Up => true, - _ => false, - }) - { - state.update(|s| { - if !s.history.is_empty() { - if s.history_pos < s.history.len() { + else if keys.clone().any(|key_press| match key_press.key { + Key::Up => true, + _ => false, + }) { + if !state.history.is_empty() { + if state.history_pos < state.history.len() { + state.update(|s| { s.history_pos += 1; - if let Some(string) = s.history.get(s.history_pos - 1) { - s.input.clear(); - s.input.push_str(string); + s.history_command = match s.history.get(s.history_pos - 1) { + Some(string) => Some(string.to_string()), + None => None, } - } + }); } - }); - None + } + match &state.history_command { + Some(string) => Some(Event::SetText(string.to_string())), + None => None, + } } // If down is pressed, use history - else if ui - .widget_input(state.ids.input) - .presses() - .key() - .any(|key_press| match key_press.key { - Key::Down => true, - _ => false, - }) - { - state.update(|s| { - if !s.history.is_empty() { - if s.history_pos > 1 { - s.history_pos -= 1; - s.input.clear(); - if let Some(string) = s.history.get(s.history_pos - 1) { - s.input.push_str(string); - } - } else { - s.history_pos = 0; - s.input.clear(); + else if keys.clone().any(|key_press| match key_press.key { + Key::Down => true, + _ => false, + }) { + if state.history_pos > 1 { + state.update(|s| { + s.history_pos -= 1; + s.history_command = match s.history.get(s.history_pos - 1) { + Some(string) => Some(string.to_string()), + None => None, } - } - }); - None + }); + } else { + state.update(|s| { + s.history_command = None; + s.history_pos = 0; + s.input.clear(); + }); + } + match &state.history_command { + Some(string) => Some(Event::SetText(string.to_string())), + None => None, + } } else { None } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a3ea9f4177..1881df7abd 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -758,8 +758,19 @@ impl Hud { Some(chat::Event::Focus(focus_id)) => { self.to_focus = Some(Some(focus_id)); } + Some(chat::Event::SetText(text)) => { + let mut q = VecDeque::new(); + let mut chat = Chat::new(&mut q, &self.imgs, &self.fonts); + chat = chat.input(text.to_string()); + chat = chat.cursor_pos(Index { + line: 0, + char: text.len(), + }); + chat.set(self.ids.chat, ui_widgets); + } None => {} } + self.new_messages = VecDeque::new(); // Windows From 47d09045889bb742826377848b94d779bbbbbfb1 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 2 Sep 2019 00:54:44 -0400 Subject: [PATCH 3/4] Directly adjust chat input when traversing history --- voxygen/src/hud/chat.rs | 109 ++++++++++++++++++---------------------- voxygen/src/hud/mod.rs | 26 ++-------- 2 files changed, 53 insertions(+), 82 deletions(-) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index f9f9188c36..1aa82bab4a 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -36,6 +36,9 @@ pub struct Chat<'a> { #[conrod(common_builder)] common: widget::CommonBuilder, + + // TODO: add an option to adjust this + history_max: usize, } impl<'a> Chat<'a> { @@ -51,6 +54,7 @@ impl<'a> Chat<'a> { imgs, fonts, common: widget::CommonBuilder::default(), + history_max: 32, } } @@ -87,14 +91,11 @@ pub struct State { // Index into the history Vec, history_pos == 0 is history not in use // otherwise index is history_pos -1 history_pos: usize, - history_max: usize, - history_command: Option, } pub enum Event { SendMessage(String), Focus(Id), - SetText(String), } impl<'a> Widget for Chat<'a> { @@ -108,8 +109,6 @@ impl<'a> Widget for Chat<'a> { messages: VecDeque::new(), history: VecDeque::new(), history_pos: 0, - history_max: 32, - history_command: None, ids: Ids::new(id_gen), } } @@ -134,6 +133,39 @@ impl<'a> Widget for Chat<'a> { } }); + // If up or down are pressed move through history + // TODO: move cursor to the end of the last line + match ui.widget_input(state.ids.input).presses().key().fold( + (false, false), + |(up, down), key_press| match key_press.key { + Key::Up => (true, down), + Key::Down => (up, true), + _ => (up, down), + }, + ) { + (true, false) => { + if state.history_pos < state.history.len() { + state.update(|s| { + s.history_pos += 1; + s.input = s.history.get(s.history_pos - 1).unwrap().to_owned(); + }); + } + } + (false, true) => { + if state.history_pos > 0 { + state.update(|s| { + s.history_pos -= 1; + if s.history_pos > 0 { + s.input = s.history.get(s.history_pos - 1).unwrap().to_owned(); + } else { + s.input.clear(); + } + }); + } + } + _ => {} + } + let keyboard_capturer = ui.global_input().current.widget_capturing_keyboard; if let Some(input) = &self.force_input { @@ -146,8 +178,7 @@ impl<'a> Widget for Chat<'a> { // Only show if it has the keyboard captured. // Chat input uses a rectangle as its background. if input_focused { - let input = self.force_input.as_ref().unwrap_or(&state.input); - let mut text_edit = TextEdit::new(input) + let mut text_edit = TextEdit::new(&state.input) .w(460.0) .restrict_to_height(false) .color(TEXT_COLOR) @@ -261,73 +292,29 @@ impl<'a> Widget for Chat<'a> { } } - // Take a key from the input queue - let keys = ui.widget_input(state.ids.input).presses().key().take(1); - // If the chat widget is focused, return a focus event to pass the focus to the input box. if keyboard_capturer == Some(id) { Some(Event::Focus(state.ids.input)) } // If enter is pressed and the input box is not empty, send the current message. - else if keys.clone().any(|key_press| match key_press.key { - Key::Return if !state.input.is_empty() => true, - _ => false, - }) { + else if ui + .widget_input(state.ids.input) + .presses() + .key() + .any(|key_press| match key_press.key { + Key::Return if !state.input.is_empty() => true, + _ => false, + }) + { let msg = state.input.clone(); state.update(|s| { s.input.clear(); // Update the history s.history.push_front(msg.clone()); s.history_pos = 0; - s.history.truncate(s.history_max); + s.history.truncate(self.history_max); }); Some(Event::SendMessage(msg)) - } - // If up is pressed, use history - else if keys.clone().any(|key_press| match key_press.key { - Key::Up => true, - _ => false, - }) { - if !state.history.is_empty() { - if state.history_pos < state.history.len() { - state.update(|s| { - s.history_pos += 1; - s.history_command = match s.history.get(s.history_pos - 1) { - Some(string) => Some(string.to_string()), - None => None, - } - }); - } - } - match &state.history_command { - Some(string) => Some(Event::SetText(string.to_string())), - None => None, - } - } - // If down is pressed, use history - else if keys.clone().any(|key_press| match key_press.key { - Key::Down => true, - _ => false, - }) { - if state.history_pos > 1 { - state.update(|s| { - s.history_pos -= 1; - s.history_command = match s.history.get(s.history_pos - 1) { - Some(string) => Some(string.to_string()), - None => None, - } - }); - } else { - state.update(|s| { - s.history_command = None; - s.history_pos = 0; - s.input.clear(); - }); - } - match &state.history_command { - Some(string) => Some(Event::SetText(string.to_string())), - None => None, - } } else { None } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 1881df7abd..8ff3e1676e 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -741,33 +741,17 @@ impl Hud { } // Chat box - let mut chat = Chat::new(&mut self.new_messages, &self.imgs, &self.fonts); - - if let Some(input) = self.force_chat_input.take() { - chat = chat.input(input); - } - - if let Some(pos) = self.force_chat_cursor.take() { - chat = chat.cursor_pos(pos); - } - - match chat.set(self.ids.chat, ui_widgets) { + match Chat::new(&mut self.new_messages, &self.imgs, &self.fonts) + .and_then(self.force_chat_input.take(), |c, input| c.input(input)) + .and_then(self.force_chat_cursor.take(), |c, pos| c.cursor_pos(pos)) + .set(self.ids.chat, ui_widgets) + { Some(chat::Event::SendMessage(message)) => { events.push(Event::SendMessage(message)); } Some(chat::Event::Focus(focus_id)) => { self.to_focus = Some(Some(focus_id)); } - Some(chat::Event::SetText(text)) => { - let mut q = VecDeque::new(); - let mut chat = Chat::new(&mut q, &self.imgs, &self.fonts); - chat = chat.input(text.to_string()); - chat = chat.cursor_pos(Index { - line: 0, - char: text.len(), - }); - chat.set(self.ids.chat, ui_widgets); - } None => {} } From 625f83e53511ed609ad9d5639cb3732a3c556883 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 2 Sep 2019 01:07:16 -0400 Subject: [PATCH 4/4] Don't add adjacent duplicates to the history --- voxygen/src/hud/chat.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 1aa82bab4a..82de74380f 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -310,9 +310,12 @@ impl<'a> Widget for Chat<'a> { state.update(|s| { s.input.clear(); // Update the history - s.history.push_front(msg.clone()); + // Don't add if this is identical to the last message in the history s.history_pos = 0; - s.history.truncate(self.history_max); + if s.history.get(0).map_or(true, |h| h != &msg) { + s.history.push_front(msg.clone()); + s.history.truncate(self.history_max); + } }); Some(Event::SendMessage(msg)) } else {