From 3520369e35b08670df412c4e407385e9cb5e1196 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 9 Aug 2025 02:27:16 +0200 Subject: [PATCH] Added ghost elements to avoid focusing on them --- src/demos/mod.rs | 22 ++++++++++++++++------ src/elements/div.rs | 4 ++++ src/elements/flex.rs | 8 ++++++++ src/elements/heading.rs | 4 ++++ src/elements/mod.rs | 4 ++++ src/extensions/focus.rs | 11 ++++++++++- src/widget.rs | 33 +++++++++++++++++++++++++++++---- 7 files changed, 75 insertions(+), 11 deletions(-) diff --git a/src/demos/mod.rs b/src/demos/mod.rs index e40515f..c7864ec 100644 --- a/src/demos/mod.rs +++ b/src/demos/mod.rs @@ -51,13 +51,23 @@ pub fn app(screen: Arc) -> Rsx { %count "This will increment every second: {count}" - @Transform::new().padding(1, 1).dimensions(40, 1); - @Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xff0000) }; - Input { } + FlexRow + { + "Username" + @Transform::new().padding(1, 1).dimensions(40, 1); + @Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xff0000) }; + @Focused; + Input { } + } - @Transform::new().padding(1, 1).dimensions(40, 1); - @Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xffff00) }; - Input { } + @Transform::new().margin(0, 1); + FlexRow + { + "Password" + @Transform::new().padding(1, 1).dimensions(40, 1); + @Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xffff00) }; + Input { } + } } } } diff --git a/src/elements/div.rs b/src/elements/div.rs index 3752349..d5c5700 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -57,6 +57,10 @@ impl Element for Div { element.inject(|w| w.component(NoRenderRoot)); } + fn is_ghost(&mut self) -> bool { + true + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/src/elements/flex.rs b/src/elements/flex.rs index 32acaab..abdc17a 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -78,6 +78,10 @@ impl Element for FlexRow { element.inject(|w| w.component(NoRenderRoot)); } + fn is_ghost(&mut self) -> bool { + true + } + fn as_any(&self) -> &dyn std::any::Any { self } @@ -123,6 +127,10 @@ impl Element for FlexCol { element.inject(|w| w.component(NoRenderRoot)); } + fn is_ghost(&mut self) -> bool { + true + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/src/elements/heading.rs b/src/elements/heading.rs index 78ccb88..6970c77 100644 --- a/src/elements/heading.rs +++ b/src/elements/heading.rs @@ -57,6 +57,10 @@ impl Element for Heading { self.children.push(element.clone()); } + fn is_ghost(&mut self) -> bool { + true + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 6e1d045..f7edd3e 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -21,6 +21,10 @@ impl Element for String { scope.draw_text(0, 0, self); } + fn is_ghost(&mut self) -> bool { + true + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/src/extensions/focus.rs b/src/extensions/focus.rs index f766be9..ea3e66f 100644 --- a/src/extensions/focus.rs +++ b/src/extensions/focus.rs @@ -7,6 +7,7 @@ use super::Extension; use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyModifiers}; component!(AlwaysFocused); +component!(Focused); enum Direction { Left, @@ -21,6 +22,14 @@ pub struct RelativeFocusExtension { } impl Extension for RelativeFocusExtension { + fn init(&mut self, _ctx: &super::Context) { + for widget in _ctx.get_widgets().iter() { + if let Some(Focused) = widget.get() { + widget.set_focused(true); + } + } + } + fn event(&mut self, ctx: &super::Context, event: &dyn super::Event) { if let Some(e) = event.get::() { match e { @@ -145,7 +154,7 @@ impl Extension for RelativeFocusExtension { if let Some(p) = ctx .get_widgets() .iter() - .position(|w| Arc::ptr_eq(w, &widget)) + .position(|w| Arc::ptr_eq(w, &widget) && !w.is_ghost()) { rendered.lock().unwrap().push((p, t.x, t.y)); } diff --git a/src/widget.rs b/src/widget.rs index fa595fa..8800ce4 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -31,11 +31,21 @@ pub type BoxedComponent = Box; pub trait Element: Send + Sync { /// Called to perform rendering for the element. #[allow(unused)] - fn render(&mut self, scope: &mut RenderScope, render_context: &crate::render_scope::RenderContext) {} + fn render( + &mut self, + scope: &mut RenderScope, + render_context: &crate::render_scope::RenderContext, + ) { + } /// Called after rendering, for follow-up logic or cleanup. #[allow(unused)] - fn after_render(&mut self, scope: &mut RenderScope, render_context: &crate::render_scope::RenderContext) {} + fn after_render( + &mut self, + scope: &mut RenderScope, + render_context: &crate::render_scope::RenderContext, + ) { + } /// Called to draw child widgets, if any. #[allow(unused)] @@ -44,6 +54,10 @@ pub trait Element: Send + Sync { #[allow(unused)] fn event(&mut self, event: &dyn Event) {} + fn is_ghost(&mut self) -> bool { + false + } + /// Returns a type-erased reference to this object. fn as_any(&self) -> &dyn Any; @@ -138,10 +152,21 @@ impl Widget { } } + pub fn is_ghost(&self) -> bool { + match self { + Self::Dynamic(w) => w.element.lock().unwrap().is_ghost(), + Self::Static(w) => w.element.lock().unwrap().is_ghost(), + } + } + pub fn set_focused(&self, f: bool) { match self { - Self::Dynamic(w) => *w.focused.lock().unwrap() = f, - Self::Static(w) => *w.focused.lock().unwrap() = f, + Self::Dynamic(w) => { + *w.focused.lock().unwrap() = f && !w.element.lock().unwrap().is_ghost() + } + Self::Static(w) => { + *w.focused.lock().unwrap() = f && !w.element.lock().unwrap().is_ghost() + } } }