Iris IMGUI for Roblox

repository·main·Indexed 43 days ago

https://github.com/sirmallard/iris

An Immediate Mode GUI (IMGUI) library for Roblox inspired by Dear ImGui. Designed for high-performance visualization, debugging, and data input tools, Iris allows developers to declare UI widgets every frame without the overhead of the standard Roblox UI API. It supports customizable themes, global and per-widget configuration, and integrates with various Roblox GUI containers including PlayerGui, CoreGui, and PluginGui.

Tokens
17.1K
Snippets
34
Records
51
Agent score
55%

What's inside Iris

  1. What is Iris?

    main
    Iris is an Immediate Mode GUI (IMGUI) library for Roblox, inspired by Dear ImGui. It is designed for high-productivity UI creation, specifically for visualization, debugging, and data input tools. Unlike traditional Roblox UI libraries that wrap the Roblox UI API, Iris provides its own streamlined Immediate-Mode paradigm and set of widgets to simplify the development of developer tools.
  2. Explore Iris extensions and libraries

    main

    Iris can be extended with specialized libraries to add advanced functionality like 2D graphing or 3D gizmo rendering. Notable extensions include:

    Additionally, projects like Rocket use Iris under-the-hood with custom theming to provide visual user interfaces for Roblox Studio plugins.

  3. What is Iris and how does it work?

    main

    Iris is an Immediate mode GUI (IMGUI) library for Roblox, inspired by Dear ImGui. It is designed for building debug, visualization, and content-creation tools.

    Key Concepts

    • Immediate Mode Paradigm: Unlike conventional Roblox UI libraries where you maintain references to UI elements, in Iris you declare the UI you want to appear every single frame. Iris handles the layout, arrangement, and rendering based on these declarations.
    • Declarative UI: You describe what you want to see (e.g., a window with a button) rather than how to construct and position it. This allows for rapid iteration of developer tools.
    • State Management: Iris provides mechanisms to bridge your game variables with UI widgets, allowing you to control and modify game state directly through the interface.
  4. How the Immediate Mode (IMGUI) paradigm works in Iris

    main

    Iris is an Immediate Mode GUI library. Unlike Retained Mode UI (where you create an object once and update its properties later), in Immediate Mode:

    1. You call a widget function (like Iris.Button) every frame.
    2. You check for interactions (like clicks) immediately via the function's return value.
    3. You do not need to manage UI instances, cleanup, or persistent references; Iris handles the lifecycle of the UI elements automatically based on what you declare in the current frame.

    This makes Iris ideal for rapid development of visualization, debugging, and data input tools.

  5. How Iris configuration scoping works

    main

    Iris uses two levels of configuration: Global and Per-Widget.

    1. Global Configuration: Managed via Iris.UpdateGlobalConfig(). It sets the baseline for the entire UI. Changes here are heavy as they trigger a full UI redraw.
    2. Per-Widget Configuration: Managed via a stack using Iris.PushConfig() and Iris.PopConfig(). This allows for local overrides. Because the configuration is stacked, you can nest different styles for parent and child widgets.

    Configuration values are stored in a global table accessible via Iris.Internal._config for debugging or reference.

  6. Use WeakState to disconnect widgets

    main
    A WeakState behaves like a standard State, but provides a mechanism to clear all connections. When you call the state by ID using Iris.WeakState(), all connected widgets and functions are removed, but the underlying value is preserved. This is useful when you want to stop UI elements from updating based on a specific state without losing the data itself.
  7. How State works in Iris

    main

    An Iris State object is a table containing a value and an array of connected widgets. It acts as a reference-like object in Luau to allow reactive UI updates.

    Key behaviors:

    • Get/Set: You can retrieve or update the value. Setting the value automatically updates all connected widgets' UIs.
    • Reactivity: Functions can be connected to a state so they fire whenever the value changes.
    • Reference Pattern: Because Luau tables are passed by reference, State objects allow different parts of your code to share and react to the same underlying data.
  8. Understand the event firing lifecycle

    main

    Iris events typically fire one frame after the initial user action. This delay ensures that state changes (like visual toggles) have time to propagate through the UI system before the event logic is processed.

    For example, with a checkbox:

    1. The user interacts with the widget (e.g., releases a mouse button).
    2. On the subsequent frame, the visual state updates (e.g., the checkmark appears).
    3. The event method (e.g., .checked()) returns true on that same frame where the visual change is finalized.
  9. How the Iris lifecycle works

    main

    Iris operates on a frame-based lifecycle called a 'cycle'. During each cycle, Iris updates global variables and cleans up unused widgets. This process is similar to ImGui::EndFrame() in Dear ImGui.

    To function correctly, all Iris UI code must be executed every frame and before the cycle event occurs. If you run Iris code after the cycle event, the UI state may not update or clean up properly.

    By default, Iris uses the Roblox RunService.Heartbeat event as its cycle. This means any Iris code must run before Heartbeat fires in the Roblox task scheduler. While you can change the cycle event during initialization, RunService.Heartbeat is the recommended default for most use cases.

  10. How widgets and widget classes work together

    main

    In Iris, a widget is a simple table of data containing properties like ID, type, tick, events, arguments, zindex, parent widget, and gui instance. It contains no logic itself.

    Functionality is provided by a widget class using a factory pattern. A widget class is a table of functions that are applied to a widget instance to modify it. To define a new type of widget, you must construct a widget class using WidgetConstructor: (type: string, widgetClass: WidgetClass) -> () from Iris.Internal.

    -- Example of the conceptual relationship
    -- WidgetConstructor is used to register the class
    Iris.Internal.WidgetConstructor("MyWidgetType", myWidgetClass)
    
    -- When the widget is called in the UI loop, Iris applies the class functions
    -- to the widget data table.
  11. How the Iris Widget API works

    main

    The Iris API is consistent across all widgets. Every widget follows a specific pattern involving Children, Arguments, States, and Events.

    1. Children

    If a widget is marked as hasChildren = true, you must call Iris.End() after the widget call to close the scope of that widget.

    2. Arguments

    Arguments are passed as an array (table with integer indices). The first index is typically a required value (like a Title), while subsequent indices are optional configuration flags.

    3. States

    States allow for two-way data binding. They are used for properties that can be both read from and written to by the widget (e.g., a window's position or size). States are passed as a dictionary where the key is the state name (string) and the value is an Iris.State object.

    4. Events

    Events are functions called on the widget instance that return a boolean. They allow you to react to user interactions (e.g., .clicked() or .opened()) during the frame the interaction occurs.

    local window = Iris.Window({"Window"})
    -- Check events
    if window.opened() and window.uncollapsed() then
        if Iris.Button({"Click me"}).clicked() then
            -- Handle click
        end
    end
    Iris.End()