面板布局再修:按钮直接进根表格(两列多行),列表区是唯一 expand+fill 的行;列表内容 Table 设 top()+left(),否则 ScrollPane 里控件比视口小时会居中显示,看着像'列表跑到下面'

This commit is contained in:
tianmo committed 2026-10-16 15:51:46 +08:00
1 parent f4cf713ded
commit 7f5ee1f00e
1 file changed
+40 -44
+40 -44
View File
@@ -38,6 +38,7 @@ public final class ObjectPanel {
Object listTable;
Object listScroll;
Object status;
String statusText;
Object nameField;
List<Object> objects = new ArrayList<Object>();
Object target;
@@ -62,6 +63,9 @@ public final class ObjectPanel {
Object rows = tableClass.getConstructor().newInstance();
call(rows, Obf.TABLE_TOP, new Class<?>[0], tableClass);
state.listTable = tableClass.getConstructor().newInstance();
// 列表内容靠左上:ScrollPane 里控件比视口小时默认会居中,看起来就像"列表跑到中间/下面"
call(state.listTable, Obf.TABLE_TOP, new Class<?>[0], tableClass);
call(state.listTable, Obf.TABLE_LEFT, new Class<?>[0], tableClass);
Object scroll = scrollClass.getConstructor(actorClass, skinClass).newInstance(state.listTable, skin);
state.listScroll = scroll;
state.status = label(loader, skin, "Press Refresh to list scene objects.");
@@ -82,38 +86,21 @@ public final class ObjectPanel {
Object rename = button(loader, skin, "Rename");
Object inspect = button(loader, skin, "Inspect");
Object cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, refresh);
layoutCell(cell, Obf.CELL_LEFT);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, newBone);
layoutCell(cell, Obf.CELL_LEFT);
call(rows, Obf.TABLE_ROW, new Class<?>[0], cellClass);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, newSlot);
layoutCell(cell, Obf.CELL_LEFT);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, delete);
layoutCell(cell, Obf.CELL_LEFT);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, rename);
layoutCell(cell, Obf.CELL_LEFT);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, inspect);
layoutCell(cell, Obf.CELL_LEFT);
call(rows, Obf.TABLE_ROW, new Class<?>[0], cellClass);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, newBBox);
layoutCell(cell, Obf.CELL_LEFT);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, newPath);
layoutCell(cell, Obf.CELL_LEFT);
call(rows, Obf.TABLE_ROW, new Class<?>[0], cellClass);
cell = call(rows, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.nameField);
layoutCell(cell, Obf.CELL_LEFT, Obf.CELL_EXPAND);
call(rows, Obf.TABLE_ROW, new Class<?>[0], cellClass);
// 第 1 行:按钮区;第 2 行:对象列表(撑满);第 3 行:状态行。
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, rows);
// 按钮直接放进根表格:两行按钮 + 一行滚动列表(与阶段四验证过的结构一致)
Object[] top = {refresh, newBone, newSlot, newBBox, newPath, delete, rename, inspect};
for (int i = 0; i < top.length; i++) {
Object cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, top[i]);
layoutCell(cell, Obf.CELL_LEFT);
if (i % 2 == 1 || i == top.length - 1) {
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
}
}
Object cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.nameField);
layoutCell(cell, Obf.CELL_LEFT);
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
// 列表区:唯一 expand+fill 的单元格,吃掉剩下的高度
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, scroll);
layoutCell(cell, Obf.CELL_EXPAND, Obf.CELL_FILL, Obf.CELL_TOP);
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.status);
layoutCell(cell, Obf.CELL_LEFT);
layoutCell(cell, Obf.CELL_TOP, Obf.CELL_EXPAND, Obf.CELL_FILL);
wire(loader, refresh, new Runnable() {
@Override
@@ -185,6 +172,21 @@ public final class ObjectPanel {
return root;
}
/** 状态文字:作为列表区的第一行显示(这样不再单独占根表格的一行,布局更稳)。 */
private static void statusRow(final State state, final String text) {
try {
Class<?> cellClass = Class.forName(Obf.CELL, true, state.loader);
Class<?> actorClass = Class.forName(Obf.ACTOR, true, state.loader);
Object label = label(state.loader, state.skin, text);
Object cell = call(state.listTable, Obf.TABLE_ADD, new Class<?>[] {actorClass},
cellClass, label);
layoutCell(cell, Obf.CELL_LEFT);
call(state.listTable, Obf.TABLE_ROW, new Class<?>[0], cellClass);
} catch (Throwable error) {
System.err.println("[panel] 状态行写入失败: " + error);
}
}
/** 重新读一遍树、重建对象列表(全部在编辑器渲染线程上做)。 */
private static void refreshList(final State state) {
SceneActions.onEditorThread(state.loader, new Runnable() {
@@ -201,6 +203,9 @@ public final class ObjectPanel {
try {
// Table.clearChildren()(载荷名 `SourceFile()V`):把列表里的行全清掉重建。
call(state.listTable, Obf.TABLE_CLEAR_CHILDREN, new Class<?>[0], void.class);
statusRow(state, objects.size() + " scene objects"
+ (state.target == null ? "" : "; target = " + SceneObjects.kind(state.target)
+ " " + SceneObjects.nameOf(state.target)));
Class<?> cellClass = Class.forName(Obf.CELL, true, state.loader);
Class<?> actorClass = Class.forName(Obf.ACTOR, true, state.loader);
for (int i = 0; i < objects.size() && i < 300; i++) {
@@ -223,9 +228,6 @@ public final class ObjectPanel {
return;
}
invalidateList(state);
setStatus(state, objects.size() + " scene objects"
+ (state.target == null ? "" : "; target = " + SceneObjects.kind(state.target)
+ " " + SceneObjects.nameOf(state.target)));
}
});
}
@@ -374,19 +376,13 @@ public final class ObjectPanel {
}
}
/**
* 状态文字:改完布局后不再单独占一行(那会把列表挤下去),改成写日志 +
* 记在 state 里,下一次 {@link #refreshList} 会把它显示为列表区第一行。
*/
private static void setStatus(final State state, final String text) {
if (state.status == null) return;
SceneActions.onEditorThread(state.loader, new Runnable() {
@Override
public void run() {
try {
call(state.status, Obf.LABEL_SET_TEXT, new Class<?>[] {CharSequence.class},
void.class, text);
} catch (Throwable error) {
System.err.println("[panel] 状态行更新失败: " + error);
}
}
});
state.statusText = text;
System.err.println("[panel] " + text);
}
/** TextField 里的文字(名字被混淆了,按"无参 + 返回 String"取)。 */