DreamGUI
Concepts

The widget model

What a UDreamWidget is — a UObject with a rect, anchors and a pivot, carrying a visual, a layout, a slot and behaviours, arranged in a tree — plus render transforms, opacity and visibility, and the sizing difference from UMG.

A widget in DreamGUI is a UDreamWidget: a UObject with a rect, a pivot and anchors, arranged into a tree and drawn by a canvas that batches the whole tree into as few draw calls as it can. That is Unity's uGUI shape rather than Slate's. It is not an actor and not an actor component; there is no SWidget behind it, so none of UMG's widgets, styles or bindings apply.

A widget on its own draws nothing and arranges nothing. What it draws, how it arranges its children, where its parent puts it and what logic it runs are each decided by a sub-object it carries — in the words of ConstructWidget's comment, "panel" is a sub-object here, not a separate class.

What a widget is made of

PartProperty / entry pointDecides
RectAnchorData (FDreamUIAnchorData)Where it is and how big, relative to its parent
VisualVisual (UDreamVisual), at most one per widgetWhat it draws
Layout containerLayoutContainer (UDreamLayoutContainer)How it arranges its children
Layout selfLayoutSelf (UDreamLayoutSelf)A sizing rule of its own: an aspect ratio, a spacer
Panel slotPanelSlot (UDreamPanelSlot)How its parent's panel places it
BehavioursThe component array: AddComponent, GetComponent, GetComponentsLogic: buttons, canvases, navigation scopes…
ChildrenGetChildren, AddChild, SetParentThe tree

In a .dui the parts are easy to tell apart: the node type is a visual (Text, Image) or a layout container (VerticalBox), + Class { } attaches a behaviour or a container, and @slot lines are what the parent's panel does with the node.

Widget Root {
    // All four anchors on the parent's corners, no inset: fills the parent at any resolution
    AnchorData.AnchorMin = (0, 0)
    AnchorData.AnchorMax = (1, 1)
    AnchorData.SizeDelta = (0, 0)

    Widget Card {
        // Anchored to one point, the parent's centre: SizeDelta is its size, AnchoredPosition the pivot's offset from the anchor (y up)
        AnchorData.AnchorMin = (0.5, 0.5)
        AnchorData.AnchorMax = (0.5, 0.5)
        AnchorData.SizeDelta = (420, 220)
        AnchorData.AnchoredPosition = (0, 40)

        // A visual: a backdrop that fills the card
        Image Backdrop {
            Brush.TintColor = #1B1E26FF
            AnchorData.AnchorMin = (0, 0)
            AnchorData.AnchorMax = (1, 1)
            AnchorData.SizeDelta = (0, 0)
        }

        // A behaviour: a plain widget carrying the button behaviour
        Widget Confirm {
            AnchorData.SizeDelta = (160, 44)
            + UIButton { TransitionType = None }
            Text Label { Text = "OK" }
        }
    }
}

The rect: anchors, pivot, size

FDreamUIAnchorData has five fields, and its defaults are a 100 × 100 square anchored at the parent's centre:

FieldDefaultMeaning
AnchorMin / AnchorMax(0.5, 0.5)Two normalized points in the parent's rect. Equal on an axis, the widget is pinned to a point on that axis; different, it stretches along it
SizeDelta(100, 100)The size, on a pinned axis; on a stretched one, the size beyond the anchor span — 0 fits exactly, negative insets
AnchoredPosition(0, 0)The pivot's offset from the anchor
Pivot(0.5, 0.5)A normalized point in the widget's own rect, (0, 0) bottom-left and (1, 1) top-right

Coordinates run y up: Pivot, RenderTransformPivot and PerspectiveOrigin all put their origin at the bottom left, unlike UMG's top-left origin — deliberately, because consistency inside the plugin beats consistency with the other engine.

At run time each field has its setter: SetAnchorMin, SetAnchorMax, SetAnchoredPosition, SetSizeDelta, SetPivot, and the result-shaped SetWidth, SetHeight and SetAnchorOffset (the four edge distances of a stretched widget). Each has a tween too — AnchoredPositionTo, SizeDeltaTo, WidthTo, HeightTo — returning a UDreamTweener (see Animation).

RelativeLocation can be set as well, but its setter recomputes the anchors and asks the parent to lay out again; a .dui spells AnchorData only, so the same position is never written twice.

Box-first: the sizing difference from UMG

The difference is structural rather than cosmetic, and worth knowing before you commit to either:

