Skip to content

widget_sample

Jan Boon edited this page Jul 19, 2026 · 20 revisions

title: GUI Widget Sample description: Standalone NeL GUI showcase sample (nl_sample_gui) using the legacy w_ interface skin published: true editor: markdown

nl_sample_gui (nel/samples/gui/) is a standalone showcase of the NLGUI widget library. It boots the widget manager, view renderer and interface parser outside the Ryzom client, parses a self-contained interface definition, and renders it with the legacy w_ interface skin — the original beveled reference style whose window frames (w_header_*, w_l0_*, w_l1_*, w_modal_*, w_scroll_l0_*) were later replaced name-for-name by the shipped skin_* set. The sample keeps this otherwise-unused skin exercised and doubles as a minimal reference for embedding NLGUI in any NeL application.

Layout

  • main.cpp — bootstrap: UDriver + text context, CViewRenderer::setDriver/setTextContext + init, atlas loadTextures, parseInterface, updateAllLocalisedElements, activateMasterGroup("ui:sample"), then the pump/draw loop (EventServer.pumpsendClockTickEventcheckCoordsdrawViews). A NLGUI::CEventListener routes driver mouse events into the widget manager.
  • data/config_sample.xml — master <root id="sample">, database defaults, and the w_ skin options blocks (layer0, skin_modal, container_move_opt, container_insertion_opt, context_menu_back, …).
  • data/widgets_sample.xml — shared widget styles (text_button_16, button_ok, tab_button) on the w_text_button_* / w_tab_* textures.
  • data/main_sample.xml — the showcase windows, the generic_pointer view, and the <tree> nodes that register windows.

Textures — packed on the fly

No interface texture data lives in the code repository. At build time, CMake runs the build_interface tool over interfaces/v3/ of a ryzomcore_graphics checkout (NL_GRAPHICS_DIR, default sibling of the source tree) and packs gui_sample_atlas.png/.txt into the build directory — the same pattern the WebGL water sample uses for its assets. build_interface gained a -k/--keep-going flag for this: input files that cannot be loaded as bitmaps (e.g. unmaterialized git-lfs pointer files) are skipped with a warning instead of aborting the pack. The font comes from fonts/basic.ttf in the same checkout.

The Emscripten build preloads the generated atlas, the XML and the font into the .data bundle; when cross-compiling, point NL_BUILD_INTERFACE_TOOL at a host-built build_interface.

Building and running

Native (needs WITH_GUI=ON, which needs Lua and ryzom/luabind; build both into a local prefix and pass LUA_*/LUABIND_* cache variables if the distribution does not package them):

cmake -DWITH_GUI=ON -DWITH_NEL_SAMPLES=ON ...
ninja nl_sample_gui
bin/nl_sample_gui                          # interactive, ESC quits
bin/nl_sample_gui --screenshot out.png --frames 10   # headless witness (works under xvfb-run)

Sample-side action handlers

NLGUI ships the widget-level handlers (set, tab_select, ic_* container buttons, combo box, select number, …) but some conventional handlers live in the Ryzom client. The sample registers its own equivalents: proc (run an interface procedure), enter_modal / leave_modal (CWidgetManager::enableModalWindow), and sample_quit.

Widget coverage (M2)

A launcher window toggles one showcase window per family; the four container layer skins (layer0layer3, pure w_l0_*w_l3_* options blocks) are spread across them. Covered and interaction-verified under Xvfb with xdotool: text views (sizes, colors, case modes, multi-line), bitmaps (scaled/tiled), text_number/digit/text_quantity, bar/bar3 gauges, push/toggle/radio buttons, text buttons, sliders on both axes bound to UI:TEMP:* database entries, edit boxes (typed input works), scroll_text (which contains the runtime-filled group_list), group_tree with arbo_* fold arrows, menus (checkable, grayed, separator, nested submenu — a submenu is a nested <action> inside an <action>, not a <menu> element), the combo box with its dropdown, select_number, the color picker (palette + a makeRGB(...) link recoloring a swatch), tab groups, stacked modals (push_modal/pop_modal), a <link> showing a warning icon above a threshold, and the contacts-style popin rollout with the original anatomy: window contents live in the header groups, the section below is the open rollout where child containers dock via nested <tree> nodes, and a contact entry is a closed container — just its title bar with icons in header_closed. With the child list open, the original layout draws the top block, the m_open left rail (with the list scrollbar riding it), and the floating el/em/er_open ending strip; the frame stops below the header and content, the child bevel bars carry the open section, and the right edge of the rollout is genuinely open (the shipped skin_ layers instead patch a single repeated right-edge texture over it, e.g. skin_l1_r for tx_tr/tx_r/tx_br). The engine had always stretched the frame over the whole open list — which degenerates the open-section geometry (negative rail height, b_open landing on the ending-strip row) — so the sample introduced the frame_covers_open_list layer option (default true, shipped skins unchanged); the sample's w_ layer blocks set it to false for the original behavior. Not exercised: group_html/group_table (need the web stack wired), Lua-scripted UI (handler registered, no demo), edit_box_decor, group_frame/header.

