From f5d1b42db432902ea8ded353e74f79ee3a61e503 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 2 Nov 2025 10:26:15 +0100 Subject: [PATCH] Velocity element --- examples/velocity.rs | 20 ++++ src/elements/mod.rs | 2 + src/elements/velocity_handler.rs | 164 +++++++++++++++++++++++++++++++ src/extensions/mod.rs | 2 - src/extensions/velocity.rs | 64 ------------ 5 files changed, 186 insertions(+), 66 deletions(-) create mode 100644 examples/velocity.rs create mode 100644 src/elements/velocity_handler.rs delete mode 100644 src/extensions/velocity.rs diff --git a/examples/velocity.rs b/examples/velocity.rs new file mode 100644 index 0000000..5cd8a5f --- /dev/null +++ b/examples/velocity.rs @@ -0,0 +1,20 @@ +use osui::prelude::*; + +fn main() -> std::io::Result<()> { + let screen = Screen::new(); + + app().draw(&screen); + + screen.run() +} + +pub fn app() -> Rsx { + rsx! { + @Style { foreground: None, background: Background::Outline(0xff0000) }; + @Transform::new().padding(1, 1); + VelocityHandler { + @Velocity(200, 100); + "test" + } + } +} diff --git a/src/elements/mod.rs b/src/elements/mod.rs index f408d11..4a580a1 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -4,6 +4,7 @@ pub mod heading; pub mod input; pub mod paginator; pub mod scroll; +pub mod velocity_handler; pub use div::*; pub use flex::*; @@ -11,6 +12,7 @@ pub use heading::*; pub use input::*; pub use paginator::*; pub use scroll::*; +pub use velocity_handler::*; use crate::Element; diff --git a/src/elements/velocity_handler.rs b/src/elements/velocity_handler.rs new file mode 100644 index 0000000..e70d953 --- /dev/null +++ b/src/elements/velocity_handler.rs @@ -0,0 +1,164 @@ +use std::sync::Arc; + +use crate::{ + component, + prelude::ElementRenderer, + style::RawTransform, + widget::{Element, Widget}, + NoRenderRoot, +}; + +pub struct VelocityRenderer<'a>(&'a mut RawTransform, &'a mut Vec<(i32, i32)>, usize, u16); + +pub struct VelocityHandler { + children: Vec>, + progress: Vec<(i32, i32)>, + render: (u16, u16), + tick: u16, +} + +impl VelocityHandler { + pub fn new() -> Self { + Self { + children: Vec::new(), + progress: Vec::new(), + render: (0, 0), + tick: 0, + } + } +} + +impl Element for VelocityHandler { + 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 + }; + + let mut scope = crate::render_scope::RenderScope::new(); + scope.set_parent_transform(transform.clone()); + + transform.width = 0; + transform.height = 0; + let mut renderer = VelocityRenderer(&mut transform, &mut self.progress, 0, self.tick); + for widget in &self.children { + scope.render_widget(&mut renderer, render_context.get_context(), widget); + } + + self.render = (transform.width, transform.height); + + if self.tick == 1000 { + self.tick = 0; + } else { + self.tick += 1; + } + } + + fn draw_child(&mut self, element: &Arc) { + self.progress.push((0, 0)); + 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); + self.progress.remove(i); + } + } + + 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 VelocityRenderer<'_> { + fn before_draw(&mut self, scope: &mut crate::prelude::RenderScope, widget: &Arc) { + let t = scope.get_transform_mut(); + + if let Some(Velocity(vx, vy)) = widget.get() { + if let Some((ax, ay)) = self.1.get_mut(self.2) { + add_i32_to_u16(&mut t.x, *ax); + add_i32_to_u16(&mut t.y, *ay); + + if apply_velocity(self.3, vx, &mut t.x) { + apply_value(ax, vx); + } + if apply_velocity(self.3, vy, &mut t.y) { + apply_value(ay, vy); + } + } else { + self.1.push((0, 0)); + } + } + + self.2 += 1; + + self.0.width = self.0.width.max(t.x + t.width + (t.px * 2)); + self.0.height = self.0.height.max(t.y + 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; + } +} + +fn apply_velocity(ticks: u16, velocity: i32, x: &mut u16) -> bool { + let abs_v = velocity.abs(); + if abs_v == 0 { + return false; + } + + let interval = (1000 / abs_v).max(1); // prevent divide-by-zero + if ticks as i32 % interval == 0 { + if velocity > 0 { + *x = x.saturating_add(1); + } else { + *x = x.saturating_sub(1); + } + return true; + } + false +} + +fn add_i32_to_u16(value: &mut u16, delta: i32) { + if delta >= 0 { + *value = value.saturating_add(delta as u16); + } else { + *value = value.saturating_sub((-delta) as u16); + } +} + +fn apply_value(x: &mut i32, velocity: i32) { + *x = if velocity > 0 { + x.saturating_add(1) + } else { + x.saturating_sub(1) + }; +} + +component!(Velocity(pub i32, pub i32)); diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 2e375cf..69a4f3f 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -2,13 +2,11 @@ pub mod focus; pub mod id; pub mod input_handling; pub mod tick; -pub mod velocity; pub use focus::*; pub use id::*; pub use input_handling::*; pub use tick::*; -pub use velocity::*; use std::{ any::{type_name, Any}, diff --git a/src/extensions/velocity.rs b/src/extensions/velocity.rs deleted file mode 100644 index 13fae15..0000000 --- a/src/extensions/velocity.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::sync::Arc; - -use crate::{ - component, - extensions::Extension, - style::{Position, Transform}, - widget::Widget, -}; - -pub struct VelocityExtension; - -component!(Velocity(pub i32, pub i32)); - -impl VelocityExtension { - fn apply_velocity(ticks: u16, velocity: i32, x: &mut u16) { - if velocity.abs() != 0 && ticks as i32 % (1000 / velocity.abs()) == 0 { - if velocity > 0 { - *x += 1; - } else if velocity < 0 { - *x -= 1; - } - } - } - - fn apply_velocity_xy(ticks: u16, widget: &Arc) { - if let Some(velocity) = widget.get::() { - if let Some(mut t) = widget.get::() { - match &mut t.x { - Position::Const(x) => Self::apply_velocity(ticks, velocity.0, x), - _ => {} - } - - match &mut t.y { - Position::Const(y) => Self::apply_velocity(ticks, velocity.1, y), - _ => {} - } - - widget.set_component(t); - } - } - } -} - -impl Extension for VelocityExtension { - fn init(&mut self, ctx: &super::Context) { - let ctx = ctx.clone(); - std::thread::spawn({ - move || { - let mut tick = 0; - loop { - for widget in ctx.get_widgets().iter() { - Self::apply_velocity_xy(tick, widget); - } - if tick > 1000 { - tick = 0; - } else { - tick += 1; - } - std::thread::sleep(std::time::Duration::from_millis(1)); - } - } - }); - } -}