Getting Started with RmlUi

Configuration

RmlUi clips and rounds corners with the stencil buffer, so the window needs one. Set this before ShowBase opens the window:

from acron.core import loadPrcFileData
loadPrcFileData("", "framebuffer-stencil true")

RmlUi’s rendering effects – filters, masks, box-shadow, blend modes – are implemented with shaders that need GLSL 1.50. Plain documents render on any OpenGL context; if you use the effects on macOS, request a core-profile context with gl-version 3 2 (mind that this disables the fixed-function pipeline for the rest of your application).

A region, a context, a document

An interface starts with an RmlRegion. RmlRegion.make() takes a context name and the window (or off-screen buffer) to draw on, creates the RmlUi context at the region’s size and adds the region to the window. Like any display region it can be given dimensions (left, right, bottom, top) as fractions of the window; the default covers all of it.

from direct.showbase.ShowBase import ShowBase
from acron.rmlui import RmlRegion, RmlInputHandler

class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)

        # Mouse and keyboard reach RmlUi through a data graph node.
        self.rml_input = RmlInputHandler()
        self.mouseWatcher.attachNewNode(self.rml_input)

        self.region = RmlRegion.make("main-ui", self.win)
        self.region.set_input_handler(self.rml_input)

        # Fonts are loaded per context, before the documents that use them.
        context = self.region.get_context()
        context.load_font_face("fonts/LatoLatin-Regular.ttf")

        self.menu = context.load_document("ui/menu.rml")
        self.menu.show()

app = MyApp()
app.run()

The region renders and updates its context every frame; there is nothing to pump. load_document returns None when the file cannot be read or parsed (the reason is logged under the rmlui notify category), so check the result.

The document itself is a plain RML file:

<rml>
  <head>
    <title>Menu</title>
    <link type="text/rcss" href="menu.rcss"/>
  </head>
  <body>
    <div id="title">Paused</div>
    <button id="resume">Resume</button>
    <button id="quit">Quit</button>
  </body>
</rml>

Fonts

Every context loads its own fonts with RmlContext.load_font_face(), before any document that uses them is loaded. A font is referred to in RCSS by its family name as recorded in the file, which is not always the file name. Pass fallback=True for a font that should supply glyphs the others lack.

Names and lifetime

Context names are global to the process: creating a second region with a name that is still in use fails, and RmlRegion.is_valid() returns False. A context lives as long as its region. Removing the region from its window (GraphicsOutput.remove_display_region()) and dropping the last reference destroys the context together with its documents, elements and data models, and frees the name.

self.win.remove_display_region(self.region)
self.region = None

Keep the RmlDocument and RmlElement handles you need in attributes; they are ordinary reference-counted objects, but the DOM nodes they wrap belong to the context and disappear with it.

A first debug aid

RmlUi’s own debugger – an element inspector with the computed style and layout boxes – ships with the module. Call RmlRegion.init_debugger() once and toggle RmlRegion.debugger_visible; it draws on top of the region’s documents.