## 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>
|
||
|---|---|---|
| .. | ||
| Core | ||
| Rendering | ||
| Widgets | ||
| CMakeLists.txt | ||
| README.md | ||
| UIModule.cpp | ||
| UIModule.h | ||
UIModule
⚠️ Development Stage: Experimental, part of non-deterministic engine. See main README for limitations.
Complete UI widget system for GroveEngine with layout, scrolling, tooltips, and automatic input handling.
Features
- 10 Widget Types: Button, Label, Panel, Checkbox, Slider, TextInput, ProgressBar, Image, ScrollPanel, Tooltip
- JSON-Based Layouts: Define UI hierarchies in JSON files
- Automatic Input Handling: Consumes
input:*topics from InputModule - Retained Mode Rendering: Widgets cache state, reducing IIO traffic by 85%+
- Layer Management: UI renders on top of game content (layer 1000+)
- Hot-Reload Support: Full state preservation across module reloads
- Thread-Safe Design: Architecture ready for future multi-threaded systems (currently single-threaded only)
Quick Start
#include <grove/ModuleLoader.h>
#include <grove/IntraIOManager.h>
// Create IIO instances
auto& ioManager = IntraIOManager::getInstance();
auto uiIO = ioManager.createInstance("ui_module");
auto gameIO = ioManager.createInstance("game");
// Load UIModule
ModuleLoader uiLoader;
auto uiModule = uiLoader.load("./modules/UIModule.dll", "ui_module");
// Configure
JsonDataNode config("config");
config.setInt("windowWidth", 1920);
config.setInt("windowHeight", 1080);
config.setString("layoutFile", "./ui/menu.json");
config.setInt("baseLayer", 1000);
uiModule->setConfiguration(config, uiIO.get(), nullptr);
// Subscribe to UI events with callback handlers
gameIO->subscribe("ui:action", [this](const grove::Message& msg) {
std::string action = msg.data->getString("action", "");
handleAction(action);
});
gameIO->subscribe("ui:value_changed", [this](const grove::Message& msg) {
std::string widgetId = msg.data->getString("widgetId", "");
double value = msg.data->getDouble("value", 0.0);
handleValueChange(widgetId, value);
});
// Game loop
while(running) {
// Handle UI events - pull and auto-dispatch to callbacks
while (gameIO->hasMessages() > 0) {
gameIO->pullAndDispatch(); // Callbacks invoked automatically
}
uiModule->process(deltaTime);
}
Documentation
- Widget Reference - All widgets with JSON properties
- IIO Topics - Complete topic reference and usage examples
- Architecture & Design - Threading, limitations, future features
- Rendering - Retained mode rendering architecture
Example UI Layout
ui/menu.json:
{
"widgets": [
{
"type": "panel",
"id": "background",
"x": 0, "y": 0,
"width": 1920, "height": 1080,
"style": {"bgColor": "0x2d3436FF"}
},
{
"type": "button",
"id": "play_button",
"x": 860, "y": 500,
"width": 200, "height": 60,
"text": "Play Game",
"onClick": "start_game",
"style": {
"normal": {"bgColor": "0x0984e3FF"},
"hover": {"bgColor": "0x74b9ffFF"}
}
},
{
"type": "slider",
"id": "volume_slider",
"x": 800, "y": 650,
"width": 320, "height": 40,
"min": 0.0, "max": 100.0, "value": 75.0
}
]
}
See Widget Reference for all widget properties.
Building
cmake -DGROVE_BUILD_UI_MODULE=ON -B build
cmake --build build -j4
Testing
# Visual showcase (run from project root for correct asset paths)
./build/tests/test_ui_showcase
# Integration test
cd build && ctest -R IT_014 --output-on-failure
Dependencies
- GroveEngine Core: IModule, IIO, IDataNode
- BgfxRenderer: For rendering (via IIO, not direct dependency)
- InputModule: For input (via IIO, not direct dependency)
- nlohmann/json: JSON parsing
- spdlog: Logging
Implementation Status
- ✅ Phase 1-3: Core widgets (Button, Label, Panel, Checkbox, Slider, TextInput, ProgressBar, Image)
- ✅ Phase 4-5: Layout system and styling
- ✅ Phase 6: Interactive demo
- ✅ Phase 7: ScrollPanel + Tooltips
- ✅ Phase 8: Retained mode rendering
Files Structure
modules/UIModule/
├── README.md # This file
├── CMakeLists.txt # Build configuration
├── UIModule.h/.cpp # Main module
├── Core/
│ ├── UIContext.h/.cpp # Global UI state
│ ├── UILayout.h/.cpp # Layout management
│ ├── UITooltip.h/.cpp # Tooltip system
│ ├── UITree.h/.cpp # Widget hierarchy
│ └── UIWidget.h # Base widget interface
├── Widgets/
│ ├── UIButton.h/.cpp
│ ├── UILabel.h/.cpp
│ ├── UIPanel.h/.cpp
│ ├── UICheckbox.h/.cpp
│ ├── UISlider.h/.cpp
│ ├── UITextInput.h/.cpp
│ ├── UIProgressBar.h/.cpp
│ ├── UIImage.h/.cpp
│ └── UIScrollPanel.h/.cpp
└── Rendering/
├── UIRenderer.h/.cpp # Publishes render commands
License
See LICENSE at project root.
UIModule - Complete UI system for GroveEngine 🎨