跳转至

UIElement

Since 2.2.1

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);
val root = element root@{
    element({
        layout = { size(40.px) }
        style = { background(MCSprites.RECT) }
        focusable = true
        cls = {
            +"add-class"
            -"remove-class"
        }
    }) {
        events { UIEvents.MOUSE_DOWN += { it.currentElement.focus() } }
    }
}
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 样式(包括布局)可以通过以下方式访问:

element.style(style -> style.background(...));
element.layout(layout -> layout.width(...));
element.getStyle().background(...);
element.getLayout().width(...);
element({
    layout = { width(20.pct) }
    style = { background(MCSprites.RECT) }
}) { }

element.layoutDsl { 
    width(20.pct)
}
element.styleDsl { 
    background(MCSprites.RECT)
}
element.style(style -=> style.background(...));
element.layout(layout => layout.width(...));
element.getStyle().background(...);
element.getLayout().width(...);

布局属性

在使用前,建议先阅读 Layout

display

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

layout.display(TaffyDisplay.FLEX);
layout.display(TaffyDisplay.GRID); // enable grid layout
element.setDisplay(false); // equals to layout.display(TaffyDisplay.NONE);
layout {
    display(false)
    display(TaffyDisplay.GRID)
}
element {
    display: flex;
    display: grid;
}

layout-direction

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

layout.layoutDirection(TaffyDirection.LTR);
layout {
    direction(TaffyDirection.LTR)
}
element {
    layout-direction: ltr;
}

flex-basis

设置 flex 伸缩前的初始主轴尺寸。支持 pointpercentauto

layout.flexBasis(1);
layout {
    flexBasis(1)
}
element {
    flex-basis: 1;
}

flex

使元素沿主轴具有弹性。

layout.flex(1);
layout {
    flex(1)
}
element {
    flex: 1;
}

flex-grow

控制当有额外空间时元素的增长比例。

layout.flexGrow(1);
layout {
    flexGrow(1)
}
element {
    flex-grow: 1;
}

flex-shrink

控制当空间不足时元素的收缩比例。

layout.flexShrink(1);
layout {
    flexShrink(1)
}
element {
    flex-shrink: 1;
}

flex-direction

定义主轴方向,例如 ROWCOLUMN

layout.flexDirection(FlexDirection.ROW);
layout {
    flexDirection(FlexDirection.ROW)
}
element {
    flex-direction: row;
}

flex-wrap

控制子元素是否换行到多行。

layout.wrap(FlexWrap.WRAP);
layout {
    wrap(FlexWrap.WRAP)
}
element {
    flex-wrap: wrap;
}

position

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

layout.positionType(TaffyPosition.ABSOLUTE);
layout {
    position(TaffyPosition.ABSOLUTE)
}
element {
    position: absolute;
}

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

positionRELATIVEABSOLUTE 时使用的偏移量。

layout.top(10);
layout.leftPercent(30); // 30%
layout.allAuto()
layout = {
    pos {
        top(10.px)
        left(10.px)
        bottom(auto)
    }
}
element {
    top: 10;
    left: 30%;
    all: auto;
}

margin-*

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

设置元素周围的外边距。

layout.marginTop(5);
layout.marginAll(3);
layout = {
    margin {
        top(5.px)
        all(3.px)
    }
}
element {
    margin-top: 5;
    margin-all: 3;
}

padding-*

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

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

layout.paddingLeft(8);
layout = {
    padding { left(8) }
}
element {
    padding-left: 8;
}

gap-*

*: row / column / all

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

layout.rowGap(6);
layout = {
    gap { row(6) }
}
element {
    gap-row: 6;
}

width

设置元素宽度。支持 pointpercentauto 模式。

layout.width(100);
layout.widthPercent(20); // 20%
layout = {
    width(100)
    width(20.pct) // 20%
}
element {
    width: 100;
    width: 20%;
}

height

设置元素高度。支持 pointpercentauto 模式。

layout.height(50);
layout = {
    widheightth(100)
    height(20.pct) // 20%
}
element {
    height: 50;
}

min-width / min-height

设置最小尺寸约束。

layout.minWidth(20);
layout = {
    minWidth(20);
}
element {
    min-width: 20;
}

max-width / max-height

设置最大尺寸约束。

layout.maxHeight(200);
layout = {
    maxHeight(200);
}
element {
    max-height: 200;
}

aspect-rate

锁定宽高比。适用于正方形或图标元素。

