Velocity element

This commit is contained in:
2025-11-02 10:26:15 +01:00
parent 63ed7b8da4
commit f5d1b42db4
5 changed files with 186 additions and 66 deletions
+20
View File
@@ -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"
}
}
}
+2
View File
@@ -4,6 +4,7 @@ pub mod heading;
pub mod input; pub mod input;
pub mod paginator; pub mod paginator;
pub mod scroll; pub mod scroll;
pub mod velocity_handler;
pub use div::*; pub use div::*;
pub use flex::*; pub use flex::*;
@@ -11,6 +12,7 @@ pub use heading::*;
pub use input::*; pub use input::*;
pub use paginator::*; pub use paginator::*;
pub use scroll::*; pub use scroll::*;
pub use velocity_handler::*;
use crate::Element; use crate::Element;
+164
View File
@@ -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<Arc<Widget>>,
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<Widget>) {
self.progress.push((0, 0));
self.children.push(element.clone());
element.inject(|w| w.component(NoRenderRoot));
}
fn undraw_child(&mut self, element: &Arc<Widget>) {
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<Widget>) {
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));
-2
View File
@@ -2,13 +2,11 @@ pub mod focus;
pub mod id; pub mod id;
pub mod input_handling; pub mod input_handling;
pub mod tick; pub mod tick;
pub mod velocity;
pub use focus::*; pub use focus::*;
pub use id::*; pub use id::*;
pub use input_handling::*; pub use input_handling::*;
pub use tick::*; pub use tick::*;
pub use velocity::*;
use std::{ use std::{
any::{type_name, Any}, any::{type_name, Any},
-64
View File
@@ -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<Widget>) {
if let Some(velocity) = widget.get::<Velocity>() {
if let Some(mut t) = widget.get::<Transform>() {
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));
}
}
});
}
}