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">
<!-- add children here -->
<button text="click me!"/>
<inventory-slots/>
</element>
样式
布局
布局属性实际上也是样式的一部分。
UIElement 样式(包括布局)可以通过以下方式访问:
布局属性
在使用前,建议先阅读 Layout。
display
控制元素是否参与布局。FLEX 启用 flex 布局,GRID 启用 grid 布局,NONE 将元素从布局计算中移除,CONTENTS 不影响布局但渲染其子元素。
layout-direction
设置布局方向。通常从父元素继承。
flex-basis
设置 flex 伸缩前的初始主轴尺寸。支持 point、percent 和 auto。
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
设置元素宽度。支持 point、percent 和 auto 模式。
height
设置元素高度。支持 point、percent 和 auto 模式。
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-row 和 grid-column 放在子元素上以控制它们的位置。
grid-template-rows
定义网格容器的显式行轨道。
支持的轨道尺寸: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-template-rows 相同的轨道尺寸语法。
grid-template-areas
为网格单元格分配命名区域。每个引号字符串代表一行;其中的单词命名该行中的单元格。使用 . 表示空单元格。所有行必须具有相同数量的单元格。
grid-auto-rows
设置隐式创建的行的行轨道尺寸——即 grid-template-rows 未覆盖的行。
grid-auto-columns
设置隐式创建的列的列轨道尺寸。
grid-auto-flow
控制自动放置的项目如何填充网格。ROW 先填充行(默认);COLUMN 先填充列。ROW_DENSE / COLUMN_DENSE 回填较早的空隙。
grid-row
控制子元素在网格容器中的行位置。在子元素上设置,而不是容器上。
放置值:"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-row 相同的放置语法。
基本属性
overflow
控制如何处理溢出内容。如果为 'hidden',超出边界的内容将被隐藏。
tooltips
定义悬停元素时显示的工具提示内容。
opacity
设置元素的透明度级别。0 为完全透明,1 为完全不透明。
color
使用 ARGB 乘数为当前元素的 background 和 overlay 纹理着色。
此着色仅应用于当前元素,不会影响子元素。
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 指定要引用的内部元素:
<button>
<!-- obtain the internal text component here -->
<internal index="0">
</internal>
</button>
在 LSS 中,你可以使用 :host 和 :internal 来明确定位宿主或内部元素。默认情况下,选择器会匹配两者,除非有约束。
字段
仅列出外部可观察或可配置的 public 或 protected 字段。
| 名称 | 类型 | 访问权限 | 描述 |
|---|---|---|---|
layoutNode |
YogaNode |
protected (getter) | 用于布局计算的底层 Yoga 节点。 |
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) | 布局相关的样式包装器(基于 Yoga)。 |
style |
BasicStyle |
private (getter) | 基本视觉样式(背景、覆盖层着色、透明度、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>) |
流式修改布局属性。 |
node(...) |
UIElement node(Consumer<YogaNode>) |
直接修改底层 Yoga 节点。 |
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。 |