GUI
Cheryl integrates existing UI toolkits with its ordered input and retained rendering contracts. The application owns the session and interface objects; the toolkit supplies widgets or documents. A text score drawn directly through the rendering system is useful HUD content, but interactive UI also needs widget construction, input routing, focus and a publication path.
Choose an interface
Integration |
Application authors |
Guide |
|---|---|---|
TGUI |
Native C++ widgets and layouts through the session’s |
|
RmlUi |
Native contexts, RML documents and RCSS styles through a document session. |
Neither integration provides a common widget API. Select one according to the application’s authoring needs. Both convert ordered Engine input and publish retained scenes; Engine headers do not import either toolkit.
Construct widgets on the UI owner
Create the session before toolkit objects, on the thread that will own its UI
updates. In concurrent runtime mode that is the simulation thread, so create it
during init_simulation(), after the runtime establishes its services and initial view.
Construct the scene uploader from the resource provider during platform-owned
init_platform(). It obtains default rendering materials automatically during upload.
The session executes widget logic, callbacks, layout, and CPU recording. Native event polling, graphics uploads, and scene playback remain on platform. The UIController tutorial shows that division across initialization, updates, frame preparation, and cleanup. Components such as HUD, menu, and chat borrow the enclosing owner’s session; the owner drives shared input/time and publishes their combined scene.
Construct either session with the Engine context and a distinct nonzero focus
target, for example CE::UI::TGUI::Session(engine, tgui_target), with a named
constexpr CE::Input::Routing::TargetId tgui_target = 2;. It obtains input and the
configured Engine font catalogue/default. Configure engine.fonts() before
creating consumers; no separate toolkit font-file registration is needed for the
default interface. A native button and edit field are ordinary toolkit objects:
auto button = tgui::Button::create("Apply");
button->setSize({120, 32});
button->setPosition({16, 16});
button->onPress([this] { apply_requested_ = true; });
session_->gui().add(button);
auto field = tgui::EditBox::create();
field->setSize({240, 32});
field->setPosition({16, 60});
field->setDefaultText("Type a name");
session_->gui().add(field);
This fragment belongs to an application class retaining session_ and the
callback’s target state. Include the toolkit’s Button/EditBox headers and link
the selected UI target. It creates an interactive interface; upload/publication
is still required before it appears on screen. The maintained
TGUI demo supplies the complete
widget and scene wiring.
Select fonts
The context default inherits through native containers/documents and individual
elements can choose a different family. Both adapters accept the same retained
CE::Text::Font handle used by direct text:
auto selected = engine.fonts().choose_random("books");
// TGUI:
tgui_session.set_font(book_label, selected);
// RmlUi:
rmlui_session.set_font(*book_element, selected);
This uses the books group from catalogue configuration;
configure it before creating the sessions. An unknown group returns the default,
so matching the configured name matters even if text still appears.
Choose during application initialization/restoration, then save
selected.reference() in the shared book data. Rendering reuses the handle and
does not reroll. The font service owns lookup, restoration and
fallback; native widget/document styles still own size, color, weight and italics.
The TGUI font contract and
RmlUi font contract describe toolkit-specific style
and naming constraints. Standalone sessions and explicit native loading remain
available.
Route input and focus explicitly
The UIController example
enables SessionOptions::automatic_view on its Context-aware session. The runtime
applies its coherent seed and later selected size changes before the matching
game update. Manual sessions instead initialize set_view() from
tick.logical_size/tick.framebuffer_size and refresh both pairs, including zero
extents. A valid manual call detaches automatic sizing. Do not read the live window
on simulation. Feed the
complete tick.input.records() batch once, with an explicit decision about
which UI receives pointer input. Do not prefilter away modifier releases.
Use the enclosing UI owner’s policy to request or release the session’s keyboard
focus. Exclusive focus withholds keyboard gameplay actions while text is directed
to the UI; pointer selection is a separate application choice. Requests affect later
polls, so pending records retain the focus epoch under which they were captured.
Session before_record callbacks support ordered application controls without
rewriting that entry epoch.
Ending exclusive focus resumes currently held keyboard controls on the next poll. If Escape both releases an editor and quits gameplay, suppress that release gesture until its physical key release has been processed. The GUI task shows the application guard.
Choose manual time after input, as the tutorials do, or opt in to
automatic_time before future game updates; never advance through both paths.
The TGUI and
RmlUi references define attach,
detach and first-step behavior. Apply game-state changes on the UI owner, then
record the scene. A widget callback may update simulation-owned intent such as
apply_requested_; graphics operations belong to the platform owner. Do not run
a toolkit’s independent main loop over Cheryl’s runtime.
Session routing and widget focus
The constructor’s target is an application-chosen identity for the whole session.
Any nonzero TargetId is suitable, and larger values have no priority. For example,
TGUI can use 2 and RmlUi 3 when both share one input system. The latest focus
publication wins; records retain the target and request epoch of their input poll.
Each session filters keyboard/text delivery to its own requested target/epoch.
Within one TGUI session, HUD, menu, and chat share that target. Native widget focus selects which control receives keyboard/text. The application decides which panels are visible or modal and which widget regains focus when a panel closes.
Using the field and button from the construction example, the following blocks illustrate separate UI actions. When entering a field, acquire session focus if needed, then focus the native widget:
if (!session_->owns_keyboard_focus())
session_->request_keyboard_focus();
field->setFocused(true);
When moving from a chat field to a menu control within the same session, retain the session’s focus lease and select the new widget:
button->setFocused(true);
Closing the menu can restore field->setFocused(true) if chat remains active.
When the whole TGUI interface relinquishes keyboard input to gameplay:
session_->release_keyboard_focus();
Release also unfocuses the session’s widgets. A component closing while another
needs input should transfer widget focus through the enclosing UI policy. Native
RmlUi element focus uses the same two-level relationship with its session lease.
Pointer delivery remains an independent decision passed to handle_input().
The TGUI demo and
RmlUi task
show ordered application controls and complete input-batch handling.
Publish and clean up
Each toolkit adapter follows these publication roles while keeping its own session and authoring API. Live widget or document objects stay with the UI owner.
flowchart LR
accTitle: Toolkit UI becomes retained frame data
accDescr: The UI owner records widgets or documents into owned CPU data. Platform uploads a scene; simulation adopts the ready scene and appends retained packets to a frame.
session["Live toolkit UI"] -->|record| recording["Owned recording"]
recording -->|upload| scene["Retained scene"]
scene -->|adopt and append| frame["Frame packets"]
Recording creates owned CPU scene data. Send upload work to the platform through
its dispatcher, consume completed results without blocking simulation, and
append the latest uploaded scene to a prepared frame. Both integrations support
uploader.submit(platform, session.record()); their default materials are created
lazily on the provider owner. OpenGL embeds the shaders, so the UI needs no
application material builder or additional shader assets. The optional third
argument accepts custom material handles.
Each frame retains its published resources; later UI edits must not mutate an
earlier frame’s data.
After simulation joins, release application-held widget, document, font and
texture references before session teardown. close_after_quiescence() permits
final closure on platform after the UI owner has stopped. Keep input and upload
dependencies alive until accepted work settles. Do not destroy the toolkit while
callbacks or UI operations can still run.
Continue
Use the UIController tutorial for shared session ownership, the HUD tutorial for a widget component, the RmlUi GUI task for document authoring and editing, GUI system relationships for ownership, and input focus contracts for routing detail. Clipboard, cursor, composition and other services depend on reported integration capabilities; a text field does not imply a complete IME or editor service. See UI adapter authoring when implementing a different toolkit bridge.