-
Notifications
You must be signed in to change notification settings - Fork 113
first_window
title: Your First Window description: A minimal working NeL interface file, explained line by line published: true editor: markdown
This walkthrough builds a complete little window — a click counter — in pure interface XML: no C++, no Lua. It exercises the four things every interface uses: a window, a view, a control, and the database.

The easiest playground is the GUI widget sample. Save the file below as hello.xml anywhere, then:
nl_sample_gui -d /path/to/your/dir --xml hello.xml
The sample boots the whole widget stack (skin textures, styles, fonts, master group) and parses your file on top, so your window appears next to the showcase windows. In the Ryzom client the equivalent iteration loop is /loadui user/hello.xml.
<interface_config>
<variable entry="UI:TEMP:HELLO" type="sint32" value="0" />
<group type="container" id="hello" w="260" pop_min_w="260" pop_max_w="260" h="130" pop_min_h="130" pop_max_h="130"
title="Hello NeL" resizer="true" global_color="false" header_color="UI:SAVE:WIN:COLORS:SAMPLE"
title_bar_open="false" open_button="false" right_button="true" lockable="false" openable="false"
opened="true" movable="true" active="true" x="450" y="560">
<group id="header_closed" x="0" y="0" h="12" posref="TL TL"></group>
<group id="header_opened" x="0" y="0" w="0" h="16" posref="TL TL"></group>
<group id="content" x="0" y="0" posref="TL TL" w="0" h="0">
<view type="text" id="label" posref="TL TL" x="12" y="-12" fontsize="14" shadow="true"
global_color="false" color="255 255 255 255" hardtext="You clicked:" />
<view type="text_number" id="count" posparent="label" posref="MR ML" x="8" fontsize="16" shadow="true"
global_color="false" color="150 255 150 255" value="UI:TEMP:HELLO" />
<ctrl style="text_button_16" id="bt_click" posparent="label" posref="BL TL" y="-12"
onclick_l="set" params_l="dblink=UI:TEMP:HELLO|value=add(@UI:TEMP:HELLO,1)" hardtext="Click me" />
</group>
</group>
<tree node="hello"></tree>
</interface_config><interface_config> — every interface file has this root element. All parsed files are merged into one document, so your file can reference templates, styles, options and database entries declared in files parsed before it (here: the sample's own definition files). See Interface XML structure.
<variable entry="UI:TEMP:HELLO" type="sint32" value="0"/> — creates a leaf in the interface database and seeds it with 0. The database is the reactive state store: widgets bound to a leaf update when it changes.
<group type="container" id="hello" ...> — a container is the standard window widget: title bar, close button, dragging, resizing. The attribute pile is normal for containers; the load-bearing ones here:
-
id="hello"— becomesui:sample:hello(the sample's master group isui:sample). Every element needs an id. -
x="450" y="560"with the default posref — window position. Interface coordinates are y-up: on a 720-high canvas,y="560"puts the window's top edge 160 pixels from the top of the screen. -
w/hpluspop_min_*/pop_max_*— the size and its resize clamps. A container computes its height from content unless pinned like this; a fixed window without clamps and with empty-sized content collapses to its title bar. -
title,movable,resizer,active="true"— self-describing.header_colornames a database entry, not a literal color.
header_closed / header_opened / content — containers look up these three child groups by id. Content goes in content; the header groups hold widgets shown on the title bar in the collapsed/expanded states (empty here, but they must exist for the layouts that use them). See Windows and containers.
The text view — <view type="text" ...> with posref="TL TL" x="12" y="-12": pin my top-left to the content group's top-left, then offset 12 right and 12 down (negative y moves down in the y-up coordinate system). See Layout.
The counter — <view type="text_number" value="UI:TEMP:HELLO"/> displays a database leaf and tracks it automatically: bind once, never repaint by hand. posparent="label" posref="MR ML" chains it to the label instead of the group: "my middle-left on the label's middle-right", i.e. inline to the right.
The button — <ctrl style="text_button_16" ...> uses a style defined by the sample (a text_button with the w_text_button_* skin at fontsize 10). The interesting part is the event wiring:
onclick_l="set" params_l="dblink=UI:TEMP:HELLO|value=add(@UI:TEMP:HELLO,1)"
Left-click runs the action handler set, whose params say: evaluate the interface expression add(@UI:TEMP:HELLO,1) — @path reads a database leaf — and write the result back to that leaf. The text_number view sees the change and redraws. That is the whole loop: control → action handler → database → view.
<tree node="hello"></tree> — registers the group as a window of the current master group. This is the step everyone forgets: an unregistered group is parsed, laid out and never drawn. Modal groups are the exception (auto-registered).
- Position and size machinery: Layout
- All the widgets: views, controls, windows and containers
- Making it react: database and links, action handlers
- Making it live: Lua scripting — the showcase's Minesweeper, inventory and console are pure XML+Lua