Skip to content

UIElement

自 2.2.1

UIElement 是 LDLib2 中最基础且最常用的 UI 组件。 所有 UI 组件都继承自它。

从概念上讲,它类似于 HTML 中的 #!html <div/> 元素:一个通用的容器,可以进行样式化、布局,并通过行为进行扩展。

因此,本页介绍的所有内容同样适用于 LDLib2 中的所有其他 UI 组件——所以请务必仔细阅读。


用法

java
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);

Xml

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 的样式(包括布局)可以通过以下方式访问:

java
element.style(style -> style.background(...));
element.layout(layout -> layout.width(...));
element.getStyle().background(...);
element.getLayout().width(...);

布局属性

使用前最好先阅读 布局

INFO

display

控制元素是否参与布局。FLEX 启用 flex 布局,GRID 启用 grid 布局,NONE 将元素从布局计算中移除,CONTENTS 不影响布局但会渲染其子元素。

java
layout.display(TaffyDisplay.FLEX);
layout.display(TaffyDisplay.GRID); // enable grid layout
element.setDisplay(false); // equals to layout.display(TaffyDisplay.NONE);

INFO

layout-direction

设置布局方向。通常从父元素继承。

java
layout.layoutDirection(TaffyDirection.LTR);

INFO

flex-basis

设置在 flex 增长/收缩之前的主轴初始大小。支持 百分比自动 模式。

java
layout.flexBasis(1);

INFO

flex

使元素沿主轴灵活伸缩。

java
layout.flex(1);

INFO

flex-grow

控制当有额外空间时,元素的增长程度。

java
layout.flexGrow(1);

INFO

flex-shrink

控制当空间不足时,元素的收缩程度。

java
layout.flexShrink(1);

INFO

flex-direction

定义主轴方向,例如 ROWCOLUMN

java
layout.flexDirection(FlexDirection.ROW);

INFO

flex-wrap

控制子元素是否换行。

java
layout.wrap(FlexWrap.WRAP);

INFO

position

设置定位模式。RELATIVE 参与布局,ABSOLUTE 不影响兄弟元素。

java
layout.positionType(TaffyPosition.ABSOLUTE);

INFO

top / right / bottom / left / start / end / horizontal / vertical / all

positionRELATIVEABSOLUTE 时使用的偏移量。

java
layout.top(10);
layout.leftPercent(30); // 30%
layout.allAuto()

INFO

margin-*

*: top / right / bottom / left / start / end / horizontal / vertical / all

设置元素周围的外边距。

java
layout.marginTop(5);
layout.marginAll(3);

INFO

padding-*

*: top / right / bottom / left / start / end / horizontal / vertical / all

设置边框与内容之间的内边距。

java
layout.paddingLeft(8);

INFO

gap-*

*: row / column / all

设置 flex 布局中子元素之间的间距。

java
layout.rowGap(6);

INFO

width

设置元素宽度。支持 百分比自动 模式。

java
layout.width(100);
layout.widthPercent(20); // 20%

INFO

height

设置元素高度。支持 百分比自动 模式。

java
layout.height(50);

INFO

min-width / min-height

设置最小尺寸约束。

java
layout.minWidth(20);

INFO

max-width / max-height

设置最大尺寸约束。

java
layout.maxHeight(200);

INFO

aspect-rate

锁定宽高比。对于方形或图标元素很有用。

java
layout.aspectRate(1);

INFO

align-items

沿交叉轴对齐子元素(容器属性)。

java
layout.alignItems(AlignItems.CENTER);

INFO

justify-content

沿主轴对齐子元素(容器属性)。

java
layout.justifyContent(AlignContent.CENTER);

INFO

align-self

覆盖单个元素的交叉轴对齐方式。

java
layout.alignSelf(AlignItems.CENTER);

INFO

align-content

当启用 flex-wrap 时,对齐换行后的行。

java
layout.alignContent(AlignContent.CENTER);

Grid 属性

INFO

要使用 grid 布局,请在容器元素上设置 display(TaffyDisplay.GRID)。模板属性在容器上定义 grid 结构,而 grid-rowgrid-column 放在子元素上以控制其位置。

INFO

grid-template-rows

定义 grid 容器的显式行轨道。

支持的轨道大小:Npx(固定像素)、N%(百分比)、Nfr(分数单位)、automin-contentmax-contentminmax(min, max)fit-content(limit)repeat(count, size)[name](命名线)。多个轨道以空格分隔。