layout.aspectRate(1);
layout = {
    aspectRate(1);
}
element {
    aspect-rate: 1;
}

align-items

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

layout.alignItems(AlignItems.CENTER);
layout = {
    alignItems(AlignItems.CENTER)
}
element {
    align-items: center;
}

justify-content

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

layout.justifyContent(AlignContent.CENTER);
layout = {
    justifyContent(AlignContent.CENTER)
}
element {
    justify-content: center;
}

align-self

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

layout.alignSelf(AlignItems.CENTER);
layout = {
    alignSelf(AlignItems.CENTER)
}
element {
    align-self: center;
}

align-content

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

layout.alignContent(AlignContent.CENTER);
layout = {
    alignContent(AlignContent.CENTER)
}
element {
    align-content: center;
}

Grid 属性

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

grid-template-rows

定义网格容器的显式行轨道。

支持的轨道尺寸:Npx(固定像素)、N%(百分比)、Nfr(弹性单位)、automin-contentmax-contentminmax(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
layout = {
    display(TaffyDisplay.GRID)
    grid {
        templateRows("1fr 1fr 1fr")
        templateRows("repeat(3, 100px)")
    }
}
element {
    display: grid;
    grid-template-rows: 1fr 1fr 1fr;
    grid-template-rows: repeat(3, 100px);
}

grid-template-columns

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

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");
layout = {
    display(TaffyDisplay.GRID)
    grid {
        templateColumns("10px 1fr 10px")
        templateColumns("repeat(3, 1fr)")
    }
}
element {
    display: grid;
    grid-template-columns: 10px 1fr 10px;
    grid-template-columns: repeat(3, 1fr);
}

grid-template-areas

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

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
layout = {
    display(TaffyDisplay.GRID)
    grid {
        templateColumns("1fr 1fr 1fr")
        templateRows("auto 1fr auto")
        templateAreas("\"header header header\" \"sidebar content content\" \"footer footer footer\"")
    }
}
element {
    display: grid;
    grid-template-rows: auto 1fr auto;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-areas: "header header header" "sidebar content content" "footer footer footer";
}

grid-auto-rows

设置隐式创建的行的行轨道尺寸——即 grid-template-rows 未覆盖的行。

layout.gridAutoRows("auto");
layout.gridAutoRows("minmax(50px, auto)");
layout = {
    display(TaffyDisplay.GRID)
    grid { autoRows("minmax(50px, auto)") }
}
element {
    display: grid;
    grid-auto-rows: minmax(50px, auto);
}

grid-auto-columns

设置隐式创建的列的列轨道尺寸。

layout.gridAutoColumns("auto");
layout.gridAutoColumns("100px");
layout = {
    display(TaffyDisplay.GRID)
    grid { autoColumns("100px") }
}
element {
    display: grid;
    grid-auto-columns: 100px;
}

grid-auto-flow

控制自动放置的项目如何填充网格。ROW 先填充行(默认);COLUMN 先填充列。ROW_DENSE / COLUMN_DENSE 回填较早的空隙。

layout.gridAutoFlow(GridAutoFlow.ROW);
layout.gridAutoFlow(GridAutoFlow.COLUMN);
layout.gridAutoFlow(GridAutoFlow.ROW_DENSE);
layout = {
    display(TaffyDisplay.GRID)
    grid { autoFlow(GridAutoFlow.COLUMN) }
}
element {
    display: grid;
    grid-auto-flow: column;
}

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
element({
    layout = {
        grid { row("1 / span 2") }
    }
}) { }
child {
    grid-row: 1;
    grid-row: 1 / 3;
    grid-row: span 2;
    grid-row: header;
}

grid-column

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

child.layout(layout -> layout.gridColumn("2"));
child.layout(layout -> layout.gridColumn("1 / span 3"));
child.layout(layout -> layout.gridColumn("sidebar"));
element({
    layout = {
        grid { column("1 / span 2") }
    }
}) { }
child {
    grid-column: 2;
    grid-column: 1 / span 3;
    grid-column: sidebar;
}

基本属性

background

设置元素下方的背景渲染,如颜色、矩形、图片。

layout.background(MCSprites.BORDER);
style = {
    background(MCSprites.BORDER)
}

查看 Texture in LSS 了解 LSS 支持。

element {
    background: #FFF;
    background: rect(#2ff, 3);
    background: sprite(ldlib2:textures/gui/icon.png);
}

overflow

控制如何处理溢出内容。如果为 'hidden',超出边界的内容将被隐藏。

style.overflow(YogaOverflow.HIDDEN);
element.setOverflowVisible(false); // equals to style.overflow(YogaOverflow.HIDDEN);
style = {
    overflowVisible(false)
}
element {
    overflow: hidden;
}

overlay

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

layout.overlay(...);
style = {
    overlay(MCSprites.BORDER)
}

查看 Texture in LSS 了解 LSS 支持。

element {
    overlay: #FFF;
    overlay: rect(#2ff, 3);
    overlay: sprite(ldlib2:textures/gui/icon.png);
}

tooltips

定义悬停元素时显示的工具提示内容。

layout.tooltips("tips.0" "tips.1");
layout.appendTooltips("tips.2");
style = {
    tooltips("tips.0" "tips.1")
}
element {
    tooltips: this is my tooltips;
}

z-index

控制元素的堆叠顺序。值越高越靠前显示。

layout.zIndex(1);
style = {
    zIndex(1)
}
element {
    z-index: 1;
}

opacity

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

layout.opacity(0.8f);
style = {
    opacity(0.8)
}
element {
    opacity: 0.8;
}

color

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

style.color(0x80FF8080);
style = {
    color(0x80FF8080.toInt())
}
element {
    color: #80FF8080;
}

overflow-clip

如果元素的 overflow 设置为 hidden,则根据给定纹理的红色通道裁剪元素渲染。

layout.overflowClip(true);
style = {
    overflowClip(true)
}

查看 Texture in LSS 了解 LSS 支持。

element {
    overflow-clip: sprite(ldlib2:textures/gui/icon.png);
}

transform-2d

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

layout.transform2D(Transform2D.identity().scale(0.5f));
element.transform(transform -> transform.translate(10, 0))
style = {
    transform2D(Transform2D.identity().scale(0.5f))
}
element {
    transform: translate(10, 20) rotate(45) scale(2 2) pivot(0.5, 0.5);
    transform: translateX(10) scale(0.5);
}

transition

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

layout.transition(new Transition(Map.of(LayoutProperties.HEIGHT, new Animation(1, 0, Eases.LINEAR))));
style = {
    transition(Transition(mapOf(LayoutProperties.HEIGHT to Animation(1f, 0f, Eases.LINEAR))))
}
element {
    transition: width 1;
    transition: background 0.8 quad_in_out,
                transform 0.3;
}

状态

isVisible

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

isActive

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

Note

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

selector.__disabled__ {
}

selector:disabled {
}

selector:not(.__disabled__) {
}

selector:not(:disabled) {
}

focusable

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

Note

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

selector.__focused__ {
}

selector:focused {
}

selector:not(.__focused__) {
}

selector:not(:focused) {
}

hover state

当元素被悬停时,会自动添加 __hovered__ 类。 为了 CSS 兼容性,你可以使用 :hover 作为选择器语法糖,它等同于 .__hovered__

selector.__hovered__ {
}

selector:hover {
}

isInternalUI

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

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

在 XML 中,你可以使用 <internal index="..."/> 标签访问内部元素,其中 index 指定要引用的内部元素:

<button>
    <!-- obtain the internal text component here -->
    <internal index="0">
    </internal>
</button>

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

button > text {
}

button > text:internal {
}

button > text:host {
}


字段

仅列出外部可观察或可配置的 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();
element {
    // Bubble events (default)
    events {
        UIEvents.MOUSE_DOWN += UIEventListener { it.currentElement.focus() }
        UIEvents.CLICK on { event -> /* handle click */ }
    }
    // Capture events
    events(capture = true) {
        UIEvents.CLICK on { it.stopPropagation() }
    }
}
element.addEventListener(UIEvents.MOUSE_DOWN, event => {
    event.currentElement.focus();
});

可用事件

事件 描述
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 机制自动同步。

// Runs on the server when UIEvents.TICK fires
element.addServerEventListener(UIEvents.TICK, event -> {
    // server-side tick logic
});
element {
    serverEvents {
        UIEvents.TICK on { event ->
            // server-side logic
        }
    }
    // Capture phase on server
    serverEvents(capture = true) {
        UIEvents.CLICK on { it.stopPropagation() }
    }
}

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

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

    // Server → client only
    bindS2C({ myData.count })

    // Client → server only
    bindC2S({ v -> myData.count = v })

    // Bind a mutable property directly (bidirectional)
    bind(myData::count)
}

渲染

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