Skip to main content

hello_agpu/
hello_agpu.rs

1//! Hello agpu — minimal standalone application.
2//!
3//! Demonstrates the Elm-architecture Model/update/view loop with
4//! direct painter-based rendering (no widget library needed).
5//!
6//! Run with: `cargo run --example hello_agpu`
7
8use agpu::prelude::*;
9
10struct App {
11    count: i32,
12}
13
14#[derive(Debug)]
15enum Msg {
16    Increment,
17    Decrement,
18}
19
20impl Model for App {
21    type Msg = Msg;
22
23    fn update(&mut self, msg: Msg) -> Command<Msg> {
24        match msg {
25            Msg::Increment => self.count += 1,
26            Msg::Decrement => self.count -= 1,
27        }
28        Command::None
29    }
30
31    fn view(&self, frame: &mut Frame<'_>) {
32        let area = frame.area;
33        let btn_y = area.y + 50.0;
34        let dec_rect = Rect::new(area.x + 10.0, btn_y, 80.0, 36.0);
35        let inc_rect = Rect::new(area.x + 100.0, btn_y, 80.0, 36.0);
36
37        // Paint everything first
38        {
39            let style = TextStyle {
40                font_size: 24.0,
41                color: Color::WHITE,
42                ..Default::default()
43            };
44            let p = frame.painter();
45
46            // Background
47            p.fill_rect(area, Color::rgba(0.1, 0.1, 0.12, 1.0), 0.0);
48
49            // Counter label
50            p.text(
51                Position::new(area.x + 10.0, area.y + 8.0),
52                &format!("Count: {}", self.count),
53                &style,
54            );
55
56            // Decrement button
57            p.fill_rect(dec_rect, Color::rgba(0.8, 0.2, 0.2, 1.0), 4.0);
58            p.text(
59                Position::new(dec_rect.x + 30.0, dec_rect.y + 6.0),
60                "−",
61                &style,
62            );
63
64            // Increment button
65            p.fill_rect(inc_rect, Color::rgba(0.2, 0.6, 0.2, 1.0), 4.0);
66            p.text(
67                Position::new(inc_rect.x + 30.0, inc_rect.y + 6.0),
68                "+",
69                &style,
70            );
71        }
72
73        // Register hitboxes for event routing
74        frame.register_hitbox("decrement_btn", dec_rect, 0);
75        frame.register_hitbox("increment_btn", inc_rect, 0);
76    }
77
78    fn handle_event(&self, event: Event) -> Option<Msg> {
79        if let Event::Mouse(me) = &event {
80            if let agpu::MouseEventKind::Click(_) = me.kind {
81                // Hit-testing handled by the runtime; here we just
82                // demonstrate manual event mapping.
83                if me.position.y > 50.0 {
84                    return if me.position.x < 95.0 {
85                        Some(Msg::Decrement)
86                    } else {
87                        Some(Msg::Increment)
88                    };
89                }
90            }
91        }
92        None
93    }
94
95    fn title(&self) -> &str {
96        "agpu — Counter"
97    }
98}
99
100fn main() -> Result<(), Box<dyn std::error::Error>> {
101    AgpuApp::new(App { count: 0 })
102        .with_options(ProgramOptions {
103            width: 400.0,
104            height: 200.0,
105            ..Default::default()
106        })
107        .run()
108}