Behavioral Modifiers & Implicit Interfaces¶
Behavioral modifiers are template decorators that change how an element behaves — how it responds to keys, what events it generates, or how its value is interpreted. Unlike visual modifiers, they don't change the element's appearance; they change its interaction model.
These modifiers rely on implicit interfaces — a convention in method naming within EVA Boxy. Instead of a formal inheritance hierarchy or abstract base classes, EVA Boxy uses duck typing: if an element has the required methods, the modifier works. This approach keeps the library lightweight and avoids virtual table overhead.
The Four Implicit Interfaces¶
EVA Boxy recognizes four implicit interfaces that elements can implement:
| Interface | Methods | Purpose |
|---|---|---|
| Value | SetValue(T), GetValue() |
Numeric data (int, float) |
| Increment | Increment(delta) |
Step-based navigation (next/previous) |
| Index | Select(idx), Selected(), Count() |
Selection from a set |
| Percent | SetPercent(0-100), GetPercent() |
Relative values (0-100%) |
The Increment Interface: This interface represents the ability to move forward or backward by a step. Many interactive elements support it — from InputInt (changes value) to LayoutBase (changes focus).
// Increment is meaningful for these
InputInt value(42);
value.Increment(+1); // → 43
LayoutBase form;
form.Increment(+1); // → moves focus to next child
Elements and Their Interface Support¶
| Element | Value | Increment | Index | Percent |
|---|---|---|---|---|
InputInt |
🟢 Yes | 🟢 Yes | 🔴 No | 🔴 No |
InputFloat |
🟢 Yes | 🟢 Yes | 🔴 No | 🔴 No |
InputIntDiscrete |
🟢 Yes | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
InputFloatDiscrete |
🟢 Yes | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
InputSelectorPx<T> |
🔴 No | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
InputAnimationPx |
🔴 No | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
InputButton |
🔴 No | 🟡 Dummy | 🔴 No | 🔴 No |
InputButtonPx |
🔴 No | 🟡 Dummy | 🔴 No | 🔴 No |
LayoutBase |
🔴 No | 🟢 Yes | 🔴 No | 🔴 No |
ListBox<T> |
🔴 No | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
| StretchBars | 🔴 No | 🟢 Yes | 🔴 No | 🟢 Yes |
ScrollListbox |
🔴 No | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
FlipListbox |
🔴 No | 🟢 Yes | 🟢 Yes | 🟡 Yes (via Percent<T>) |
Legend:
-
🟢 Yes — Supported natively (built into the class)
-
🟡 Dummy — Implemented as no-operation (for interface completeness)
-
🟡 Yes (via mod.) — Supported via
Percent<T>modifier -
🔴 No — Not supported
Note on StretchBars This group includes all progress and scroll bar variants:
-
VerticalProgressBarPx,HorizontalProgressBarPx -
VerticalScrollBarPx,HorizontalScrollBarPx -
VerticalProgressBar,HorizontalProgressBar -
VerticalScrollBar,HorizontalScrollBar
The step of Increment() for these elements is variable — it depends on the visual representation. Each Increment(+1) moves the bar by exactly one visual block (one pictogram or one character position). The actual percent change depends on how many blocks fit in the current drawing area.
This is why StretchBar elements support Increment (visual step) and Percent (0-100% value), but not Index (no fixed discrete positions) — the number of positions is determined by the available space at draw time.
The Percent Modifier¶
Percent<T> transforms an Index-based element into a Percent-based interface. This is useful when you want to control selection using a 0-100% scale.
How it works: Percent<T> takes an element that implements the Index interface (Select(), Selected(), Count()) and adds SetPercent() and GetPercent() methods. The percentage is automatically mapped to the index range.
#include <evabPercent.h>
// Album selector (Index) → Percent control
Percent<InputSelectorPx<AlbumBattery>> batteryPercent(3);
// Animation speed (Index) → Percent control
Percent<InputAnimationPx<AlbumFan>> fanSpeedPercent(5); // 5 → 50% of max speed
// ListBox (Index) → Percent control
Percent<ScrollListbox> listPercent(33);
// Discrete int (Value + Index) → Percent control
Percent<InputIntDiscrete> discretePercent(50);
| Feature | Description |
|---|---|
SetPercent(0-100) |
Sets value as a percentage, mapped to index range |
GetPercent() |
Returns current value as a percentage |
GetStep() |
Returns the step size in percent |
Behavioral Modifiers¶
KeyModifier¶
Adds keyboard control to any element that has a meaningful Increment() method. When the specified keys are pressed, the value changes accordingly.
#include <evabKeyModifier.h>
// Int with UP/DOWN control — meaningful
KeyModifier<InputInt, KEY_DOWN, KEY_UP> intControl(42);
// Album selector with LEFT/RIGHT control — meaningful
KeyModifier<InputSelectorPx<AlbumBattery>, KEY_LEFT, KEY_RIGHT> batteryControl(3);
// Animation speed with UP/DOWN — meaningful
KeyModifier<InputAnimationPx<AlbumFan>, KEY_DOWN, KEY_UP> fanControl(5);
// Layout navigation with UP/DOWN — meaningful
KeyModifier<LayoutBase, KEY_DOWN, KEY_UP> formNavigation();
// Stretch bar with LEFT/RIGHT — steps by one visual block
KeyModifier<HorizontalProgressBarPx, KEY_LEFT, KEY_RIGHT> progressControl(50);
// Percent-modified selector with UP/DOWN — meaningful
KeyModifier<Percent<InputSelectorPx<AlbumBattery>>, KEY_DOWN, KEY_UP> percentControl(3);
| Parameter | Description |
|---|---|
T |
Element type (must have Increment()) |
kDec |
Key code for decrement |
kInc |
Key code for increment |
Note on Buttons:
InputButtonandInputButtonPximplementIncrement()as a dummy (no-operation) method. This is done for interface completeness, but makesKeyModifiereffectively useless on them — pressing keys does nothing. For buttons, useKeyCatcherinstead.
// Button with keys — compiles but does nothing (dummy Increment)
KeyModifier<InputButton, KEY_DOWN, KEY_UP> uselessButtonControl(F("OK")); // ⚠️ No effect!
KeyReactor¶
Similar to KeyModifier, but additionally generates an event when the value changes. The event can be captured by a handler.
#include <evabKeyReactor.h>
class MyForm : public LayoutBase, private eva::IHandler {
KeyReactor<InputInt, KEY_DOWN, KEY_UP> mValue {this, 42};
public:
void invoke(void* sender, CallbackInfo info) override {
Serial.println("Value changed!");
}
};
KeyCatcher¶
Intercepts specific keys and generates an event. This is the recommended way to handle button presses, since KeyModifier has no effect on buttons.
#include <evabKeyCatcher.h>
class MyForm : public LayoutBase, private eva::IHandler {
// KeyCatcher is the right tool for buttons
Focusable<KeyCatcher<InputButton, KEY_ENTER>> mSaveButton {this, F("Save")};
Focusable<KeyCatcher<InputButtonPx, KEY_ENTER>> mIconButton {this, GalleryRemixicon24::PICTO_F243};
public:
void invoke(void* sender, CallbackInfo info) override {
if (sender == &mSaveButton) {
Serial.println("Save pressed!");
}
if (sender == &mIconButton) {
Serial.println("Icon button pressed!");
}
}
};