1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! Tiny library to get you started learning RAUI as quick as possible 🚀
//!
//! # Example
//!
//! ```no_run
//! # use raui::prelude::*;
//! # fn my_widget(_: WidgetContext) -> WidgetNode { widget!(()) }
//! // Import the builder
//! use raui_quick_start::RauiQuickStartBuilder;
//!
//! // Create the builder
//! RauiQuickStartBuilder::default()
//!     // Set our window title
//!     .window_title("My RAUI App".into())
//!     // Set the RAUI widget tree for our app
//!     .widget_tree(widget! {
//!         (my_widget)
//!     })
//!     // Build the app
//!     .build()
//!     .expect("Error building quick start")
//!     // And run it! 🚀
//!     .run()
//!     .expect("Error running RAUI app");
//! ```

use derive_builder::Builder;
use raui::prelude::*;
use raui_tetra_renderer::prelude::*;
use tetra::{input::Key, Event};

pub use tetra;

pub type RauiQuickStartOnUpdate =
    Option<Box<dyn FnMut(&mut tetra::Context, &mut TetraSimpleHost) -> bool>>;
pub type RauiQuickStartOnEvent =
    Option<Box<dyn FnMut(&mut tetra::Context, &mut TetraSimpleHost, tetra::Event) -> bool>>;
pub type RauiQuickStartOnDraw =
    Option<Box<dyn FnMut(&mut tetra::Context, &mut TetraSimpleHost) -> bool>>;

/// The quick-start builder
#[derive(Builder)]
#[builder(default)]
pub struct RauiQuickStart {
    /// The title of the window
    window_title: String,
    /// The initial width and height of the window
    window_size: (i32, i32),
    /// The RAUI widget tree
    widget_tree: WidgetNode,
    /// Color of the window background.
    clear_color: Color,
    /// Tetra on update callback.
    #[builder(setter(skip))]
    on_update: RauiQuickStartOnUpdate,
    /// Tetra on event callback.
    #[builder(setter(skip))]
    on_event: RauiQuickStartOnEvent,
    /// Tetra on draw callback.
    #[builder(setter(skip))]
    on_draw: RauiQuickStartOnDraw,
    /// View-model collection.
    #[builder(setter(skip))]
    view_models: ViewModelCollection,
}

impl Default for RauiQuickStart {
    fn default() -> Self {
        Self {
            window_title: "RAUI Quick Start".into(),
            window_size: (800, 600),
            widget_tree: widget!(()),
            clear_color: Color::default(),
            on_update: None,
            on_event: None,
            on_draw: None,
            view_models: Default::default(),
        }
    }
}

impl RauiQuickStart {
    pub fn on_update<F>(mut self, f: F) -> Self
    where
        F: FnMut(&mut tetra::Context, &mut TetraSimpleHost) -> bool + 'static,
    {
        self.on_update = Some(Box::new(f));
        self
    }

    pub fn on_event<F>(mut self, f: F) -> Self
    where
        F: FnMut(&mut tetra::Context, &mut TetraSimpleHost, tetra::Event) -> bool + 'static,
    {
        self.on_event = Some(Box::new(f));
        self
    }

    pub fn on_draw<F>(mut self, f: F) -> Self
    where
        F: FnMut(&mut tetra::Context, &mut TetraSimpleHost) -> bool + 'static,
    {
        self.on_draw = Some(Box::new(f));
        self
    }

    pub fn view_model_raw(mut self, name: impl ToString, view_model: ViewModel) -> Self {
        self.view_models.insert(name.to_string(), view_model);
        self
    }

