diff --git a/viewpanel/src/com/spineview/ObjectPanel.java b/viewpanel/src/com/spineview/ObjectPanel.java index 1dd95ce..8705596 100644 --- a/viewpanel/src/com/spineview/ObjectPanel.java +++ b/viewpanel/src/com/spineview/ObjectPanel.java @@ -38,6 +38,7 @@ public final class ObjectPanel { Object listTable; Object listScroll; Object status; + String statusText; Object nameField; List objects = new ArrayList(); 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"取)。 */