diff --git a/viewpanel/src/com/spineview/ObjectPanel.java b/viewpanel/src/com/spineview/ObjectPanel.java index 8705596..543a929 100644 --- a/viewpanel/src/com/spineview/ObjectPanel.java +++ b/viewpanel/src/com/spineview/ObjectPanel.java @@ -40,6 +40,7 @@ public final class ObjectPanel { Object status; String statusText; Object nameField; + final java.util.Deque log = new java.util.ArrayDeque(); List objects = new ArrayList(); Object target; } @@ -68,7 +69,7 @@ public final class ObjectPanel { 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."); + state.status = label(loader, skin, "Ready. Press Refresh to list scene objects."); state.nameField = fieldClass.getConstructor(String.class, skinClass) .newInstance("particle_bone", skin); @@ -98,9 +99,13 @@ public final class ObjectPanel { 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 的单元格,吃掉剩下的高度 + // 中段:列表区 —— 唯一 expand+fill 的单元格,吃掉剩余的高度 cell = call(root, Obf.TABLE_ADD, new Class[] {actorClass}, cellClass, scroll); layoutCell(cell, Obf.CELL_TOP, Obf.CELL_EXPAND, Obf.CELL_FILL); + // 下段:实时提示(多行、只靠左,不加 fill,保持自然高度贴在底部) + 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, Obf.CELL_BOTTOM); wire(loader, refresh, new Runnable() { @Override @@ -203,7 +208,7 @@ 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" + setStatus(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); @@ -380,9 +385,38 @@ public final class ObjectPanel { * 状态文字:改完布局后不再单独占一行(那会把列表挤下去),改成写日志 + * 记在 state 里,下一次 {@link #refreshList} 会把它显示为列表区第一行。 */ + /** + * 下段的"实时提示":保留最近 {@link #LOG_LINES} 条,多行显示在底部。 + * 所有 setText 都要在渲染线程上做。 + */ + private static final int LOG_LINES = 6; + private static void setStatus(final State state, final String text) { state.statusText = text; System.err.println("[panel] " + text); + if (state.status == null) return; + synchronized (state.log) { + state.log.addLast(text); + while (state.log.size() > LOG_LINES) state.log.removeFirst(); + } + 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] 提示区更新失败: " + error); + } + } + }); } /** TextField 里的文字(名字被混淆了,按"无参 + 返回 String"取)。 */