Skip to content

GUI screens (RmlUi)

Status: Partly implemented (API 1.22). cl_rml_open shows an .rml document from the mod folder as a screen, a panel inside the mod’s own screen, or a HUD overlay, with click, change and submit events by element id, functions to change elements, a data model ({{ value }}, data-for, data-value, …) and actions (data-event-*), all themed by Appearance and sandboxed: HTML/CSS Overlays and Panels. Container screens with slot elements and changing vanilla screens (ui.inject) are Planned. For simple screens, the widget API is shorter: Screens, widgets and 2d drawing; for state-driven screens, Components.

Function table: rml. Client only.

Celestial’s own screens are RmlUi documents (RML markup and RCSS styles). Mods can show their own documents the same way.

Documents

Put the document and its styles in your mod folder, for example assets/<modid>/gui/shop.rml and shop.rcss:

<rml>
<head>
  <link type="text/rcss" href="shop.rcss"/>
  <title>Ruby shop</title>
</head>
<body>
  <p id="balance">Rubies: 0</p>
  <mc-button id="buy">Buy a diamond</mc-button>
  <mc-checkbox id="confirm">Ask before buying</mc-checkbox>
  <mc-slider id="amount" min="1" max="16" step="1">Amount</mc-slider>
  <mc-editbox id="note"></mc-editbox>
</body>
</rml>
Element Vanilla counterpart
<mc-button> Button (vanilla sprites, hover and disabled states, click sound)
<mc-checkbox> Checkbox
<mc-slider> AbstractSliderButton
<mc-editbox> EditBox

Plain RML elements (div, p, img, input, select, textarea, …) work as in RmlUi. Sandbox rule: stylesheets, templates and other files a document references load only from the same mod folder; URLs, absolute paths, .., scripts and event attributes are refused when the document opens (CL_E_PERMISSION_DENIED, the full list: the sandbox). Images go through the resource packs (<modid>:textures/..., the mod’s own assets/). Link celestial:theme.rcss for the player’s Appearance. A document screen shows the mod’s name in its corner (mod UI can’t pass for a vanilla or server screen).

Functions

typedef struct cl_rml_desc {
    uint32_t       struct_size;
    cl_str         path;        // relative to the mod root: "assets/my_mod/gui/shop.rml"
    bool           as_screen;   // true (only screens for now)
    bool           pause;       // pause singleplayer while open (default false)
    cl_rml_event_fn on_event;   // (doc, element_id, event, value, user): "click", "change", "submit", "data"
    void*          user;
    // API 1.22 (45_HTML_Overlays.md)
    cl_screen      parent;      // a panel inside this open screen of the mod
    int32_t        anchor, x, y, width, height;  // panels: the box; overlays: anchor + offset + size
    cl_str         id, label;   // overlays: the HUD element id and its name in the HUD editor
    cl_str         model;       // data model variables: "rubies, items[]"
    cl_str         actions;     // names data-event-* may call: "buy, sell"
    cl_rml_action_fn on_action; // (doc, action, args_json, user)
} cl_rml_desc;
/* CL_RML_DESC_INIT */

cl_result cl_rml_open(const cl_rml_desc* desc, cl_rml_doc* out);
cl_result cl_rml_close(cl_rml_doc doc);
bool      cl_rml_is_open(cl_rml_doc doc);
cl_result cl_rml_set_text(cl_rml_doc doc, cl_str element_id, cl_str s);            // escaped text
cl_result cl_rml_set_inner_rml(cl_rml_doc doc, cl_str element_id, cl_str rml);     // markup (same file rules)
cl_result cl_rml_set_attribute(cl_rml_doc doc, cl_str element_id, cl_str name, cl_str value);
cl_result cl_rml_set_class(cl_rml_doc doc, cl_str element_id, cl_str class_name, bool on);
cl_result cl_rml_set_property(cl_rml_doc doc, cl_str element_id, cl_str property, cl_str value);   // an RCSS property
cl_result cl_rml_value(cl_rml_doc doc, cl_str element_id, char* buf, size_t cap, size_t* out_len); // input, select, textarea
cl_result cl_rml_set_value(cl_rml_doc doc, cl_str element_id, cl_str value);

All of these are client-side and may be called from any client callback.

Bindings

C C++ (clight::RmlDoc) Lua
cl_rml_open(&d, &doc) clight::rml::open(&d) → Expected<RmlDoc> cl.rml.open{ path = "...", on_event = function(doc, id, event, value) ... end }
cl_rml_set_text(doc, id, s) doc.set_text(id, s) doc:set_text(id, s)
cl_rml_value(doc, id, ...) doc.value(id) → Expected<std::string> doc:value(id)
cl_rml_close(doc) doc.close() doc:close()

Example (Lua)

local rubies = 0
local function open_shop()
  local doc = cl.rml.open{
    path = "assets/my_mod/gui/shop.rml",
    on_event = function(doc, id, event)
      if id == "buy" and event == "click" then
        cl.net.send_to_server("my_mod:buy", cl.buf.create(0))
      end
    end,
  }
  doc:set_text("balance", "Rubies: " .. rubies)
end

The server does the buying (a payload handler); the screen only shows and asks.

Planned

Container screens with <mc-slot> elements for custom menus, item and entity preview elements, and patching vanilla or other mods’ screens behind the ui.inject capability. Overlays, panels, data models and actions are here since API 1.22: HTML/CSS Overlays and Panels.