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/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..38e52cb 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 before_draw(&mut self, scope: &mut RenderScope, widget: &Arc) {} +} + /// Represents a single render instruction. #[derive(Clone)] enum RenderMethod { @@ -291,6 +300,49 @@ 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); + } + + renderer.before_draw(self, widget); + + self.draw(); + + widget.get_elem().after_render(self, ctx); + ctx.after_render(widget, self); + } + + widget.auto_refresh(); + + true + } } impl Debug for RenderScope {