## Breaking Change
IIO API redesigned from manual pull+if-forest to callback dispatch.
All modules must update their subscribe() calls to pass handlers.
### Before (OLD API)
```cpp
io->subscribe("input:mouse");
void process(...) {
while (io->hasMessages()) {
auto msg = io->pullMessage();
if (msg.topic == "input:mouse") {
handleMouse(msg);
} else if (msg.topic == "input:keyboard") {
handleKeyboard(msg);
}
}
}
```
### After (NEW API)
```cpp
io->subscribe("input:mouse", [this](const Message& msg) {
handleMouse(msg);
});
void process(...) {
while (io->hasMessages()) {
io->pullAndDispatch(); // Callbacks invoked automatically
}
}
```
## Changes
**Core API (include/grove/IIO.h)**
- Added: `using MessageHandler = std::function<void(const Message&)>`
- Changed: `subscribe()` now requires `MessageHandler` callback parameter
- Changed: `subscribeLowFreq()` now requires `MessageHandler` callback
- Removed: `pullMessage()`
- Added: `pullAndDispatch()` - pulls and auto-dispatches to handlers
**Implementation (src/IntraIO.cpp)**
- Store callbacks in `Subscription.handler`
- `pullAndDispatch()` matches topic against ALL subscriptions (not just first)
- Fixed: Regex pattern compilation supports both wildcards (*) and regex (.*)
- Performance: ~1000 msg/s throughput (unchanged from before)
**Files Updated**
- 31 test/module files migrated to callback API (via parallel agents)
- 8 documentation files updated (DEVELOPER_GUIDE, USER_GUIDE, module READMEs)
## Bugs Fixed During Migration
1. **pullAndDispatch() early return bug**: Was only calling FIRST matching handler
- Fix: Loop through ALL subscriptions, invoke all matching handlers
2. **Regex pattern compilation bug**: Pattern "player:.*" failed to match
- Fix: Detect ".*" in pattern → use as regex, otherwise escape and convert wildcards
## Testing
✅ test_11_io_system: PASSED (IIO pub/sub, pattern matching, batching)
✅ test_threaded_module_system: 6/6 PASSED
✅ test_threaded_stress: 5/5 PASSED (50 modules, 100x reload, concurrent ops)
✅ test_12_datanode: PASSED
✅ 10 TopicTree scenarios: 10/10 PASSED
✅ benchmark_e2e: ~1000 msg/s throughput
Total: 23+ tests passing
## Performance Impact
No performance regression from callback dispatch:
- IIO throughput: ~1000 msg/s (same as before)
- ThreadedModuleSystem: Speedup ~1.0x (barrier pattern expected)
## Migration Guide
For all modules using IIO:
1. Update subscribe() calls to include handler lambda
2. Replace pullMessage() loops with pullAndDispatch()
3. Move topic-specific logic from if-forest into callbacks
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
12 KiB
UIModule Interactive Showcase Demo
Date: 2025-11-29 Status: ✅ COMPLETE
Overview
The UIModule Interactive Showcase Demo is a complete, interactive application that demonstrates all features of the UIModule in a real window with live user interaction.
This is NOT a test - it's a real application showing how to use UIModule in production.
What It Demonstrates
All Widgets (9 types)
- ✅ Buttons - 4 colors (Primary, Success, Warning, Danger)
- ✅ Sliders - Volume, Brightness, Difficulty
- ✅ TextInput - Username, Search fields with placeholders
- ✅ Checkboxes - Fullscreen, VSync, Shadows, Particles
- ✅ Progress Bars - Health, Loading, Experience
- ✅ Labels - Headers, descriptions, info text
- ✅ Panels - Sidebar, content panels with backgrounds
- ✅ ScrollPanel - Main scrollable content + nested scroll
- ✅ Tooltips - All widgets have hover tooltips
Features
- ✅ Live event console - See all UI events in real-time
- ✅ Event statistics - Counts clicks, actions, value changes, hovers
- ✅ Hot-reload - Press 'R' to reload UI from JSON
- ✅ Mouse interaction - Click, hover, drag, wheel
- ✅ Keyboard input - Text fields, shortcuts
- ✅ Layouts - Vertical, horizontal, nested
- ✅ Styling - Colors, fonts, borders, padding
- ✅ Tooltips - Smart positioning with edge avoidance
Files
tests/demo/
└── demo_ui_showcase.cpp # Main demo application (370 lines)
assets/ui/
└── demo_showcase.json # Full UI layout (1100+ lines)
Building
Prerequisites
- SDL2 installed
- BgfxRenderer module built
- UIModule built
- X11 (Linux) or native Windows environment
Build Commands
cd /path/to/GroveEngine
# Configure with UI and renderer enabled
cmake -DGROVE_BUILD_UI_MODULE=ON -DGROVE_BUILD_BGFX_RENDERER=ON -B build-bgfx
# Build the demo
cmake --build build-bgfx --target demo_ui_showcase -j4
Running
cd build-bgfx/tests
./demo_ui_showcase
Controls
| Key | Action |
|---|---|
| Mouse | Click, hover, drag widgets |
| Mouse Wheel | Scroll panels |
| Keyboard | Type in text fields |
| ESC | Quit demo |
| R | Hot-reload UI from JSON |
Expected Output
========================================
UIModule Interactive Showcase Demo
========================================
Controls:
- Mouse: Click, hover, drag widgets
- Keyboard: Type in text fields
- Mouse wheel: Scroll panels
- ESC: Quit
- R: Reload UI from JSON
[0.0] Demo starting...
[0.02] SDL window created
[0.10] BgfxRenderer loaded
[0.12] Renderer configured
[0.15] UIModule loaded
[0.18] UIModule configured
[0.18] Ready! Interact with widgets below.
✅ Renderer healthy
Then a 1200x800 window opens with:
- Left sidebar (250px) - Controls and info
- Main content (950px) - Scrollable showcase of all widgets
Interacting
- Hover over any widget → Tooltip appears after 500ms
- Click buttons → Event logged in console
- Drag sliders → Value changes logged
- Type in text fields → Text changes logged
- Check checkboxes → State changes logged
- Scroll with mouse wheel → Smooth scrolling
- Click "Clear Log" → Clears event console
- Click "Reset Stats" → Resets all counters
- Press 'R' → Reloads UI from JSON (hot-reload)
Architecture
Module Stack
┌─────────────────────┐
│ demo_ui_showcase │ SDL2 event loop
│ (main app) │ Input forwarding
└──────────┬──────────┘
│ IIO pub/sub
┌──────┴──────┬─────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌▼─────────┐
│BgfxRend│ │UIModule│ │ Event │
│erer │ │ │ │ Console │
└────────┘ └────────┘ └──────────┘
Event Flow
User Input (SDL)
↓
Input Events (IIO topics)
input:mouse:move
input:mouse:button
input:mouse:wheel
input:key:press
input:text
↓
UIModule (processes events)
↓
UI Events (IIO topics)
ui:click
ui:action
ui:value_changed
ui:text_changed
ui:text_submit
ui:hover
ui:focus_gained
ui:focus_lost
↓
Demo App (logs events)
Hot-Reload Flow
- Press 'R' key
- Demo calls
uiModule->setConfiguration(uiConfig, ...) - UIModule reloads
demo_showcase.json - UI updates without restarting app
- Event log shows "🔄 Reloading UI from JSON..."
Layout Structure
The demo_showcase.json layout is organized as:
root (horizontal layout)
├── sidebar (250px)
│ ├── Title + Info
│ ├── Controls panel
│ ├── Features checklist
│ ├── Clear Log button
│ └── Reset Stats button
│
└── main_content (950px, scrollable)
├── Welcome header
├── Buttons panel (4 buttons)
├── Sliders panel (3 sliders)
├── Text Input panel (2 text fields)
├── Checkboxes panel (4 checkboxes)
├── Progress Bars panel (3 bars)
├── Nested ScrollPanel (20 items)
└── End message
Code Highlights
SDL Event Forwarding
// Mouse move
auto mouseMove = std::make_unique<JsonDataNode>("mouse_move");
mouseMove->setDouble("x", static_cast<double>(event.motion.x));
mouseMove->setDouble("y", static_cast<double>(event.motion.y));
uiIO->publish("input:mouse:move", std::move(mouseMove));
// Mouse wheel
auto mouseWheel = std::make_unique<JsonDataNode>("mouse_wheel");
mouseWheel->setDouble("delta", static_cast<double>(event.wheel.y));
uiIO->publish("input:mouse:wheel", std::move(mouseWheel));
// Text input
auto textInput = std::make_unique<JsonDataNode>("text_input");
textInput->setString("text", event.text.text);
uiIO->publish("input:text", std::move(textInput));
Event Logging
// Subscribe to UI events with callbacks (during setup)
uiIO->subscribe("ui:click", [&clickCount, &eventLog](const grove::Message& msg) {
clickCount++;
std::string widgetId = msg.data->getString("widgetId", "");
eventLog.add("🖱️ Click: " + widgetId);
});
uiIO->subscribe("ui:action", [&actionCount, &eventLog](const grove::Message& msg) {
actionCount++;
std::string action = msg.data->getString("action", "");
eventLog.add("⚡ Action: " + action);
});
// In main loop - pull and dispatch to callbacks
while (uiIO->hasMessages() > 0) {
uiIO->pullAndDispatch(); // Callbacks invoked automatically
}
Hot-Reload Implementation
if (event.key.keysym.sym == SDLK_r) {
eventLog.add("🔄 Reloading UI from JSON...");
uiModule->setConfiguration(uiConfig, uiIO.get(), nullptr);
eventLog.add("✅ UI reloaded!");
}
Known Limitations
WSL / Headless Environments
- ⚠️ Requires graphical environment (X11, Wayland, or Windows native)
- ⚠️ WSL without X server: Renderer fails to initialize
- Demo runs in "UI-only mode" (no visual output)
- Events still processed correctly
- Safe fallback with health checks
Renderer Health Check
The demo checks renderer health and gracefully handles failures:
auto rendererHealth = renderer->getHealthStatus();
bool rendererOK = rendererHealth &&
rendererHealth->getString("status", "") == "healthy";
if (!rendererOK) {
std::cout << "⚠️ Renderer not healthy, running in UI-only mode\n";
}
// In main loop
if (rendererOK) {
renderer->process(frameInput);
}
This prevents segfaults when running in environments without GPU/display.
Performance
- 60 FPS target (16ms frame time)
- Main loop: ~0.2ms per frame (UI + event processing)
- Renderer: ~5ms per frame (when active)
- Total: ~5-6ms per frame = ~165 FPS capable
Bottleneck is SDL_Delay(16) to cap at 60 FPS.
Use Cases
1. Learning UIModule
- See all widgets in action
- Understand event flow
- Learn JSON layout syntax
- Try hot-reload
2. Testing New Features
- Add new widgets to
demo_showcase.json - Press 'R' to reload without restarting
- See changes immediately
3. Visual Regression Testing
- Run demo after changes
- Verify all widgets still work
- Check tooltips, hover states, interactions
4. Integration Example
- Shows proper BgfxRenderer + UIModule integration
- SDL2 event forwarding patterns
- IIO pub/sub communication
- Module lifecycle management
5. Showcase / Portfolio
- Demonstrates GroveEngine capabilities
- Shows hot-reload system
- Production-quality UI
Extending the Demo
Add a New Widget
- Edit
assets/ui/demo_showcase.json:
{
"type": "button",
"id": "my_new_button",
"text": "New Feature",
"tooltip": "This is a new button I added",
"onClick": "demo:new_action",
"width": 150,
"height": 40
}
-
Run demo and press 'R' to reload
-
(Optional) Handle action in demo code:
if (action == "demo:new_action") {
eventLog.add("New action triggered!");
}
Modify Styling
Change colors, fonts, sizes in JSON:
"style": {
"fontSize": 20.0,
"normal": {
"bgColor": "0xFF5722FF", // Material Orange
"textColor": "0xFFFFFFFF"
}
}
Press 'R' to see changes instantly.
Troubleshooting
Window doesn't appear
- WSL: Install X server (VcXsrv, Xming) and set DISPLAY
- Linux: Ensure X11 is running
- Windows: Should work natively
Renderer fails to initialize
- Expected in WSL/headless environments
- Demo runs in UI-only mode (events work, no visual)
- To fix: Use native display or X server
No events logged
- Check that widgets have
onClick,onChange, etc. - Verify IIO subscriptions
- Look for errors in console output
Hot-reload doesn't work
- Ensure JSON file path is correct
- Check JSON syntax (use validator)
- Look for parsing errors in log
Conclusion
The UIModule Interactive Showcase Demo is a complete, production-quality application that:
- ✅ Shows all UIModule features in one place
- ✅ Provides live interaction and real-time feedback
- ✅ Demonstrates hot-reload capability
- ✅ Serves as integration example for new projects
- ✅ Works as visual test for regression checking
- ✅ Handles failures gracefully (renderer health checks)
Perfect starting point for anyone building UIs with GroveEngine! 🚀
Summary Table
| Feature | Status | Description |
|---|---|---|
| All 9 widgets | ✅ | Complete showcase |
| Tooltips | ✅ | Every widget has one |
| Scrolling | ✅ | Main + nested panels |
| Hot-reload | ✅ | Press 'R' to reload |
| Event console | ✅ | Live event logging |
| Stats tracking | ✅ | Click/action counters |
| Keyboard input | ✅ | Text fields work |
| Mouse interaction | ✅ | All input types |
| Graceful degradation | ✅ | Handles renderer failure |
| Documentation | ✅ | This file |
Related Documentation: