面板恢复成上/中/下三段:上=按钮块(两列紧邻+名字输入框)、中=列表(expand+fill)、下=日志(独立 Label,自然高度贴底);去掉列表里内嵌的日志行与之前未验证的尺寸调用
This commit is contained in:
1 parent
4b05804479
commit
55c0794ed4
1 file changed
+75
-49
@@ -50,6 +50,7 @@ public final class ObjectPanel {
|
|||||||
List<Object> objects = new ArrayList<Object>();
|
List<Object> objects = new ArrayList<Object>();
|
||||||
Object target;
|
Object target;
|
||||||
boolean scrollFocusLogged;
|
boolean scrollFocusLogged;
|
||||||
|
boolean geometryDumped;
|
||||||
Class<?> tableClass;
|
Class<?> tableClass;
|
||||||
Class<?> scrollClass;
|
Class<?> scrollClass;
|
||||||
Class<?> actorClass;
|
Class<?> actorClass;
|
||||||
@@ -116,58 +117,29 @@ public final class ObjectPanel {
|
|||||||
|
|
||||||
// 按钮直接放进根表格:两行按钮 + 一行滚动列表(与阶段四验证过的结构一致)
|
// 按钮直接放进根表格:两行按钮 + 一行滚动列表(与阶段四验证过的结构一致)
|
||||||
Object[] top = {refresh, newBone, newSlot, newBBox, newPath, delete, rename, inspect, setValue};
|
Object[] top = {refresh, newBone, newSlot, newBBox, newPath, delete, rename, inspect, setValue};
|
||||||
|
Object buttons = tableClass.getConstructor().newInstance();
|
||||||
|
call(buttons, Obf.TABLE_TOP, new Class<?>[0], tableClass);
|
||||||
for (int i = 0; i < top.length; i++) {
|
for (int i = 0; i < top.length; i++) {
|
||||||
Object cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, top[i]);
|
Object buttonCell = call(buttons, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, top[i]);
|
||||||
layoutCell(cell, Obf.CELL_LEFT);
|
layoutCell(buttonCell, Obf.CELL_LEFT);
|
||||||
if (i % 2 == 1 || i == top.length - 1) {
|
if (i % 2 == 1) call(buttons, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||||
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
|
||||||
}
|
}
|
||||||
}
|
call(buttons, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||||
Object cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.nameField);
|
Object nameCell = call(buttons, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.nameField);
|
||||||
|
layoutCell(nameCell, Obf.CELL_LEFT);
|
||||||
|
// 上段:按钮块(自适应高度,紧贴顶部)
|
||||||
|
Object cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, buttons);
|
||||||
layoutCell(cell, Obf.CELL_LEFT);
|
layoutCell(cell, Obf.CELL_LEFT);
|
||||||
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||||
// 唯一 expand+fill 的单元格:内容区(上面是列表/Inspector,最下面是日志块,同一个滚动区)
|
// 中段:列表(唯一 expand+fill 的行,吃掉剩余高度)
|
||||||
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, scroll);
|
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, scroll);
|
||||||
layoutCell(cell, Obf.CELL_TOP, Obf.CELL_EXPAND, Obf.CELL_FILL);
|
layoutCell(cell, Obf.CELL_TOP, Obf.CELL_EXPAND, Obf.CELL_FILL);
|
||||||
state.listCell = cell;
|
state.listCell = cell;
|
||||||
try {
|
// 下段:日志(独立一行,自然高度贴底;不用任何未验证的尺寸方法)
|
||||||
// 给列表一个**固定高度**:这样 ScrollPane 必定比内容矮,可滚范围(maxY)一定 > 0。
|
call(root, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||||
// Cell.height(float) 的载荷名是 `I`(描述符 (F)LdR;,MapMethods 指纹确认)。
|
cell = call(root, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.status);
|
||||||
call(cell, "I", new Class<?>[] {float.class}, cellClass, Float.valueOf(360f));
|
layoutCell(cell, Obf.CELL_LEFT);
|
||||||
} catch (Throwable error) {
|
|
||||||
System.err.println("[panel] 列表固定高度设置失败: " + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
wire(loader, refresh, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refreshList(state);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wire(loader, newBone, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
create(state, "Bone");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wire(loader, newSlot, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
create(state, "Slot");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wire(loader, newBBox, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
create(state, "Bounding Box");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wire(loader, newPath, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
create(state, "Path");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wire(loader, setValue, new Runnable() {
|
wire(loader, setValue, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -215,6 +187,29 @@ public final class ObjectPanel {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 把日志缓冲写进底部 Label(多行)。 */
|
||||||
|
private static void writeLogLabel(final State state) {
|
||||||
|
if (state.status == null) return;
|
||||||
|
final StringBuilder joined = new StringBuilder();
|
||||||
|
synchronized (state.log) {
|
||||||
|
for (String line : state.log) {
|
||||||
|
if (joined.length() > 0) joined.append('\n');
|
||||||
|
joined.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SceneActions.onEditorThread(state.loader, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
call(state.status, Obf.LABEL_SET_TEXT, new Class<?>[] {CharSequence.class},
|
||||||
|
void.class, joined.toString());
|
||||||
|
} catch (Throwable error) {
|
||||||
|
System.err.println("[panel] 日志 Label 更新失败: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 只往内容表格末尾追加**一行**日志(必要时先补一行分隔标记)。 */
|
/** 只往内容表格末尾追加**一行**日志(必要时先补一行分隔标记)。 */
|
||||||
private static void appendLogRow(State state, String text) {
|
private static void appendLogRow(State state, String text) {
|
||||||
try {
|
try {
|
||||||
@@ -319,8 +314,21 @@ public final class ObjectPanel {
|
|||||||
+ (state.target == null ? "" : "; target = " + SceneObjects.kind(state.target)
|
+ (state.target == null ? "" : "; target = " + SceneObjects.kind(state.target)
|
||||||
+ " " + SceneObjects.nameOf(state.target)));
|
+ " " + SceneObjects.nameOf(state.target)));
|
||||||
state.rendering = false;
|
state.rendering = false;
|
||||||
appendLogRows(state);
|
|
||||||
invalidateList(state);
|
invalidateList(state);
|
||||||
|
if (!state.geometryDumped) {
|
||||||
|
state.geometryDumped = true;
|
||||||
|
dumpFloats("pane(before)", state.listScroll);
|
||||||
|
for (String candidate : new String[] {"I", "L", "B", "Z", "V", "C", "S"}) {
|
||||||
|
try {
|
||||||
|
call(state.listCell, candidate, new Class<?>[] {float.class}, state.cellClass,
|
||||||
|
Float.valueOf(400f));
|
||||||
|
System.err.println("[panel] tried Cell." + candidate + "(400)");
|
||||||
|
dumpFloats("pane(after " + candidate + ")", state.listScroll);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
// 该名字上没有 (F)Cell 方法
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -621,6 +629,26 @@ public final class ObjectPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 只读诊断:打印对象上所有**无参返回 float**的方法名和值(用来看 ScrollPane 的真实宽高)。 */
|
||||||
|
static void dumpFloats(String tag, Object target) {
|
||||||
|
StringBuilder out = new StringBuilder("[panel] " + tag + ": ");
|
||||||
|
try {
|
||||||
|
for (Method method : target.getClass().getMethods()) {
|
||||||
|
if (method.getParameterTypes().length != 0) continue;
|
||||||
|
if (method.getReturnType() != float.class) continue;
|
||||||
|
try {
|
||||||
|
method.setAccessible(true);
|
||||||
|
out.append(method.getName()).append('=').append(method.invoke(target)).append(" ");
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
// 跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.err.println(out.toString());
|
||||||
|
} catch (Throwable error) {
|
||||||
|
System.err.println("[panel] dumpFloats 失败: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void attachEventProbe(final Object actor, final String tag) {
|
static void attachEventProbe(final Object actor, final String tag) {
|
||||||
try {
|
try {
|
||||||
Class<?> listenerClass = Class.forName("dn", true, actor.getClass().getClassLoader());
|
Class<?> listenerClass = Class.forName("dn", true, actor.getClass().getClassLoader());
|
||||||
@@ -791,7 +819,6 @@ public final class ObjectPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.rendering = false;
|
state.rendering = false;
|
||||||
appendLogRows(state);
|
|
||||||
invalidateList(state);
|
invalidateList(state);
|
||||||
} catch (Throwable error) {
|
} catch (Throwable error) {
|
||||||
System.err.println("[panel] 字段列表填充失败: " + error);
|
System.err.println("[panel] 字段列表填充失败: " + error);
|
||||||
@@ -834,7 +861,6 @@ public final class ObjectPanel {
|
|||||||
call(state.listTable, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
call(state.listTable, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||||
}
|
}
|
||||||
state.rendering = false;
|
state.rendering = false;
|
||||||
appendLogRows(state);
|
|
||||||
invalidateList(state);
|
invalidateList(state);
|
||||||
} catch (Throwable error) {
|
} catch (Throwable error) {
|
||||||
System.err.println("[panel] inspector 填充失败: " + error);
|
System.err.println("[panel] inspector 填充失败: " + error);
|
||||||
@@ -858,8 +884,8 @@ public final class ObjectPanel {
|
|||||||
state.log.addLast(text);
|
state.log.addLast(text);
|
||||||
while (state.log.size() > LOG_LINES) state.log.removeFirst();
|
while (state.log.size() > LOG_LINES) state.log.removeFirst();
|
||||||
}
|
}
|
||||||
// 只追加一行日志:千万不要在这里重跑列表渲染,否则 Inspect 写好的字段清单会被覆盖
|
// 写进底部日志 Label(多行、只保留最近几条)——不再动列表内容
|
||||||
if (!state.rendering) appendLogRow(state, text);
|
writeLogLabel(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TextField 里的文字(名字被混淆了,按"无参 + 返回 String"取)。 */
|
/** TextField 里的文字(名字被混淆了,按"无参 + 返回 String"取)。 */
|
||||||
|
|||||||
Reference in new issue
Block a user