DreamGUI
The .dui Language

Writing a component

A widget Blueprint used inside other trees, written in .dui — props, events and emit, default and named slots, and how a host sets, hears and fills them.

A component is a widget Blueprint used inside other trees. Written in .dui, it declares beside its tree what its hosts (the files that place it) may set, what they may hear, and where they may put widgets of their own:

DeclarationOffers the hostCompiles into
props { … }properties to set or bindBlueprint variables, editable on instances
events { … }events to routeevent dispatchers
slot Nameholes to put widgets innamed slots

A whole component and its host:

// DUI/UI/Components/Row.dui
class /Game/UI/Components/WBP_Row

props {
    Text   Label
    Text   Value
    Number ValueIndex = 0
}
events {
    Picked(Number Index)
}

Widget Root {
    + HorizontalBox { Spacing = 12 }
    + UIButton { OnClick -> emit Picked(ValueIndex) }

    Text LabelText {
        Text <- Label
        @fill
    }
    Text ValueText { Text <- Value }
}
// DUI/UI/Settings.dui
class /Game/UI/WBP_Settings
use "UI/Components/Row.dui" as Row

VerticalBox Root {
    Spacing = 8
    Row Audio {
        Label = "Audio"
        Value = "On"
        Picked -> HandleAudioPicked
    }
    Row Track { Label <- GetTrackName() }
}

props

class /Game/UI/Components/WBP_Cycle

props {
    Text   Label
    Text   Value
    Number ValueIndex = 0
    Enum   /Script/MyGame.ERowKind Kind = Cycle
}

Widget Root {
    + HorizontalBox { }
    Text LabelText { Text <- Label }
}

One property per line: a type, a name, and optionally = default. The types are Text, String, Number, Integer, Bool, Color, Vector2, Asset, Class, and Enum followed by the enum's path. Each becomes a Blueprint variable of the class, editable on instances; a C++ parent's property of the same name and type is used instead of declaring one.

A host sets them like any property, and the component binds them (Text <- Label in the component). A host may bind a prop as well:

use "UI/Components/Row.dui" as Row

VerticalBox Root {
    Row Audio { Label = "Audio" }
    Row Track { Label <- GetTrackName() }
}

A prop has no setter — it is a Blueprint variable — so the binding writes it straight into the instance when the value changes and announces it (props are FieldNotify), and the component's own bindings on it show the change. This is the one setter-less property a binding accepts: a user widget's own variable, one way (<-, not <->).

CodeWhen
DUI2016a line that is not Type Name or Type Name = value, or props inside a node
DUI3019two lines declare one name
DUI6008at compile: a type with no Blueprint pin
DUI6009at compile: a name another member of the class already has with another type, or a widget's id
DUI6013at compile: a default its type cannot hold

events and emit

class /Game/UI/Components/WBP_Picker

props {
    Number ValueIndex = 0
}
events {
    Picked(Number Index)
    Closed
}

Widget Root {
    + VerticalBox { }
    Widget Choose {
        + UIButton { OnClick -> emit Picked(ValueIndex) }
    }
    Native.Button Close { OnClicked -> emit Closed }
}

Entries end at a line break or a ;, so a short list fits on one line: events { Picked(Number Index); Closed }. Parameter types are the props' types. Each entry becomes an event dispatcher of the class.

The component raises one from any route with emit, its arguments written as binding expressions (see Bindings and routes). A host routes it like any event:

use "UI/Components/Picker.dui" as Picker

Widget Root {
    + VerticalBox { }
    Picker Audio {
        Picked -> HandleAudioPicked
        Closed -> HandleClosed
    }
}
CodeWhen
DUI2017an entry that is not Name or Name(Type Param, …), or events inside a node
DUI3020two entries of one name, or one parameter twice
DUI6010emit of an event this file does not declare
DUI6011emit arguments that do not match the event's parameters
DUI6012an emit that cannot be compiled where it stands: inside a loop body, or on an event whose parameters Blueprints cannot take
DUI6014an event named like another member of the class

Slots

A component opens a hole its host fills with slot:

// DUI/UI/Components/ListPage.dui
class /Game/UI/Components/WBP_ListPage

style DetailPanel {
    AnchorData.SizeDelta = (600, 0)
}

HorizontalBox Root {
    Spacing = 24
    slot Rows default {
        + VerticalBox { Spacing = 15 }
        @slot SizeRule = Fill
    }
    slot Detail : DetailPanel
    slot Footer
}
  • A slot is written in the tree where the hole goes, and its name shares the space of node ids: two slots of one name are DUI3001 (the old DUI3007 is retired).
  • default marks the slot that content goes to when the host names none, in place of a GetDefaultSlotName override. One per file (DUI3022; default twice on one slot is DUI2019).
  • A block, or a style, lays the hole out: components, properties and slot lines — never children, since what goes in it is the host's (DUI2019).
  • A slot with a layout container takes several widgets; one without takes one.
  • (was: OldName) renames a slot, as it does a node.

Filling them

A host fills the default slot by nesting, and a named one with a slot block of its own inside the instance:

use "UI/Components/ListPage.dui" as ListPage
use "UI/Components/Row.dui" as Row

Widget Root {
    ListPage Page_2 {
        Row Item_0 { Label = "Warped Wire" }        // into the default slot, Rows
        Row Item_1 { Label = "Large Gear" }
        slot Detail {                               // into the slot named Detail
            Text Note { Text = "A length of wire bent out of shape." }
        }
    }
}

A fill holds widgets and nothing else. A named slot holds one widget — put several in a container (unless the component declared that slot with a layout container of its own).

CodeWhen
DUI5019slot Name { … } filling a slot under a node that is not a component instance
DUI5020a fill naming a slot the component does not declare
DUI3009a parent that will not take this child: a full content widget, a named slot filled twice
DUI2019a block that both declares and fills

A widget dropped in the designer into a named slot of an instance is written into that slot's slot Detail { … }, which is written first when the instance has none. See Designer write-back.

To animate a component inside a panel, use its render transform: RenderTranslationTo, RenderOffsetTo, RenderScaleTo, RenderAngleTo. They move, scale and turn what is drawn and never the layout; the panel writes the anchored position and size back on its next pass, so a tween of those is overwritten. See Animation.

On this page