    pub fn view_model<T: 'static>(mut self, name: impl ToString, data: T) -> Self {
        self.view_models
            .insert(name.to_string(), ViewModel::new(data, Default::default()));
        self
    }

    pub fn view_model_produce<T: 'static>(
        mut self,
        name: impl ToString,
        producer: impl FnOnce(&mut ViewModelProperties) -> T,
    ) -> Self {
        self.view_models
            .insert(name.to_string(), ViewModel::produce(producer));
        self
    }

    /// Run the app!
    ///
    /// Displays the window and runs the RAUI UI.
    pub fn run_with(
        self,
        mut setup: impl FnMut(&mut Application),
    ) -> Result<(), tetra::TetraError> {
        // Create a new tetra context, setting the window title and size
        tetra::ContextBuilder::new(&self.window_title, self.window_size.0, self.window_size.1)
            // Configure the tetra window options
            .resizable(true)
            .key_repeat(true)
            .show_mouse(true)
            .build()?
            // And run the Tetra game. We pass it a closure that returns our App
            .run(|context| {
                let mut app = App::new(
                    context,
                    self.widget_tree,
                    tetra::graphics::Color::rgba(
                        self.clear_color.r,
                        self.clear_color.g,
                        self.clear_color.b,
                        self.clear_color.a,
                    ),
                    self.on_update,
                    self.on_event,
                    self.on_draw,
                    self.view_models,
                )?;
                setup(&mut app.ui.application);
                Ok(app)
            })?;

        Ok(())
    }

    pub fn run(self) -> Result<(), tetra::TetraError> {
        self.run_with(|_| {})
    }
}

/// Our App struct is responsible for handling the Tetra events and rendering
/// the GUI when necessary
struct App {
    /// The UI field is a `TetraSimpleHost` which comes from the
    /// `raui_tetra_renderer` crate and helps wrap some the of the setup
    /// necessary to use RAUI with Tetra.
    ui: TetraSimpleHost,
    /// Color of the window background.
    clear_color: tetra::graphics::Color,
    /// Tetra on update callback.
    on_update: RauiQuickStartOnUpdate,
    /// Tetra on event callback.
    on_event: RauiQuickStartOnEvent,
    /// Tetra on draw callback.
    on_draw: RauiQuickStartOnDraw,
}

impl App {
    fn new(
        context: &mut tetra::Context,
        tree: WidgetNode,
        clear_color: tetra::graphics::Color,
        on_update: RauiQuickStartOnUpdate,
        on_event: RauiQuickStartOnEvent,
        on_draw: RauiQuickStartOnDraw,
        view_models: ViewModelCollection,
    ) -> Result<Self, tetra::TetraError> {
        // Finally we need to provide a setup function that will initialize the
        // RAUI application created by the TetraSimpleHost. We will use the
        // default setup function provided by RAUI.
        let setup = raui::core::widget::setup;

        // Create a TetraSimpleHost out of all the pieces we assigned above
        let mut ui = TetraSimpleHost::new(context, tree, &[], &[], setup)?;
        ui.application.view_models = view_models;

        Ok(Self {
            ui,
            clear_color,
            on_update,
            on_event,
            on_draw,
        })
    }
}

/// Finally, we need to hook into the Tetra events and drive the RAUI UI. For
/// each of these functions we just forward the arguments to the our
/// `TetraSimpleHost` and let it paint the UI on the screen.
impl tetra::State for App {
    fn update(&mut self, ctx: &mut tetra::Context) -> Result<(), tetra::TetraError> {
        if let Some(callback) = &mut self.on_update {
            if callback(ctx, &mut self.ui) {
                self.ui.application.mark_dirty();
            }
        }
        self.ui.update(ctx);
        Ok(())
    }

    fn draw(&mut self, ctx: &mut tetra::Context) -> Result<(), tetra::TetraError> {
        // Clear the screen white first
        tetra::graphics::clear(ctx, self.clear_color);
        if let Some(callback) = &mut self.on_draw {
            if callback(ctx, &mut self.ui) {
                self.ui.application.mark_dirty();
            }
        }
        // Then draw the UI
        self.ui.draw(ctx, PrintLogger)?;
        Ok(())
    }

    fn event(
        &mut self,
        ctx: &mut tetra::Context,
        event: tetra::Event,
    ) -> Result<(), tetra::TetraError> {
        if let Some(callback) = &mut self.on_event {
            if callback(ctx, &mut self.ui, event.clone()) {
                self.ui.application.mark_dirty();
            }
        }

        self.ui.event(ctx, &event);
        if let Event::KeyPressed { key: Key::F2 } = event {
            println!("LAYOUT: {:#?}", self.ui.application.layout_data());
        }
        if let Event::KeyPressed { key: Key::F3 } = event {
            println!("INTERACTIONS: {:#?}", self.ui.interactions);
        }
        if let Event::KeyPressed { key: Key::F4 } = event {
            println!(
                "INSPECT TREE: {:#?}",
                self.ui.application.rendered_tree().inspect()
            );
        }
        Ok(())
    }
}