UMG / SlateDreamGUI
WidgetSWidget, retained-mode SlateA UObject in a component-like tree
SizingContent-sized: a widget's size is its desired sizeBox-first: you author a rect, content is arranged inside it
TextThe box grows to the textThe text is aligned in the box, and may overflow it
PlacementSlot-relativeAnchors + pivot, resolution-independent
ReuseWidget Blueprint subclassingWidget Blueprint subclassing, plus named slots for content
In the worldWidgetComponent, a rendered quadA first-class render mode

The text row is the one that surprises people. A UMG TextBlock cannot overflow, because its box is derived from the text. Here the box is authored, so text can overflow it, and you get controls UMG has no need for — Margin, LineHeightPercentage, WrapTextAt and Best Fit; see Text.

Box-first does not mean there is no content size. Once a widget is inside a panel, the panel asks it how big it wants to be (GetDesiredSize) and writes the arranged rect back into its AnchorData. A visual answering that question with a negative number abstains, and the authored rect is used; 0 is a real answer. The full rules are in Layout.

Visuals: what a widget draws

A visual (UDreamVisual) is a sub-object of its widget, at most one per widget. A widget without one is a rect — Widget in a .dui — which is what a pure container is. Every visual has Color (sRGB, keyable in Sequencer), bRaycastTarget and RaycastType (Rect, Mesh, VisiblePixel, Custom).

.dui tagClassModuleDraws
TextUDreamTextDreamGUIText; see Text
ImageUDreamImageDreamGUIAn image brush
RectBlockUDreamRectBlockDreamGUIA procedural block: corner radii, a body, a border, inner and outer shadows, the body and border optionally graded
Sprite / TextureUDreamSprite / UDreamTextureDreamGUIAn atlas sprite / a whole texture
EmptyUDreamVisualEmptyDreamGUINothing, but takes raycasts
Ring / Polygon / PolygonLineUDreamRing / UDreamPolygon / UDreamPolygonLineDreamGUIExtensionsA ring, a polygon, a polygon outline
Line2DRaw / Line2DChildrenUDream2DLineRaw / UDream2DLineChildrenAsPointsDreamGUIExtensions2D lines: through given points / through the children
StaticMeshUDreamStaticMeshDreamGUIExtensionsA static mesh, a draw call of its own
BackgroundBlur / BackgroundPixelate / PixelSortUDreamBackgroundBlur / UDreamBackgroundPixelate / UDreamPixelSortDreamGUIExtensionsScreen effects; see Rendering
CanvasRenderTargetPreviewerUDreamCanvasRenderTargetPreviewerDreamGUIExtensionsWhat a render-target canvas drew

Visuals come in three kinds (EDreamVisualType): BatchMesh, which the canvas batches and which almost every visual is; DirectMesh, which writes its own mesh section and is a draw call each, for content with many vertices that change often (the static mesh); and PostProcess, an effect that reads and writes the screen. A plugin can add a visual of its own, registering a .dui tag for it with DECLARE_DREAM_GUI_VISUAL.

Procedural shapes — the ring, the polygon, the polygon line — have no answer to "how big do you want to be": their vertices are derived from the rect they are given. They abstain from measurement and follow the authored rect.

Behaviours: logic on a widget

Behaviours are subclasses of UDreamUIBehaviour, held in order in the widget's component array. They have a Unity-shaped life cycle — Awake, OnEnable, Start, Tick, OnDisable, OnDestroy — plus callbacks such as OnDimensionsChanged, OnAttachmentChanged and OnInteractableChanged; a Blueprint subclass gets events of the same names at the same moments. bStartWithTickEnabled and bTickEvenWhenPaused govern its tick.

Most of the plugin is behaviours: the canvas itself (UDreamCanvas), the UI* family the Dream* controls are built from (UIButton and its siblings), navigation scopes, named slots (UDreamNamedSlot), drag sources and drop targets, the mesh modifiers (shadow, outline, gradient colour, text animation). In code, AddComponent, GetComponent, GetComponents, GetComponentByInterface, RemoveComponent and MoveComponentToIndex; in a .dui, + UIButton { … }.

Render transform: drawing moves, layout does not

A render transform moves where a widget is drawn without telling layout anything. Layout still measures and arranges it exactly as before, siblings do not shift, and nothing is ever written back to AnchorData. That is the whole difference from RelativeLocation, whose setter recomputes the anchors and asks the parent layout to rebuild — which is why animating that inside a panel never works: the animation and the layout just take turns.

