DreamGUI
The .dui Language

Timelines

Animations the file owns — tracks, keys, ease names, the event track, loop modes, and external for an animation edited in Sequencer.

A timeline block is an animation the file owns:

class /Game/UI/WBP_Reward

timeline Pulse {
    duration = 0.6
    loop     = PingPong

    Icon.RenderScale            : 0.0 = (1, 1, 1), 0.3 = (1.25, 1.25, 1) ease InOutQuad, 0.6 = (1, 1, 1)
    Row/Title.RenderTranslation : 0.0 = (-40, 0, 0), 0.2 = (0, 0, 0) ease OutCubic
    @0.3 -> Landed
}

timeline Celebrate external

Widget Root {
    + Overlay { }
    Image Icon
    Widget Row {
        Text Title { Text = "Reward" }
    }
}

It compiles into a UDreamWidgetAnimation in the root's animation component, gets its class member variable like any other animation, and plays through the same entry points (see Animation). Because the file owns it, every compile rebuilds it and the animation editor opens it read-only: edit the .dui, or hand the animation to Sequencer for good with external.

Block settings

SettingValuesDefault
durationsecondsthe time of the last key
loopOnce, Loop, PingPongOnce

Tracks

One line per track: a path of node ids (Row/Title, the chain from the animated widget down to the one it drives), the property it drives, and keys time = value, each with an optional ease. Times are seconds. A line with no path drives the widget the animation lives on:

timeline Grow {
    Width      : 0.0 = 200, 0.4 = 400 ease OutBack
    Icon.Color : 0.0 = #FFFFFF00, 0.4 = #FFFFFFFF
}

Widget Root {
    + Overlay { }
    Image Icon
}

The path is the same display-name path an animation binding already resolves through — a node id is its display name, so the language needed no path model of its own. The values are spelled the way they are everywhere else.

What a track may drive is exactly what the animation editor offers: a property marked Interp, named by its own name (RenderScale, Color) or by the label on its row (Width, Height — the Animatable* mirrors that exist so the anchor block can be keyed, since a struct has no property track). There is one list, so a line that compiles is a track you can see in the animation editor.

Note the asymmetry with assignment, which is deliberate: Width = 400 as an assignment is still DUI4001 pointing at AnchorData.SizeDelta. An assignment writes a property; a timeline drives a track.

A timeline drives numbers, 2-, 3- and 4-component vectors, and colours; anything else belongs in an external timeline.

CodeWhen
DUI2014a timeline block or line that is not the grammar
DUI3016two timelines of one name
DUI5015a track path that names no node
DUI5016a property not on the node, its visual or its behaviours, not marked Interp, or of a type no track can drive
DUI4003a key whose value cannot be read as a value of the property

Easing

Easing is a name out of EDreamTweenEase — one word list for the whole plugin — never a tangent quadruple: tangents are stored data, and a text form that expressed them would be four numbers nobody can write by hand, or lossy to read back. An ease written on a key shapes the segment from that key to the next one; with none it is Linear. An eased segment is sampled into several keys at compile time.

The names:

Linear, InQuad, OutQuad, InOutQuad, InCubic, OutCubic, InOutCubic, InQuart, OutQuart, InOutQuart, InSine, OutSine, InOutSine, InExpo, OutExpo, InOutExpo, InCirc, OutCirc, InOutCirc, InElastic, OutElastic, InOutElastic, InBack, OutBack, InOutBack, InBounce, OutBounce, InOutBounce.

The enum also has CurveFloat, but that points at a curve asset and a key has nowhere to put one, so it is refused by name. A name that is not a curve name is DUI5017.

Events

@time -> Name is a key on the block's event track: when playback crosses that time, the name is broadcast through the animation component's OnAnimationEvent.

timeline Intro {
    duration = 1.0
    Panel.RenderTranslation : 0.0 = (0, -60, 0), 0.5 = (0, 0, 0) ease OutCubic
    @0.5 -> Arrived
    @1.0 -> Finished
}

Widget Root {
    + Overlay { }
    Widget Panel
}

external

timeline Celebrate external
timeline Outro external

Widget Root { }

external builds nothing. It is a manifest entry: the animation lives in the asset, Sequencer edits it freely, and the file still lists it — which is what makes "what animations does this class have" answerable by reading the file. Material-parameter tracks, hand-shaped curves and anything else this layer cannot express stay external by design.

A compile checks the manifest both ways, with warnings: an animation in the asset that the file does not mention (it still works), and a listed one the class does not have.

To hand a file-owned timeline to Sequencer, write timeline Pulse external where the block was. The keys the previous compile built are carried onto that animation, and from then on it belongs to the animation editor.

Timelines are not merged across use: a timeline is an object on this class's tree and a class member variable, so importing one would mean two classes owning one animation.

The VS Code extension highlights and outlines timeline blocks and leaves their track lines alone: a track resolves against the widget tree and the engine's reflection, which one file's characters cannot see, so every verdict about one is the compiler's.

On this page