diff --git a/examples/iter.rs b/examples/iter.rs index 457278c..9661634 100644 --- a/examples/iter.rs +++ b/examples/iter.rs @@ -1,24 +1,27 @@ +use std::sync::Arc; + use osui::prelude::*; fn main() -> std::io::Result<()> { let screen = Screen::new(); + screen.extension(InputExtension); - app().draw(&screen); + app(screen.clone()).draw(&screen); screen.run() } -pub fn app() -> Rsx { +pub fn app(screen: Arc) -> Rsx { let items: State> = use_state(Vec::new()); let count = use_state(1); run! { ref count { loop { - if count.get_dl() == 5 { + if count.get_dl() == 20 { break; } - std::thread::sleep(std::time::Duration::from_millis(300)); + std::thread::sleep(std::time::Duration::from_millis(200)); **count.get() += 1; } } @@ -31,9 +34,22 @@ pub fn app() -> Rsx { } rsx! { - FlexRow { - for item in items { - static "{item}" + @AlwaysFocused; + @Handler::new({ + 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::Esc { + screen.close(); + } + } + } + }); + Scroll { + FlexRow { + for item in items { + static "{item}" + } } } } diff --git a/src/elements/div.rs b/src/elements/div.rs index 79824da..efd5478 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -84,5 +84,6 @@ impl ElementRenderer for DivRenderer<'_> { 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; + t.offset_y = self.0.offset_y; } } diff --git a/src/elements/flex.rs b/src/elements/flex.rs index d82f601..53b5665 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -170,6 +170,7 @@ impl ElementRenderer for RowRenderer<'_> { t.py += self.0.py; self.0.height += t.height + (t.py * 2) + self.1; + t.offset_y = self.0.offset_y; } } @@ -187,5 +188,6 @@ impl ElementRenderer for ColumnRenderer<'_> { t.py += self.0.py; self.0.width += t.width + (t.px * 2) + self.1; + t.offset_y = self.0.offset_y; } } diff --git a/src/elements/mod.rs b/src/elements/mod.rs index f7edd3e..f408d11 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -3,12 +3,14 @@ pub mod flex; pub mod heading; pub mod input; pub mod paginator; +pub mod scroll; pub use div::*; pub use flex::*; pub use heading::*; pub use input::*; pub use paginator::*; +pub use scroll::*; use crate::Element; diff --git a/src/elements/scroll.rs b/src/elements/scroll.rs new file mode 100644 index 0000000..910c0c5 --- /dev/null +++ b/src/elements/scroll.rs @@ -0,0 +1,120 @@ +use std::sync::Arc; + +use crossterm::event::Event; + +use crate::{ + prelude::ElementRenderer, + style::RawTransform, + widget::{Element, Widget}, + NoRenderRoot, +}; + +pub struct ScrollRenderer<'a>(&'a mut RawTransform); + +pub struct Scroll { + pub scroll_offset: u16, + children: Vec>, + render: (u16, u16), +} + +impl Scroll { + pub fn new() -> Self { + Self { + scroll_offset: 0, + children: Vec::new(), + render: (0, 0), + } + } +} + +impl Element for Scroll { + fn render( + &mut self, + scope: &mut crate::prelude::RenderScope, + _: &crate::render_scope::RenderContext, + ) { + scope.use_area(self.render.0, self.render.1); + } + + fn after_render( + &mut self, + parent_scope: &mut crate::render_scope::RenderScope, + render_context: &crate::render_scope::RenderContext, + ) { + let mut transform = { + let (width, height) = parent_scope.get_size_or_parent(); + let mut t = parent_scope.get_transform().clone(); + t.width = width; + t.height = height; + t.transform_parent(parent_scope.get_parent_transform()); + t + }; + + transform.offset_y = self.scroll_offset; + + let mut scope = crate::render_scope::RenderScope::new(); + scope.set_parent_transform(transform.clone()); + + transform.width = 0; + transform.height = 0; + let mut renderer = ScrollRenderer(&mut transform); + for widget in &self.children { + scope.render_widget(&mut renderer, render_context.get_context(), widget); + } + + self.render = (transform.width, transform.height); + } + + fn draw_child(&mut self, element: &Arc) { + self.children.push(element.clone()); + element.inject(|w| w.component(NoRenderRoot)); + } + + fn undraw_child(&mut self, element: &Arc) { + if let Some(i) = self.children.iter().position(|v| Arc::ptr_eq(v, element)) { + self.children.remove(i); + } + } + + fn event(&mut self, event: &dyn crate::prelude::Event) { + if let Some(e) = event.get::() { + match e { + Event::Key(k) => match k.code { + crossterm::event::KeyCode::Up => { + self.scroll_offset = self.scroll_offset.saturating_sub(1) + } + crossterm::event::KeyCode::Down => { + if self.scroll_offset + 1 < self.render.1 { + self.scroll_offset += 1; + } + } + _ => {} + }, + _ => {} + } + } + } + + fn is_ghost(&mut self) -> bool { + true + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} + +impl ElementRenderer for ScrollRenderer<'_> { + 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; + t.offset_y = self.0.offset_y; + } +} diff --git a/src/style.rs b/src/style.rs index 229ed39..a7abc1d 100644 --- a/src/style.rs +++ b/src/style.rs @@ -19,6 +19,7 @@ pub struct RawTransform { pub height: u16, pub px: u16, pub py: u16, + pub offset_y: u16, } /// Horizontal or vertical position relative to a parent container. @@ -169,6 +170,7 @@ impl RawTransform { height: 0, px: 0, py: 0, + offset_y: 0, } } diff --git a/src/utils.rs b/src/utils.rs index f3dd66f..da5e8f9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -111,10 +111,16 @@ pub fn hex_ansi_bg(hex: u32) -> String { pub(crate) fn print(x: u16, y: u16, text: &str, parent_transform: &RawTransform) { for (i, line) in text.lines().enumerate() { - if y + i as u16 >= parent_transform.height + parent_transform.y + parent_transform.py { + if parent_transform.offset_y > y + i as u16 { + continue; + } + + let y = y + i as u16 - parent_transform.offset_y; + + if y >= parent_transform.height + parent_transform.y + parent_transform.py { break; } - print!("\x1b[{};{}H{line}\x1b[0m", y + i as u16 + 1, x + 1); + print!("\x1b[{};{}H{line}\x1b[0m", y + 1, x + 1); flush().unwrap(); } } @@ -127,10 +133,16 @@ pub(crate) fn print_liner( parent_transform: &RawTransform, ) { for (i, line) in text.lines().enumerate() { + if parent_transform.offset_y > y + i as u16 { + continue; + } + + let y = y + i as u16 - parent_transform.offset_y; + if y + i as u16 >= parent_transform.height + parent_transform.y + parent_transform.py { break; } - print!("\x1b[{};{}H{liner}{line}\x1b[0m", y + i as u16 + 1, x + 1); + print!("\x1b[{};{}H{liner}{line}\x1b[0m", y + 1, x + 1); flush().unwrap(); } }