Note on container layers: standalone windows can force a deeper skin with options="layer1..3", but the engine normally derives the layer from popin nesting depth.

What the application must provide

NLGUI expects several things from the embedding application that the Ryzom client normally supplies:

  • CInterfaceLink::CInterfaceLinkUpdater — instantiate one after creating the widget manager; without it, <link> elements never evaluate (database observer flushes are what pump triggered links). Call CInterfaceLink::updateAllLinks() once after parsing for the initial evaluation.
  • CDBGroupComboBox::selectMenu / selectMenuOut / measureMenu — static ids the combo dropdown machinery resolves; point them at your master group's menus.
  • Action handlers with client-side conventions: proc, enter_modal/leave_modal, push_modal/pop_modal, active_menu, lua.

Bootstrap pitfalls (learned the hard way)

  • Container windows need the global options blocks. CGroupContainer::setup dereferences container_move_opt unchecked — without that options block the first updateAllLocalisedElements crashes.
  • Fresh database leaves default to 0. UI:SAVE:CONTENT_ALPHA, UI:SAVE:CONTAINER_ALPHA and friends must be declared (255) in the XML or every window renders fully transparent. Likewise UI:SAVE:*_ROLLOVER_FACTOR are inverted factors: 255 = no fade-out when the mouse is not over the window, 0 = fade to invisible.
  • Root-level plain <group>s are parsed but never drawn — only windows registered on the master group (containers via <tree>, modals via enableModalWindow) render. Put HUD-style elements inside a registered window.
  • %case_upper etc. are interface defines, not built-ins — declare case_normalcase_first_word_letter_up (0…5).
  • The texture lookup normalizes .png.tga on both the atlas-load and lookup sides, so interface XML keeps the canonical .tga names while the atlas is packed from .png sources. The color picker is the exception: it reads its palette as a loose file (it needs pixel access), so the palette png is staged next to the atlas and CPath::remapExtension("png", "tga", true) is set before the search paths are indexed.
  • Container windows compute their height from the content group unless resizer="true"; a fixed-size window with resizer="false" and an empty-sized content collapses to its title bar.
  • dbview_quantity's registered class name is text_quantity (not quantity).
  • Two NLGUI bugs fixed while building this: CEventListener never subscribed keyboard events (edit boxes were deaf outside the client), and CGroupTree::SNode::parse crashed on a <node> with opened= but no show= (wrong pointer guarded).

Emscripten / WebGL build (M3)

The GUI stack builds for the web sample target. Recipe (dev-box; prefix paths are examples):

  1. Cross-build the static deps with the emsdk into a local prefix:
    • Lua 5.1.5: make generic CC="emcc -O2" AR="emar rcu" RANLIB=emranlib, make install INSTALL_TOP=<prefix>.
    • libxml2 (2.12.x): emcmake cmake with -DBUILD_SHARED_LIBS=OFF -DLIBXML2_WITH_{PYTHON,ICONV,LZMA,ZLIB,TESTS,PROGRAMS}=OFF.
    • ryzom/luabind: emcmake cmake against the wasm Lua. Boost is headers-only here, but do not pass /usr/include as the Boost include dir — host headers poison the emscripten sysroot; symlink boost/ into a clean directory instead.
  2. curl/openssl are not builtnel/samples/gui/emscripten/nel_wasm_stubs.c (compiled with emcc, archived as libcurl.a/libssl.a/libcrypto.a in the prefix, with real curl headers copied alongside) provides symbol-level stubs whose entry points fail the way the real libraries would. NeL's error paths handle that; a JS missing-function import would abort at the first call instead (CCurlHttpClient::connect dereferences curl_version_info() unconditionally, so that one returns a static struct). SysV IPC stubs cover nelmisc shared memory.
  3. Configure the emscripten tree with WITH_GUI=ON WITH_WEB=ON WITH_LUA51=ON plus LUA_*, LUABIND_*, Boost_INCLUDE_DIR, LibXml2_DIR=<prefix>/lib/cmake/libxml2-<ver>, CURL_INCLUDE_DIR/CURL_LIBRARY, OPENSSL_* pointing at the prefix, and NL_BUILD_INTERFACE_TOOL=<host build>/bin/build_interface for the atlas pack. The root CMake emscripten branch probes these instead of compiling XML support out (nelmisc gains real CIXml on wasm this way).
  4. The sample selects UDriver::OpenGlEs3 under emscripten and defaults to the 1600x1200 showcase layout; the html page CSS-scales the canvas. Deploys next to the other WebGL samples (nl_sample_gui.html/.js/.wasm/.data).

Verified in headless chromium over SwiftShader WebGL: rendering matches the native build, including evaluated <link>s and the color picker palette.

Status

  • M1 (landed): skeleton — w_ layer0 container window with header/title/buttons, w_modal modal via enter_modal, mouse pointer, frame-capped loop, headless screenshot mode, xdotool-verified event chain under Xvfb.
  • M2 (landed): comprehensive widget coverage as described above, every interaction verified headless.
  • M3 (landed): Emscripten/WebGL build deployed with the other NeL web samples.

Clone this wiki locally