Loading...
Searching...
No Matches
SceneManager.h
Go to the documentation of this file.
1// Copyright (c) 2025 Dave R. Smith. All rights reserved.
2// Malena Framework — Proprietary Software. See LICENSE for terms.
3
4//
5// SceneManager.h
6//
7
8#ifndef MALENA_SCENEMANAGER_H
9#define MALENA_SCENEMANAGER_H
10
11#pragma once
12
14#include <Malena/Core/Core.h>
16#include <unordered_map>
17#include <functional>
18#include <vector>
19#include <memory>
20#include <type_traits>
21
22namespace ml
23{
111 template<typename StateEnum>
113 {
114 // ── Internal scene entry ────────────────────────────────────────────
115
117 struct SceneEntry
118 {
119 Core* scene;
120 std::function<Core*()> factory;
121 std::unique_ptr<Core> owned;
122
123 bool isLazy() const { return static_cast<bool>(factory); }
124 bool isActive() const { return scene != nullptr; }
125
127 Core* get() const { return scene; }
128 };
130
131 // ── Static storage ──────────────────────────────────────────────────
132
133 inline static std::unordered_map<StateEnum, SceneEntry, EnumClassHash> _bindings;
134 inline static std::vector<StateEnum> _history;
135 inline static StateEnum _current{};
136 inline static bool _started = false;
137 inline static bool _attached = false;
138
140 inline static std::function<void(Core&)> _add;
141
143 inline static std::function<void(Core&)> _remove;
144
146 inline static std::function<void(StateEnum)> _setter;
147
148 public:
149
150 // ── Bind ────────────────────────────────────────────────────────────
151
162 static void bind(StateEnum state, Core& scene)
163 {
164 _bindings[state] = SceneEntry{ &scene, nullptr, nullptr };
165 }
166
184 template<typename SceneType, typename... Args>
185 static void bindLazy(StateEnum state, Args&&... args)
186 {
187 static_assert(std::is_base_of_v<Core, SceneType>,
188 "[SceneManager] bindLazy: SceneType must derive from ml::Core.");
189
190 // Capture args by value so the factory is self-contained
191 _bindings[state] = SceneEntry
192 {
193 nullptr,
194 [capturedArgs = std::make_tuple(std::forward<Args>(args)...)]() mutable -> Core*
195 {
196 return std::apply([](auto&&... a) -> Core*
197 {
198 return new SceneType(std::forward<decltype(a)>(a)...);
199 }, capturedArgs);
200 },
201 nullptr
202 };
203 }
204
205 // ── Attach ──────────────────────────────────────────────────────────
206
226 template<typename App>
227 static void attach(App& app)
228 {
229 _attached = true;
230
231 // Store navigation primitives
232 _setter = [&app](StateEnum s) { app.setState(s); };
233 _add = [&app](Core& s) { app.addComponent(s); };
234
235 // removeComponent lives on CoreManager — call through app if available,
236 // otherwise fall back to CoreManager<Core> directly
237 _remove = [&app](Core& s)
238 {
239 if constexpr (requires { app.removeComponent(s); })
240 app.removeComponent(s);
241 else
243 };
244
245 // Hook into the app's state machine — SceneManager reacts to transitions
246 app.onStateExit([](StateEnum leaving)
247 {
248 SceneManager::deactivate(leaving);
249 });
250
251 app.onStateEnter([](StateEnum entering)
252 {
253 SceneManager::activate(entering);
254 });
255 }
256
257 // ── Start ───────────────────────────────────────────────────────────
258
268 static void start(StateEnum state)
269 {
270 _current = state;
271 _started = true;
272 activate(state); // show first scene — no history push
273 }
274
275 // ── Navigation ──────────────────────────────────────────────────────
276
287 static void back()
288 {
289 if (_history.empty()) return;
290
291 StateEnum previous = _history.back();
292 _history.pop_back();
293
294 // Call setState without re-pushing to history
295 // We set _current ahead of time so activate() knows not to push
296 _current = previous;
297
298 if (_setter)
299 _setter(previous);
300 }
301
302 // ── Query ───────────────────────────────────────────────────────────
303
308 static StateEnum current() { return _current; }
309
314 static bool isActive(StateEnum state) { return _started && _current == state; }
315
320 static bool has(StateEnum state) { return _bindings.count(state) > 0; }
321
329 static void clear()
330 {
331 _bindings.clear();
332 _history.clear();
333 _started = false;
334 _attached = false;
335 _add = nullptr;
336 _remove = nullptr;
337 _setter = nullptr;
338 }
339
340 private:
341
342 // ── Internal transition helpers ─────────────────────────────────────
343
353 static void activate(StateEnum state)
354 {
355 auto it = _bindings.find(state);
356 if (it == _bindings.end()) return;
357
358 SceneEntry& entry = it->second;
359
360 if (entry.isLazy())
361 {
362 // Construct on demand if not already alive
363 if (!entry.owned)
364 {
365 entry.owned.reset(entry.factory());
366 entry.scene = entry.owned.get();
367 }
368 }
369
370 if (entry.scene && _add)
371 _add(*entry.scene);
372 }
373
382 static void deactivate(StateEnum state)
383 {
384 auto it = _bindings.find(state);
385 if (it == _bindings.end()) return;
386
387 SceneEntry& entry = it->second;
388
389 if (entry.scene && _remove)
390 _remove(*entry.scene);
391
392 // Push to history so back() can return
393 _history.push_back(state);
394 _current = state; // will be overwritten by activate() shortly
395
396 if (entry.isLazy())
397 {
398 // Free the lazy instance — next visit will reconstruct
399 entry.scene = nullptr;
400 entry.owned.reset();
401 }
402 }
403 };
404
405} // namespace ml
406
407#endif // MALENA_SCENEMANAGER_H
Virtual base class for all Malena framework objects.
Definition Core.h:69
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:22