.dui 语言
完整示例
一个由一个行组件、一个库和一个界面三个文件组成的设置界面,逐段讲解每一部分依赖语言的哪条规则。
一个用同一个行组件搭起来的设置界面。三个文件:组件本身;一个给它起名、并给这一族组件定样式的库;以及界面。
WBP_SettingsRow.uasset
WBP_SettingsScreen.uasset
SettingsRow.dui
SettingsLibrary.dui
SettingsScreen.dui
两个 Blueprint 都以 UDreamTextUserWidget 为父类,Source File 分别指向 Settings/SettingsRow.dui 和 Settings/SettingsScreen.dui。
库没有根节点,不对应任何 Blueprint。
组件:一行设置
行的左边是标签,右边是一个带左右箭头的值。它声明宿主能设什么、能听到什么:
// DUI/Settings/SettingsRow.dui
class /Game/UI/Settings/WBP_SettingsRow
use "Settings/SettingsLibrary.dui"
props {
Text Label
Text Value
Number Index = 0
}
events {
Changed(Number Index, Number Step)
}
Widget Root : RowBox {
+ HorizontalBox { Padding = (16, 0, 16, 0) Spacing = 12 }
+ UIButton { TransitionType = None }
Text LabelText : Caption {
Text <- Label
@fill
}
Native.Button Previous : Arrow {
OnClicked -> emit Changed(Index, -1)
Text { Text = "◀" }
}
Text ValueText : Caption {
Text <- Value
HAlign = Center
@slot MinDesiredSize = (220, 0)
}
Native.Button Next : Arrow {
OnClicked -> emit Changed(Index, 1)
Text { Text = "▶" }
}
}class行写明了它编译进WBP_SettingsRow。库里的use … as Row就是从这一行读出类的,所以不依赖编辑器去找 Blueprint。use "Settings/SettingsLibrary.dui"让这一行拿到库里的样式(RowBox、Caption、Arrow)。库反过来也use了这个文件—— 这不是循环:use … as只读组件文件的头部,不跟它的use行(见use)。props声明了三个 Blueprint 变量Label、Value、Index,events声明了一个带两个参数的事件分发器Changed。- 根节点用两个
+块挂了一个横排盒子和一个按钮行为;RowBox样式给它高度。 LabelText把文字绑到 propLabel上,@fill让它占掉横排里剩下的宽度。ValueText绑Value,并用槽位行要求至少 220 宽。- 两个箭头按钮各自
emit Changed,参数是绑定表达式:Index是 prop,-1和1是步长。按钮里的Text没写 id,它们的 id 是Previous__Text0和Next__Text0。
库:名字和样式
库放这一族组件的外观和名字。它没有根节点,所以既能被普通 use,也能放进命名空间;它的 use … as 行会随它一起走:
// DUI/Settings/SettingsLibrary.dui
use "Settings/SettingsRow.dui" as Row
resources {
Color Ink = #E6E9F0
Color Muted = #8C93A6
Color Panel = #1B1E26
}
style Caption {
FontSize = 18
Color = @Ink
}
style RowBox {
AnchorData.SizeDelta = (0, 48)
}
style Arrow {
AnchorData.SizeDelta = (40, 40)
}
style Column {
+ VerticalBox { Spacing = 8 }
@fill
}use … as Row给行组件起名。每个用这个库的界面都会得到这个名字:普通use后是Row,use … as ui后是ui.Row。- 三个颜色资源会成为每个导入它的类的变量。
Caption里的@Ink永远指这个库的Ink,即使它是在命名空间下被用到的。 Column样式带着一个竖排盒子和一个@fill,于是“一列”就是一个名字(见样式与资源)。
界面
界面放了三行,把每一行的 Changed 路由到各自的处理函数,只在有未保存改动时显示一条提示,并按类的数据列出按键提示:
// DUI/Settings/SettingsScreen.dui
class /Game/UI/Settings/WBP_SettingsScreen
use "Settings/SettingsLibrary.dui" as ui
Widget Root {
AnchorData.AnchorMin = (0, 0)
AnchorData.AnchorMax = (1, 1)
AnchorData.SizeDelta = (0, 0)
+ Overlay { }
Image Backdrop {
Brush.TintColor = @ui.Panel
@slot { HorizontalAlignment = Fill VerticalAlignment = Fill }
}
VerticalBox Page {
Spacing = 24
Padding = (64, 48, 64, 48)
@slot { HorizontalAlignment = Fill VerticalAlignment = Fill }
Text Heading : ui.Caption {
Text = "Settings"
FontSize = 32
}
Widget Rows : ui.Column {
ui.Row Audio {
Label = "Master volume"
Value = "80 %"
Changed -> HandleVolumeChanged
}
ui.Row Display {
Label = "Display mode"
Value = "Fullscreen"
Index = 1
Changed -> HandleDisplayModeChanged
}
ui.Row Language {
Label = "Language"
Value = "English"
Index = 2
Changed -> HandleLanguageChanged
}
}
if HasUnsavedChanges() {
Text UnsavedHint : ui.Caption {
Text = "Unsaved changes"
Color = @ui.Muted
}
}
HorizontalBox {
Spacing = 16
Native.Button Apply { OnClicked -> HandleApply }
Native.Button Back { OnClicked -> HandleBack }
for Hint in KeyHints {
Text : ui.Caption { Text <- Hint.Label }
}
}
}
}逐段看它依赖的规则:
- 命名空间。
use … as ui让库的名字和界面自己的名字分开:: ui.Caption、@ui.Panel,行组件则是ui.Row——库自己那行use "Settings/SettingsRow.dui" as Row随库一起进了命名空间。见use。 - 组件实例。
ui.Row Audio { … }是行组件的一个实例:Label、Value、Index设的是它的 props——只是初始值,之后处理函数通过行的变量Audio去改Value;Changed ->路由的是行从箭头按钮里emit出来的事件。见编写组件。 - 容器作为类型。
VerticalBox Page和那个无名的HorizontalBox都是把容器写成节点类型,它们的Spacing和Padding是容器的属性。 无名的那个 id 是Page__HorizontalBox0。见节点与值。 - 样式带组件。
Widget Rows : ui.Column从样式拿到它的竖排盒子和 Fill 槽位。 if。 提示只在HasUnsavedChanges()为真时显示;它翻转时没有东西被创建或销毁,只是可见性变了。见 if 与 for。for。 类的KeyHints(一个 FieldNotify 的对象数组变量,元素有Label成员)每一项一个文字,排在两个按钮之后;Text <- Hint.Label把每个副本绑到它那一项上。- 背景和铺满。 根节点四角锚定、零内缩,挂一个
Overlay;Backdrop和Page都用@slot { … }在两个方向上 Fill。
类要提供什么
界面的类需要提供这些成员,写在 C++ 里或 Blueprint 的图表里都行:
行组件的类则什么都不用额外写:它的 props 和事件都由 .dui 声明,编译时生成。