From 70ef85ba1d1153fd897b9fea4f774e161bc62579 Mon Sep 17 00:00:00 2001 From: Louis Thevenet Date: Sun, 29 Sep 2024 09:39:53 +0200 Subject: [PATCH 1/4] feat: add a mean for components to access raw key events --- component/template/src/action.rs | 2 ++ component/template/src/app.rs | 4 ++++ component/template/src/components.rs | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/component/template/src/action.rs b/component/template/src/action.rs index 2830433..c8a3abb 100644 --- a/component/template/src/action.rs +++ b/component/template/src/action.rs @@ -1,3 +1,4 @@ +use crossterm::event::KeyEvent; use serde::{Deserialize, Serialize}; use strum::Display; @@ -8,6 +9,7 @@ pub enum Action { Resize(u16, u16), Suspend, Resume, + RawKeyEvent(KeyEvent), Quit, ClearScreen, Error(String), diff --git a/component/template/src/app.rs b/component/template/src/app.rs index 951ca7b..ffccf59 100644 --- a/component/template/src/app.rs +++ b/component/template/src/app.rs @@ -110,6 +110,10 @@ impl App { let Some(keymap) = self.config.keybindings.get(&self.mode) else { return Ok(()); }; + if self.components.iter().any(|c| c.is_editing()) { + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); + } match keymap.get(&vec![key]) { Some(action) => { info!("Got action: {action:?}"); diff --git a/component/template/src/components.rs b/component/template/src/components.rs index 84c12c9..8713922 100644 --- a/component/template/src/components.rs +++ b/component/template/src/components.rs @@ -55,6 +55,13 @@ pub trait Component { let _ = area; // to appease clippy Ok(()) } + /// Whether the app should send `Action::RawKeyEvent` or the corresponding `Action` variant. + /// + /// # Returns + /// * `bool` - Whether the component is waiting only for raw key events actions or not. + fn is_editing(&self) -> bool { + false + } /// Handle incoming events and produce actions if necessary. /// /// # Arguments From 04bf7109287623d5ac5f1887346b1832b978ea0a Mon Sep 17 00:00:00 2001 From: Louis Thevenet Date: Sun, 29 Sep 2024 09:54:03 +0200 Subject: [PATCH 2/4] chore: regenerate template --- component-generated/src/action.rs | 2 ++ component-generated/src/app.rs | 4 ++++ component-generated/src/components.rs | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/component-generated/src/action.rs b/component-generated/src/action.rs index 2830433..c8a3abb 100644 --- a/component-generated/src/action.rs +++ b/component-generated/src/action.rs @@ -1,3 +1,4 @@ +use crossterm::event::KeyEvent; use serde::{Deserialize, Serialize}; use strum::Display; @@ -8,6 +9,7 @@ pub enum Action { Resize(u16, u16), Suspend, Resume, + RawKeyEvent(KeyEvent), Quit, ClearScreen, Error(String), diff --git a/component-generated/src/app.rs b/component-generated/src/app.rs index 951ca7b..ffccf59 100644 --- a/component-generated/src/app.rs +++ b/component-generated/src/app.rs @@ -110,6 +110,10 @@ impl App { let Some(keymap) = self.config.keybindings.get(&self.mode) else { return Ok(()); }; + if self.components.iter().any(|c| c.is_editing()) { + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); + } match keymap.get(&vec![key]) { Some(action) => { info!("Got action: {action:?}"); diff --git a/component-generated/src/components.rs b/component-generated/src/components.rs index 84c12c9..8713922 100644 --- a/component-generated/src/components.rs +++ b/component-generated/src/components.rs @@ -55,6 +55,13 @@ pub trait Component { let _ = area; // to appease clippy Ok(()) } + /// Whether the app should send `Action::RawKeyEvent` or the corresponding `Action` variant. + /// + /// # Returns + /// * `bool` - Whether the component is waiting only for raw key events actions or not. + fn is_editing(&self) -> bool { + false + } /// Handle incoming events and produce actions if necessary. /// /// # Arguments From e74ea41e956751ef6fc50153ebf0da3a6ead8559 Mon Sep 17 00:00:00 2001 From: Louis Thevenet Date: Thu, 3 Oct 2024 13:01:23 +0200 Subject: [PATCH 3/4] feat: allow some actions to be passed while in editing mode --- component/template/src/app.rs | 45 +++++++++++++++++----------- component/template/src/components.rs | 7 +++++ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/component/template/src/app.rs b/component/template/src/app.rs index ffccf59..7c61bf5 100644 --- a/component/template/src/app.rs +++ b/component/template/src/app.rs @@ -110,25 +110,36 @@ impl App { let Some(keymap) = self.config.keybindings.get(&self.mode) else { return Ok(()); }; - if self.components.iter().any(|c| c.is_editing()) { - action_tx.send(Action::RawKeyEvent(key))?; - return Ok(()); - } - match keymap.get(&vec![key]) { - Some(action) => { - info!("Got action: {action:?}"); - action_tx.send(action.clone())?; + if let Some(action) = keymap.get(&vec![key]) { + info!("Got action: {action:?}"); + // Look for components in editing mode + // and that should receive a raw key event instead of the action. + for component in &self.components { + // Is it in editing mode and is the action not in the escape list ? + if component.is_editing() && !component.escape_editing_mode().contains(action) { + info!("Action was sent as raw key"); + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); + } + } + // Send the actual action to components + action_tx.send(action.clone())?; + } else { + // If there is a component in editing mode, send the raw key + if self.components.iter().any(|c| c.is_editing()) { + info!("Got raw key: {key:?}"); + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); } - _ => { - // If the key was not handled as a single key action, - // then consider it for multi-key combinations. - self.last_tick_key_events.push(key); - // Check for multi-key combinations - if let Some(action) = keymap.get(&self.last_tick_key_events) { - info!("Got action: {action:?}"); - action_tx.send(action.clone())?; - } + // If the key was not handled as a single key action, + // then consider it for multi-key combinations. + self.last_tick_key_events.push(key); + + // Check for multi-key combinations + if let Some(action) = keymap.get(&self.last_tick_key_events) { + info!("Got action: {action:?}"); + action_tx.send(action.clone())?; } } Ok(()) diff --git a/component/template/src/components.rs b/component/template/src/components.rs index 8713922..81ddd0b 100644 --- a/component/template/src/components.rs +++ b/component/template/src/components.rs @@ -62,6 +62,13 @@ pub trait Component { fn is_editing(&self) -> bool { false } + /// Returns zero or more `Action` that should never be sent as `Action::RawKeyEvent` to this component even if `Self::is_editing` returns `true`. + /// + /// # Returns + /// * `Vec` - A list of `Action` that should never be sent as `Action::RawKeyEvent`. + fn escape_editing_mode(&self) -> Vec { + vec![] + } /// Handle incoming events and produce actions if necessary. /// /// # Arguments From 249899d4459de149a8d154f9cf6d4c898b9761d7 Mon Sep 17 00:00:00 2001 From: Louis Thevenet Date: Thu, 3 Oct 2024 13:02:58 +0200 Subject: [PATCH 4/4] chore: regenerate template --- component-generated/src/app.rs | 45 +++++++++++++++++---------- component-generated/src/components.rs | 7 +++++ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/component-generated/src/app.rs b/component-generated/src/app.rs index ffccf59..7c61bf5 100644 --- a/component-generated/src/app.rs +++ b/component-generated/src/app.rs @@ -110,25 +110,36 @@ impl App { let Some(keymap) = self.config.keybindings.get(&self.mode) else { return Ok(()); }; - if self.components.iter().any(|c| c.is_editing()) { - action_tx.send(Action::RawKeyEvent(key))?; - return Ok(()); - } - match keymap.get(&vec![key]) { - Some(action) => { - info!("Got action: {action:?}"); - action_tx.send(action.clone())?; + if let Some(action) = keymap.get(&vec![key]) { + info!("Got action: {action:?}"); + // Look for components in editing mode + // and that should receive a raw key event instead of the action. + for component in &self.components { + // Is it in editing mode and is the action not in the escape list ? + if component.is_editing() && !component.escape_editing_mode().contains(action) { + info!("Action was sent as raw key"); + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); + } + } + // Send the actual action to components + action_tx.send(action.clone())?; + } else { + // If there is a component in editing mode, send the raw key + if self.components.iter().any(|c| c.is_editing()) { + info!("Got raw key: {key:?}"); + action_tx.send(Action::RawKeyEvent(key))?; + return Ok(()); } - _ => { - // If the key was not handled as a single key action, - // then consider it for multi-key combinations. - self.last_tick_key_events.push(key); - // Check for multi-key combinations - if let Some(action) = keymap.get(&self.last_tick_key_events) { - info!("Got action: {action:?}"); - action_tx.send(action.clone())?; - } + // If the key was not handled as a single key action, + // then consider it for multi-key combinations. + self.last_tick_key_events.push(key); + + // Check for multi-key combinations + if let Some(action) = keymap.get(&self.last_tick_key_events) { + info!("Got action: {action:?}"); + action_tx.send(action.clone())?; } } Ok(()) diff --git a/component-generated/src/components.rs b/component-generated/src/components.rs index 8713922..81ddd0b 100644 --- a/component-generated/src/components.rs +++ b/component-generated/src/components.rs @@ -62,6 +62,13 @@ pub trait Component { fn is_editing(&self) -> bool { false } + /// Returns zero or more `Action` that should never be sent as `Action::RawKeyEvent` to this component even if `Self::is_editing` returns `true`. + /// + /// # Returns + /// * `Vec` - A list of `Action` that should never be sent as `Action::RawKeyEvent`. + fn escape_editing_mode(&self) -> Vec { + vec![] + } /// Handle incoming events and produce actions if necessary. /// /// # Arguments