Skip to content

A GUI screen

Status: Implemented for screens built from vanilla widgets and for RmlUi document screens. Data bindings in documents, overlays and buttons added to vanilla screens are Planned.

Goal: a Ruby Settings screen opened with a key: a slider, a checkbox and a Done button, then the same screen as an RML document.

Language: Lua, with C++ at the end. APIs: Screens, widgets and 2d drawing, GUI screens (RmlUi), Input and keybinds.

1. A widget screen

local radius, glow = 8, true

local function open_settings()
  cl.screen.open{
    title = "Ruby Settings",
    pause = false,
    init = function(s)                       -- runs again after every resize
      local cx = s:width() // 2
      s:slider(cx - 100, 50, 200, 20, "Radius", 1, 32, radius, 1, function(w) radius = w:value() end)
      s:checkbox(cx - 100, 80, "Glow", glow, function(w) glow = w:value() == 1 end)
      s:button(cx - 100, s:height() - 40, 200, 20, "Done", function() s:close() end)
    end,
    render = function(s, g)
      g:text_centered("Ruby Settings", s:width() // 2, 20, 0xFFFFFF, true)
    end,
  }
end

Keep the values outside the screen (radius, glow): the widgets are rebuilt on every resize. To keep them across restarts, use the config API (Config and a config screen).

2. Open it with a key

local key
cl.on("lifecycle.client_setup", function()
  key = cl.keybind.register{ id = "my_mod:settings", default_key = "key.keyboard.o", label = "Ruby settings" }
end)
cl.on("tick.client_post", function()
  if key then while key:consume_click() do open_settings() end end
end)

3. The same screen as a document

assets/my_mod/gui/settings.rml:

<rml>
<head>
  <link type="text/rcss" href="settings.rcss"/>
  <title>Ruby Settings</title>
</head>
<body>
  <p id="title">Ruby Settings</p>
  <mc-slider id="radius" min="1" max="32" step="1">Radius</mc-slider>
  <mc-checkbox id="glow">Glow</mc-checkbox>
  <mc-button id="done">Done</mc-button>
</body>
</rml>
local function open_settings_doc()
  cl.rml.open{
    path = "assets/my_mod/gui/settings.rml",
    on_event = function(doc, id, event, value)
      if id == "done" and event == "click" then doc:close()
      elseif id == "radius" and event == "change" then radius = tonumber(value) or radius
      elseif id == "glow" and event == "change" then glow = value == "true" or value == "1" end
    end,
  }
end

Documents may only reference files in your own mod folder (the sandbox rule); images go through the resource packs.

4. C++

void open_settings() {
    clight::screen::open({
        .title = "Ruby Settings",
        .pause = false,
        .init = [this](clight::Screen s) {
            const int32_t cx = s.width() / 2;
            s.slider(cx - 100, 50, 200, 20, "Radius", 1, 32, radius_, 1, [this](clight::Widget w) { radius_ = w.value(); });
            s.button(cx - 100, s.height() - 40, 200, 20, "Done", [s] { s.close(); });
        },
    });
}

Why it stays fast

Nothing runs while the screen is closed. Widgets are vanilla’s own, drawn in the GUI batch with everything else.