Overview
What a .dui file is, where it lives, the statements it is made of, and how it becomes a widget Blueprint's hierarchy.
A .dui file is the source of a DreamGUI widget Blueprint: a tree of widgets, their properties, their bindings and their
animations, as text. The Blueprint names the file as its Source File, compiling the Blueprint reads the file and builds
the hierarchy from it, and the designer writes its edits back into the file.
Text replaces the authoring step, not the runtime. A class compiled from a .dui and a class built by dragging widgets
around are the same kind of class: the same generated class, one member variable per widget, the same property bindings,
the same designer. The file is read at compile time, because that is the only moment that can declare a member
variable per widget, resolve a binding against the functions the class declares, and check the whole tree. So one class
has one file: SourceFile is a class default (EditDefaultsOnly), not something each instance can point elsewhere.
Where a file lives
Under a DUI/ directory: the project's own <Project>/DUI/, or an enabled plugin's <Plugin>/DUI/. Not under
Content/: a .dui is source, not an asset. Under Content/ the cooker would walk it, the Content Browser would show a
file it cannot open, and a plugin shipping widget classes would have to ship its sources inside its cooked content to have
them found at all.
A path to another file — in a Blueprint's Source File, or after use — is written one of three ways:
| Spelling | Means |
|---|---|
Panels/Settings.dui | Relative to a DUI/ root: the project's first, then each plugin's in plugin-manager order; the first that has the file wins. |
Plugin.MyPlugin:Panels/Settings.dui | The DUI/ directory of plugin MyPlugin. |
D:/Work/Proj/DUI/Panels/Settings.dui | Absolute, used as written. |
The first is convenient, and ambiguous the moment two roots hold the same relative path — which is what the second is
for. When you pick a file with Set Source File..., the portable spelling is what gets stored: a relative path under the
project's root, Plugin.X:… under a plugin's, and an absolute path only for a file outside every root (a path that then
works on one machine). The Source File is editor-only data and is stripped at cook: the hierarchy was baked into the
generated class long before.
File structure
A file is a list of statements. A statement ends at the end of its line or at a ;. Comments are // … to the end of
the line and /* … */.
At the top level a file holds:
| Statement | Purpose | See |
|---|---|---|
class /Game/UI/WBP_Settings | The Blueprint this file is the source of. At most one. | below |
use … | Another file or class made available to this one. | use |
resources { … } | Named constants. | Styles and resources |
props { … } | Properties this file's class declares. | Writing a component |
events { … } | Events this file's class raises. | Writing a component |
style Name { … } | A named set of lines. | Styles and resources |
timeline Name { … } | An animation. | Timelines |
| one node | The root of the tree. | Nodes and values |
The order of these is free: a style may be declared below the node that wears it. A file compiled into a class has
exactly one root node; none or more than one is DUI2006 (with no root, the build
also reports DUI5009, the parser's message saying why). A file with no root is a
library: it can be used, and it cannot be compiled.
The mistakes a whole file can make: a character that cannot begin any token is
DUI1001; a /* that never reaches */ is
DUI1003; a { with no matching } is
DUI2002, a ( with no matching )
DUI2003; a token where the grammar wants something else is
DUI2001, and the message names both. Blocks or parentheses nested deeper than 256
are DUI2013: no hand-written or generated screen comes near that, so reaching it means
a malformed file, and a line number beats recursing into a stack overflow that takes the editor — and the unsaved work in
it — down.
The class line
The class line does two jobs:
- It says which Blueprint the file is the source of. Compiling it into a different one warns (DUI6003).
- Its path is the namespace of every localized string in the file, which keeps the keys stable when the file is renamed. Without one, the file's own name is the namespace.
class twice, with an empty path, or inside a node is DUI2007.
It is also where another file reads the class from when it names this one as a component with use "…" as Row; without
the line, only the editor can answer, by finding the Blueprint whose Source File this is (see use).
So give every component file a class line.
Attaching it to a Blueprint
Make a text-backed Blueprint
Create a DreamUI widget Blueprint in the Content Browser and pick DreamUI Text User Widget (UDreamTextUserWidget)
as its parent class. Only that class's descendants carry SourceFile; with a plain UDreamUserWidget parent the
designer toolbar shows no Source File button. Put it where the file's class line says, or edit the line to match.
Point it at the file
Open the Blueprint. The designer toolbar has a Source File combo button (it reads No Source File until one is set). Its menu holds:
- Create Source File...: only while there is no file. Opens a save dialog (in the project's
DUI/directory, made first if there is none), writes a starter.duithere (a root and one centred label), points the class at it, and compiles — so the first compile answers "did this work" by appearing. - Set Source File...: choose an existing
.dui, then recompile. - Open in Default Editor, Show in Explorer, Reveal in VS Code: open the file, select it in the file browser, or put VS Code's cursor on the line of the selected node.
Compile
The compile reads the file, builds the tree and installs it as the Blueprint's hierarchy. Mistakes show in the compiler
results as DUInnnn diagnostics. A file that does not exist or cannot be read is
DUI6001; one that parsed and produced no tree is
DUI6002.
Show it
UDreamUIBPLibrary::AddWidgetOfClassToViewport adds the class to the viewport. The screen root, the raycaster and the
event system are created on demand; nothing else needs configuring. The full path is in
Your first screen.
From then on, saving the .dui recompiles the loaded Blueprints built from it, and the classes of files that use it.
A class that is not loaded is not updated, and loading a Blueprint does not recompile it; Rebuild DUI in the Tools
menu recompiles every text-backed widget Blueprint in the project, loading the ones that are not (it asks first).
Deleting or renaming a file a loaded class reads is reported when it happens, rather than as DUI6001 at the next compile.
A first file
The plugin ships Content/Samples/HelloDreamGUI.dui, the smallest .dui that is still a real screen. Copy it into your
own project's DUI/ before editing it: a plugin update overwrites the copy in the plugin folder. Here is a shorter file of
the same shape:
// DUI/UI/Hello.dui
class /Game/UI/WBP_Hello
style Title {
Font = /DreamGUI/DefaultFont_DistanceField
FontSize = 28
Color = #E6E9F0
HAlign = Center
}
Widget Root {
// Anchored to all four corners with no inset: fills whatever it is added to, at any resolution.
AnchorData.AnchorMin = (0, 0)
AnchorData.AnchorMax = (1, 1)
AnchorData.SizeDelta = (0, 0)
+ Overlay { }
Overlay Card {
AnchorData.SizeDelta = (420, 120)
@slot { HorizontalAlignment = Center VerticalAlignment = Center }
// Drawn first, so it is behind: an Overlay stacks its children in declaration order.
Image Backdrop {
Brush.TintColor = #1B1E26
@slot { HorizontalAlignment = Fill VerticalAlignment = Fill }
}
VerticalBox Column {
Spacing = 10
Padding = (24, 28, 24, 28)
@slot { HorizontalAlignment = Fill VerticalAlignment = Fill }
Text Heading : Title {
AnchorData.SizeDelta = (0, 34)
Text = "Hello, DreamGUI"
WrapTextAt = 372
@slot { HorizontalAlignment = Fill SizeRule = Auto }
}
}
}
}Part by part:
classsays this is the source of/Game/UI/WBP_Hello.style Titleis a named set of properties;Text Heading : Titlemakes the node wear it, and the node's own lines override the style's.Widgetis a rect with no visual, there to hold others;+ Overlay { }attaches a layout container that stacks its children.Overlay CardandVerticalBox Columnare the short way to say the same thing: the type is the container, andSpacingandPaddingset the container's properties.ImageandTextare nodes whose visual draws something.@slotis what this node asks of the panel above it, not a setting of its own.AnchorData.SizeDeltais the node's own rect;@slot SizeRuleis the parent's arrangement of it. The distinction is worth reading twice.- A column asks how tall a child wants to be before it gives the child a width, so a text in a Fill slot names a width to
break at with
WrapTextAt: here the card's 420 less the column's 24 on each side. Without it the heading is measured at its own width, 0, as one character per line.
Diagnostics
Every message has a code, DUInnnn, printed as File.dui(line,col): error DUI3001: …. The front end recovers and reads
on: a file with five mistakes reports five, not the first. The first digit says which stage refused:
| Codes | Stage |
|---|---|
| DUI1xxx | lexer: characters that are not a token |
| DUI2xxx | parser: tokens that do not form the grammar |
| DUI3xxx | meaning: grammatical, but the tree does not hold together (a duplicate id, an unknown type) |
| DUI4xxx | values: a value that does not fit the property it is written on |
| DUI5xxx | building the tree: reflection refused the write, or the target does not exist |
| DUI6xxx | compiling: the .dui could not be attached to its Blueprint |
| DUI7xxx | writing back: the designer's edit could not be put into the text |
The VS Code extension reports what one file settles as you type, and while the editor runs it also delivers every compile's verdicts to the Problems panel; see the VS Code extension.
The other pages
Nodes and values
Nodes, ids, nodes with no id, the order node types resolve in, properties and every kind of value.
Slot lines and components
How @slot and @fill tell the parent panel where a node goes, and how + Class attaches behaviours and containers.
Styles and resources
Style inheritance, what a style may carry, and resources blocks of named constants.
use
Merging a library, short names for components and classes, namespaces, re-export, and the older @Name.
Writing a component
props, events and emit, default and named slots, and how a host fills them.
Bindings and routes
The three arrows: driving a property, routing an event, mirroring both ways; and Shown.
if and for
Switching branches on a condition, repeating a template from data, and the rules both keep.
rows tables
Many instances of one component as a table, each row named from its first value.
Timelines
Animations the file owns: tracks, keys, ease names, events, and external.
Designer write-back
Which line the designer writes an edit into, and which edits it refuses, and why.
Worked example
A settings screen from three files — a component, a library and the screen — walked through.
Animation
The three ways to animate — tweens from the DreamTween module (RenderOffsetTo, RenderScaleTo, RenderAngleTo and RenderTranslationTo for widgets inside panels, EDreamTweenEase, DreamTween.MaxStepSeconds), widget animations (UDreamWidgetAnimation and Sequencer), and .dui timelines.
Nodes and values
How a node is written, the rules an id keeps, nodes with no id, the order node types resolve in, and every kind of value a property takes.