-
Notifications
You must be signed in to change notification settings - Fork 113
embedding
title: Embedding NeL GUI description: What a host application must provide to run NLGUI standalone: bootstrap order, handlers, hooks and pitfalls published: true editor: markdown
NLGUI was extracted from the Ryzom client, and several things the library expects are still supplied by the embedding application: conventional action handlers, Lua entry points, the link updater, pointer button state. This page is the complete contract, distilled from building the standalone widget sample (nel/samples/gui/main.cpp is the reference implementation; everything below exists there in working form).
// driver + text
UDriver *driver = UDriver::createDriver(0, false, 0);
driver->setDisplay(UDriver::CMode(width, height, 32, true));
UTextContext *tc = driver->createTextContext(fontPath);
tc->setKeep800x600Ratio(false); // fontsize means pixels; see pitfalls
// link evaluation pipeline (before any parsing)
new CInterfaceLink::CInterfaceLinkUpdater(); // leaked once, process lifetime
// renderer
CViewRenderer::setDriver(driver);
CViewRenderer::setTextContext(tc);
CViewRenderer::getInstance()->init();
CViewRenderer::getInstance()->loadTextures("atlas.png", "atlas.txt", false);
// Lua before parsing: initLUA registers the stock API; the parser does NOT call it
IParser *parser = CWidgetManager::getInstance()->getParser();
parser->initLUA();
CLuaManager::getInstance().getLuaState()->registerFunc("getUI", luaGetUI); // yours
// parse, evaluate, activate
parser->parseInterface(xmlFiles, false);
CWidgetManager::getInstance()->updateAllLocalisedElements();
CInterfaceLink::updateAllLinks();
CWidgetManager::getInstance()->activateMasterGroup("ui:sample", true);Main loop each frame: pump the driver's event server, sendClockTickEvent, checkCoords, drawViews. If the application ships no translation files, CI18N::setNoResolution(true) renders literals instead of <NotExist:...> noise.
Instantiate an NLGUI::CEventListener and add it to the driver's event server; it adapts mouse and keyboard events into the widget manager.
Under Emscripten the driver feeds the same event server from HTML5 callbacks (CEmscriptenEventEmitter in the opengl3/opengles3 driver): mouse, wheel and keyboard map to the usual NeL events, coordinates are normalized against the canvas CSS size so a CSS-scaled canvas just works, and the first touch is mapped to the left mouse button. The listener and pointer-mirroring path described here works unmodified in the browser, including touch drag on mobile.
The widget manager updates the pointer view's position only; nothing in the library ever calls setPointerDown. The client mirrors button state into the pointer in its own input handler, and a standalone app must do the same. Without it, anything reading the pointer's button state (drag logic, getMouseDown-style hooks) sees buttons that never press. A small IEventListener on mouse down/up/focus-lost suffices:
// on EventMouseDownId: pointer->setPointerDown(button == leftButton);
// pointer->setPointerMiddleDown / setPointerRightDown likewise
// on EventMouseUpId: clear the pressed states
// on EventSetFocusId: reset everything (focus loss mid-drag)The parser auto-creates a bare generic_pointer view when none is declared. Only a type="pointer" view (a class name the client registers) is wired as the pointer by the parser, so a type="generic_pointer" element in XML is decorative; declare cursor textures on it only if your application registers and wires it.
The library and standard interface data invoke these by name; implementations live in the client, so register your own (a few lines each; see the sample's main.cpp):
| name | minimal implementation |
|---|---|
proc |
split params on ` |
lua |
execute params via CLuaManager's state. Without it, setOnDraw, addOnDbChange, lua_class and every onclick="lua:..." are dead: the library calls this handler internally |
enter_modal / push_modal / leave_modal
|
enableModalWindow / pushModalWindow / popModalWindow with the group= param |
active_menu |
getParam(params, "menu") → enableModalWindow(caller, menu)
|
browse / browse_undo / browse_redo
|
resolve the name= param to a CGroupHTML*, call browse(url) / browseUndo() / browseRedo(); required for clickable HTML links |
submit_quick_help |
no-op: every single handler invocation is forwarded to this name and warns when it is missing |
set_keyboard_focus / reset_keyboard_focus, launch_help
|
optional; focus convenience and the container help button |
initLUA provides the stock API (catalog) but not getUI: widget lookup from Lua is the embedder's. Minimal version: CWidgetManager::getElementFromId + CLuaIHM::pushUIOnStack. Register extra functions before parseInterface; registrations survive since initLUA reuses the same state.
For gesture-driven UI (drag and drop), the sample registers the client-compatible getMousePos() / getMouseDown() reading the widget manager's pointer. Coordinates are in interface pixels, the same space as reflected *_real geometry. They depend on the button mirroring above.
The drop-down machinery resolves three static ids; point them at a menu group in your XML before combos are used:
CDBGroupComboBox::selectMenu = "ui:sample:combo_menu";
CDBGroupComboBox::selectMenuOut = "ui:sample:combo_menu";
CDBGroupComboBox::measureMenu = "ui:sample:combo_menu";<link> evaluation is driven by database observer flushes, closed by the CInterfaceLink::CInterfaceLinkUpdater instance, which nothing in the library creates. Without it, links evaluate once at updateAllLinks() and never again. Instantiate it before parsing and call CInterfaceLink::updateAllLinks() once after (mechanics).
Details in the linked pages.
-
container_move_optoptions block missing → crash in the first layout pass of any container (options blocks). -
Fresh database = all zeros → invisible UI: declare the
UI:SAVE:*ALPHAentries, mind the inverted*_ROLLOVER_FACTOR(database). -
Unregistered groups never draw: windows need
<tree>registration; modals are automatic (xml structure). -
%case_upperetc. are defines, not built-ins; declare them (0–5). -
setKeep800x600Ratio(false)or every font size scales with window height and overflows the skin's header bands (skinning). -
.png→.tgaremap before indexing search paths when sources are png but XML uses canonical tga names; the color picker additionally needs its palette as a loose file (skinning). -
w=does not wrap text;line_maxwdoes (views). -
enter_loose_focusdefaults true on edit boxes (controls). - Container height comes from content unless resizer-clamped; an empty fixed window collapses to its title bar.
-
dbview_quantity's registered type name istext_quantity, and the digit view's isdigit.
WITH_GUI requires Lua (5.1 classic; 5.2+ supported) and ryzom/luabind, plus libxml2 and libpng/jpeg via NLMISC. CGroupHTML uses CURL when WITH_WEB/curl are present; local pages need none of it. The stack builds for Emscripten/WebGL: cross-compiled deps, curl symbol stubs and the recipe are documented in the widget sample, which also serves as the wasm reference embedding.