UIElement
UIElement 是 LDLib2 中最基础且最常用的 UI 组件。
所有 UI 组件都继承自它。
从概念上讲,它类似于 HTML 中的 <div/> 元素:一个通用的容器,可以进行样式化、布局,并通过行为进行扩展。
因此,本页介绍的所有内容同样适用于 LDLib2 中的所有其他 UI 组件——所以请务必仔细阅读。
用法
var element = new UIElement();
element.style(style -> style.background(MCSprites.RECT));
element.layout(layout -> layout.width(40).height(40));
element.setFocusable(true);
element.addEventListener(UIEvents.MOUSE_DOWN, e -> e.currentElement.focus());
element.addClass("add-class");
element.removeClass("add-class");
root.addChild(element);
let element = new UIElement();
element.style(style => style.background(MCSprites.RECT));
element.layout(layout => layout.width(40).height(40));
element.setFocusable(true);
element.addEventListener(UIEvents.MOUSE_DOWN, e => e.currentElement.focus());
element.addClass("add-class");
element.removeClass("add-class");
root.addChild(element);
Xml
<element id="my_id" class="class1 class2" focusable="false" visible="true" active="true" style="background: #fff; width: 50">
<!-- 在此添加子元素 -->
<button text="click me!"/>
<inventory-slots/>
</element>
样式
布局
布局属性实际上也是样式。
UIElement 的样式(包括布局)可以通过以下方式访问:
布局属性
使用前最好先阅读 布局。
display
控制元素是否参与布局。FLEX 启用 flex 布局,GRID 启用 grid 布局,NONE 将元素从布局计算中移除,CONTENTS 不影响布局但会渲染其子元素。
layout-direction
设置布局方向。通常从父元素继承。
flex-basis
设置在 flex 增长/收缩之前的主轴初始大小。支持 点、百分比 和 自动 模式。
flex-shrink
控制当空间不足时,元素的收缩程度。
flex-direction
定义主轴方向,例如 ROW 或 COLUMN。
flex-wrap
控制子元素是否换行。
position
设置定位模式。RELATIVE 参与布局,ABSOLUTE 不影响兄弟元素。
top / right / bottom / left / start / end / horizontal / vertical / all
当 position 为 RELATIVE 或 ABSOLUTE 时使用的偏移量。
margin-*
*: top / right / bottom / left / start / end / horizontal / vertical / all
设置元素周围的外边距。
padding-*
*: top / right / bottom / left / start / end / horizontal / vertical / all
设置边框与内容之间的内边距。
gap-*
*: row / column / all
设置 flex 布局中子元素之间的间距。
width
设置元素宽度。支持 点、百分比 和 自动 模式。
height
设置元素高度。支持 点、百分比 和 自动 模式。
min-width / min-height
设置最小尺寸约束。
max-width / max-height
设置最大尺寸约束。
aspect-rate
锁定宽高比。对于方形或图标元素很有用。
align-items
沿交叉轴对齐子元素(容器属性)。
justify-content
沿主轴对齐子元素(容器属性)。
align-self
覆盖单个元素的交叉轴对齐方式。
align-content
当启用 flex-wrap 时,对齐换行后的行。
Grid 属性
要使用 grid 布局,请在容器元素上设置 display(TaffyDisplay.GRID)。模板属性在容器上定义 grid 结构,而 grid-row 和 grid-column 放在子元素上以控制其位置。
grid-template-rows
定义 grid 容器的显式行轨道。
支持的轨道大小:Npx(固定像素)、N%(百分比)、Nfr(分数单位)、auto、min-content、max-content、minmax(min, max)、fit-content(limit)、repeat(count, size)、[name](命名线)。多个轨道以空格分隔。
layout.display(TaffyDisplay.GRID);
layout.gridTemplateRows("1fr 1fr 1fr"); // three equal rows
layout.gridTemplateRows("50px 1fr auto"); // fixed, flexible, auto
layout.gridTemplateRows("repeat(3, 100px)"); // three 100px rows
layout.gridTemplateRows("[header] 50px [content] 1fr [footer] 50px"); // named lines
grid-template-columns
定义 grid 容器的显式列轨道。使用与 grid-template-rows 相同的轨道尺寸语法。
grid-template-areas
为 grid 单元格分配命名区域。每个引号字符串代表一行;其中的单词命名该行中的单元格。使用 . 表示空单元格。所有行必须具有相同数量的单元格。
grid-auto-rows
为隐式创建的行设置行轨道大小——即未被 grid-template-rows 覆盖的行。
grid-auto-columns
为隐式创建的列设置列轨道大小。
grid-auto-flow
控制自动放置的元素如何填充 grid。ROW 优先填充行(默认);COLUMN 优先填充列。ROW_DENSE / COLUMN_DENSE 会回填之前的空隙。
grid-row
控制子元素在 grid 容器中的行位置。请在子元素上设置,而不是容器上。
位置值:"1"(行号)、"1 / 3"(起始/结束行)、"span 2"(跨越 N 行)、"1 / span 2"(起始 + 跨越)、"header"(命名区域行)、"-1"(最后一行)。
child.layout(layout -> layout.gridRow("1")); // row 1
child.layout(layout -> layout.gridRow("1 / 3")); // rows 1-3
child.layout(layout -> layout.gridRow("span 2")); // span 2 rows
child.layout(layout -> layout.gridRow("header")); // named area row
child.layout(layout -> layout.gridRow("-1")); // last row line
grid-column
控制子元素在 grid 容器中的列位置。使用与 grid-row 相同的位置语法。
基本属性
overflow
已过时,仅适用于 1.21 API。 在 26.1 及更新版本中请使用 clip。
在 1.21 API 中,overflow 用于控制如何处理溢出内容。overflow 本身是布局属性,同时也有 overflowVisible(...) 和 setOverflowVisible(...) 等便捷辅助方法。在 26.1+ 中,UIElement#setOverflowVisible(false) 会映射为 Clip.SCISSOR。
clip
控制元素子树是否被裁剪,以及以何种方式裁剪。clip 用于替代旧的 1.21 overflow / overflow-clip API。除 NONE 以外的所有模式也会阻止元素内容边界外的命中测试。
| 模式 | 描述 |
|---|---|
NONE |
不裁剪。这是默认值。 |
SCISSOR |
将渲染裁剪到元素的内容边界内。可作为 26.1+ 中 overflow: hidden 或 setOverflowVisible(false) 的替代方案。 |
MASK |
使用 mask 设置的纹理裁剪渲染。适用于静态遮罩纹理。 |
DYNAMIC_MASK |
与 MASK 相同,但每帧都会刷新遮罩。适用于动画遮罩或会动态变化的遮罩。 |
mask
定义 clip: mask 和 clip: dynamic-mask 使用的纹理。遮罩会绘制在元素边界上,并使用采样得到的遮罩系数乘到已渲染子树的颜色和 alpha 上。
不透明的灰度遮罩使用纹理亮度:白色保留内容可见,黑色隐藏内容。使用 alpha 编码的遮罩会使用 alpha 通道,这适合透明 PNG 遮罩和柔和边缘。除非 clip 为 MASK 或 DYNAMIC_MASK,否则 mask 属性不会产生可见效果。
查看 LSS 中的纹理 了解 lss 支持。
tooltips
定义当鼠标悬停在元素上时显示的悬停提示内容。
z-index
控制元素的堆叠顺序。数值较大的元素显示在数值较小的元素之上。
opacity
设置元素的透明度等级。0 为完全透明,1 为完全不透明。
color
使用 ARGB 乘数对当前元素的 background 和 overlay 纹理进行着色。
此着色仅应用于当前元素,不会影响子元素。
overflow-clip
已过时,仅适用于 1.21 API。 在 26.1 及更新版本中请使用 clip: mask 和 mask。
在 1.21 API 中,当元素的 overflow 为隐藏时,overflow-clip 会使用给定纹理的红色通道作为遮罩来裁剪子元素渲染。在 26.1+ 中,请将 clip 设置为 MASK 或 DYNAMIC_MASK,然后使用 mask 设置遮罩纹理。
查看 LSS 中的纹理 了解 lss 支持。
transform-2d
应用 2D 变换,例如平移、缩放或旋转。
transition
定义属性变化之间的动画过渡效果。
状态
isVisible
当 isVisible 设置为 false 时,该元素及其所有子元素将不再被渲染。
与 display: NONE 不同,这不会影响布局计算。
isVisible = false 的元素也会被排除在命中测试之外,因此许多 UI 事件(如点击)将不会被触发。
isActive
当 isActive 设置为 false 时,元素可能会失去其交互行为——例如,按钮无法再被点击——并且元素将不再接收 tick 事件。
Note
当 isActive 设置为 false 时,会自动向元素添加一个 __disabled__ 类。
你可以使用以下 LSS 选择器来设置活动和非活动状态的样式:
focusable
元素默认是 focusable: false。有些组件(如 TextField)在设计上是可聚焦的,但你仍然可以手动更改元素的可聚焦状态。
只有当 focusable 设置为 true 时,元素才能通过 focus() 或鼠标交互获得焦点。
Note
当元素处于 focused(已聚焦)状态时,会自动添加一个 __focused__ 类。
你可以使用以下 LSS 选择器来设置已聚焦和未聚焦状态的样式:
hover state
当元素被悬停时,会自动添加一个 __hovered__ 类。
为了兼容 CSS,你可以使用 :hover 作为选择器简写,它等价于 .__hovered__。
isInternalUI
这是一个特殊状态,表示一个元素是否是组件的内部部分。
例如,一个 button 包含一个用于渲染其标签的内部 text 元素。
从语义上讲,不允许直接添加、移除或重新排序内部元素。 但是,你仍然可以通过编辑器或 XML 编辑它们的样式并管理它们的子元素。 在编辑器中,内部元素在层级视图中显示为灰色。
在 XML 中,你可以使用 <internal index="..."/> 标签访问内部元素,其中 index 指定要引用的内部元素:
在 LSS 中,你可以使用 :host 和 :internal 来明确指定宿主元素或内部元素。默认情况下,选择器会匹配两者,除非加以限制。
字段
仅列出外部可观察或可配置的公共或受保护字段。
| 名称 | 类型 | 访问权限 | 描述 |
|---|---|---|---|
taffyStyle |
TaffyLayoutStyle |
protected (getter) | 用于布局计算的底层 Taffy 样式桥接对象。 |
nodeId |
NodeId |
protected (getter) | 注册在 TaffyTree 中的节点句柄。 |
modularUI |
ModularUI |
private (getter) | 此元素所属的 ModularUI 实例。 |
id |
String |
private (getter/setter) | 元素 ID,用于选择器和查询。 |
classes |
Set<String> |
private (getter) | 应用于此元素的类似 CSS 的类列表。 |
styleBag |
StyleBag |
private (getter) | 存储已解析的样式候选值和计算后的样式。 |
styles |
List<Style> |
private (getter) | 附加到此元素的内联样式。 |
layoutStyle |
LayoutStyle |
private (getter) | 布局属性对应的样式包装器。 |
style |
BasicStyle |
private (getter) | 基础视觉样式(background、overlay 着色、opacity、zIndex 等)。 |
isVisible |
boolean |
private (getter/setter) | 元素是否可见。 |
isActive |
boolean |
private (getter/setter) | 元素是否参与逻辑和事件。 |
focusable |
boolean |
private (getter/setter) | 元素是否可以获得焦点。 |
isInternalUI |
boolean |
private (getter) | 标记内部(组件拥有的)元素。 |
方法
布局与几何
| 方法 | 签名 | 描述 |
|---|---|---|
getLayout() |
LayoutStyle |
返回布局样式控制器。 |
layout(...) |
UIElement layout(Consumer<LayoutStyle>) |
以流式方式修改布局属性。 |
getTaffyLayout() |
Layout |
返回该元素解析后的 Taffy 布局结果。 |
getPositionX() |
float |
屏幕上的绝对 X 坐标。 |
getPositionY() |
float |
屏幕上的绝对 Y 坐标。 |
getSizeWidth() |
float |
元素的计算宽度。 |
getSizeHeight() |
float |
元素的计算高度。 |
getContentX() |
float |
内容区域的 X 坐标(不包括边框和内边距)。 |
getContentY() |
float |
内容区域的 Y 坐标。 |
getContentWidth() |
float |
内容区域的宽度。 |
getContentHeight() |
float |
内容区域的高度。 |
adaptPositionToScreen() |
void |
调整位置以保持在屏幕边界内。 |
adaptPositionToElement(...) |
void |
调整位置以保持在另一个元素内部。 |
树结构
| 方法 | 签名 | 描述 |
|---|---|---|
getParent() |
UIElement |
返回父元素,或 null。 |
getChildren() |
List<UIElement> |
返回一个不可修改的子元素列表。 |
addChild(...) |
UIElement addChild(UIElement) |
添加一个子元素。 |
addChildren(...) |
UIElement addChildren(UIElement...) |
添加多个子元素。 |
removeChild(...) |
boolean removeChild(UIElement) |
移除一个子元素。 |
removeSelf() |
boolean |
从其父元素中移除此元素。 |
clearAllChildren() |
void |
移除所有子元素。 |
isAncestorOf(...) |
boolean |
检查此元素是否是另一个元素的祖先。 |
getStructurePath() |
ImmutableList<UIElement> |
从根元素到此元素的路径。 |
样式与类
| 方法 | 签名 | 描述 |
|---|---|---|
style(...) |
UIElement style(Consumer<BasicStyle>) |
修改内联视觉样式。 |
lss(...) |
UIElement lss(String, Object) |
以编程方式应用样式表风格的属性。 |
addClass(...) |
UIElement addClass(String) |
添加一个类似 CSS 的类。 |
removeClass(...) |
UIElement removeClass(String) |
移除一个类。 |
hasClass(...) |
boolean |
检查类是否存在。 |
getLocalStylesheets() |
List<Stylesheet> |
返回附加到此元素的本地样式表。 |
addLocalStylesheet(...) |
UIElement addLocalStylesheet(Stylesheet) |
添加本地样式表(仅自身 + 后代)。 |
addLocalStylesheet(...) |
UIElement addLocalStylesheet(String) |
从 LSS 文本解析并添加本地样式表。 |
removeLocalStylesheet(...) |
UIElement removeLocalStylesheet(Stylesheet) |
从此元素作用域中移除本地样式表。 |
clearLocalStylesheets() |
UIElement |
移除附加到此元素的所有本地样式表。 |
transform(...) |
UIElement transform(Consumer<Transform2D>) |
应用 2D 变换。 |
animation() |
StyleAnimation |
创建一个以此元素为目标的样式动画。查看 StyleAnimation。 |
animation(a -> {}) |
StyleAnimation |
如果 ModularUI 有效则立即运行动画设置,或在变为有效时通过 MUI_CHANGED 运行一次。 |
焦点与交互
| 方法 | 签名 | 描述 |
|---|---|---|
focus() |
void |
请求此元素获得焦点。 |
blur() |
void |
如果此元素已聚焦,则清除其焦点。 |
isFocused() |
boolean |
如果此元素已聚焦,则返回 true。 |
isHover() |
boolean |
如果鼠标直接悬停在此元素上,则返回 true。 |
isSelfOrChildHover() |
boolean |
如果自身或子元素被悬停,则返回 true。 |
startDrag(...) |
DragHandler |
开始一个拖拽操作。 |
事件
| 方法 | 签名 | 描述 |
|---|---|---|
addEventListener(...) |
UIElement addEventListener(String, UIEventListener) |
注册一个冒泡阶段的事件监听器。 |
addEventListener(..., true) |
UIElement addEventListener(String, UIEventListener, boolean) |
注册一个捕获阶段的监听器。 |
removeEventListener(...) |
void |
移除一个事件监听器。 |
stopInteractionEventsPropagation() |
UIElement |
停止鼠标和拖拽事件的传播。 |
用法
// Bubble-phase listener (default): fires after children handle the event
element.addEventListener(UIEvents.MOUSE_DOWN, event -> {
event.currentElement.focus();
});
// Capture-phase listener: fires before children handle the event
element.addEventListener(UIEvents.CLICK, event -> {
event.stopPropagation(); // prevent children from seeing this event
}, true);
// Removing a specific listener
UIEventListener listener = event -> { /* ... */ };
element.addEventListener(UIEvents.CLICK, listener);
element.removeEventListener(UIEvents.CLICK, listener);
// Stop all mouse/drag events from bubbling to parent elements
element.stopInteractionEventsPropagation();
可用事件
| 事件 | 描述 |
|---|---|
UIEvents.MOUSE_DOWN |
鼠标按钮在元素上按下 |
UIEvents.MOUSE_UP |
鼠标按钮释放 |
UIEvents.CLICK |
鼠标点击(在同一元素上按下并释放) |
UIEvents.DOUBLE_CLICK |
鼠标双击 |
UIEvents.MOUSE_MOVE |
鼠标在元素上移动 |
UIEvents.MOUSE_ENTER |
鼠标指针进入元素边界 |
UIEvents.MOUSE_LEAVE |
鼠标指针离开元素边界 |
UIEvents.MOUSE_WHEEL |
鼠标滚轮滚动 |
UIEvents.DRAG_ENTER |
拖拽操作进入此元素 |
UIEvents.DRAG_LEAVE |
拖拽操作离开此元素 |
UIEvents.DRAG_UPDATE |
拖拽目标位置已更新 |
UIEvents.DRAG_SOURCE_UPDATE |
拖拽源位置已更新 |
UIEvents.DRAG_PERFORM |
物品被放置到此元素上 |
UIEvents.DRAG_END |
拖拽操作结束 |
UIEvents.FOCUS |
元素获得键盘焦点 |
UIEvents.BLUR |
元素失去键盘焦点 |
UIEvents.FOCUS_IN |
焦点移入此元素的子树 |
UIEvents.FOCUS_OUT |
焦点移出此元素的子树 |
UIEvents.KEY_DOWN |
键盘按键按下 |
UIEvents.KEY_UP |
键盘按键释放 |
UIEvents.CHAR_TYPED |
输入了一个可打印字符 |
UIEvents.HOVER_TOOLTIPS |
悬停时收集悬停提示 |
UIEvents.VALIDATE_COMMAND |
斜杠命令验证 |
UIEvents.EXECUTE_COMMAND |
斜杠命令执行 |
UIEvents.LAYOUT_CHANGED |
布局已重新计算 |
UIEvents.STYLE_CHANGED |
样式已重新计算 |
UIEvents.REMOVED |
元素已从其父元素中移除 |
UIEvents.ADDED |
元素已添加到父元素 |
UIEvents.MUI_CHANGED |
ModularUI 关联已更改 |
UIEvents.TICK |
周期性的客户端 tick |
客户端-服务端同步与 RPC
| 方法 | 签名 | 描述 |
|---|---|---|
addSyncValue(...) |
UIElement |
注册一个同步值。 |
removeSyncValue(...) |
UIElement |
注销一个同步值。 |
addRPCEvent(...) |
RPCEmitter |
注册一个 RPC 事件。 |
sendEvent(...) |
void |
向服务端发送一个 RPC 事件。 |
sendEvent(..., callback) |
<T> void |
发送一个带有响应回调的 RPC 事件。 |
服务端事件
服务端事件监听器在服务端而不是客户端上运行。它们使用相同的 UIEvents 类型常量,并支持冒泡和捕获阶段。它们通过内部 RPC 机制自动同步。
RPC 事件
RPC(远程过程调用)事件允许客户端显式调用服务端逻辑,并可选择接收响应。
// Register an RPC event during element initialization
RPCEmitter emitter = element.addRPCEvent(ele ->
RPCEventBuilder.simple(UIEvents.CLICK, (e, args) -> {
// This runs on the server
ServerPlayer player = e.modularUI.player;
player.sendSystemMessage(Component.literal("Hello from server!"));
})
);
// Trigger the RPC from client (e.g., inside a client event listener)
element.addEventListener(UIEvents.CLICK, event ->
element.sendEvent(emitter.event())
);
数据绑定
数据绑定自动在服务端和客户端之间同步数值。在 Java 中使用 addSyncValue,或在 Kotlin 中使用 bind* DSL 辅助函数。
渲染
| 方法 | 签名 | 描述 |
|---|---|---|
isDisplayed() |
boolean |
如果 display 不是 NONE,则返回 true。 |