让鼠标滚轮能滚列表:把列表 ScrollPane 的 touchable 从默认 childrenOnly 改成 enabled(无参返回枚举的方法=getTouchable,枚举 dv 第0常量=enabled)

- 诊断证据:程序化 scrollY(200) 生效(before=0 after=200),说明可滚范围没问题,
  是滚轮事件没送到面板 —— 因为 childrenOnly 时 ScrollPane 自己不是命中目标,收不到 enter、
  也就不会 setScrollFocus。
- 顺带把这条 Actor/Touchable 映射写进 anchors.tsv + names.tsv。
This commit is contained in:
tianmo committed 2026-10-16 16:18:28 +08:00
1 parent d59287937d
commit 13bce4509d
3 files changed
+73

No files matched your search

@@ -84,6 +84,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;
makeTouchable(scroll);
state.status = label(loader, skin, "Ready. Press Refresh to list scene objects.");
Object logScroll = null;
state.nameField = fieldClass.getConstructor(String.class, skinClass)
@@ -537,6 +538,64 @@ public final class ObjectPanel {
state.logBlockStarted = false;
}
/**
* 把 ScrollPane 的 touchable 设成 `enabled`(它默认是 childrenOnly)。
*
* 为什么:ScrollPane 是靠自己的 InputListener 在鼠标"进入"时调 `Stage.setScrollFocus(this)`
* 来接收滚轮的;childrenOnly 时它自己不是命中目标,就永远收不到 enter → 滚轮事件不会送到它,
* 表现就是"面板能程序化滚动,但鼠标滚轮没用"。
*
* 不靠名字定位:`Actor.getTouchable()` 是"**无参 + 返回枚举**"的那个方法;枚举第 0 个常量
* 就是 `Touchable.enabled`(libGDX 的声明顺序 enabled/disabled/childrenOnly)。
*/
static void makeTouchable(Object actor) {
try {
Class<?> enumType = null;
for (Method method : actor.getClass().getMethods()) {
if (method.getParameterTypes().length != 0) continue;
Class<?> returns = method.getReturnType();
if (!returns.isEnum()) continue;
enumType = returns;
break;
}
if (enumType == null) {
System.err.println("[panel] 找不到 Touchable 枚举,跳过");
return;
}
Object[] constants = enumType.getEnumConstants();
if (constants == null || constants.length == 0) return;
Object enabled = constants[0];
for (Method method : actor.getClass().getMethods()) {
Class<?>[] types = method.getParameterTypes();
if (types.length != 1 || types[0] != enumType) continue;
if (method.getReturnType() != void.class) continue;
method.setAccessible(true);
method.invoke(actor, enabled);
System.err.println("[panel] ScrollPane.touchable = " + enabled
+ "(枚举 " + enumType.getName() + " 第 0 个常量)");
return;
}
System.err.println("[panel] 找不到 setTouchable");
} catch (Throwable error) {
System.err.println("[panel] 设置 touchable 失败: " + error);
}
}
private static float readFloat(Object target, String name) {
try {
for (Method method : target.getClass().getMethods()) {
if (!method.getName().equals(name)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() != float.class) continue;
method.setAccessible(true);
return ((Float) method.invoke(target)).floatValue();
}
} catch (Throwable ignored) {
// 读不到就返回 -1
}
return -1f;
}
private static void invalidateList(final State state) {
try {
call(state.listTable, "b_", new Class<?>[0], void.class);
@@ -563,6 +622,13 @@ public final class ObjectPanel {
// 再打开"强制纵向滚动":setForceScroll 在载荷里叫 `I`((ZZ)V,指纹确认 ~2/3)
call(state.listScroll, "I", new Class<?>[] {boolean.class, boolean.class}, void.class,
Boolean.FALSE, Boolean.TRUE);
// 诊断(只读+可回滚):读一下当前滚动位置,再程序化滚 200,再读一次。
// before/after 都写日志:如果 after 还是 0 → 说明 ScrollPane 认为"内容装得下"(高度没测到);
// 如果 after 变成 200 → 说明面板能滚,是滚轮事件没送到面板。
float before = readFloat(state.listScroll, "invoke");
call(state.listScroll, "Class", new Class<?>[] {float.class}, void.class, Float.valueOf(200f));
float after = readFloat(state.listScroll, "invoke");
System.err.println("[panel] scroll diag: before=" + before + " after=" + after);
} catch (Throwable error) {
System.err.println("[panel] 恢复滚动失败: " + error);
}