Skip to content

UI documents and pages

Client scripts build UI with RML and RCSS, RmlUi's HTML and CSS (see Styling and layouts). They get three things:

  • Documents: their own screens, HUDs and dialogs, over the game.
  • Pause menu pages: a page of their own in the pause menu, after SAGL's own.
  • The message box: SAGL's, with their title, text and buttons.

Scripts can't reach SAGL's own UI (the pause menu's own pages, the message box's insides); they work on their own elements only, by id.

Documents

const hud = sagl.ui.open(`
  <rml>
  <head>
    <link type="text/rcss" href="/sagl/ui/theme/base.rcss"/>
    <style>
      body { position: absolute; right: 24dp; top: 210dp; width: 220dp; }
      .panel { display: block; width: 192dp; padding: 10dp 14dp; }
    </style>
  </head>
  <body><div class="panel">Cops <span id="cops">0</span></div></body>
  </rml>`, { input: false });

hud.setText("cops", 12);

Or from a client file, where relative links (images, styles) resolve beside it:

const shop = sagl.ui.openFile("ui/shop.rml");

Options

Option Default
input true Takes the player's input while shown. false for HUDs.
cursor input Shows the mouse cursor.
keyboard input Keys go to the document.
gamepad input The gamepad moves focus (A activates, B backs out).
movement false Leaves the player's movement to the game (walk with a phone open).
escapeCloses true Esc (or gamepad B) closes it.

Methods

Documents and pages share these; each returns the document or page, so calls chain.

setText(id, text) An element's text (escaped).
setHtml(id, rml) An element's contents, as RML.
setValue(id, value), getValue(id) Form controls' values.
isChecked(id) Checkboxes and radio buttons.
setClass(id, name, on?) Adds or removes a class.
setAttribute(id, name, value) null removes it.
focus(id)
on(type, [id,] handler) Events, from the whole document or one element: click, change, submit, keydown, focus, blur, mouseover, mouseout. The handler gets { type, id, value, key, checked }; key is RmlUi's key identifier (KI_A is 12 ... KI_Z 37, KI_0 2 ... KI_9 11).
off(type, handler?)

Documents also have show(), hide(), close(), onClose(fn) and open (false once closed).

const menu = sagl.ui.openFile("ui/garage.rml");
menu.on("click", "buy", () => sagl.emit("garage", { buy: menu.getValue("car") }));
menu.on("keydown", (e) => { if (e.key === 28) menu.close(); });   // 28: Q (RmlUi KI_Q)
menu.onClose(() => sagl.emit("garage", { closed: true }));

Pause menu pages

const shop = sagl.ui.pauseMenu.addPage("Shop", `
  <h2 class="first">Ammu-Nation</h2>
  <span class="subtitle">Pick a weapon. It's taken from your cash.</span>
  <div class="list">
    <div class="item" id="pistol">Pistol - $200</div>
    <div class="item" id="shotgun">Shotgun - $600</div>
  </div>
  <span class="status" id="status"></span>`);

shop.on("click", (e) => {
  if (e.id) sagl.emit("shop", { buy: e.id });
});
shop.onShow(() => shop.setText("status", ""));

A client script's pause menu page

The page's title goes in the options row, before Quit Game. The RML is the page's body: it's styled by the pause menu's theme (so h2, .subtitle, .list, button look like SAGL's own). Pages have remove() and onShow(fn).

The message box

const answer = await sagl.ui.messageBox({
  title: "Pay 'n' Spray",
  text: "Respray your car for $100? It'll lose two wanted stars.",
  buttons: ["No", "Yes"],
});
if (answer === "Yes") sagl.emit("spray");

The message box

It resolves to the chosen button's label, or null if the player dismissed it (Esc). The last button is the default. Boxes queue: one shows at a time.