Events

Every element can have Python callbacks attached to its DOM events with RmlElement.add_event_listener(). The first argument is the event name as RmlUi spells it – click, mousedown, mouseover, mouseout, keydown, change, submit, focus, blur, scroll, … – and the second a callable that receives one RmlEvent.

def on_resume(event):
    print("clicked at", event.mouse_x, event.mouse_y)
    self.resume_game()

doc.get_element_by_id("resume").add_event_listener("click", on_resume)

Several listeners may be attached to one element; they fire in the order they were added. A listener belongs to the RmlUi element and lives as long as it does, whatever happens to the Python handle that added it.

The event object

RmlEvent is valid only for the duration of the callback; read what you need from it there.

  • event_type – the name the listener was registered for.

  • get_parameter(), get_parameter_int(), get_parameter_float(), has_parameter() – RmlUi’s event parameters by name: button and mouse_x/mouse_y for mouse events, key_identifier for key events, value for change, and the form’s fields for submit.

  • mouse_x, mouse_y – the common case, as properties.

  • get_target_element() – the element the event was dispatched to; get_current_element() – the element whose listener is running, as the event bubbles up the tree. Both are non-owning wrappers, valid during the callback.

  • stop_propagation() – stop the event from bubbling further.

Firing events from code

RmlElement.click() dispatches a click on the element as if the user had clicked it, which is how menus are driven from a gamepad and how tests press buttons. The listeners run before click returns; what they changed in the document is laid out on the context’s next update, which the region performs every frame – a test calls RmlContext.update() itself.

Rules for callbacks

A callback runs inside RmlUi’s event dispatch, in the middle of the engine’s frame. Two things must not happen there:

  • Do not end the program from a callback: no sys.exit(), no base.userExit(). Schedule it instead, for example with taskMgr.doMethodLater(0, ...).

  • Do not destroy the region, context or document that is delivering the event. Set a flag or schedule a task; tear the interface down from there.

Both leave RmlUi mid-dispatch on freed objects. Deferring through the task manager costs one frame and is always safe.

Who gets the click?

A shown document with a <body> that covers the region takes every mouse event over it, so the 3D scene beneath sees nothing. For a HUD that should let clicks through, give the body pointer-events: none in its style sheet and let interactive elements opt back in with pointer-events: auto. Code that handles clicks in the scene can also ask RmlContext.is_mouse_interacting() whether the pointer is currently over an interactive part of the interface.