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 107 : 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) {
38 30 : props_ = new_props;
39 30 : }
40 :
41 : /**
42 : * @brief Render the component using current props_ and state_.
43 : * Must be implemented by every concrete component.
44 : */
45 : virtual void render() = 0;
46 :
47 : /** @brief Read the current local state. */
48 : const State& get_state() const { return state_; }
49 :
50 : /**
51 : * @brief React-style setState: replace local state and trigger the
52 : * optional on_state_change() hook.
53 : */
54 6 : void set_state(const State& new_state) {
55 6 : state_ = new_state;
56 6 : on_state_change();
57 6 : }
58 :
59 : /**
60 : * @brief Functional setState: apply an updater lambda to a copy of the
61 : * current state, then commit and notify.
62 : */
63 6 : void set_state(std::function<void(State&)> updater) {
64 6 : State next = state_;
65 6 : updater(next);
66 6 : set_state(next);
67 6 : }
68 :
69 : protected:
70 : Props props_;
71 : State state_;
72 :
73 : /** @brief Optional hook called after state has been updated. */
74 6 : virtual void on_state_change() {}
75 : };
76 :
77 : } // namespace Amplitron
|