use core::time::Duration; use actix::prelude::*; use gtk4::prelude::*; use send_wrapper::SendWrapper; const BALL_RADIUS: f64 = 20.0; struct WindowActor { area_size: [f64; 2], draw_area: gtk4::DrawingArea, ball: Ball, } impl actix::Actor for WindowActor { type Context = actix::Context; } impl actix::Handler for WindowActor { type Result = woab::SignalResult; fn handle(&mut self, msg: woab::Signal, _ctx: &mut Self::Context) -> Self::Result { Ok(match msg.name() { "resize" => { let woab::params! { _, width: i32, height: i32, } = msg.params()?; self.area_size = [width as f64, height as f64]; None } _ => msg.cant_handle()?, }) } } struct Draw(SendWrapper); impl actix::Message for Draw { type Result = (); } impl actix::Handler for WindowActor { type Result = (); fn handle(&mut self, msg: Draw, _ctx: &mut Self::Context) -> Self::Result { let draw_ctx = msg.0.take(); draw_ctx.arc( self.ball.position[0], self.ball.position[1], BALL_RADIUS, 0.0, 2.0 * std::f64::consts::PI, ); draw_ctx.set_source_rgb(0.5, 0.5, 0.5); draw_ctx.fill().unwrap(); } } struct Step(Duration); impl actix::Message for Step { type Result = (); } impl actix::Handler for WindowActor { type Result = (); fn handle(&mut self, msg: Step, _ctx: &mut Self::Context) -> Self::Result { if self.area_size[0] <= 0.0 || self.area_size[1] <= 0.0 { return; } let Step(step_length) = msg; let step_length = step_length.as_secs_f64(); let min_pos = [BALL_RADIUS, BALL_RADIUS]; let max_pos = [self.area_size[0] - BALL_RADIUS, self.area_size[1] - BALL_RADIUS]; self.ball.run_step(step_length, min_pos, max_pos); self.draw_area.queue_draw(); } } struct Ball { position: [f64; 2], velocity: [f64; 2], } impl Ball { fn run_step(&mut self, step_length: f64, min_pos: [f64; 2], max_pos: [f64; 2]) { for coord in 0..2 { let mut position = self.position[coord]; let velocity = self.velocity[coord]; position += velocity * step_length; if (0.0 < velocity && max_pos[coord] < position) || (velocity < 0.0 && position < min_pos[coord]) { self.velocity[coord] = -velocity; } else { self.position[coord] = position; } } } } fn main() -> woab::Result<()> { let factory = woab::BuilderFactory::from(std::fs::read_to_string("examples/example_canvas.ui")?); woab::main(Default::default(), move |app| { woab::shutdown_when_last_window_is_closed(app); WindowActor::create(|ctx| { let bld = factory.instantiate_route_to(ctx.address()); bld.set_application(app); bld.get_object::("win_app").unwrap().show(); actix::spawn({ let addr = ctx.address(); async move { use actix::clock::Instant; let mut last_step_time = Instant::now(); loop { let step_time = Instant::now(); addr.send(Step(step_time - last_step_time)).await.unwrap(); last_step_time = step_time; } } }); let draw_area: gtk4::DrawingArea = bld.get_object("draw_area").unwrap(); let addr = ctx.address(); draw_area.set_draw_func(move |_, draw_ctx, _, _| { woab::block_on(addr.send(Draw(SendWrapper::new(draw_ctx.clone())))).unwrap(); }); WindowActor { area_size: [0.0, 0.0], draw_area, ball: Ball { position: [BALL_RADIUS * 2.0, BALL_RADIUS * 2.0], velocity: [100.0, 100.0], }, } }); Ok(()) }) }