PropertyTypeNotes
RenderTranslationFVectorLocal space. X is depth and points away from the viewer, so a negative X brings the widget toward the screen
RenderRotationFRotatorAbout RenderTransformPivot, in degrees; SetRenderTransformAngle writes only the in-plane channel
RenderScaleFVectorAbout RenderTransformPivot
RenderTransformPivotFVector2DNormalized in the widget's own rect, (0, 0) bottom-left; independent of AnchorData.Pivot, which belongs to layout
RenderShearFVector2DA slant in degrees, applied to the widget and its whole subtree

It is three-dimensional on purpose: the transform it mirrors is already an FVector / FQuat / FVector, world-space canvases exist, and a card flipping about its vertical axis is an ordinary thing to want. The batching rule is the one the authored transform already has: rolling, scaling and sliding in the canvas plane stay batched; a widget that gains depth, yaw or pitch leaves the batched 2D path, so a flip costs a draw call — exactly as it would if you had authored the rotation.

RenderShear differs in two ways: layout never sees it, and hit testing does not follow it — a sheared widget is clicked on its unslanted rect (the raycaster switches to the matrix path only inside a perspective scope). It is also not keyable, while the other three channels are. ClearRenderTransform puts them all back in one invalidation; HasRenderTransform answers whether drawing currently differs from layout.

A widget whose render transform keeps changing is made a render layer by its canvas and moved on the GPU; see Rendering. To animate a widget inside a panel use RenderOffsetTo, RenderScaleTo, RenderAngleTo and RenderTranslationTo; see Animation.

Perspective, per widget

bPerspective makes a widget establish a perspective for its descendants, in the shape CSS perspective has: children with depth are foreshortened toward an eye standing in front of this widget's plane. PerspectiveFieldOfView (60 degrees by default) sets how far away the eye stands and PerspectiveOrigin is the vanishing point (CSS perspective-origin). A nested perspective nests rather than overrides. It costs nothing when off, and nothing for a subtree with no depth in it — a flat widget comes back untouched. It needs a screen-space canvas with a perspective projection and is inert otherwise; see Canvas.

Visible, transparent, interactive

Each switch decides one thing; do not use one for another:

SwitchDecides
Visibility (EDreamWidgetVisibility)One for one with UMG: Visible, Hidden (takes space, not drawn), Collapsed (takes no space), HitTestInvisible, SelfHitTestInvisible
ShownVisibility as a yes or no: true is Visible, false is Collapsed. It stores nothing of its own; it is what Shown <- HasSave() binds in a .dui
bWidgetActiveThe behaviour life-cycle switch: inactive is invisible, takes no layout space, is not interactive and not hit-testable, and its behaviours hear OnDisable
bIsEnabled / SetIsEnabledUMG's enabled switch: the subtree stops taking input and its controls take on their disabled look; GetIsEnabledInHierarchy is the cascaded answer
Interactable, RaycastableThree-valued (Inherit, Enabled, Disabled) interactivity and hit-testability
RenderOpacityOpacity, multiplied down the tree; GetFinalRenderOpacity is the product
SetContentTintTints the visuals of the descendants (not its own), as UMG's content colour does

Shown never undoes hidden or hit-test-invisible: a Hidden widget set to shown stays Hidden. Clipping is Clipping (Inherit, ClipToBounds, ClipToBoundsWithoutIntersecting, Disabled), with ClippingCornerRadius and ClippingMargin. OnVisibilityChanged, OnFocusReceived and OnFocusLost are the three events a widget offers for binding.

Making widgets in code

UDreamUIBPLibrary::ConstructWidget(WorldContext, DisplayName, VisualClass) is the counterpart of UMG's WidgetTree::ConstructWidget<T>(): the widget exists but is not on screen. It draws nothing and its behaviours do not run until you hand it to AddChild or AddToViewport; until then the DreamUI manager holds it, so it is not collected while you configure it. Leave VisualClass empty for a pure container, then give it a layout container to make it a panel.

CallResult
AddChild(Child, SiblingIndex)Attaches it and returns the child's UDreamPanelSlot (one slot class for every panel, so nothing to cast)
RemoveChild / RemoveFromParentDetaches it and keeps it alive — still registered, subtree intact, back in the not-yet-added state; the verb to pool with
DestroyChild / DestroyWidgetTears the subtree down; IsValid answers false afterwards
FindChildByDisplayNameFinds a child by display name, paths included: "Content/ListItem/NameLabel"

An instance of a widget Blueprint is made with UDreamUIBPLibrary::CreateDreamWidgetOfClass or AddWidgetOfClassToViewport; see Widget Blueprints. Every property and function is listed in the UDreamWidget reference, which ends with a member-by-member comparison with UMG's UWidget.

On this page