GroveEngine/docs/UI_TOPICS.md
StillHammer 0441a9d648 feat: UIModule - Dynamic text updates, documentation restructure, and IIO improvements
UIModule Enhancements:
- Add ui:set_text topic handler to update widget text dynamically (UILabel support)
- Add example: slider value updates linked label via game module coordination
- Add timestamp logging for IIO latency measurement (T0-T3 tracking)

Documentation Restructure:
- Split UIModule README.md (600+ lines) into focused docs:
  * docs/UI_WIDGETS.md - Widget properties and JSON configuration
  * docs/UI_TOPICS.md - IIO topics reference and usage patterns
  * docs/UI_ARCHITECTURE.md - Threading model, limitations, design principles
- Update CLAUDE.md with clear references to UIModule docs
- Add warning: "READ BEFORE WORKING ON UI" for essential docs

Asset Path Fixes:
- Change test_ui_showcase texture paths from ../../assets to assets/
- Tests now run from project root (./build/tests/test_ui_showcase)
- Add texture loading success/failure logs to TextureLoader and ResourceCache

IIO Performance:
- Re-enable batch flush thread in IntraIOManager (was disabled for debugging)
- Document message latency: ~16ms in single-threaded tests, <1ms with threading
- Clarify intentional architecture: no direct data binding, all via IIO topics

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-14 22:34:36 +07:00

4.1 KiB

UIModule - IIO Topics Reference

Complete reference of all IIO topics consumed and published by UIModule.

Topics Consumed

From InputModule

Topic Payload Description
input:mouse:move {x, y} Mouse position
input:mouse:button {button, pressed, x, y} Mouse click
input:mouse:wheel {delta} Mouse wheel
input:keyboard {keyCode, pressed, char} Keyboard event

UI Control Commands

Topic Payload Description
ui:set_text {id, text} Update label text dynamically
ui:set_visible {id, visible} Show/hide widget
ui:set_value {id, value} Set slider/progressbar value
ui:load {layoutPath} Load new UI layout from file

Topics Published

UI Events

Topic Payload Description
ui:click {widgetId, x, y} Widget clicked
ui:action {widgetId, action} Button action triggered
ui:value_changed {widgetId, value} Slider/checkbox/input changed
ui:text_submitted {widgetId, text} Text input submitted (Enter)
ui:hover {widgetId, enter} Mouse entered/left widget
ui:scroll {widgetId, scrollX, scrollY} Scroll panel scrolled

Rendering (Retained Mode)

Topic Payload Description
render:sprite:add {renderId, x, y, scaleX, scaleY, color, textureId, layer} Register new sprite
render:sprite:update {renderId, x, y, scaleX, scaleY, color, textureId, layer} Update existing sprite
render:sprite:remove {renderId} Unregister sprite
render:text:add {renderId, x, y, text, fontSize, color, layer} Register new text
render:text:update {renderId, x, y, text, fontSize, color, layer} Update existing text
render:text:remove {renderId} Unregister text

Rendering (Immediate Mode - Legacy)

Topic Payload Description
render:sprite {x, y, w, h, color, layer, ...} Ephemeral sprite (1 frame)
render:text {x, y, text, fontSize, color, layer} Ephemeral text (1 frame)

See UI Rendering Documentation for details on retained vs immediate mode.

Usage Examples

Handling UI Events

// Subscribe to UI events
gameIO->subscribe("ui:action");
gameIO->subscribe("ui:value_changed");

// In game loop
while (m_io->hasMessages() > 0) {
    auto msg = m_io->pullMessage();

    if (msg.topic == "ui:action") {
        std::string action = msg.data->getString("action", "");
        if (action == "start_game") {
            startGame();
        }
    }

    if (msg.topic == "ui:value_changed") {
        std::string widgetId = msg.data->getString("widgetId", "");
        if (widgetId == "volume_slider") {
            double value = msg.data->getDouble("value", 50.0);
            setVolume(value);
        }
    }
}

Updating UI Dynamically

// Update label text
auto msg = std::make_unique<JsonDataNode>("set_text");
msg->setString("id", "score_label");
msg->setString("text", "Score: " + std::to_string(score));
m_io->publish("ui:set_text", std::move(msg));

// Hide/show widget
auto msg = std::make_unique<JsonDataNode>("set_visible");
msg->setString("id", "loading_panel");
msg->setBool("visible", false);
m_io->publish("ui:set_visible", std::move(msg));

// Update progress bar
auto msg = std::make_unique<JsonDataNode>("set_value");
msg->setString("id", "health_bar");
msg->setDouble("value", 0.75);  // 75%
m_io->publish("ui:set_value", std::move(msg));

Slider + Label Pattern

Common pattern: update a label when a slider changes.

if (msg.topic == "ui:value_changed" && widgetId == "volume_slider") {
    double value = msg.data->getDouble("value", 50.0);
    setVolume(value);

    // Update label to show current value
    auto updateMsg = std::make_unique<JsonDataNode>("set_text");
    updateMsg->setString("id", "volume_label");
    updateMsg->setString("text", "Volume: " + std::to_string((int)value) + "%");
    m_io->publish("ui:set_text", std::move(updateMsg));
}