Loading...
Searching...
No Matches
SceneManager.h
Go to the documentation of this file.
1//
2// SceneManager.h
3//
4
5#ifndef SCENEMANAGER_H
6#define SCENEMANAGER_H
7
8#pragma once
9
10#include <Malena/Core/Core.h>
12#include <unordered_map>
13#include <functional>
14#include <vector>
15#include <memory>
16#include <type_traits>
17
18namespace ml
19{
107 template<typename StateEnum>
109 {
110 // ── Internal scene entry ────────────────────────────────────────────
111
113 struct SceneEntry
114 {
115 Core* scene;
116 std::function<Core*()> factory;
117 std::unique_ptr<Core> owned;
118
119 bool isLazy() const { return static_cast<bool>(factory); }
120 bool isActive() const { return scene != nullptr; }
121
123 Core* get() const { return scene; }
124 };
126
127 // ── Static storage ──────────────────────────────────────────────────
128
129 inline static std::unordered_map<StateEnum, SceneEntry, EnumClassHash> _bindings;
130 inline static std::vector<StateEnum> _history;
131 inline static StateEnum _current{};
132 inline static bool _started = false;
133 inline static bool _attached = false;
134
136 inline static std::function<void(Core&)> _add;
137
139 inline static std::function<void(Core&)> _remove;
140
142 inline static std::function<void(StateEnum)> _setter;
143
144 public:
145
146 // ── Bind ────────────────────────────────────────────────────────────
147
158 static void bind(StateEnum state, Core& scene)
159 {
160 _bindings[state] = SceneEntry{ &scene, nullptr, nullptr };
161 }
162
180 template<typename SceneType, typename... Args>
181 static void bindLazy(StateEnum state, Args&&... args)
182 {
183 static_assert(std::is_base_of_v<Core, SceneType>,
184 "[SceneManager] bindLazy: SceneType must derive from ml::Core.");
185
186 // Capture args by value so the factory is self-contained
187 _bindings[state] = SceneEntry
188 {
189 nullptr,
190 [capturedArgs = std::make_tuple(std::forward<Args>(args)...)]() mutable -> Core*
191 {
192 return std::apply([](auto&&... a) -> Core*
193 {
194 return new SceneType(std::forward<decltype(a)>(a)...);
195 }, capturedArgs);
196 },
197 nullptr
198 };
199 }
200
201 // ── Attach ──────────────────────────────────────────────────────────
202
222 template<typename App>
223 static void attach(App& app)
224 {
225 _attached = true;
226
227 // Store navigation primitives
228 _setter = [&app](StateEnum s) { app.setState(s); };
229 _add = [&app](Core& s) { app.addComponent(s); };
230
231 // removeComponent lives on CoreManager — call through app if available,
232 // otherwise fall back to CoreManager<Core> directly
233 _remove = [&app](Core& s)
234 {
235 if constexpr (requires { app.removeComponent(s); })
236 app.removeComponent(s);
237 else
239 };
240
241 // Hook into the app's state machine — SceneManager reacts to transitions
242 app.onStateExit([](StateEnum leaving)
243 {
244 SceneManager::deactivate(leaving);
245 });
246
247 app.onStateEnter([](StateEnum entering)
248 {
249 SceneManager::activate(entering);
250 });
251 }
252
253 // ── Start ───────────────────────────────────────────────────────────
254
264 static void start(StateEnum state)
265 {
266 _current = state;
267 _started = true;
268 activate(state); // show first scene — no history push
269 }
270
271 // ── Navigation ──────────────────────────────────────────────────────
272
283 static void back()
284 {
285 if (_history.empty()) return;
286
287 StateEnum previous = _history.back();
288 _history.pop_back();
289
290 // Call setState without re-pushing to history
291 // We set _current ahead of time so activate() knows not to push
292 _current = previous;
293
294 if (_setter)
295 _setter(previous);
296 }
297
298 // ── Query ───────────────────────────────────────────────────────────
299
304 static StateEnum current() { return _current; }
305
310 static bool isActive(StateEnum state) { return _started && _current == state; }
311
316 static bool has(StateEnum state) { return _bindings.count(state) > 0; }
317
325 static void clear()
326 {
327 _bindings.clear();
328 _history.clear();
329 _started = false;
330 _attached = false;
331 _add = nullptr;
332 _remove = nullptr;
333 _setter = nullptr;
334 }
335
336 private:
337
338 // ── Internal transition helpers ─────────────────────────────────────
339
349 static void activate(StateEnum state)
350 {
351 auto it = _bindings.find(state);
352 if (it == _bindings.end()) return;
353
354 SceneEntry& entry = it->second;
355
356 if (entry.isLazy())
357 {
358 // Construct on demand if not already alive
359 if (!entry.owned)
360 {
361 entry.owned.reset(entry.factory());
362 entry.scene = entry.owned.get();
363 }
364 }
365
366 if (entry.scene && _add)
367 _add(*entry.scene);
368 }
369
378 static void deactivate(StateEnum state)
379 {
380 auto it = _bindings.find(state);
381 if (it == _bindings.end()) return;
382
383 SceneEntry& entry = it->second;
384
385 if (entry.scene && _remove)
386 _remove(*entry.scene);
387
388 // Push to history so back() can return
389 _history.push_back(state);
390 _current = state; // will be overwritten by activate() shortly
391
392 if (entry.isLazy())
393 {
394 // Free the lazy instance — next visit will reconstruct
395 entry.scene = nullptr;
396 entry.owned.reset();
397 }
398 }
399 };
400
401} // namespace ml
402
403#endif // SCENEMANAGER_H
Virtual base class for all Malena framework objects.
Definition Core.h:67
static bool removeComponent(T &component)
Unregister a T object by reference.
State-driven scene router for Malena applications.
static void bind(StateEnum state, Core &scene)
Bind an existing scene instance to a state (eager).
static void attach(App &app)
Wire SceneManager into the application's state machine.
static void back()
Navigate to the previously visited state.
static void clear()
Clear all bindings and history.
static bool isActive(StateEnum state)
Return true if state is currently the active scene.
static StateEnum current()
Return the currently active state.
static void bindLazy(StateEnum state, Args &&... args)
Bind a lazily-constructed scene type to a state.
static void start(StateEnum state)
Activate the initial scene without recording a history entry.
static bool has(StateEnum state)
Return true if a scene has been bound to state.
Definition Component.h:18