java
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

INFO

grid-template-columns

定义 grid 容器的显式列轨道。使用与 grid-template-rows 相同的轨道尺寸语法。

java
layout.display(TaffyDisplay.GRID);
layout.gridTemplateColumns("10px 1fr 10px");    // fixed margins + flexible center
layout.gridTemplateColumns("repeat(3, 1fr)");   // three equal columns
layout.gridTemplateColumns("minmax(100px, 1fr) 200px");

INFO

grid-template-areas

为 grid 单元格分配命名区域。每个引号字符串代表一行;其中的单词命名该行中的单元格。使用 . 表示空单元格。所有行必须具有相同数量的单元格。

java
layout.display(TaffyDisplay.GRID);
layout.gridTemplateColumns("1fr 1fr 1fr");
layout.gridTemplateRows("auto 1fr auto");
layout.gridTemplateAreas(
    "\"header header header\" \"sidebar content content\" \"footer footer footer\""
);
// Children reference areas via gridRow/gridColumn by area name

INFO

grid-auto-rows

为隐式创建的行设置行轨道大小——即未被 grid-template-rows 覆盖的行。

java
layout.gridAutoRows("auto");
layout.gridAutoRows("minmax(50px, auto)");

INFO

grid-auto-columns

为隐式创建的列设置列轨道大小。

java
layout.gridAutoColumns("auto");
layout.gridAutoColumns("100px");

INFO

grid-auto-flow

控制自动放置的元素如何填充 grid。ROW 优先填充行(默认);COLUMN 优先填充列。ROW_DENSE / COLUMN_DENSE 会回填之前的空隙。

java
layout.gridAutoFlow(GridAutoFlow.ROW);
layout.gridAutoFlow(GridAutoFlow.COLUMN);
layout.gridAutoFlow(GridAutoFlow.ROW_DENSE);

INFO

grid-row

控制子元素在 grid 容器中的行位置。请在子元素上设置,而不是容器上。

位置值:"1"(行号)、"1 / 3"(起始/结束行)、"span 2"(跨越 N 行)、"1 / span 2"(起始 + 跨越)、"header"(命名区域行)、"-1"(最后一行)。

java
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

INFO

grid-column

控制子元素在 grid 容器中的列位置。使用与 grid-row 相同的位置语法。

java
child.layout(layout -> layout.gridColumn("2"));
child.layout(layout -> layout.gridColumn("1 / span 3"));
child.layout(layout -> layout.gridColumn("sidebar"));

基本属性

INFO

background

设置绘制在元素后方的纹理,例如纯色、矩形精灵或图片。

java
style.background(MCSprites.BORDER);

INFO

overflow

已过时,仅适用于 1.21 API。 在 26.1 及更新版本中请使用 clip

在 1.21 API 中,overflow 用于控制如何处理溢出内容。overflow 本身是布局属性,同时也有 overflowVisible(...)setOverflowVisible(...) 等便捷辅助方法。在 26.1+ 中,UIElement#setOverflowVisible(false) 会映射为 Clip.SCISSOR

java
layout.overflow(YogaOverflow.HIDDEN);
style.overflowVisible(false);
element.setOverflowVisible(false); // UIElement 上的辅助方法

INFO

clip

Since mc26.1

控制元素子树是否被裁剪,以及以何种方式裁剪。clip 用于替代旧的 1.21 overflow / overflow-clip API。除 NONE 以外的所有模式也会阻止元素内容边界外的命中测试。

模式描述
NONE不裁剪。这是默认值。
SCISSOR将渲染裁剪到元素的内容边界内。可作为 26.1+ 中 overflow: hiddensetOverflowVisible(false) 的替代方案。
MASK使用 mask 设置的纹理裁剪渲染。适用于静态遮罩纹理。
DYNAMIC_MASKMASK 相同,但每帧都会刷新遮罩。适用于动画遮罩或会动态变化的遮罩。
java
style.clip(Clip.SCISSOR);
style.clip(Clip.MASK).mask(MCSprites.BORDER);
style.clip(Clip.DYNAMIC_MASK).mask(animatedMask);

INFO

mask

Since mc26.1

定义 clip: maskclip: dynamic-mask 使用的纹理。遮罩会绘制在元素边界上,并使用采样得到的遮罩系数乘到已渲染子树的颜色和 alpha 上。

