Changelog
What changed in each DreamGUI version, from 1.0.0, the first public release, back through the 2.1.0 and 2.0.0 development builds.
What changed in each version of DreamGUI, grouped by what it changes for a project. Moving a project from one version to
the next — what loads differently, which defaults moved, what C++ has to change — is in
Upgrading; a project coming from LGUI, LexUI or the early 1.x builds starts at
Migration. This page follows the repository's CHANGELOG.md:
https://github.com/TypeDreamMoon/DreamGUI/blob/main/CHANGELOG.md
1.0.0 is the first public release. The builds before it were the fork's development line: the single-module 1.x
builds, then 2.0.0 and 2.1.0 (commits 2416e3f8 and 3049561a on main). Public numbering starts again at 1.0.0, so a 2.0 or a 2.1
below is a development build, and older than 1.0.0. The descriptor's integer Version keeps rising — 100 for 1.0.0,
after 3 for 2.1.0 — so the engine never takes 1.0.0 for the older of the two.
1.0.0
Everything of the 2.1.0 development build, and the .dui language grown into what screens are written in: components
written in .dui alone, use … as, if, for, rows, shorter layout; the reference for all of it; and the fixes the
first two showcase screens found. Projects on 2.1.0 open unchanged (see Upgrading, “From 2.1
to 1.0.0”), except for assets that still loaded through a redirect: the plugin ships none any more.
Removed
- The CoreRedirects.
Config/DefaultDreamGUI.iniand its 763 redirects — from LGUI and LexUI, from the prefab vocabulary, from the control and event renames and from the module split — are gone, and the engine no longer applies any of them to a project that mounts the plugin. The plugin's own assets and the test fixtures name the current types. An asset saved against an old name is resaved once with 2.1.0's block borrowed into the project'sConfig/DefaultEngine.ini(Upgrading). The fourDreamGUI.Packagingtests that held the redirect file went with it, andDreamGUI.Packaging.ThePluginShipsNoCoreRedirectskeeps a[CoreRedirects]section from coming back unnoticed. - The old-asset fixtures saved before the module split. They loaded only through the redirects; the test host's
DreamGUIFixturesare saved by 1.0.0 now, the baseline later versions keep loading. Tools/ModuleSplit/generate_split_redirects.py, which appended a split's redirects to that file. It ismoved_types.pynow: it lists the types a split moved and, with--redirects, prints a block for a project's own config, and writes nothing into the plugin's.retarget_script_paths.pytakes the names from it.
New
rows: a table of instances in.dui.rows Row : ListRow (Label, Description) { "City Ruins", "…" … }writes the type, the style and the property names once, then one line per instance — the same component N times, differing in a few values, which is what a settings page or a list of menu entries is made of. The table is read into the ordinary unnamed children its lines stand for, so nothing downstream changed. Each row is named from its first value (Page_0__Row_City_Ruins), so inserting or reordering rows moves no other row's id or localization keys. A line may end in a block for what that row needs beyond the columns. The designer writes a column's value back into its cell and refuses, with the reason, what a row's line cannot spell (DUI7004). New codes DUI2020 MalformedRows and DUI3023 DuplicateRowKey (a warning). Seerows.- Components by a short name in
.dui.@Row Row1 { }is a node whose type is named by theAssetentryRowof aresourcesblock — this file's, or one ausebrought in — so a family of components is named once, in the library that styles it, and each screen writes@Rowinstead of the asset path on every line. An entry that is missing, is not anAsset, or names no user widget is reported as such. use … asin.dui.use "Components/Row.dui" as Rownames the class a component file compiles into — itsclassline, or the Blueprint whose Source File it is — anduse /Game/UI/WBP_Row as Rowa class with no.dui;Row Row1 { }is then an instance.use "Lib.dui" as nieron a library enters its styles, resources and component names undernier.(: nier.Label,@nier.Ink,nier.Row), so two libraries can be used side by side, and theuse … aslines of a library travel with it.@Rowkeeps working. Seeuse.- Components written in
.duialone. A file declares what its class offers its hosts:props { Text Label }(Blueprint variables, with defaults, which a host sets or binds —Label <- GetName()— even though they have no setter; a C++ parent's property of the same name is used),events { Picked(Number Index) }(event dispatchers, raised withOnClick -> emit Picked(Index)and routed by a host like any event), and slots that carry a layout and a style and can be markeddefault— the slot nested content goes to, in place of aGetDefaultSlotNameoverride. A host fills a named slot withslot Detail { … }inside the instance. See Components. - Shorter layout in
.dui. A layout container is a node type (VerticalBox Column { Spacing = 29 }), a node nobody refers to needs no id (HorizontalBox { … }), slot lines group into@slot { … },@fill/@fill 2stand for the two written most, and a style can carry+ Componentblocks and slot lines, so a kind of column is one name. ifandforin.dui.if Cond { } else if … { } else { }shows one branch's widgets at a time (each gets aShownbound to its branch; a hidden branch keeps its state),Shown <- Expris visibility as a yes or no on every widget (UDreamWidget::Shown), andfor Item in Items { … }makes one copy of its template per item inside any panel, whereeachkeeps filling a virtualized list view. See Control flow.- The language reference.
Docs/DuiLanguage.md— on this site, the.duisection — describes the whole of.dui: file structure, types, values, styles,use, components, bindings, the control statements, timelines, with a table of every diagnostic (Diagnostics). The editor's symbol dump (DUI/.dui-symbols.json) lists the keywords, the container types and their properties for an editor's completion. - The designer writes the new syntax back. An edit lands on the line that holds the value whatever the node's type
is written as, inside a
@slot { … }block when there is one, and on a node with no id by the id made for it; a node made in the designer is written by the file's alias for its class, or as its container; content dropped into a component's named slot is written into that slot's fill. What no line spells is said rather than written: a visibility anifdecides and a shorthand that cannot take the change are DUI7004, a value from a style's component DUI7005. See Write-back. - Render-transform tweens.
UDreamWidget::RenderTranslationTo,RenderOffsetTo(on the canvas plane, depth kept),RenderScaleToandRenderAngleTomove, scale and turn what is drawn, never the layout: the tweens for a widget a panel places, whose anchored position and size the panel writes back on its next pass. - A control can refuse the focus ring.
UUISelectable::bUseFocusRing(on by default;SetUseFocusRingat run time) turns the project's ring off for a control whose own look marks its focus. The focus moving onto such a control takes the ring away instead of leaving it on the control it came from, and a screen whose controls all refuse it never makes one. An unsetNavigationSelectionClassnow means no ring anywhere, without a warning at every focus change. - A cap on how far one frame moves the tweens.
DreamTween.MaxStepSeconds(console variable; 0, the default, is off): a frame longer than it — a screen loading its assets, the first draw of new text — moves the tweens the world ticks by the cap only, so an entrance started just before such a hitch plays on from where it was instead of appearing at its end. Manual ticks are never capped.
Fixed
- The focus ring fits the control it marks. The plugin's ring drew its frame on a child authored at a fixed 100x100 in the centre of the ring's root, so the frame stayed 100x100 over a full-width row and a small icon alike, while only the invisible root took the control's size. A ring class with no Blueprint handler of its own now has each child that covers its root (centre-anchored, at least the root's size) anchored to stretch with the root, its authored margin kept; smaller or corner-anchored children keep their own size.
- A ring that has just appeared no longer stays transparent. Moving the ring killed its running tweens, the fade-in of a ring shown a moment before among them, so a second key press inside a quarter second — or the same focus arriving as a select and then a navigation enter — left the ring at the opacity the fade had reached. Moving now finishes the fade.
- Focus moved by code leaves the control it came from.
SetFocusmoves the navigation cursor with the focus, but the control it left heard no exit until the next navigation step, so it went on drawing itself Focused — and answering Focused toGetCurrentSelectionState— beside the control that had the focus, through a screen opening a sub-list or a dialog, and through being hidden and shown again. A deselect now ends the navigation's hold on it. - What the built-in shader draws after an image drawn through a material is drawn by the built-in shader again. On a screen or render-target canvas, a draw through a material bound the material's pipeline but left the pass's cache of the built-in pipeline saying it was still bound, so the next text or block drew through the material's shaders: text after a material-drawn image went blank or turned to boxes, and the RHI ensured on parameters set for a vertex shader that was not bound. World-space canvases already set the built-in pipeline again; screen ones now do too.
- A brush material whose only parameter of the canvas's is the renderer flag is told the renderer. The canvas asked
a material for its texture parameters only, so
DreamUI_IsRenderByDreamUIRenderer, a scalar, never counted: a procedural material that premultiplies by it was drawn with the flag at its default, and premultiplied a second time by DreamGUI's renderer. - A material instance given to an image shows the parameters its owner sets on it. The canvas draws a material
through a proxy of its own that caches the material's uniform expressions, and rebuilt that cache only when
DreamGUI's own parameters changed: a
SetScalarParameterValueon the instance after its first draw never reached the screen, so an animated material stood still. The proxy now rebuilds its cache, as the draw is gathered, whenever the instance's own render proxy has.
2.1.0 (development build)
Five passes of engineering after 2.0: the frame made cheap enough for thousands of animated widgets, every control and
editor path audited, text measured against Chrome and Slate and rebuilt where it differed, gradient text, keyboard and
gamepad navigation, and what it takes to ship. DreamGUI.uplugin is version 3. Assets saved by 2.1 cannot be opened by
2.0.
New
- Gradient text. A text's face, its outline and an overlay over its face can each be painted with a gradient:
linear, radial, conic, diamond or four-corner, with up to 16 stops, pad, repeat or reflect, mixed in sRGB, linear light
or Oklab, measured across the text block, the content box, each line, each glyph or each rich-text run. Gradients are
written as CSS writes them (
linear-gradient(180deg, #FFF3B0, #E8B64A 55%, #9C6A12)) — in the details panel, in a.dui, or as a rich-text run (<gradient=Name>...</gradient>, the name being a custom style, a project preset or the CSS itself). Presets areUDreamGradientAssetassets or CSS strings in the project settings (GradientPresets); texts that paint with the same gradient share its row on the GPU. A paint's phase, angle, centre and scale animate without laying the text out or painting it again — keyable in Sequencer (FacePaintPhase,OutlinePaintPhase,OverlayPaintPhase,PaintAngleOffset) and tweened byUDreamTextPaintLibrary(PaintPhaseTo,PaintAngleTo,PlayShimmer). A material that does not shade throughMF_DreamUI_Shadepaints by vertex colour instead. - Gradients in the editor. A gradient property shows a strip of the gradient whose stops can be dragged, a CSS box with Copy and Paste, and a presets menu that lists the project's presets and gradient assets and saves the gradient as either. A gradient asset has a thumbnail and Copy as CSS. A text's paint fields are one Paint group in its details.
- Tab navigation. Tab and Shift+Tab, with no Ctrl, Alt or Cmd held, move the focus in hierarchy order, siblings
sorted by the new
TabIndex;bIsTabStopandTabNavigation(Continue, Cycle, Contained, Once, None) shape the walk; lists, tiles and trees are one stop, entered at their selected row; a stop scrolled out of view is scrolled into it. A modal, a dimmed dialog and a popup that cycles keep Tab inside them, and another player's popups are never stops. Tab commits a text field's edit and moves on, and arriving by Tab starts editing; a dropdown commits its highlighted row on Tab and a menu closes its chain. Slate's own navigation from the bare game viewport — Tab, the arrows — never moves the focus into UMG while the player has a DreamGUI focus or Tab stop.TabOrder = LegacyGeometrickeeps 2.0's geometric order for this release. - Gamepad and input modes. A navigation scope says whether its screen takes menu or game input (
InputMode: All, Menu, Game; Game turns built-in navigation, confirm and back off for that player). Keys and the pad act on the focus: Enter, Space and the pad's accept press the focused widget, never the hovered one, and nothing when nothing is focused. The focus look shows only when keys or a pad moved the focus, as CSS:focus-visibledoes (bFocusVisibleOnlyFromKeys), and in DreamGUI's UI-only input mode the cursor hides while a pad is in use (bHideCursorOnGamepad). The confirm, back, direction, page, extent and tab-switch keys are project settings (ConfirmKeys,BackKeys,DirectionKeys,PageKeys,ExtentKeys,PreviousTabKeys,NextTabKeys), read again when they change; the platform's accept and back buttons are read at run time (bUsePlatformAcceptBack); the triggers page, and the shoulder buttons switch the active tab view's tabs, with prompts in the action bar. Split screen: scopes, action triggers and the action bar belong to their widget's owning player unless given an index. - Text, measured against Chrome and Slate and brought to them: grapheme clusters and bidi levels with ligatures and
CSS line boxes; fallback faces chosen by Unicode range, culture and presentation, with real bold and italic faces;
colour emoji (CBDT/CBLC, sbix, COLRv0), whose shadow, outline and long-shadow copies are silhouettes; justification,
tab stops and a middle ellipsis; long texts laid out again only where an edit touched them; line breaking by the
game's culture; a rich-text
<lang=xx>tag; images in rich text sized and aligned. - Small text — up to
SmallTextMaxPixelSize, 20 px by default — is drawn from hinted coverage glyphs placed on the device pixel grid, as crisp as Slate's. Text with an outline, a glow or an underlay draws its face that way and its effects from the field (SmallTextEffectFace), and text in a render layer does once the layer has held still for 3 frames. A font keeps the atlas cells of its coverage glyphs while any text draws from them; past twiceMaxCoverageCells, new ones wait, drawn from the field meanwhile, with one warning per font. - The default font is Slate's: Roboto in real regular, bold, italic and bold-italic faces, with DroidSansFallback for CJK, on the outline (multi-channel) distance field.
- Panels catch up with UMG: scroll box fill slots and front and back padding, wrap box fill, scale box modes and stated scales, grid spans, drag switches, the border's tint.
- Popups: a popup layer per player replaces the click blockers, gives the focus back to whoever opened a popup, and
Back closes only the top one. A popup says what Tab does in it (
TabBehavior: Cycle, or CloseAndContinue for dropdown lists and menus). A modal shown over an open popup closes it and takes the first click and the first Back; a press on a layer drawn in front of an open popup closes the popup and still reaches the layer, and Back goes to that layer first. A popup closed by its own focus-on-open ends closed, and a lifted panel menu keeps its slot's padding, nudge and size bounds. DreamGUI.Memory, a console command: each font's glyph atlas (slices, GPU bytes and the CPU copy, cells, glyphs, face bytes), the sprite atlas pages, and each world's canvas mesh sections and paint rows;DreamGUI.Memory Jsonprints it as JSON, andFile=<path>writes it to a file.- A check on request:
r.DreamUI.VerifyKeptPointerslooks up again every object DreamGUI keeps between frames — a widget's canvas, a canvas's render layers, the UI manager's canvases, an animated property's object — and says where the two disagree, asr.DreamUI.VerifyPartialPreparedoes for a canvas's prepare. - Switches to measure with.
DreamGUI.Text.SmallTextCoverageandDreamGUI.Text.SmallTextMaxPixelSizeoverride the small-text settings for every font, and a change repaints every text.DreamGUI.Text.SmallTextOnMove,DreamGUI.Scroll.SnapToDevicePixelsand thebFieldTextCorrectionsetting try other ways to draw moving, scrolled and large text, and are off until measured.DreamGUI.Text.IncrementalParse,IncrementalMeasureandInPlaceDisplayListeach turn part of the incremental layout off, andDreamGUI.Text.VerifyIncrementalholds it to layouts made from nothing.
Performance
- Widgets whose render transform keeps changing become render layers that their canvas moves on the GPU, any number of them sharing a draw call.
- World transforms are composed when they are read, and moves are announced once a frame.
- Widget animations made of plain property tracks are played by DreamGUI's own player rather than the sequencer's entity system.
- World-space canvases are drawn in shared render-graph passes, and the per-panel work moved to the workers. The two 5000-button benchmark levels run at the 60 FPS cap.
- A canvas walks only the widgets that asked to change, patches in place the sections whose geometry changed, and keeps its draw calls when its widgets only moved.
- An edit inside a long paragraph reads and measures only a window around it, and the text's display list is edited where it stands rather than written again.
- A world repaints at most
SmallTextRepaintBudgetPerFrame(512) texts a frame onto coverage glyphs, and a small text drawn from the field while it turns, scales or moves is looked at again once it has held still, not at every move. - Incremental layout keeps every switch-off path exact: an edit near the start of a long paragraph that opens with punctuation, a digit or an emoji, or one just after an inline image, is shaped as the whole paragraph would shape it.
- In the editor, DreamGUI's own copy of ICU is given the engine's ICU data, so line breaking follows the game's culture there too, as it does in a packaged game; without the data it falls back to the engine's iterators and says so once.
Fixed
- Field text composited its layers premultiplied: the underlay, glow, outline and face of a field glyph were mixed as straight colours, so over nothing a layer's colour was pulled towards black and came out at its alpha squared. A translucent or fading text darkened, a gradient's transparent stop greyed, glows fell off too fast, and light text's edges were thinner than Slate draws them in the viewport. Each layer now goes over the one under it.
- Viewport capture in a game:
UDreamUICaptureLibrary::SaveViewportToPngandReadViewportPixelsread a game's viewport, which is drawn straight into its window and has no texture between frames: that fired an ensure and gave a black picture. They now return false there, and say to use the engine's screenshot request. - Chinese fallbacks in a packaged game: the ICU data a game is cooked with (English or EFIGSCJK) has no likely
subtags, so the engine expands
zh-CNtozh-CN, zhwith nozh-Hans, and fallback entries for"zh-Hans"or"zh-Hant"matched in the editor but never in the package. DreamGUI puts the script back for Chinese culture names. - Crashes on the render thread: a renderer freed while its render commands were still waiting (the intermittent all-preset crash); render roots and render-graph passes that held a raw pointer to something that could go first now hold it by reference.
- Extensions: TextAnimation's properties pass over a character that has no vertices instead of reading past them;
a cylinder render-target surface whose canvas has no target or that has no vertices, a static mesh set after its
widget left its canvas, a 2D line whose children changed in number, and render-target input sent to a canvas that was
replaced no longer crash or misdirect; a cylinder's arc of 0 is 1 degree, and a negative arc's bounds are on the side
it bends to. The retainer box retains: it sets its canvas to render-target mode, puts it back after, leaves editor
worlds alone, and warns once when it has no
DisplayVisual. The static mesh visual refuses a mesh past the vertex budget, with an error, instead of truncating its indices, and its bounds are where the canvas draws it. The render-target surface (UDreamUIRenderTargetGeometrySource) in StaticMesh mode puts its material into the static mesh's slot 0 only in play: in the editor, the transient instance it put there made saving the level write null over the mesh's own material. Pixelate hands its target to its proxy; pixel sort keeps its pass count within 1 to 512; the lyrics' TTML reader stops at 256 levels of nesting; TextAnimation properties set at run time start, and the ones they replace stop. - DreamTween: sequences survive children the collector took; a callback that restarts or seeks its own sequence no
longer loops, and a seek nested 16 deep is refused with an error;
Killfires its callbacks once; finished and killed tweens retire and their handles read invalid —Kill,ForceCompleteandGotodo nothing on a killed or retired tween,Restarton a retired one only warns, andRestartfrom a tween's ownOnCompletekeeps it running;ForceCompleteon a tween that has not started starts it first;Gototo the end completes once, andGoto(0)applies the start value; an empty curve falls back to linear with a warning instead of freezing; colour and alpha tweens round and clamp rather than wrap on overshooting eases; InExpo ends exactly at 1; a backward yoyo cycle ends at 0; a spring at rest wakes onSetTarget;RepeatCallwith a count of 0 calls nothing; time scale, delay and delta are checked; a second tick helper no longer doubles tween speed; a tween whose owner is gone is dropped without callbacks; layout animations and the embedded UMG widget follow the pause and time-dilation rule other widget tweens follow. - Controls: about forty findings of an audit, each with a test — list and scroll view rows during item changes, a heap write before a text field's buffer, a dropdown holding a raw pointer, buttons and selections switched off while pressed, drags whose source is destroyed, the key selector, the text field editing by UTF-16 offset with the IME.
- Text: packed text-style values that made a denormal float — a zero first value of a pair, such as face softness 0
beside a dilation, or an effect alpha of exactly 128 — are nudged to the nearest normal one, so a GPU that flushes
denormals no longer turns them to 0; a rich-text tag name too long for an
FName(1024 characters or more) is shown as text instead of asserting. - Rendering: a mesh whose shadow copy would take it past the vertex budget is drawn without the shadow, with one warning, rather than not at all.
- Fonts: bitmap fonts reload on undo and redo; an embedded font file's bytes are read again at cook, and an empty one is a cook error; a font's atlas texture and its list of texts are no longer recorded in undo transactions.
- Input: a player index with no player no longer gets an input user made for it.
- Widget Blueprints run their graphs in a game on uncooked content (
-game, Standalone):DreamGUIEditoris UncookedOnly. - The editor: a designer gesture is one undo step; the designer writes its edits to the
.duithe asset names, and never over a file changed on disk; a recompile, and the designer's own rebuild of its preview, no longer record into an open transaction.
Platforms and servers
- Only Win64 has been built and run: the editor and the automation suite, and a packaged Development game once the release gate has run the test host's packaged text smoke test. Mac, Linux, iOS and Android are listed by the descriptor but have never been compiled (the README's Platforms section says so instead of “Builds: yes”; see Platforms).
- The UI manager no longer starts in a dedicated server's worlds (a play-in-editor server included): no canvases, no renderer, no paint rows.
Documentation and tools
Docs/FontsAndPackaging.md(Fonts and packaging): colour emoji fonts, three ways to package a font file, the ICU packaging preset and what degrades without it.Docs/Migration.mdhas a section for moving from 2.0 to 2.1 (Upgrading).- msdfgen's MIT licence ships with the plugin, beside its source.
- The test host has a packaged text smoke test, part of the release gate: a probe in its game module writes what a
packaged game draws, and
Tools/Tests/compare_text_smoke.pyholds it to the same build run on uncooked content. Tools/ModuleSplit/check_game_includes.pyalso finds engine macros and globals a file uses without including what defines them, editor headers in code a game compiles, and runtimeBuild.csfiles that depend on editor modules.- The render benchmark measures small text with coverage on and off under seven kinds of motion; the text parity run
measures stroke edges, ink in linear light and grey against Slate, and reports gradient fills beside Chrome's
background-clip: text. Tools/Benchholds the benchmarks and the scripts that read their profiles (Benchmarks); the test runner has presets, a nightly script and a coverage table of which control is tested with which input (Tests).
Changes to know about
Upgrading from 2.0 changes some defaults and behaviour: the default font (texts reflow), small-text rendering, Tab, keys
and the pad acting on the focus rather than the hover, the focus look, player indices, scroll bars and lists as Tab
stops, the effect alpha of faded outlined text, how field text composites its layers, MSAA blending, line breaking by
the game's culture, and the 64-byte vertex with its new UV4 channel for anyone with custom shaders or mesh modifiers.
They are listed in Upgrading, “From 2.0 to 2.1”.
2.0.0 (development build)
The modular release (2026-09-29).
- Modules. The single runtime module is split by layer:
DreamGUIRenderer(the view extension, shaders and proxies),DreamGUI(widgets, visuals, canvases, layout, text,.dui),DreamGUIInput(event systems, raycasters, navigation, the viewport client),DreamGUIControls,DreamGUIExtensions,DreamGUISamplesandDreamTween, withDreamGUIEditorandDreamGUIK2Nodesuncooked-only andDreamGUITestseditor-only. A lower layer never includes a higher one. - Redirects ship with the plugin:
Config/DefaultDreamGUI.inicarries every redirect from LGUI and LexUI, from the prefab vocabulary, from the control renames and from the module split (763 of them). - Input per player: each local player has its own pointers, focus and text target; pointer ids have ranges (mouse 0, fingers 100 and up, scripted 1000 and up); an optional Slate input source hears input in every input mode; characters reach text fields through the game viewport client.
- Rendering: a canvas makes no material instance and answers its materials' parameters through render-thread proxies; a render-target canvas is drawn by a render command of its own; the screen effects read and write the screen through one path on the render graph's textures.
- Widget Blueprints replace the prefab asset model; the designer was rebuilt against UMG's; a widget's hierarchy can
be authored as text (
.dui), with timelines, routes and styles. - Layout along the lines of Blink and Yoga, with the UMG-shaped panels; the Lex layout family is gone.
- Removed: the root Blueprints,
UDreamWidgetPresenterComponent, the world-space raycaster source family, and the settings and console variables that put old renderer behaviour back. See Migration, “What no longer exists”.