Loading...
Searching...
No Matches
DynamicPanel.h
Go to the documentation of this file.
1// Copyright (c) 2025 Dave R. Smith.
2// Malena Framework — Licensed under PolyForm Noncommercial 1.0.0; commercial use requires a paid license. See LICENSE.
3
4//
5// DynamicPanel — a retained container for data-driven UI you rebuild declaratively.
6//
7// You describe the contents in an onBuild(builder) callback using text()/button()/
8// rect()/input(); the panel POOLS the underlying widgets (reuses them across
9// rebuilds, hides leftovers) so nothing is ever destroyed — and it defers the
10// actual rebuild to a frame boundary, so calling invalidate() from inside a
11// button's own click handler is safe (no rebuild-during-dispatch UAF). Text
12// inputs are keyed by a stable id so the same physical field survives a rebuild
13// (focus/typing preserved) and are auto-parked when a rebuild stops using them.
14//
15// This replaces the hand-rolled "pool + _dirty/onUpdate + park at -100000"
16// boilerplate that data-driven hooks otherwise re-implement.
17//
18#ifndef MALENA_DYNAMICPANEL_H
19#define MALENA_DYNAMICPANEL_H
20
25#include <functional>
26#include <map>
27#include <memory>
28#include <set>
29#include <string>
30#include <vector>
31
32namespace ml {
33
35{
36public:
38
39 // Non-copyable. Required, not stylistic: __declspec(dllexport) on a class
40 // instantiates EVERY member, including the implicit copy assignment — and
41 // that cannot be formed for a std::vector<std::unique_ptr<...>> member, so
42 // MSVC fails the shared/DLL build with C2280. (Static builds and Clang/GCC
43 // never instantiate it, which is why this only broke Windows Shared.)
44 // Copying a scene-graph node was never valid anyway.
45 DynamicPanel(const DynamicPanel&) = delete;
47
50 class MALENA_API Builder
51 {
52 DynamicPanel& _p;
53 friend class DynamicPanel;
54 explicit Builder(DynamicPanel& p) : _p(p) {}
55 public:
57 Text& text(const std::string& s, float x, float y, unsigned size,
58 const sf::Color& color, bool center = false);
59
62 RectangleButton& button(const std::string& label, float x, float y, float w, float h,
63 int style, std::function<void()> onClick);
64
66 RectangleButton& rect(float x, float y, float w, float h, const sf::Color& fill);
67
71 TextInput& input(const std::string& id, const std::string& placeholder,
72 float x, float y, float w, float h, const std::string& value,
73 std::function<void(const std::string&)> onChange);
74 };
75
77 void onBuild(std::function<void(Builder&)> fn);
78
81 void setButtonStyler(std::function<void(RectangleButton&, int /*style*/, float /*w*/, float /*h*/)> styler);
82
85 void invalidate() { _dirty = true; }
86
89 bool isRebuildPending() const { return _dirty; }
90
91protected:
94 void doRebuild();
95
96private:
97 RectangleButton& acquireButton();
98 Text& acquireText();
99
100 std::function<void(Builder&)> _build;
101 std::function<void(RectangleButton&, int, float, float)> _styler;
102
103 std::vector<std::unique_ptr<RectangleButton>> _buttons;
104 std::vector<std::unique_ptr<Text>> _texts;
105 std::map<std::string, std::unique_ptr<TextInput>> _inputs;
106
107 std::size_t _btnUsed = 0, _txtUsed = 0;
108 std::set<std::string> _usedInputs;
109 bool _dirty = false;
110};
111
112} // namespace ml
113
114#endif // MALENA_DYNAMICPANEL_H
void onClick(std::function< void()> callback, bool overwrite=true)
RectangleButton & rect(float x, float y, float w, float h, const sf::Color &fill)
A non-interactive coloured rectangle (decor / background / divider).
TextInput & input(const std::string &id, const std::string &placeholder, float x, float y, float w, float h, const std::string &value, std::function< void(const std::string &)> onChange)
Text & text(const std::string &s, float x, float y, unsigned size, const sf::Color &color, bool center=false)
A label. Pass center=true to horizontally centre it on x.
RectangleButton & button(const std::string &label, float x, float y, float w, float h, int style, std::function< void()> onClick)
void setButtonStyler(std::function< void(RectangleButton &, int, float, float)> styler)
void onBuild(std::function< void(Builder &)> fn)
Declare the panel's content. Runs on the next rebuild.
DynamicPanel & operator=(const DynamicPanel &)=delete
bool isRebuildPending() const
DynamicPanel(const DynamicPanel &)=delete
A rectangular button with a centered text label.
A framework-integrated text object with word-wrap support.
Definition Text.h:57
Single-line rich text input with horizontal scrolling.
Definition TextInput.h:48
#define MALENA_API
Definition Animate.h:25