不透明的灰度遮罩使用纹理亮度:白色保留内容可见,黑色隐藏内容。使用 alpha 编码的遮罩会使用 alpha 通道,这适合透明 PNG 遮罩和柔和边缘。除非 clipMASKDYNAMIC_MASK,否则 mask 属性不会产生可见效果。

java
style.clip(Clip.MASK);
style.mask(MCSprites.BORDER);

INFO

overlay

控制在元素内容上方绘制的覆盖层渲染。

java
style.overlay(...);

INFO

tooltips

定义当鼠标悬停在元素上时显示的悬停提示内容。

java
style.tooltips("tips.0", "tips.1");
style.appendTooltipsString("tips.2");

INFO

z-index

控制元素的堆叠顺序。数值较大的元素显示在数值较小的元素之上。

java
style.zIndex(1);

INFO

opacity

设置元素的透明度等级。0 为完全透明,1 为完全不透明。

java
style.opacity(0.8f);

INFO

color

使用 ARGB 乘数对当前元素的 backgroundoverlay 纹理进行着色。 此着色仅应用于当前元素,不会影响子元素。

java
style.color(0x80FF8080);

INFO

overflow-clip

已过时,仅适用于 1.21 API。 在 26.1 及更新版本中请使用 clip: maskmask

在 1.21 API 中,当元素的 overflow 为隐藏时,overflow-clip 会使用给定纹理的红色通道作为遮罩来裁剪子元素渲染。在 26.1+ 中,请将 clip 设置为 MASKDYNAMIC_MASK,然后使用 mask 设置遮罩纹理。

java
style.overflowClip(MCSprites.BORDER);

INFO

transform-2d

应用 2D 变换,例如平移、缩放或旋转。

java
style.transform2D(Transform2D.identity().scale(0.5f));
element.transform(transform -> transform.translate(10, 0))

INFO

transition

定义属性变化之间的动画过渡效果。

java
layout.transition(new Transition(Map.of(LayoutProperties.HEIGHT, new Animation(1, 0, Eases.LINEAR))));

状态

isVisible

isVisible 设置为 false 时,该元素及其所有子元素将不再被渲染。 与 display: NONE 不同,这不会影响布局计算。 isVisible = false 的元素也会被排除在命中测试之外,因此许多 UI 事件(如点击)将不会被触发。

isActive

isActive 设置为 false 时,元素可能会失去其交互行为——例如,按钮无法再被点击——并且元素将不再接收 tick 事件。

INFO

isActive 设置为 false 时,会自动向元素添加一个 __disabled__ 类。 你可以使用以下 LSS 选择器来设置活动和非活动状态的样式:

css
selector.__disabled__ {
}

selector:disabled {
}

selector:not(.__disabled__) {
}

selector:not(:disabled) {
}

focusable

元素默认是 focusable: false。有些组件(如 TextField)在设计上是可聚焦的,但你仍然可以手动更改元素的可聚焦状态。 只有当 focusable 设置为 true 时,元素才能通过 focus() 或鼠标交互获得焦点。

INFO

当元素处于 focused(已聚焦)状态时,会自动添加一个 __focused__ 类。 你可以使用以下 LSS 选择器来设置已聚焦和未聚焦状态的样式:

css
selector.__focused__ {
}

selector:focused {
}

selector:not(.__focused__) {
}

selector:not(:focused) {
}

hover state

当元素被悬停时,会自动添加一个 __hovered__ 类。 为了兼容 CSS,你可以使用 :hover 作为选择器简写,它等价于 .__hovered__

css
selector.__hovered__ {
}

selector:hover {
}

isInternalUI

这是一个特殊状态,表示一个元素是否是组件的内部部分。 例如,一个 button 包含一个用于渲染其标签的内部 text 元素。

从语义上讲,不允许直接添加、移除或重新排序内部元素。 但是,你仍然可以通过编辑器或 XML 编辑它们的样式并管理它们的子元素。 在编辑器中,内部元素在层级视图中显示为灰色。

在 XML 中,你可以使用 #!xml &lt;internal index="..."/&gt; 标签访问内部元素,其中 index 指定要引用的内部元素:

xml
<button>
    <!-- 在这里获取内部文本组件 -->
    <internal index="0">
    </internal>
</button>

INFO

在 LSS 中,你可以使用 :host 和 :internal 来明确指定宿主元素或内部元素。默认情况下,选择器会匹配两者,除非加以限制。

css
button > text {
}

button > text:internal {
}

button > text:host {
}

字段

