Line data Source code
1 : #pragma once
2 :
3 : #include <functional>
4 :
5 : namespace Amplitron {
6 :
7 : /**
8 : * @brief Sentinel type for components that have no local render state.
9 : *
10 : * Used as the default State parameter in UIComponent so that components
11 : * without internal state don't carry a useless `int state_` member.
12 : */
13 : struct EmptyState {};
14 :
15 : /**
16 : * @brief Base template class for all reactive UI components.
17 : *
18 : * Enforces a React-like component structure:
19 : * - Props: Immutable configuration/data passed from the parent GuiManager
20 : * every frame before render() is called.
21 : * - State: Local, mutable state owned by the component (e.g., animation
22 : * timers, transient open/close flags). Defaults to EmptyState for
23 : * components that need no persistent local state.
24 : *
25 : * Contract:
26 : * 1. Parent calls set_props(p) to push new data into the component.
27 : * 2. Parent calls render() — the component only reads props_ and state_.
28 : * 3. Mutations are communicated back through callbacks stored in Props,
29 : * NOT by letting the component reach into the engine directly.
30 : */
31 : template <typename Props, typename State = EmptyState>
32 29 : class UIComponent {
33 : public:
34 103 : virtual ~UIComponent() = default;
35 :
36 : /** @brief Push new props (inputs) into the component before render(). */
37 30 : virtual void set_props(const Props& new_props) { props_ = new_props; }
38 :
39 : /**
40 : * @brief Render the component using current props_ and state_.
41 : * Must be implemented by every concrete component.
42 : */
43 : virtual void render() = 0;
44 :
45 : /** @brief Read the current local state. */
46 : const State& get_state() const { return state_; }
47 :
48 : /**
49 : * @brief React-style setState: replace local state and trigger the
50 : * optional on_state_change() hook.
51 : */
52 6 : void set_state(const State& new_state) {
53 6 : state_ = new_state;
54 6 : on_state_change();
55 6 : }
56 :
57 : /**
58 : * @brief Functional setState: apply an updater lambda to a copy of the
59 : * current state, then commit and notify.
60 : */
61 6 : void set_state(std::function<void(State&)> updater) {
62 6 : State next = state_;
63 6 : updater(next);
64 6 : set_state(next);
65 6 : }
66 :
67 : protected:
68 : Props props_;
69 : State state_;
70 :
71 : /** @brief Optional hook called after state has been updated. */
72 6 : virtual void on_state_change() {}
73 : };
74 :
75 : } // namespace Amplitron
|