Skip to content

Latest commit

 

History

History
183 lines (135 loc) · 5.44 KB

File metadata and controls

183 lines (135 loc) · 5.44 KB

JsonUI Implementation Guide

The JsonUI system provides a platform-agnostic way to create interactive UI components that communicate via JSON. This guide explains how to integrate JsonUI into your platform.

Overview

JsonUI components emit JSON events when they are added, destroyed, or updated. A JsonUIManager manages internal state and handles bidirectional communication between the sketch and the platform.

Architecture

The JsonUI system consists of several key components:

  • JsonUiInternal: Core component that holds the JSON serialization and update callbacks
  • JsonUiManager: Platform-agnostic manager that tracks components and handles JSON communication
  • Platform Implementation: Platform-specific code that bridges JsonUiManager to the platform's UI system
  • UI Components: High-level components (Slider, Button, etc.) that users interact with

ITL Dependencies

The JsonUI system makes extensive use of FastLED's Internal Template Library:

  • fl::WeakPtr<T>: Weak references allow UI elements to be removed on the client side and removed by the UIManager on the next frame.
  • fl::function<T>: Function objects for callbacks
  • fl::vector<T>: Dynamic arrays for component storage
  • fl::string: String handling
  • fl::mutex / fl::unique_lock: Thread-safe operations
  • fl::EngineEvents::Listener: Lifecycle event handling
  • fl::Singleton<T>: Singleton pattern for managers
  • fl::FixedSet<T, N>: Fixed-size sets for component tracking

Platform Integration Steps

1. Create a Platform-Specific UI Manager

Your platform needs a UI manager that inherits from JsonUiManager:

#include "platforms/shared/ui/json/ui_manager.h"

class MyPlatformUiManager : public fl::JsonUiManager {
public:
    MyPlatformUiManager() : JsonUiManager(myUpdateJsFunction) {}
    
    static MyPlatformUiManager& instance() {
        return fl::Singleton<MyPlatformUiManager>::instance();
    }
    
private:
    static void myUpdateJsFunction(const char* jsonStr) {
        // Platform-specific code to send JSON to your UI system
        // This is called when components are added/updated
    }
};

2. Implement the Required Interface Functions

Define these global functions that UI components will call:

void addJsonUiComponent(fl::WeakPtr<JsonUiInternal> component) {
    MyPlatformUiManager::instance().addComponent(component);
}

void removeJsonUiComponent(fl::WeakPtr<JsonUiInternal> component) {
    MyPlatformUiManager::instance().removeComponent(component);
}

These functions have __attribute__((weak)) declarations, so your implementation will override the default no-op versions.

3. Handle JSON Updates from Platform UI

When your platform UI sends updates back to the sketch:

void handleUiUpdate(const std::string& jsonStr) {
    MyPlatformUiManager::instance().updateUiComponents(jsonStr.c_str());
}

The JSON format for updates should be:

{
    "id_123": {"value": 42},
    "id_456": {"pressed": true}
}

Where id_XXX corresponds to component IDs returned by JsonUiInternal::id().

4. Engine Events Integration

The JsonUiManager automatically integrates with FastLED's EngineEvents system:

  • onEndShowLeds(): Triggers JSON export when new components are added
  • onEndFrame(): Processes pending JSON updates after the frame is complete on Protocol

Component Registration (Sketch → Platform)

When components are added, the JsonUiManager sends a JSON array:

[
    {
        "name": "brightness",
        "type": "slider", 
        "id": 123,
        "value": 128,
        "min": 0,
        "max": 255,
        "group": "lighting"
    },
    {
        "name": "reset",
        "type": "button",
        "id": 456, 
        "pressed": false,
        "group": "controls"
    }
]

Component Updates (Platform → Sketch)

Platform UI sends updates back using component IDs:

{
    "id_123": {"value": 200},
    "id_456": {"pressed": true}
}

Complete WASM Example

The WASM platform provides a complete reference implementation:

// Required interface implementation
void addJsonUiComponent(fl::WeakPtr<JsonUiInternal> component) {
    // YOUR UI MANAGER GETS CALLED HERE.
}

void removeJsonUiComponent(fl::WeakPtr<JsonUiInternal> component) {
    // YOUR UI MANAGER GETS CALLED HERE.
}

Thread Safety

The JsonUiManager uses fl::mutex for thread-safe operations. Key considerations:

  • Component registration/removal is thread-safe
  • JSON updates are deferred to the main loop via EngineEvents
  • Component state updates use mutex locks during JSON operations
  • If your platform doesn't have threading then the mutex will be a fake implementation.

Error Handling

  • Weak pointers prevent crashes when components are destroyed
  • Invalid JSON updates are silently ignored
  • Components automatically clean up on destruction

Available UI Components

The system provides these built-in components:

  • JsonSlider: Numeric input with min/max/step
  • JsonButton: Click/press detection
  • JsonCheckbox: Boolean toggle
  • JsonDropdown: Selection from options
  • JsonNumberField: Numeric text input
  • JsonAudio: Audio-related controls
  • JsonTitle: Display-only text header
  • JsonDescription: Display-only text description

Each component follows the same pattern:

  1. Create JsonUiInternal with update/toJson callbacks
  2. Call addJsonUiComponent() in constructor
  3. Call removeJsonUiComponent() in destructor
  4. Implement JSON serialization and update handling