仅列出外部可观察或可配置的公共或受保护字段。

名称类型访问权限描述
taffyStyleTaffyLayoutStyleprotected (getter)用于布局计算的底层 Taffy 样式桥接对象。
nodeIdNodeIdprotected (getter)注册在 TaffyTree 中的节点句柄。
modularUIModularUIprivate (getter)此元素所属的 ModularUI 实例。
idStringprivate (getter/setter)元素 ID,用于选择器和查询。
classesSet&lt;String&gt;private (getter)应用于此元素的类似 CSS 的类列表。
styleBagStyleBagprivate (getter)存储已解析的样式候选值和计算后的样式。
stylesList&lt;Style&gt;private (getter)附加到此元素的内联样式。
layoutStyleLayoutStyleprivate (getter)布局属性对应的样式包装器。
styleBasicStyleprivate (getter)基础视觉样式(background、overlay 着色、opacity、zIndex 等)。
isVisiblebooleanprivate (getter/setter)元素是否可见。
isActivebooleanprivate (getter/setter)元素是否参与逻辑和事件。
focusablebooleanprivate (getter/setter)元素是否可以获得焦点。
isInternalUIbooleanprivate (getter)标记内部(组件拥有的)元素。

方法

布局与几何

方法签名描述
getLayout()LayoutStyle返回布局样式控制器。
layout(...)UIElement layout(Consumer&lt;LayoutStyle&gt;)以流式方式修改布局属性。
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&lt;UIElement&gt;返回一个不可修改的子元素列表。
addChild(...)UIElement addChild(UIElement)添加一个子元素。
addChildren(...)UIElement addChildren(UIElement...)添加多个子元素。
removeChild(...)boolean removeChild(UIElement)移除一个子元素。
removeSelf()boolean从其父元素中移除此元素。
clearAllChildren()void移除所有子元素。
isAncestorOf(...)boolean检查此元素是否是另一个元素的祖先。
getStructurePath()ImmutableList&lt;UIElement&gt;从根元素到此元素的路径。

样式与类

方法签名描述
style(...)UIElement style(Consumer&lt;BasicStyle&gt;)修改内联视觉样式。
lss(...)UIElement lss(String, Object)以编程方式应用样式表风格的属性。
addClass(...)UIElement addClass(String)添加一个类似 CSS 的类。
removeClass(...)UIElement removeClass(String)移除一个类。
hasClass(...)boolean检查类是否存在。
getLocalStylesheets()List&lt;Stylesheet&gt;返回附加到此元素的本地样式表。
addLocalStylesheet(...)UIElement addLocalStylesheet(Stylesheet)添加本地样式表(仅自身 + 后代)。
addLocalStylesheet(...)UIElement addLocalStylesheet(String)从 LSS 文本解析并添加本地样式表。
removeLocalStylesheet(...)UIElement removeLocalStylesheet(Stylesheet)从此元素作用域中移除本地样式表。
clearLocalStylesheets()UIElement移除附加到此元素的所有本地样式表。
transform(...)UIElement transform(Consumer&lt;Transform2D&gt;)应用 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停止鼠标和拖拽事件的传播。

用法

java
// 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_CHANGEDModularUI 关联已更改
UIEvents.TICK周期性的客户端 tick

客户端-服务端同步与 RPC

方法签名描述
addSyncValue(...)UIElement注册一个同步值。
removeSyncValue(...)UIElement注销一个同步值。
addRPCEvent(...)RPCEmitter注册一个 RPC 事件。
sendEvent(...)void向服务端发送一个 RPC 事件。
sendEvent(..., callback)&lt;T&gt; void发送一个带有响应回调的 RPC 事件。

服务端事件

服务端事件监听器在服务端而不是客户端上运行。它们使用相同的 UIEvents 类型常量,并支持冒泡和捕获阶段。它们通过内部 RPC 机制自动同步。

java
// Runs on the server when UIEvents.TICK fires
element.addServerEventListener(UIEvents.TICK, event -> {
    // server-side tick logic
});

RPC 事件

RPC(远程过程调用)事件允许客户端显式调用服务端逻辑,并可选择接收响应。

java
// 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 辅助函数。

java
// Bidirectional: synced server <-> client
element.addSyncValue(new SyncValue<>(Integer.class,
    () -> myData.count,
    v  -> myData.count = v
));

渲染

方法签名描述
isDisplayed()boolean如果 display 不是 NONE,则返回 true。

Released under the MIT License.