FastLED 3.9.15
Loading...
Searching...
No Matches
ui.h
Go to the documentation of this file.
1#pragma once
2
3
4#include "fl/namespace.h"
5#include "fl/memory.h"
6#include "fl/json.h"
7#include "fl/str.h"
8#include "fl/int.h"
9#include "fl/audio.h"
10#include "fl/engine_events.h"
11#include "fl/function_list.h"
12#include "fl/math_macros.h"
13#include "fl/type_traits.h"
14#include "fl/ui_impl.h"
15#include "fl/unused.h"
16#include "platforms/ui_defs.h"
17#include "sensors/button.h"
19#include "fl/int.h"
20
21#define FL_NO_COPY(CLASS) \
22 CLASS(const CLASS &) = delete; \
23 CLASS &operator=(const CLASS &) = delete;
24
25namespace fl {
26
27// Base class for UI elements that provides string-based group functionality
28class UIElement {
29 public:
32 virtual void setGroup(const fl::string& groupName) { mGroupName = groupName; }
33
34 fl::string getGroup() const { return mGroupName; }
35 bool hasGroup() const { return !mGroupName.empty(); }
36
37 private:
39};
40
41// If the platform is missing ui components, provide stubs.
42
43class UISlider : public UIElement {
44 public:
46 // If step is -1, it will be calculated as (max - min) / 100
47 UISlider(const char *name, float value = 128.0f, float min = 1,
48 float max = 255, float step = -1.f)
49 : mImpl(name, value, min, max, step), mListener(this) {}
50 float value() const { return mImpl.value(); }
51 float value_normalized() const {
52 float min = mImpl.getMin();
53 float max = mImpl.getMax();
54 if (ALMOST_EQUAL(max, min, 0.0001f)) {
55 return 0;
56 }
57 return (value() - min) / (max - min);
58 }
59 float getMax() const { return mImpl.getMax(); }
60 float getMin() const { return mImpl.getMin(); }
61 void setValue(float value);
62 operator float() const { return mImpl.value(); }
63 operator u8() const { return static_cast<u8>(mImpl.value()); }
64 operator fl::u16() const { return static_cast<fl::u16>(mImpl.value()); }
65 operator int() const { return static_cast<int>(mImpl.value()); }
66 template <typename T> T as() const {
67 return static_cast<T>(mImpl.value());
68 }
69
70 int as_int() const { return static_cast<int>(mImpl.value()); }
71
73 mImpl.setValue(value);
74 return *this;
75 }
77 mImpl.setValue(static_cast<float>(value));
78 return *this;
79 }
80
81 // Override setGroup to also update the implementation
82 void setGroup(const fl::string& groupName) override {
83 UIElement::setGroup(groupName);
84 // Update the implementation's group if it has the method (WASM platforms)
85 mImpl.setGroup(groupName);
86 }
87
88
89 int onChanged(function<void(UISlider &)> callback) {
90 int out = mCallbacks.add(callback);
91 mListener.addToEngineEventsOnce();
92 return out;
93 }
94 void clearCallbacks() { mCallbacks.clear(); }
95
96 protected:
98
100 Listener(UISlider *owner) : mOwner(owner) {
102 }
104 if (added) {
106 }
107 }
109 if (added) {
110 return;
111 }
113 added = true;
114 }
115 void onBeginFrame() override;
116
117 private:
119 bool added = false;
120 };
121
122 private:
127};
128
129// template operator for >= against a jsSliderImpl
130
131class UIButton : public UIElement {
132 public:
134 UIButton(const char *name) : mImpl(name), mListener(this) {}
136 bool isPressed() const {
137 if (mImpl.isPressed()) {
138 return true;
139 }
140 // If we have a real button, check if it's pressed
141 if (mRealButton) {
142 return mRealButton->isPressed();
143 }
144 // Otherwise, return the default state
145 return false;
146 }
147 bool clicked() const {
148 if (mImpl.clicked()) {
149 return true;
150 }
151 if (mRealButton) {
152 // If we have a real button, check if it was clicked
153 return mRealButton->isPressed();
154 }
155 return false;
156 }
157 int clickedCount() const { return mImpl.clickedCount(); }
158 operator bool() const { return clicked(); }
159 bool value() const { return clicked(); }
160
164
165 void click() { mImpl.click(); }
166
167 // Override setGroup to also update the implementation
168 void setGroup(const fl::string& groupName) override {
169 UIElement::setGroup(groupName);
170 // Update the implementation's group if it has the method (WASM platforms)
171 mImpl.setGroup(groupName);
172 }
173
174 int onChanged(function<void(UIButton &)> callback) {
175 int id = mCallbacks.add(callback);
176 mListener.addToEngineEventsOnce();
177 return id;
178 }
179
180 int onClicked(function<void()> callback) {
181 int id = mCallbacks.add([callback](UIButton &btn) {
182 if (btn.clicked()) {
183 callback();
184 }
185 });
186 mListener.addToEngineEventsOnce();
187 return id;
188 }
189
190 void removeCallback(int id) { mCallbacks.remove(id); }
191 void clearCallbacks() { mCallbacks.clear(); }
192
193 protected:
195
197 Listener(UIButton *owner) : mOwner(owner) {
199 }
201 if (added) {
203 }
204 }
206 if (added) {
207 return;
208 }
210 added = true;
211 }
212 void onBeginFrame() override;
213
214 private:
216 bool added = false;
217 bool mClickedLastFrame = false;
218 };
219
220 private:
224};
225
226class UICheckbox : public UIElement {
227 public:
229 UICheckbox(const char *name, bool value = false)
230 : mImpl(name, value), mListener(this) {}
232
233 operator bool() const { return value(); }
234 explicit operator int() const { return static_cast<int>(value()); }
236 mImpl = value;
237 return *this;
238 }
239 bool value() const { return mImpl.value(); }
240
241 // Override setGroup to also update the implementation
242 void setGroup(const fl::string& groupName) override {
243 UIElement::setGroup(groupName);
244 // Update the implementation's group if it has the method (WASM platforms)
245 mImpl.setGroup(groupName);
246 }
247
248
249 void onChanged(function<void(UICheckbox &)> callback) {
250 mCallbacks.add(callback);
251 mListener.addToEngineEventsOnce();
252 }
253 void clearCallbacks() { mCallbacks.clear(); }
254
255 protected:
257
259 Listener(UICheckbox *owner) : mOwner(owner) {
261 }
263 if (added) {
265 }
266 }
268 if (added) {
269 return;
270 }
272 added = true;
273 }
274 void onBeginFrame() override;
275
276 private:
278 bool added = false;
279 };
280
281 private:
283 bool mLastFrameValue = false;
286};
287
288class UINumberField : public UIElement {
289 public:
291 UINumberField(const char *name, double value, double min = 0,
292 double max = 100)
293 : mImpl(name, value, min, max), mListener(this) {}
295 double value() const { return mImpl.value(); }
296 void setValue(double value) { mImpl.setValue(value); }
297 operator double() const { return mImpl.value(); }
298 operator int() const { return static_cast<int>(mImpl.value()); }
301 return *this;
302 }
304 setValue(static_cast<double>(value));
305 return *this;
306 }
307
308 // Override setGroup to also update the implementation
309 void setGroup(const fl::string& groupName) override {
310 UIElement::setGroup(groupName);
311 // Update the implementation's group if it has the method (WASM platforms)
312 mImpl.setGroup(groupName);
313 }
314
315
316 void onChanged(function<void(UINumberField &)> callback) {
317 mCallbacks.add(callback);
318 mListener.addToEngineEventsOnce();
319 }
320 void clearCallbacks() { mCallbacks.clear(); }
321
322 protected:
324
325 private:
327 Listener(UINumberField *owner) : mOwner(owner) {
329 }
331 if (added) {
333 }
334 }
336 if (added) {
337 return;
338 }
340 added = true;
341 }
342 void onBeginFrame() override;
343
344 private:
346 bool added = false;
347 };
348
350 double mLastFrameValue = 0;
353};
354
355class UITitle : public UIElement {
356 public:
358#if FASTLED_USE_JSON_UI
359 UITitle(const char *name) : mImpl(fl::string(name), fl::string(name)) {}
360#else
361 UITitle(const char *name) : mImpl(name) {}
362#endif
364
365 // Override setGroup to also update the implementation
366 void setGroup(const fl::string& groupName) override {
367 UIElement::setGroup(groupName);
368 // Update the implementation's group if it has the method (WASM platforms)
369 mImpl.setGroup(groupName);
370 }
371
372 protected:
374};
375
376class UIDescription : public UIElement {
377 public:
379 UIDescription(const char *name) : mImpl(name) {}
381
382 // Override setGroup to also update the implementation
383 void setGroup(const fl::string& groupName) override {
384 UIElement::setGroup(groupName);
385 // Update the implementation's group if it has the method (WASM platforms)
386 mImpl.setGroup(groupName);
387 }
388
389 protected:
391};
392
393class UIHelp : public UIElement {
394 public:
398
399 // Override setGroup to also update the implementation
400 void setGroup(const fl::string& groupName) override {
401 UIElement::setGroup(groupName);
402 // Update the implementation's group if it has the method (WASM platforms)
403 mImpl.setGroup(groupName);
404 }
405
406 // Access to the markdown content
407 const fl::string& markdownContent() const { return mImpl.markdownContent(); }
408
409 protected:
411};
412
413class UIAudio : public UIElement {
414 public:
416 UIAudio(const char *name) : mImpl(name) {}
418 AudioSample next() { return mImpl.next(); }
419 bool hasNext() { return mImpl.hasNext(); }
420
421 // Override setGroup to also update the implementation
422 void setGroup(const fl::string& groupName) override {
423 UIElement::setGroup(groupName);
424 // Update the implementation's group if it has the method (WASM platforms)
425 mImpl.setGroup(groupName);
426 }
427
428 protected:
430};
431
432class UIDropdown : public UIElement {
433 public:
435
436
437
438 // Constructor with fl::span<fl::string> for arrays and containers.
439 UIDropdown(const char *name, fl::span<fl::string> options)
440 : mImpl(fl::string(name), options), mListener(this) {}
441
442 // Constructor with initializer_list
443 UIDropdown(const char *name, fl::initializer_list<fl::string> options)
444 : mImpl(name, options), mListener(this) {}
445
447
448 fl::string value() const { return mImpl.value(); }
449 int as_int() const { return mImpl.value_int(); }
450 fl::string as_string() const { return value(); }
451
452 void setSelectedIndex(int index) {
453 mImpl.setSelectedIndex(index);
454 }
455
456 fl::size getOptionCount() const { return mImpl.getOptionCount(); }
457 fl::string getOption(fl::size index) const { return mImpl.getOption(index); }
458
459 operator fl::string() const { return value(); }
460 operator int() const { return as_int(); }
461
462 UIDropdown &operator=(int index) {
463 setSelectedIndex(index);
464 return *this;
465 }
466
467 // Add a physical button that will advance to the next option when pressed
468 void addNextButton(int pin) {
470 }
471
472 // Advance to the next option (cycles back to first option after last)
473 void nextOption() {
474 int currentIndex = as_int();
475 int nextIndex = (currentIndex + 1) % static_cast<int>(getOptionCount());
476 setSelectedIndex(nextIndex);
477 }
478
479 // Override setGroup to also update the implementation
480 void setGroup(const fl::string& groupName) override {
481 UIElement::setGroup(groupName);
482 // Update the implementation's group if it has the method (WASM platforms)
483 mImpl.setGroup(groupName);
484 }
485
486 int onChanged(function<void(UIDropdown &)> callback) {
487 int out = mCallbacks.add(callback);
488 mListener.addToEngineEventsOnce();
489 return out;
490 }
491 void clearCallbacks() { mCallbacks.clear(); }
492
493 protected:
495
497 Listener(UIDropdown *owner) : mOwner(owner) {
499 }
501 if (added) {
503 }
504 }
506 if (added) {
507 return;
508 }
510 added = true;
511 }
512 void onBeginFrame() override;
513
514 private:
516 bool added = false;
517 };
518
519 private:
525};
526
527class UIGroup {
528 public:
530
531 // Constructor takes fl::string as the only parameter for grouping name
532 UIGroup(const fl::string& groupName) : mImpl(groupName.c_str()) {}
533
534 // Variadic template constructor: first argument is group name, remaining are UI elements
535 template<typename... UIElements>
536 UIGroup(const fl::string& groupName, UIElements&... elements)
537 : mImpl(groupName.c_str()) {
538 // Add all UI elements to this group
539 add(elements...);
540 }
541
543
544 // Get the group name
545 fl::string name() const { return mImpl.name(); }
546
547 // Implicit conversion to string for convenience
548 operator fl::string() const { return name(); }
549
550 // Add control to the group
551 template<typename T>
552 void addControl(T* control) {
553 control->setGroup(name());
554 }
555
556 protected:
558
559private:
560 // Helper method to add multiple controls using variadic templates
561 template<typename T>
562 void add(T& control) {
563 // Base case: add single control
564 control.setGroup(name());
565 }
566
567 template<typename T, typename... Rest>
568 void add(T& control, Rest&... rest) {
569 // Recursive case: add first control, then recurse with remaining
570 control.setGroup(name());
571 add(rest...);
572 }
573};
574
575#define FASTLED_UI_DEFINE_OPERATORS(UI_CLASS) \
576 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, >=) \
577 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, <=) \
578 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, >) \
579 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, <) \
580 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, ==) \
581 FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, !=)
582
588
589} // end namespace fl
static void addListener(Listener *listener, int priority=0)
static void removeListener(Listener *listener)
FL_NO_COPY(UIAudio) UIAudio(const char *name)
Definition ui.h:415
void setGroup(const fl::string &groupName) override
Definition ui.h:422
~UIAudio()
Definition ui.h:417
UIAudioImpl mImpl
Definition ui.h:429
AudioSample next()
Definition ui.h:418
bool hasNext()
Definition ui.h:419
FL_NO_COPY(UIButton) UIButton(const char *name)
Definition ui.h:133
fl::shared_ptr< Button > mRealButton
Definition ui.h:223
FunctionList< UIButton & > mCallbacks
Definition ui.h:221
void removeCallback(int id)
Definition ui.h:190
void setGroup(const fl::string &groupName) override
Definition ui.h:168
bool clicked() const
Definition ui.h:147
bool isPressed() const
Definition ui.h:136
int onChanged(function< void(UIButton &)> callback)
Definition ui.h:174
bool value() const
Definition ui.h:159
void clearCallbacks()
Definition ui.h:191
void addRealButton(fl::shared_ptr< Button > button)
Definition ui.h:161
int onClicked(function< void()> callback)
Definition ui.h:180
Listener mListener
Definition ui.h:222
UIButtonImpl mImpl
Definition ui.h:194
int clickedCount() const
Definition ui.h:157
~UIButton()
Definition ui.h:135
void click()
Definition ui.h:165
UICheckbox & operator=(bool value)
Definition ui.h:235
Listener mListener
Definition ui.h:285
void clearCallbacks()
Definition ui.h:253
FL_NO_COPY(UICheckbox)
bool mLastFrameValue
Definition ui.h:283
FunctionList< UICheckbox & > mCallbacks
Definition ui.h:282
bool value() const
Definition ui.h:239
void setGroup(const fl::string &groupName) override
Definition ui.h:242
void onChanged(function< void(UICheckbox &)> callback)
Definition ui.h:249
~UICheckbox()
Definition ui.h:231
UICheckboxImpl mImpl
Definition ui.h:256
bool mLastFrameValueValid
Definition ui.h:284
UICheckbox(const char *name, bool value=false)
Definition ui.h:229
FL_NO_COPY(UIDescription)
UIDescriptionImpl mImpl
Definition ui.h:390
UIDescription(const char *name)
Definition ui.h:379
void setGroup(const fl::string &groupName) override
Definition ui.h:383
void addNextButton(int pin)
Definition ui.h:468
fl::span< fl::string > options
Definition ui.h:440
FL_NO_COPY(UIDropdown) UIDropdown(const char *name
Listener mListener
Definition ui.h:523
UIDropdown(const char *name, fl::initializer_list< fl::string > options)
Definition ui.h:443
~UIDropdown()
Definition ui.h:446
bool mLastFrameValueValid
Definition ui.h:522
int onChanged(function< void(UIDropdown &)> callback)
Definition ui.h:486
fl::size getOptionCount() const
Definition ui.h:456
void clearCallbacks()
Definition ui.h:491
fl::string getOption(fl::size index) const
Definition ui.h:457
UIDropdownImpl mImpl
Definition ui.h:494
fl::string value() const
Definition ui.h:448
void setGroup(const fl::string &groupName) override
Definition ui.h:480
void setSelectedIndex(int index)
Definition ui.h:452
int as_int() const
Definition ui.h:449
fl::shared_ptr< Button > mNextButton
Definition ui.h:524
int mLastFrameValue
Definition ui.h:521
void nextOption()
Definition ui.h:473
UIDropdown & operator=(int index)
Definition ui.h:462
fl::string as_string() const
Definition ui.h:450
FunctionList< UIDropdown & > mCallbacks
Definition ui.h:520
VIRTUAL_IF_NOT_AVR ~UIElement()
Definition ui.h:31
fl::string mGroupName
Definition ui.h:38
fl::string getGroup() const
Definition ui.h:34
virtual void setGroup(const fl::string &groupName)
Definition ui.h:32
UIElement()
Definition ui.h:30
bool hasGroup() const
Definition ui.h:35
UIGroup(const fl::string &groupName)
Definition ui.h:532
UIGroup(const fl::string &groupName, UIElements &... elements)
Definition ui.h:536
fl::string name() const
Definition ui.h:545
void add(T &control, Rest &... rest)
Definition ui.h:568
void addControl(T *control)
Definition ui.h:552
FL_NO_COPY(UIGroup)
~UIGroup()
Definition ui.h:542
UIGroupImpl mImpl
Definition ui.h:557
void add(T &control)
Definition ui.h:562
UIHelp(const char *markdownContent)
Definition ui.h:396
FL_NO_COPY(UIHelp)
const fl::string & markdownContent() const
Definition ui.h:407
~UIHelp()
Definition ui.h:397
UIHelpImpl mImpl
Definition ui.h:410
void setGroup(const fl::string &groupName) override
Definition ui.h:400
double value() const
Definition ui.h:295
void setGroup(const fl::string &groupName) override
Definition ui.h:309
UINumberField & operator=(double value)
Definition ui.h:299
UINumberFieldImpl mImpl
Definition ui.h:323
FunctionList< UINumberField & > mCallbacks
Definition ui.h:352
Listener mListener
Definition ui.h:349
UINumberField & operator=(int value)
Definition ui.h:303
FL_NO_COPY(UINumberField)
UINumberField(const char *name, double value, double min=0, double max=100)
Definition ui.h:291
bool mLastFrameValueValid
Definition ui.h:351
double mLastFrameValue
Definition ui.h:350
void setValue(double value)
Definition ui.h:296
void onChanged(function< void(UINumberField &)> callback)
Definition ui.h:316
void clearCallbacks()
Definition ui.h:320
Listener mListener
Definition ui.h:126
float float float float step
Definition ui.h:48
bool mLastFramevalueValid
Definition ui.h:125
float value
Definition ui.h:47
UISlider & operator=(int value)
Definition ui.h:76
T as() const
Definition ui.h:66
FL_NO_COPY(UISlider) UISlider(const char *name
void clearCallbacks()
Definition ui.h:94
UISliderImpl mImpl
Definition ui.h:97
void setGroup(const fl::string &groupName) override
Definition ui.h:82
float float float max
Definition ui.h:48
float float min
Definition ui.h:47
float mLastFrameValue
Definition ui.h:124
FunctionList< UISlider & > mCallbacks
Definition ui.h:123
int as_int() const
Definition ui.h:70
UISlider & operator=(float value)
Definition ui.h:72
int onChanged(function< void(UISlider &)> callback)
Definition ui.h:89
float getMax() const
Definition ui_impl.h:70
float value() const
Definition ui_impl.h:69
float getMin() const
Definition ui_impl.h:71
~UITitle()
Definition ui.h:363
UITitle(const char *name)
Definition ui.h:361
UITitleImpl mImpl
Definition ui.h:373
void setGroup(const fl::string &groupName) override
Definition ui.h:366
FL_NO_COPY(UITitle)
FastLED's Elegant JSON Library: fl::Json
#define ALMOST_EQUAL(a, b, small)
Definition math_macros.h:59
Implements the FastLED namespace macros.
unsigned char u8
Definition int.h:17
Slice< T > span
Definition span.h:8
shared_ptr< T > make_shared(Args &&... args)
Definition shared_ptr.h:348
IMPORTANT!
Definition crgb.h:20
UIButton button("Button")
Listener(UIButton *owner)
Definition ui.h:197
bool mClickedLastFrame
Definition ui.h:217
void addToEngineEventsOnce()
Definition ui.h:205
UIButton * mOwner
Definition ui.h:215
void onBeginFrame() override
UICheckbox * mOwner
Definition ui.h:277
void onBeginFrame() override
Listener(UICheckbox *owner)
Definition ui.h:259
void addToEngineEventsOnce()
Definition ui.h:267
void addToEngineEventsOnce()
Definition ui.h:505
UIDropdown * mOwner
Definition ui.h:515
void onBeginFrame() override
Listener(UIDropdown *owner)
Definition ui.h:497
Listener(UINumberField *owner)
Definition ui.h:327
void addToEngineEventsOnce()
Definition ui.h:335
UINumberField * mOwner
Definition ui.h:345
void onBeginFrame() override
void addToEngineEventsOnce()
Definition ui.h:108
void onBeginFrame() override
UISlider * mOwner
Definition ui.h:118
Listener(UISlider *owner)
Definition ui.h:100
#define FASTLED_UI_DEFINE_OPERATORS(UI_CLASS)
Definition ui.h:575
#define VIRTUAL_IF_NOT_AVR