From b363c81a24dcfd51aa63ca35a9be46291ae9b8fb Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 4 Aug 2025 12:16:23 -0500 Subject: [PATCH 1/7] Added a ElementRenderer trait for element renderers --- src/extensions/mod.rs | 6 +++++ src/lib.rs | 51 ++++++++++-------------------------------- src/render_scope.rs | 52 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 423962f..7624606 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -119,6 +119,12 @@ impl Context { components } + pub fn render_root(&self, scope: &mut RenderScope) { + for ext in self.screen.extensions.lock().unwrap().iter() { + ext.lock().unwrap().render(self, scope); + } + } + pub fn render(&self, w: &Arc, scope: &mut RenderScope) { for ext in self.screen.extensions.lock().unwrap().iter() { ext.lock().unwrap().render_widget(self, scope, w); diff --git a/src/lib.rs b/src/lib.rs index f3b4a82..3b7597c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,8 +32,8 @@ use std::sync::{Arc, Mutex}; use crate::{ - extensions::{Extension, Handler}, - prelude::Context, + extensions::Extension, + prelude::{Context, ElementRenderer}, render_scope::RenderScope, widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad}, }; @@ -180,47 +180,19 @@ impl Screen { /// This method is called internally by `run`. pub fn render(self: &Arc, ctx: &Context) -> std::io::Result<()> { let mut scope = RenderScope::new(); + let mut renderer = ScreenRenderer; let (w, h) = crossterm::terminal::size().unwrap(); scope.set_parent_size(w, h); - - for ext in self.extensions.lock().unwrap().iter() { - ext.lock().unwrap().render(ctx, &mut scope); - } + ctx.render_root(&mut scope); utils::clear()?; - for elem in self.widgets.lock().unwrap().iter() { - if elem.get::().is_some() || elem.get::().is_some() { - elem.auto_refresh(); + for widget in self.widgets.lock().unwrap().iter() { + if widget.get::().is_some() { + widget.auto_refresh(); continue; } - if let Some(wrapper) = elem.get::>() { - wrapper.call(elem, &RenderWrapperEvent(&mut scope)); - } else { - scope.clear(); - - if let Some(style) = elem.get() { - scope.set_style(style); - } - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - - for ext in self.extensions.lock().unwrap().iter() { - ext.lock().unwrap().render_widget(ctx, &mut scope, elem); - } - - elem.get_elem().render(&mut scope, ctx); - - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - scope.draw(); - - elem.get_elem().after_render(&mut scope, ctx); - } - - elem.auto_refresh(); + scope.render_widget(&mut renderer, ctx, widget); } Ok(()) } @@ -228,13 +200,14 @@ impl Screen { /// Closes the loop and calls `on_close` in the extensions pub fn close(self: &Arc) { *self.running.lock().unwrap() = false; - utils::show_cursor().unwrap(); - utils::clear().unwrap(); - for ext in self.extensions.lock().unwrap().iter() { ext.lock().unwrap().on_close(); } } } + +struct ScreenRenderer; + +impl ElementRenderer for ScreenRenderer {} diff --git a/src/render_scope.rs b/src/render_scope.rs index 7f8c6f5..107574f 100644 --- a/src/render_scope.rs +++ b/src/render_scope.rs @@ -8,13 +8,22 @@ //! //! Used internally by OSUI's layout and rendering system. -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; use crate::{ + prelude::{Context, Handler}, style::{RawTransform, Style, Transform}, utils::{self, hex_ansi_bg}, + widget::Widget, + NoRender, RenderWrapperEvent, }; +pub trait ElementRenderer { + /// Called right after the `after_render` function is called + #[allow(unused)] + fn on_after_render(&mut self, scope: &mut RenderScope, widget: &Arc) {} +} + /// Represents a single render instruction. #[derive(Clone)] enum RenderMethod { @@ -291,6 +300,47 @@ impl RenderScope { pub fn get_style(&mut self) -> &mut Style { &mut self.style } + + pub fn render_widget( + &mut self, + renderer: &mut dyn ElementRenderer, + ctx: &Context, + widget: &std::sync::Arc, + ) -> bool { + if widget.get::().is_some() { + widget.auto_refresh(); + return false; + } + + if let Some(wrapper) = widget.get::>() { + wrapper.call(widget, &RenderWrapperEvent(self)); + } else { + self.clear(); + + if let Some(style) = widget.get() { + self.set_style(style); + } + if let Some(t) = widget.get() { + self.set_transform(&t); + } + + widget.get_elem().render(self, ctx); + ctx.render(widget, self); + + if let Some(t) = widget.get() { + self.set_transform(&t); + } + self.draw(); + + widget.get_elem().after_render(self, ctx); + ctx.after_render(widget, self); + renderer.on_after_render(self, widget); + } + + widget.auto_refresh(); + + true + } } impl Debug for RenderScope { From e0059585ced2bf0463b0a32bc8c1bab7b56eabfc Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 4 Aug 2025 12:35:56 -0500 Subject: [PATCH 2/7] Implemented the ElementRenderer for widgets --- src/elements/div.rs | 52 ++++++++----------- src/elements/flex.rs | 105 ++++++++++++++++---------------------- src/elements/paginator.rs | 35 +++---------- src/render_scope.rs | 6 ++- 4 files changed, 76 insertions(+), 122 deletions(-) diff --git a/src/elements/div.rs b/src/elements/div.rs index f9a193b..b554ecb 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -1,10 +1,14 @@ use std::sync::Arc; use crate::{ + prelude::ElementRenderer, + style::RawTransform, widget::{Element, Widget}, - NoRender, NoRenderRoot, + NoRenderRoot, }; +pub struct DivRenderer<'a>(pub &'a mut RawTransform); + pub struct Div { children: Vec>, size: (u16, u16), @@ -35,39 +39,17 @@ impl Element for Div { ctx: &crate::extensions::Context, ) { let mut transform = scope.get_transform().clone(); + let mut renderer = DivRenderer(&mut transform); + let (w, h) = scope.get_parent_size(); - scope.set_parent_size(transform.width, transform.height); + scope.set_parent_size(renderer.0.width, renderer.0.height); - for elem in &self.children { - if elem.get::().is_some() { - continue; - } - - scope.clear(); - - if let Some(style) = elem.get() { - scope.set_style(style); - } - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - elem.get_elem().render(scope, ctx); - ctx.render(elem, scope); - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - let t = scope.get_transform_mut(); - transform.width = transform.width.max(t.x + t.width + (t.px * 2)); - transform.height = transform.height.max(t.y + t.height + (t.py * 2)); - t.x += transform.x + transform.px; - t.y += transform.y + transform.py; - - scope.draw(); - elem.get_elem().after_render(scope, ctx); - ctx.after_render(elem, scope); + for widget in &self.children { + scope.render_widget(&mut renderer, ctx, widget); } scope.set_parent_size(w, h); - self.size = (transform.width, transform.height); + + self.size = (renderer.0.width, renderer.0.height); } fn draw_child(&mut self, element: &Arc) { @@ -83,3 +65,13 @@ impl Element for Div { self } } + +impl ElementRenderer for DivRenderer<'_> { + fn before_draw(&mut self, scope: &mut crate::prelude::RenderScope, _widget: &Arc) { + let t = scope.get_transform_mut(); + self.0.width = self.0.width.max(t.width + (t.px * 2)); + self.0.height = self.0.height.max(t.height + (t.py * 2)); + t.x += self.0.x + self.0.px; + t.y += self.0.y + self.0.py; + } +} diff --git a/src/elements/flex.rs b/src/elements/flex.rs index 3325b4e..80b8a4c 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -1,10 +1,15 @@ use std::sync::Arc; use crate::{ + prelude::ElementRenderer, + style::RawTransform, widget::{Element, Widget}, - NoRender, NoRenderRoot, + NoRenderRoot, }; +pub struct RowRenderer<'a>(&'a mut RawTransform, u16, &'a mut u16); +pub struct ColumnRenderer<'a>(&'a mut RawTransform, u16, &'a mut u16); + pub struct FlexRow { pub gap: u16, children: Vec>, @@ -56,43 +61,16 @@ impl Element for FlexRow { let transform_before = transform.clone(); let (w, h) = scope.get_parent_size(); scope.set_parent_size(transform.width, transform.height); - let mut v = 0; - for elem in &self.children { - if elem.get::().is_some() { - continue; - } + let mut renderer = RowRenderer(&mut transform, self.gap, &mut v); - scope.clear(); - if let Some(style) = elem.get() { - scope.set_style(style); - } - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - elem.get_elem().render(scope, ctx); - ctx.render(elem, scope); - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - let t = scope.get_transform_mut(); - transform.width = transform.width.max(t.width); - transform.height = transform.height.max(v + t.height); - t.x += transform.x; - t.y += transform.y + v; - v += t.height + self.gap + (t.py * 2); - t.px += transform.px; - t.py += transform.py; - - scope.draw(); - - elem.get_elem().after_render(scope, ctx); - ctx.after_render(elem, scope); + for widget in &self.children { + scope.render_widget(&mut renderer, ctx, widget); } scope.set_parent_size(w, h); - self.size = (transform.width, transform.height); scope.set_transform_raw(transform_before); + self.size = (transform.width, transform.height); } fn draw_child(&mut self, element: &Arc) { @@ -128,39 +106,12 @@ impl Element for FlexCol { let transform_before = transform.clone(); let (w, h) = scope.get_parent_size(); scope.set_parent_size(transform.width, transform.height); - let mut v = 0; - for elem in &self.children { - if elem.get::().is_some() { - continue; - } + let mut renderer = ColumnRenderer(&mut transform, self.gap, &mut v); - scope.clear(); - if let Some(style) = elem.get() { - scope.set_style(style); - } - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - elem.get_elem().render(scope, ctx); - ctx.render(elem, scope); - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - let t = scope.get_transform_mut(); - transform.width = transform.width.max(v + t.width + (t.px * 2)); - transform.height = transform.height.max(t.height + (t.py * 2)); - t.x += transform.x + v; - t.y += transform.y; - v += t.width + self.gap + (t.px * 2); - t.px += transform.px; - t.py += transform.py; - - scope.draw(); - - elem.get_elem().after_render(scope, ctx); - ctx.after_render(elem, scope); + for widget in &self.children { + scope.render_widget(&mut renderer, ctx, widget); } scope.set_parent_size(w, h); scope.set_transform_raw(transform_before); @@ -180,3 +131,33 @@ impl Element for FlexCol { self } } + +impl ElementRenderer for RowRenderer<'_> { + fn before_draw(&mut self, scope: &mut crate::prelude::RenderScope, _widget: &Arc) { + let t = scope.get_transform_mut(); + self.0.width = self.0.width.max(*self.2 + t.width + (t.px * 2)); + self.0.height = self.0.height.max(t.height + (t.py * 2)); + + t.x += self.0.x; + t.y += self.0.y + *self.2; + *self.2 += t.height + self.1 + (t.py * 2); + + t.px += self.0.px; + t.py += self.0.py; + } +} + +impl ElementRenderer for ColumnRenderer<'_> { + fn before_draw(&mut self, scope: &mut crate::prelude::RenderScope, _widget: &Arc) { + let t = scope.get_transform_mut(); + self.0.width = self.0.width.max(*self.2 + t.width + (t.px * 2)); + self.0.height = self.0.height.max(t.height + (t.py * 2)); + + t.x += self.0.x + *self.2; + t.y += self.0.y; + *self.2 += t.width + self.1 + (t.px * 2); + + t.px += self.0.px; + t.py += self.0.py; + } +} diff --git a/src/elements/paginator.rs b/src/elements/paginator.rs index f9ef1d9..ae2890d 100644 --- a/src/elements/paginator.rs +++ b/src/elements/paginator.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use crossterm::event::{KeyCode, KeyEvent}; use crate::{ + prelude::DivRenderer, widget::{Element, Widget}, - NoRender, NoRenderRoot, + NoRenderRoot, }; pub struct Paginator { @@ -38,38 +39,16 @@ impl Element for Paginator { scope: &mut crate::render_scope::RenderScope, ctx: &crate::extensions::Context, ) { - if let Some(elem) = self.children.get(self.index) { - if elem.get::().is_some() { - return; - } - + if let Some(widget) = self.children.get(self.index) { let mut transform = scope.get_transform().clone(); + let mut renderer = DivRenderer(&mut transform); let (w, h) = scope.get_parent_size(); - scope.set_parent_size(transform.width, transform.height); - scope.clear(); + scope.set_parent_size(renderer.0.width, renderer.0.height); - if let Some(style) = elem.get() { - scope.set_style(style); - } - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - elem.get_elem().render(scope, ctx); - if let Some(t) = elem.get() { - scope.set_transform(&t); - } - let t = scope.get_transform_mut(); - transform.width = transform.width.max(t.x + t.width + (t.px * 2)); - transform.height = transform.height.max(t.y + t.height + (t.py * 2)); - t.x += transform.x + transform.px; - t.y += transform.y + transform.py; - - scope.draw(); - elem.get_elem().after_render(scope, ctx); - ctx.after_render(elem, scope); + scope.render_widget(&mut renderer, ctx, widget); scope.set_parent_size(w, h); - self.size = (transform.width, transform.height); + self.size = (renderer.0.width, renderer.0.height); } } diff --git a/src/render_scope.rs b/src/render_scope.rs index 107574f..38e52cb 100644 --- a/src/render_scope.rs +++ b/src/render_scope.rs @@ -21,7 +21,7 @@ use crate::{ pub trait ElementRenderer { /// Called right after the `after_render` function is called #[allow(unused)] - fn on_after_render(&mut self, scope: &mut RenderScope, widget: &Arc) {} + fn before_draw(&mut self, scope: &mut RenderScope, widget: &Arc) {} } /// Represents a single render instruction. @@ -330,11 +330,13 @@ impl RenderScope { if let Some(t) = widget.get() { self.set_transform(&t); } + + renderer.before_draw(self, widget); + self.draw(); widget.get_elem().after_render(self, ctx); ctx.after_render(widget, self); - renderer.on_after_render(self, widget); } widget.auto_refresh(); From d3861909e7568162161afe6353f6da6e960587c3 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 8 Aug 2025 23:47:21 +0200 Subject: [PATCH 3/7] added RenderContext --- src/elements/div.rs | 6 +++--- src/elements/flex.rs | 12 ++++++------ src/elements/heading.rs | 2 +- src/elements/input.rs | 2 +- src/elements/mod.rs | 2 +- src/elements/paginator.rs | 6 +++--- src/render_scope.rs | 32 +++++++++++++++++++++++++++++--- src/widget.rs | 4 ++-- 8 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/elements/div.rs b/src/elements/div.rs index b554ecb..13a341a 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -27,7 +27,7 @@ impl Element for Div { fn render( &mut self, scope: &mut crate::render_scope::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); scope.use_area(width, height); @@ -36,7 +36,7 @@ impl Element for Div { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::extensions::Context, + ctx: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let mut renderer = DivRenderer(&mut transform); @@ -45,7 +45,7 @@ impl Element for Div { scope.set_parent_size(renderer.0.width, renderer.0.height); for widget in &self.children { - scope.render_widget(&mut renderer, ctx, widget); + scope.render_widget(&mut renderer, ctx.get_context(), widget); } scope.set_parent_size(w, h); diff --git a/src/elements/flex.rs b/src/elements/flex.rs index 80b8a4c..6c74bb3 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -46,7 +46,7 @@ impl Element for FlexRow { fn render( &mut self, scope: &mut crate::render_scope::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); scope.use_area(width, height); @@ -55,7 +55,7 @@ impl Element for FlexRow { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::extensions::Context, + ctx: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let transform_before = transform.clone(); @@ -66,7 +66,7 @@ impl Element for FlexRow { let mut renderer = RowRenderer(&mut transform, self.gap, &mut v); for widget in &self.children { - scope.render_widget(&mut renderer, ctx, widget); + scope.render_widget(&mut renderer, ctx.get_context(), widget); } scope.set_parent_size(w, h); scope.set_transform_raw(transform_before); @@ -91,7 +91,7 @@ impl Element for FlexCol { fn render( &mut self, scope: &mut crate::render_scope::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); scope.use_area(width, height); @@ -100,7 +100,7 @@ impl Element for FlexCol { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::extensions::Context, + ctx: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let transform_before = transform.clone(); @@ -111,7 +111,7 @@ impl Element for FlexCol { let mut renderer = ColumnRenderer(&mut transform, self.gap, &mut v); for widget in &self.children { - scope.render_widget(&mut renderer, ctx, widget); + scope.render_widget(&mut renderer, ctx.get_context(), widget); } scope.set_parent_size(w, h); scope.set_transform_raw(transform_before); diff --git a/src/elements/heading.rs b/src/elements/heading.rs index d4dd1e8..78ccb88 100644 --- a/src/elements/heading.rs +++ b/src/elements/heading.rs @@ -27,7 +27,7 @@ impl Element for Heading { fn render( &mut self, scope: &mut crate::prelude::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let mut s = String::new(); for element in &self.children { diff --git a/src/elements/input.rs b/src/elements/input.rs index 5c5eb4d..768337c 100644 --- a/src/elements/input.rs +++ b/src/elements/input.rs @@ -14,7 +14,7 @@ impl Element for Input { fn render( &mut self, scope: &mut crate::prelude::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let s = self.state.get(); scope.draw_text(0, 0, &s); diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 58ee1fc..6e1d045 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -16,7 +16,7 @@ impl Element for String { fn render( &mut self, scope: &mut crate::render_scope::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { scope.draw_text(0, 0, self); } diff --git a/src/elements/paginator.rs b/src/elements/paginator.rs index ae2890d..aeab8d2 100644 --- a/src/elements/paginator.rs +++ b/src/elements/paginator.rs @@ -28,7 +28,7 @@ impl Element for Paginator { fn render( &mut self, scope: &mut crate::render_scope::RenderScope, - _: &crate::extensions::Context, + _: &crate::render_scope::RenderContext, ) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); scope.use_area(width, height); @@ -37,7 +37,7 @@ impl Element for Paginator { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::extensions::Context, + ctx: &crate::render_scope::RenderContext, ) { if let Some(widget) = self.children.get(self.index) { let mut transform = scope.get_transform().clone(); @@ -45,7 +45,7 @@ impl Element for Paginator { let (w, h) = scope.get_parent_size(); scope.set_parent_size(renderer.0.width, renderer.0.height); - scope.render_widget(&mut renderer, ctx, widget); + scope.render_widget(&mut renderer, ctx.get_context(), widget); scope.set_parent_size(w, h); self.size = (renderer.0.width, renderer.0.height); diff --git a/src/render_scope.rs b/src/render_scope.rs index 38e52cb..6e10914 100644 --- a/src/render_scope.rs +++ b/src/render_scope.rs @@ -50,6 +50,30 @@ pub struct RenderScope { style: Style, } +pub struct RenderContext(Context, bool); + +impl RenderContext { + pub fn new(c: &Context, focused: bool) -> Self { + Self(c.clone(), focused) + } + + pub fn is_focused(&self) -> bool { + self.1 + } + + pub fn render(&self, w: &Arc, scope: &mut RenderScope) { + self.0.render(w, scope); + } + + pub fn after_render(&self, w: &Arc, scope: &mut RenderScope) { + self.0.after_render(w, scope); + } + + pub fn get_context(&self) -> &Context { + &self.0 + } +} + impl RenderScope { /// Creates a new, empty `RenderScope`. pub fn new() -> RenderScope { @@ -304,7 +328,7 @@ impl RenderScope { pub fn render_widget( &mut self, renderer: &mut dyn ElementRenderer, - ctx: &Context, + ctx: &crate::extensions::Context, widget: &std::sync::Arc, ) -> bool { if widget.get::().is_some() { @@ -317,6 +341,8 @@ impl RenderScope { } else { self.clear(); + let render_context = RenderContext::new(ctx, widget.is_focused()); + if let Some(style) = widget.get() { self.set_style(style); } @@ -324,7 +350,7 @@ impl RenderScope { self.set_transform(&t); } - widget.get_elem().render(self, ctx); + widget.get_elem().render(self, &render_context); ctx.render(widget, self); if let Some(t) = widget.get() { @@ -335,7 +361,7 @@ impl RenderScope { self.draw(); - widget.get_elem().after_render(self, ctx); + widget.get_elem().after_render(self, &render_context); ctx.after_render(widget, self); } diff --git a/src/widget.rs b/src/widget.rs index b155ae3..cd53615 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -31,11 +31,11 @@ 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, ctx: &crate::extensions::Context) {} + fn render(&mut self, scope: &mut RenderScope, ctx: &crate::render_scope::RenderContext) {} /// Called after rendering, for follow-up logic or cleanup. #[allow(unused)] - fn after_render(&mut self, scope: &mut RenderScope, ctx: &crate::extensions::Context) {} + fn after_render(&mut self, scope: &mut RenderScope, ctx: &crate::render_scope::RenderContext) {} /// Called to draw child widgets, if any. #[allow(unused)] From c8ebe36d7862a7b37f881ac6edb9070ec2837547 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 8 Aug 2025 23:51:15 +0200 Subject: [PATCH 4/7] Added focus cursor in input --- src/elements/div.rs | 4 ++-- src/elements/flex.rs | 8 ++++---- src/elements/input.rs | 12 +++++++----- src/elements/paginator.rs | 4 ++-- src/widget.rs | 4 ++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/elements/div.rs b/src/elements/div.rs index 13a341a..3752349 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -36,7 +36,7 @@ impl Element for Div { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::render_scope::RenderContext, + render_context: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let mut renderer = DivRenderer(&mut transform); @@ -45,7 +45,7 @@ impl Element for Div { scope.set_parent_size(renderer.0.width, renderer.0.height); for widget in &self.children { - scope.render_widget(&mut renderer, ctx.get_context(), widget); + scope.render_widget(&mut renderer, render_context.get_context(), widget); } scope.set_parent_size(w, h); diff --git a/src/elements/flex.rs b/src/elements/flex.rs index 6c74bb3..32acaab 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -55,7 +55,7 @@ impl Element for FlexRow { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::render_scope::RenderContext, + render_context: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let transform_before = transform.clone(); @@ -66,7 +66,7 @@ impl Element for FlexRow { let mut renderer = RowRenderer(&mut transform, self.gap, &mut v); for widget in &self.children { - scope.render_widget(&mut renderer, ctx.get_context(), widget); + scope.render_widget(&mut renderer, render_context.get_context(), widget); } scope.set_parent_size(w, h); scope.set_transform_raw(transform_before); @@ -100,7 +100,7 @@ impl Element for FlexCol { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::render_scope::RenderContext, + render_context: &crate::render_scope::RenderContext, ) { let mut transform = scope.get_transform().clone(); let transform_before = transform.clone(); @@ -111,7 +111,7 @@ impl Element for FlexCol { let mut renderer = ColumnRenderer(&mut transform, self.gap, &mut v); for widget in &self.children { - scope.render_widget(&mut renderer, ctx.get_context(), widget); + scope.render_widget(&mut renderer, render_context.get_context(), widget); } scope.set_parent_size(w, h); scope.set_transform_raw(transform_before); diff --git a/src/elements/input.rs b/src/elements/input.rs index 768337c..fe6ace5 100644 --- a/src/elements/input.rs +++ b/src/elements/input.rs @@ -14,14 +14,16 @@ impl Element for Input { fn render( &mut self, scope: &mut crate::prelude::RenderScope, - _: &crate::render_scope::RenderContext, + render_context: &crate::render_scope::RenderContext, ) { let s = self.state.get(); scope.draw_text(0, 0, &s); - if let Some(c) = s.chars().nth(self.cursor) { - scope.draw_text_inverted(self.cursor as u16, 0, &c.to_string()); - } else { - scope.draw_text_inverted(s.len() as u16, 0, " "); + if render_context.is_focused() { + if let Some(c) = s.chars().nth(self.cursor) { + scope.draw_text_inverted(self.cursor as u16, 0, &c.to_string()); + } else { + scope.draw_text_inverted(s.len() as u16, 0, " "); + } } } diff --git a/src/elements/paginator.rs b/src/elements/paginator.rs index aeab8d2..0a3b4cf 100644 --- a/src/elements/paginator.rs +++ b/src/elements/paginator.rs @@ -37,7 +37,7 @@ impl Element for Paginator { fn after_render( &mut self, scope: &mut crate::render_scope::RenderScope, - ctx: &crate::render_scope::RenderContext, + render_context: &crate::render_scope::RenderContext, ) { if let Some(widget) = self.children.get(self.index) { let mut transform = scope.get_transform().clone(); @@ -45,7 +45,7 @@ impl Element for Paginator { let (w, h) = scope.get_parent_size(); scope.set_parent_size(renderer.0.width, renderer.0.height); - scope.render_widget(&mut renderer, ctx.get_context(), widget); + scope.render_widget(&mut renderer, render_context.get_context(), widget); scope.set_parent_size(w, h); self.size = (renderer.0.width, renderer.0.height); diff --git a/src/widget.rs b/src/widget.rs index cd53615..fa595fa 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -31,11 +31,11 @@ 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, ctx: &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, ctx: &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)] From 3520369e35b08670df412c4e407385e9cb5e1196 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 9 Aug 2025 02:27:16 +0200 Subject: [PATCH 5/7] 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() + } } } From 20a9d2b2160e0637370299b3e5900792b774a942 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 9 Aug 2025 02:44:17 +0200 Subject: [PATCH 6/7] Improved ghost with paginator --- src/elements/paginator.rs | 4 ++++ src/extensions/focus.rs | 2 +- src/widget.rs | 8 ++------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/elements/paginator.rs b/src/elements/paginator.rs index 0a3b4cf..c07d46c 100644 --- a/src/elements/paginator.rs +++ b/src/elements/paginator.rs @@ -75,6 +75,10 @@ impl Element for Paginator { } } + fn is_ghost(&mut self) -> bool { + true + } + fn draw_child(&mut self, element: &Arc) { self.children.push(element.clone()); element.inject(|w| w.component(NoRenderRoot)); diff --git a/src/extensions/focus.rs b/src/extensions/focus.rs index ea3e66f..22d8806 100644 --- a/src/extensions/focus.rs +++ b/src/extensions/focus.rs @@ -118,7 +118,7 @@ impl Extension for RelativeFocusExtension { for (i, w) in ctx.get_widgets().iter().enumerate() { if let Some(AlwaysFocused) = w.get() { w.set_focused(true); - } else { + } else if !w.is_ghost() { w.set_focused(i == self.cursor); } } diff --git a/src/widget.rs b/src/widget.rs index 8800ce4..ed8e802 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -161,12 +161,8 @@ impl Widget { pub fn set_focused(&self, f: bool) { match self { - 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() - } + Self::Dynamic(w) => *w.focused.lock().unwrap() = f, + Self::Static(w) => *w.focused.lock().unwrap() = f, } } From 935f18178e471999f7cea30e705cb8065c88b300 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 10 Aug 2025 13:45:45 -0500 Subject: [PATCH 7/7] changed demo exit key from q to escape --- src/demos/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demos/mod.rs b/src/demos/mod.rs index c7864ec..0cee7f5 100644 --- a/src/demos/mod.rs +++ b/src/demos/mod.rs @@ -18,7 +18,7 @@ pub fn app(screen: Arc) -> Rsx { let screen = screen.clone(); move |_, e: &crossterm::event::Event| { if let crossterm::event::Event::Key(crossterm::event::KeyEvent { code, .. }) = e { - if *code == crossterm::event::KeyCode::Char('q') { + if *code == crossterm::event::KeyCode::Esc { screen.close(); } }