euv_engine/scene/impl.rs
1use super::*;
2
3/// Implements static scene creation for `SceneManager`.
4impl SceneManager {
5 /// Creates a new `SceneRc` wrapping the given scene in a reference-counted cell.
6 ///
7 /// # Arguments
8 ///
9 /// - `T: Scene + 'static` - The concrete scene type.
10 ///
11 /// # Returns
12 ///
13 /// - `SceneRc` - The wrapped scene.
14 pub fn create_scene<T>(scene: T) -> SceneRc
15 where
16 T: Scene + 'static,
17 {
18 // `EngineCell<T: ?Sized>` accepts `dyn Scene` directly,
19 // so we do not need an intermediate `Box`.
20 Rc::new(EngineCell::new(scene))
21 }
22}
23
24/// Implements scene registration and lifecycle management for `SceneManager`.
25impl SceneManager {
26 /// Registers a scene under the given name.
27 ///
28 /// # Arguments
29 ///
30 /// - `String` - The name to register the scene under.
31 /// - `SceneRc` - The scene to register.
32 pub fn register(&mut self, name: String, scene: SceneRc) {
33 self.get_mut_scenes().insert(name, scene);
34 }
35
36 /// Unregisters and removes the scene with the given name.
37 ///
38 /// # Arguments
39 ///
40 /// - `N: AsRef<str>` - The name of the scene to remove.
41 pub fn unregister<N>(&mut self, name: N)
42 where
43 N: AsRef<str>,
44 {
45 self.get_mut_scenes().remove(name.as_ref());
46 }
47
48 /// Transitions to the scene with the given name.
49 ///
50 /// Calls `on_exit` on the current scene and `on_enter` on the new scene.
51 /// Returns `false` if no scene with the given name is registered.
52 ///
53 /// # Arguments
54 ///
55 /// - `N: AsRef<str>` - The name of the scene to switch to.
56 ///
57 /// # Returns
58 ///
59 /// - `bool` - True if the transition was successful.
60 pub fn switch_to<N>(&mut self, name: N) -> bool
61 where
62 N: AsRef<str>,
63 {
64 let name_ref: &str = name.as_ref();
65 if !self.get_scenes().contains_key(name_ref) {
66 return false;
67 }
68 let current_name: Option<String> = self.get_mut_current_scene_name().clone();
69 if let Some(name) = current_name.as_ref()
70 && let Some(current_scene) = self.get_scenes().get(name)
71 {
72 current_scene.get_mut().on_exit();
73 }
74 self.set_current_scene_name(Some(name_ref.to_string()));
75 if let Some(new_scene) = self.get_scenes().get(name_ref) {
76 new_scene.get_mut().on_enter();
77 }
78 true
79 }
80
81 /// Requests a deferred scene transition to be applied on the next update.
82 ///
83 /// # Arguments
84 ///
85 /// - `String` - The name of the scene to switch to.
86 pub fn request_transition(&mut self, name: String) {
87 self.set_pending_scene_name(Some(name));
88 }
89
90 /// Processes a pending scene transition if one was requested.
91 pub fn process_pending_transition(&mut self) {
92 let Some(name) = self.get_mut_pending_scene_name().take() else {
93 return;
94 };
95 self.switch_to(&name);
96 }
97
98 /// Calls `on_update` on the current scene.
99 ///
100 /// # Arguments
101 ///
102 /// - `f64` - The delta time in seconds.
103 pub fn update(&mut self, delta_time: f64) {
104 self.process_pending_transition();
105 // OPT 38: borrow the active scene name (Option<&String>) instead of
106 // cloning the whole String, then clone the `SceneRc` handle. The
107 // old form `get_mut_current_scene_name().clone()` paid for a full
108 // String heap allocation per frame on every scene, even though the
109 // name is only used as a HashMap key.
110 let Some(current_name) = self.try_get_current_scene_name().as_ref() else {
111 return;
112 };
113 let Some(scene) = self.get_scenes().get(current_name).cloned() else {
114 return;
115 };
116 scene.get_mut().on_update(delta_time);
117 }
118
119 /// Calls `on_render` on the current scene, recording into the shared draw list.
120 ///
121 /// The manager's reusable `DrawList` is cleared, filled by the scene, and
122 /// left populated for the caller to replay (e.g. via
123 /// `CanvasRenderer::replay`). Reusing the list avoids per-frame allocation.
124 ///
125 /// # Arguments
126 ///
127 /// - `&CanvasRenderingContext2d` - The canvas rendering context used to replay
128 /// the recorded commands.
129 pub fn render(&mut self, context: &CanvasRenderingContext2d) {
130 let Some(current_name) = self.try_get_current_scene_name().as_ref() else {
131 return;
132 };
133 // Clone the `Rc` so the immutable borrow of the scenes map ends before
134 // we mutably borrow the draw list.
135 let Some(scene) = self.get_scenes().get(current_name).cloned() else {
136 return;
137 };
138 self.get_mut_draw_list().clear();
139 {
140 let draw_list: &mut DrawList = self.get_mut_draw_list();
141 scene.get().on_render(draw_list);
142 }
143 CanvasRenderer::replay_context(context, self.get_draw_list());
144 }
145
146 /// Returns whether a scene with the given name is registered.
147 ///
148 /// # Arguments
149 ///
150 /// - `N: AsRef<str>` - The scene name to check.
151 ///
152 /// # Returns
153 ///
154 /// - `bool` - True if the scene is registered.
155 pub fn has_scene<N>(&self, name: N) -> bool
156 where
157 N: AsRef<str>,
158 {
159 self.get_scenes().contains_key(name.as_ref())
160 }
161
162 /// Returns the name of the currently active scene.
163 ///
164 /// # Returns
165 ///
166 /// - `Option<&str>` - The current scene name, or `None`.
167 pub fn current_name(&self) -> Option<&str> {
168 self.try_get_current_scene_name().as_deref()
169 }
170}
171
172/// Forwards `SceneManager::update` through the [`Updatable`] trait so that
173/// scene managers can be driven alongside entities, animators, and physics
174/// worlds in a single homogeneous update loop.
175///
176/// The inherent [`SceneManager::update`] method is the canonical implementation;
177/// this impl exists purely for trait dispatch. The inherent call resolves
178/// first when both are in scope, so there is no recursion.
179impl Updatable for SceneManager {
180 /// Advances the simulation by `delta_time` seconds.
181 ///
182 /// # Arguments
183 ///
184 /// - `f64` - Seconds elapsed since the previous update.
185 fn update(&mut self, delta_time: f64) {
186 SceneManager::update(self, delta_time);
187 }
188}
189
190/// Implements `Default` for `SceneManager` as a new empty manager.
191impl Default for SceneManager {
192 /// Constructs a default [`SceneManager`] value.
193 ///
194 /// # Returns
195 ///
196 /// - `SceneManager` - A default-constructed instance with the documented initial state.
197 fn default() -> SceneManager {
198 SceneManager::new()
199 }
200}