pixels_graphics_lib/
scenes.rs

1use crate::prelude::*;
2use crate::ui::styles::UiStyle;
3use rustc_hash::{FxHashMap, FxHashSet};
4use std::fmt::Debug;
5use winit::event::MouseButton;
6use winit::keyboard::KeyCode;
7use winit::window::Window;
8
9/// Convenience method for programs built using [Scene]s
10///
11/// If you're not using scenes consider [run]
12///
13/// # Arguments
14/// * `width` - Width of the whole window canvas in pixels
15/// * `height` - Height of the whole window canvas in pixels
16/// * `title` - Window title
17/// * `window_prefs` - Optionally program info, if passed the window position and size will be persisted
18/// * `scene_switcher` - [SceneSwitcher] Adds new scenes to the stack
19/// * `init_scene` - The initial [Scene] to use
20/// * `options` - [Options] controls how fast the program can update, [UiElement] styling, etc
21#[allow(clippy::too_many_arguments)]
22pub fn run_scenes<
23    SR: Clone + PartialEq + Debug + 'static,
24    SN: Clone + PartialEq + Debug + 'static,
25>(
26    width: usize,
27    height: usize,
28    title: &str,
29    window_prefs: Option<WindowPreferences>,
30    scene_switcher: SceneSwitcher<SR, SN>,
31    init_scene: Box<dyn Scene<SR, SN>>,
32    options: Options,
33    pre_post: Box<dyn PrePost<SR, SN>>,
34) -> Result<(), GraphicsError> {
35    let system = Box::new(SceneHost::new(
36        init_scene,
37        window_prefs,
38        scene_switcher,
39        options.style.clone(),
40        pre_post,
41    )?);
42    run(width, height, title, system, options)?;
43    Ok(())
44}
45
46/// Creates new scenes.
47///
48/// # Important
49/// This method must add the new scene to `scenes`
50///
51/// # Arguments
52/// * `style` - Style data for [UiElement]s, can be ignored if UI is custom
53/// * `scenes` - The current scene stack
54/// * `new_scene` - The name and data for a new scene
55pub type SceneSwitcher<SR, SN> =
56    fn(style: &UiStyle, scenes: &mut Vec<Box<dyn Scene<SR, SN>>>, new_scene: SN);
57
58/// When a scene wants to add or remove a scene from the stack it should return [Push][SceneUpdateResult::Push] or [Pop][SceneUpdateResult::Pop] from `Scene.update`
59#[derive(Debug, Clone, PartialEq)]
60pub enum SceneUpdateResult<SR: Clone + PartialEq + Debug, SN: Clone + PartialEq + Debug> {
61    /// Do nothing, the current scene remains the foreground scene
62    Nothing,
63    /// Open a child scene
64    /// # Arguments
65    /// * `0` `bool` - If true this scene will be closed as well as opening the new scene
66    /// * `1` `SN` - Data for [SceneSwitcher] so it can create the new scene
67    Push(bool, SN),
68    /// Close this scene, data may be included to be returned to the parent scene
69    Pop(Option<SR>),
70}
71
72/// Scenes represent a mode/feature of a programs UI
73/// For example in an image editor you could have the main menu, editor, and save dialog as scenes
74/// and in an RPG you could have the field, battle and menu screens as scenes
75///
76/// Scenes can be fullscreen or smaller, such as a dialog
77///
78/// # Common mistakes
79///
80/// * If you use a field to store the [SceneUpdateResult] and return in [update()][Scene::update]
81///   and then forget to clear it in [resuming][Scene::resuming] after a child returns then the child
82///   will immediately reopen
83pub trait Scene<SR: Clone + PartialEq + Debug, SN: Clone + PartialEq + Debug> {
84    fn id(&self) -> u32 {
85        0
86    }
87
88    /// Render scene contents using `graphics`
89    ///
90    /// If this is a fullscreen scene it should draw a color over the whole screen otherwise
91    /// you may see rendering issues (use `graphics.clear(Color)`).
92    /// # Note
93    /// mouse will be empty if this screen is in the background and a non full screen scene is active
94    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
95    #[allow(unused_variables)]
96    fn render(
97        &self,
98        graphics: &mut Graphics,
99        mouse: &MouseData,
100        held_keys: &FxHashSet<KeyCode>,
101        controller: &GameController,
102    );
103    /// Render scene contents using `graphics`
104    ///
105    /// If this is a fullscreen scene it should draw a color over the whole screen otherwise
106    /// you may see rendering issues (use `graphics.clear(Color)`).
107    /// # Note
108    /// mouse will be empty if this screen is in the background and a non full screen scene is active
109    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
110    #[allow(unused_variables)]
111    fn render(&self, graphics: &mut Graphics, mouse: &MouseData, held_keys: &FxHashSet<KeyCode>) {}
112    /// Called when a keyboard key is being pressed down
113    ///
114    /// # Arguments
115    /// * `key` - The latest pressed key
116    /// * `mouse` - position, held state of mouse
117    /// * `held_keys` - Any other keys that are being pressed down
118    #[allow(unused_variables)]
119    fn on_key_down(&mut self, key: KeyCode, mouse: &MouseData, held_keys: &FxHashSet<KeyCode>) {}
120    /// Called when a keyboard key has been released
121    ///
122    /// # Arguments
123    /// * `key` - The latest pressed key
124    /// * `mouse` - position, held state of mouse
125    /// * `held_keys` - Any other keys that are being pressed down
126    #[allow(unused_variables)]
127    fn on_key_up(&mut self, key: KeyCode, mouse: &MouseData, held_keys: &FxHashSet<KeyCode>) {}
128    /// Called when a mouse button has been pressed down
129    ///
130    /// # Arguments
131    /// * `mouse` - position, held state of mouse
132    /// * `mouse_button` = which button was pressed
133    /// * `held_keys` - Any keyboards keys that are being pressed down
134    #[allow(unused_variables)]
135    fn on_mouse_down(
136        &mut self,
137        mouse: &MouseData,
138        mouse_button: MouseButton,
139        held_keys: &FxHashSet<KeyCode>,
140    ) {
141    }
142    /// Called when a mouse button has been released
143    ///
144    /// [on_mouse_click] will also be called after
145    ///
146    /// # Arguments
147    /// * `mouse` - position, held state of mouse
148    /// * `mouse_button` = which button was released
149    /// * `held_keys` - Any keyboards keys that are being pressed down
150    #[allow(unused_variables)]
151    fn on_mouse_up(
152        &mut self,
153        mouse: &MouseData,
154        mouse_button: MouseButton,
155        held_keys: &FxHashSet<KeyCode>,
156    ) {
157    }
158    /// Called when a mouse button has been pressed and released
159    ///
160    /// [on_mouse_up] will also be called before
161    ///
162    /// # Arguments
163    /// * `down_at` - position where mouse button was clicked
164    /// * `mouse` - position, held state of mouse
165    /// * `mouse_button` = which button was clicked
166    /// * `held_keys` - Any keyboards keys that are being pressed down
167    #[allow(unused_variables)]
168    fn on_mouse_click(
169        &mut self,
170        down_at: Coord,
171        mouse: &MouseData,
172        mouse_button: MouseButton,
173        held_keys: &FxHashSet<KeyCode>,
174    ) {
175    }
176    /// Called when the mouse moved while any button is held down
177    ///
178    /// # Arguments
179    /// * `mouse` - position, held state of mouse
180    /// * `held_keys` - Any keyboards keys that are being pressed down
181    #[allow(unused_variables)]
182    fn on_mouse_drag(&mut self, mouse: &MouseData, held_keys: &FxHashSet<KeyCode>) {}
183    /// Called when the mouse scroll function has been used
184    ///
185    /// # Arguments
186    /// * `xy` - The on screen coord of the cursor
187    /// * `y_diff` - The distance scrolled vertically
188    /// * `x_diff` - The distance scrolled horizontally
189    /// * `held_keys` - Any keyboards keys that are being pressed down
190    #[allow(unused_variables)]
191    fn on_scroll(
192        &mut self,
193        mouse: &MouseData,
194        x_diff: isize,
195        y_diff: isize,
196        held_keys: &FxHashSet<KeyCode>,
197    ) {
198    }
199    /// During this method the scene should update animations and anything else that relies on time
200    /// or on held keys
201    ///
202    /// # Arguments
203    /// * `timing` - Deltas and other timing info, generally you should use the `fixed_time_step` field
204    /// * `xy` - The on screen coord of the mouse cursor
205    /// * `held_keys` - Any keyboards keys that are being pressed down
206    ///
207    /// # Returns
208    ///
209    /// [SceneUpdateResult]
210    /// * In normal function this is will be [Nothing][SceneUpdateResult::Nothing]
211    /// * To close this scene return [Pop][SceneUpdateResult::Pop]
212    /// * To open a child scene return [Push][SceneUpdateResult::Push]
213    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
214    fn update(
215        &mut self,
216        timing: &Timing,
217        mouse: &MouseData,
218        held_keys: &FxHashSet<KeyCode>,
219        controller: &GameController,
220        window: &Window,
221    ) -> SceneUpdateResult<SR, SN>;
222    /// During this method the scene should update animations and anything else that relies on time
223    /// or on held keys
224    ///
225    /// # Arguments
226    /// * `timing` - Deltas and other timing info, generally you should use the `fixed_time_step` field
227    /// * `xy` - The on screen coord of the mouse cursor
228    /// * `held_keys` - Any keyboards keys that are being pressed down
229    ///
230    /// # Returns
231    ///
232    /// [SceneUpdateResult]
233    /// * In normal function this is will be [Nothing][SceneUpdateResult::Nothing]
234    /// * To close this scene return [Pop][SceneUpdateResult::Pop]
235    /// * To open a child scene return [Push][SceneUpdateResult::Push]
236    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
237    fn update(
238        &mut self,
239        timing: &Timing,
240        mouse: &MouseData,
241        held_keys: &FxHashSet<KeyCode>,
242        window: &Window,
243    ) -> SceneUpdateResult<SR, SN>;
244    /// Called when a child scene is closing
245    ///
246    /// # Arguments
247    /// * `result` - Optional data from child scene
248    #[allow(unused_variables)]
249    fn resuming(&mut self, result: Option<SR>) {}
250    /// Return true if this scene doesn't fill the screen or is transparent
251    /// If this returns false the previous fullscreen scene will render as well
252    fn is_dialog(&self) -> bool {
253        false
254    }
255}
256
257pub trait PrePost<SR, SN> {
258    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
259    fn pre_render(
260        &mut self,
261        graphics: &mut Graphics,
262        mouse: &MouseData,
263        held_keys: &FxHashSet<KeyCode>,
264        scenes: &mut [Box<dyn Scene<SR, SN>>],
265    );
266    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
267    fn post_render(
268        &mut self,
269        graphics: &mut Graphics,
270        mouse: &MouseData,
271        held_keys: &FxHashSet<KeyCode>,
272        scenes: &mut [Box<dyn Scene<SR, SN>>],
273    );
274    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
275    fn pre_render(
276        &mut self,
277        graphics: &mut Graphics,
278        mouse: &MouseData,
279        held_keys: &FxHashSet<KeyCode>,
280        scenes: &mut [Box<dyn Scene<SR, SN>>],
281        controller: &GameController,
282    );
283    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
284    fn post_render(
285        &mut self,
286        graphics: &mut Graphics,
287        mouse: &MouseData,
288        held_keys: &FxHashSet<KeyCode>,
289        scenes: &mut [Box<dyn Scene<SR, SN>>],
290        controller: &GameController,
291    );
292    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
293    fn pre_update(
294        &mut self,
295        timing: &Timing,
296        mouse: &MouseData,
297        held_keys: &FxHashSet<KeyCode>,
298        scenes: &mut [Box<dyn Scene<SR, SN>>],
299        window: &Window,
300    );
301    #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
302    fn post_update(
303        &mut self,
304        timing: &Timing,
305        mouse: &MouseData,
306        held_keys: &FxHashSet<KeyCode>,
307        scenes: &mut [Box<dyn Scene<SR, SN>>],
308        window: &Window,
309    );
310    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
311    fn pre_update(
312        &mut self,
313        timing: &Timing,
314        mouse: &MouseData,
315        held_keys: &FxHashSet<KeyCode>,
316        scenes: &mut [Box<dyn Scene<SR, SN>>],
317        controller: &GameController,
318        window: &Window,
319    );
320    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
321    fn post_update(
322        &mut self,
323        timing: &Timing,
324        mouse: &MouseData,
325        held_keys: &FxHashSet<KeyCode>,
326        scenes: &mut [Box<dyn Scene<SR, SN>>],
327        controller: &GameController,
328        window: &Window,
329    );
330}
331#[cfg(any(feature = "controller", feature = "controller_xinput"))]
332pub fn empty_pre_post<SR, SN>() -> Box<dyn PrePost<SR, SN>> {
333    struct Empty {}
334    impl<SR, SN> PrePost<SR, SN> for Empty {
335        fn pre_render(
336            &mut self,
337            _: &mut Graphics,
338            _: &MouseData,
339            _: &FxHashSet<KeyCode>,
340            _: &mut [Box<dyn Scene<SR, SN>>],
341            _: &GameController,
342        ) {
343        }
344
345        fn post_render(
346            &mut self,
347            _: &mut Graphics,
348            _: &MouseData,
349            _: &FxHashSet<KeyCode>,
350            _: &mut [Box<dyn Scene<SR, SN>>],
351            _: &GameController,
352        ) {
353        }
354
355        fn pre_update(
356            &mut self,
357            _: &Timing,
358            _: &MouseData,
359            _: &FxHashSet<KeyCode>,
360            _: &mut [Box<dyn Scene<SR, SN>>],
361            _: &GameController,
362            _: &Window,
363        ) {
364        }
365
366        fn post_update(
367            &mut self,
368            _: &Timing,
369            _: &MouseData,
370            _: &FxHashSet<KeyCode>,
371            _: &mut [Box<dyn Scene<SR, SN>>],
372            _: &GameController,
373            _: &Window,
374        ) {
375        }
376    }
377    Box::new(Empty {})
378}
379#[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
380pub fn empty_pre_post<SR, SN>() -> Box<dyn PrePost<SR, SN>> {
381    struct Empty {}
382    impl<SR, SN> PrePost<SR, SN> for Empty {
383        fn pre_render(
384            &mut self,
385            _: &mut Graphics,
386            _: &MouseData,
387            _: &FxHashSet<KeyCode>,
388            _: &mut [Box<dyn Scene<SR, SN>>],
389        ) {
390        }
391
392        fn post_render(
393            &mut self,
394            _: &mut Graphics,
395            _: &MouseData,
396            _: &FxHashSet<KeyCode>,
397            _: &mut [Box<dyn Scene<SR, SN>>],
398        ) {
399        }
400
401        fn pre_update(
402            &mut self,
403            _: &Timing,
404            _: &MouseData,
405            _: &FxHashSet<KeyCode>,
406            _: &mut [Box<dyn Scene<SR, SN>>],
407            _: &Window,
408        ) {
409        }
410
411        fn post_update(
412            &mut self,
413            _: &Timing,
414            _: &MouseData,
415            _: &FxHashSet<KeyCode>,
416            _: &mut [Box<dyn Scene<SR, SN>>],
417            _: &Window,
418        ) {
419        }
420    }
421    Box::new(Empty {})
422}
423
424struct SceneHost<SR: Clone + PartialEq + Debug, SN: Clone + PartialEq + Debug> {
425    should_exit: bool,
426    held_keys: FxHashSet<KeyCode>,
427    scenes: Vec<Box<dyn Scene<SR, SN>>>,
428    window_prefs: Option<WindowPreferences>,
429    scene_switcher: SceneSwitcher<SR, SN>,
430    style: UiStyle,
431    #[cfg(any(feature = "controller", feature = "controller_xinput"))]
432    controller: GameController,
433    mouse: MouseData,
434    mouse_down_at: FxHashMap<MouseButton, Coord>,
435    pre_post: Box<dyn PrePost<SR, SN>>,
436}
437
438impl<SR: Clone + PartialEq + Debug, SN: Clone + PartialEq + Debug> SceneHost<SR, SN> {
439    pub fn new(
440        init_scene: Box<dyn Scene<SR, SN>>,
441        window_prefs: Option<WindowPreferences>,
442        scene_switcher: SceneSwitcher<SR, SN>,
443        style: UiStyle,
444        pre_post: Box<dyn PrePost<SR, SN>>,
445    ) -> Result<Self, GraphicsError> {
446        Ok(Self {
447            pre_post,
448            should_exit: false,
449            held_keys: FxHashSet::default(),
450            scenes: vec![init_scene],
451            window_prefs,
452            scene_switcher,
453            style,
454            mouse: MouseData::default(),
455            mouse_down_at: FxHashMap::default(),
456            #[cfg(any(feature = "controller", feature = "controller_xinput"))]
457            controller: GameController::new()
458                .map_err(|e| GraphicsError::ControllerInit(e.to_string()))?,
459        })
460    }
461}
462
463impl<SR: Clone + PartialEq + Debug, SN: Clone + PartialEq + Debug> System for SceneHost<SR, SN> {
464    fn window_prefs(&mut self) -> Option<WindowPreferences> {
465        self.window_prefs.clone()
466    }
467
468    fn update(&mut self, timing: &Timing, window: &Window) {
469        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
470        self.pre_post.pre_update(
471            timing,
472            &MouseData::default(),
473            &self.held_keys,
474            &mut self.scenes,
475            &self.controller,
476            window,
477        );
478        #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
479        self.pre_post.pre_update(
480            timing,
481            &MouseData::default(),
482            &self.held_keys,
483            &mut self.scenes,
484            window,
485        );
486        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
487        self.controller.update();
488        if let Some(scene) = self.scenes.last_mut() {
489            #[cfg(any(feature = "controller", feature = "controller_xinput"))]
490            let result = scene.update(
491                timing,
492                &self.mouse,
493                &self.held_keys,
494                &self.controller,
495                window,
496            );
497            #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
498            let result = scene.update(timing, &self.mouse, &self.held_keys, window);
499            match result {
500                SceneUpdateResult::Nothing => {}
501                SceneUpdateResult::Push(pop_current, name) => {
502                    if pop_current {
503                        self.scenes.remove(self.scenes.len() - 1);
504                    }
505                    (self.scene_switcher)(&self.style, &mut self.scenes, name);
506                }
507                SceneUpdateResult::Pop(result) => {
508                    self.scenes.remove(self.scenes.len() - 1);
509                    if let Some(previous) = self.scenes.last_mut() {
510                        previous.resuming(result);
511                    }
512                }
513            }
514        }
515        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
516        self.pre_post.post_update(
517            timing,
518            &MouseData::default(),
519            &self.held_keys,
520            &mut self.scenes,
521            &self.controller,
522            window,
523        );
524        #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
525        self.pre_post.post_update(
526            timing,
527            &MouseData::default(),
528            &self.held_keys,
529            &mut self.scenes,
530            window,
531        );
532        if self.scenes.is_empty() {
533            self.should_exit = true;
534        }
535    }
536
537    fn render(&mut self, graphics: &mut Graphics) {
538        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
539        self.pre_post.pre_render(
540            graphics,
541            &MouseData::default(),
542            &self.held_keys,
543            &mut self.scenes,
544            &self.controller,
545        );
546        #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
547        self.pre_post.pre_render(
548            graphics,
549            &MouseData::default(),
550            &self.held_keys,
551            &mut self.scenes,
552        );
553        if let Some(active) = self.scenes.last() {
554            if active.is_dialog() {
555                match self.scenes.iter().rposition(|scn| !scn.is_dialog()) {
556                    None => graphics.clear(BLACK),
557                    Some(i) => {
558                        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
559                        self.scenes[i].render(
560                            graphics,
561                            &MouseData::default(),
562                            &self.held_keys,
563                            &self.controller,
564                        );
565                        #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
566                        self.scenes[i].render(graphics, &MouseData::default(), &self.held_keys);
567                    }
568                }
569                #[cfg(any(feature = "controller", feature = "controller_xinput"))]
570                active.render(graphics, &self.mouse, &self.held_keys, &self.controller);
571                #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
572                active.render(graphics, &self.mouse, &self.held_keys);
573            } else {
574                #[cfg(any(feature = "controller", feature = "controller_xinput"))]
575                active.render(graphics, &self.mouse, &self.held_keys, &self.controller);
576                #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
577                active.render(graphics, &self.mouse, &self.held_keys);
578            }
579        }
580        #[cfg(any(feature = "controller", feature = "controller_xinput"))]
581        self.pre_post.post_render(
582            graphics,
583            &MouseData::default(),
584            &self.held_keys,
585            &mut self.scenes,
586            &self.controller,
587        );
588        #[cfg(not(any(feature = "controller", feature = "controller_xinput")))]
589        self.pre_post.post_render(
590            graphics,
591            &MouseData::default(),
592            &self.held_keys,
593            &mut self.scenes,
594        );
595    }
596
597    fn on_mouse_move(&mut self, mouse_data: &MouseData) {
598        self.mouse = mouse_data.clone();
599        if self.mouse.any_held() {
600            if let Some(active) = self.scenes.last_mut() {
601                active.on_mouse_drag(&self.mouse, &self.held_keys)
602            }
603        }
604    }
605
606    fn on_mouse_down(&mut self, mouse: &MouseData, button: MouseButton) {
607        self.mouse = mouse.clone();
608        self.mouse_down_at.insert(button, self.mouse.xy);
609        if let Some(active) = self.scenes.last_mut() {
610            active.on_mouse_down(&self.mouse, button, &self.held_keys);
611        }
612    }
613
614    fn on_mouse_up(&mut self, mouse: &MouseData, button: MouseButton) {
615        self.mouse = mouse.clone();
616        if let Some(active) = self.scenes.last_mut() {
617            active.on_mouse_up(&self.mouse, button, &self.held_keys);
618            if let Some(down) = self.mouse_down_at.get(&button) {
619                active.on_mouse_click(*down, &self.mouse, button, &self.held_keys);
620            }
621            self.mouse_down_at.remove(&button);
622        }
623    }
624
625    fn on_scroll(&mut self, mouse: &MouseData, x_diff: isize, y_diff: isize) {
626        self.mouse = mouse.clone();
627        if let Some(active) = self.scenes.last_mut() {
628            active.on_scroll(&self.mouse, x_diff, y_diff, &self.held_keys);
629        }
630    }
631
632    fn on_key_down(&mut self, keys: Vec<KeyCode>) {
633        for key in keys {
634            self.held_keys.insert(key);
635            if let Some(active) = self.scenes.last_mut() {
636                active.on_key_down(key, &self.mouse, &self.held_keys);
637            }
638        }
639    }
640
641    fn on_key_up(&mut self, keys: Vec<KeyCode>) {
642        for key in keys {
643            self.held_keys.remove(&key);
644            if let Some(active) = self.scenes.last_mut() {
645                active.on_key_up(key, &self.mouse, &self.held_keys);
646            }
647        }
648    }
649
650    fn should_exit(&mut self) -> bool {
651        self.should_exit
652    }
653}