修鼠标滚轮(第3版):面板按钮动作后把编辑器的滚动焦点抢到列表上
- 诊断证据:给列表 ScrollPane 挂 EventListener 代理后,滚轮事件收到 0 条 → 滚轮被视口的 scroll focus 拿走; - grabScrollFocus():只在 **Stage 自己声明**的方法里找(addActor 声明在 Group,不会误调); setter=『1 个参数是 Actor、返回 void』,getter=『无参返回 Actor』;调 setter 后看哪个 getter 变成面板, 找到就写日志『滚动焦点已抢到面板:setter=.. getter=..』,找不到写『没找到 setScrollFocus』。 - 触发时机:每次点击面板按钮/字段行之后(路由器里统一调用)。
This commit is contained in:
1 parent
995ec585f4
commit
4b05804479
1 file changed
+129
@@ -49,6 +49,7 @@ public final class ObjectPanel {
|
||||
java.util.List<Object[]> inspectFields = new ArrayList<Object[]>();
|
||||
List<Object> objects = new ArrayList<Object>();
|
||||
Object target;
|
||||
boolean scrollFocusLogged;
|
||||
Class<?> tableClass;
|
||||
Class<?> scrollClass;
|
||||
Class<?> actorClass;
|
||||
@@ -61,6 +62,7 @@ public final class ObjectPanel {
|
||||
*/
|
||||
public static Object build(ClassLoader loader, Object skin) throws Exception {
|
||||
final State state = new State();
|
||||
LAST_STATE = state;
|
||||
state.loader = loader;
|
||||
state.skin = skin;
|
||||
|
||||
@@ -85,6 +87,7 @@ public final class ObjectPanel {
|
||||
Object scroll = scrollClass.getConstructor(actorClass, skinClass).newInstance(state.listTable, skin);
|
||||
state.listScroll = scroll;
|
||||
makeTouchable(scroll);
|
||||
attachEventProbe(scroll, "listScroll");
|
||||
state.status = label(loader, skin, "Ready. Press Refresh to list scene objects.");
|
||||
Object logScroll = null;
|
||||
state.nameField = fieldClass.getConstructor(String.class, skinClass)
|
||||
@@ -548,6 +551,128 @@ public final class ObjectPanel {
|
||||
* 不靠名字定位:`Actor.getTouchable()` 是"**无参 + 返回枚举**"的那个方法;枚举第 0 个常量
|
||||
* 就是 `Touchable.enabled`(libGDX 的声明顺序 enabled/disabled/childrenOnly)。
|
||||
*/
|
||||
/**
|
||||
* 只读诊断:给某个 Actor 挂一个事件监听(`dn` = EventListener 接口的 **动态代理**),
|
||||
* 把收到的每个事件打出来。用来确认"鼠标滚轮到底有没有送到面板"。
|
||||
*
|
||||
* 为什么要代理:载荷里的监听器都是匿名内部类,反射 new 不出来;
|
||||
* 但 `dn` 是接口,用 `java.lang.reflect.Proxy` 就能造一个。
|
||||
*/
|
||||
/**
|
||||
* 把编辑器的**滚动焦点**抢到面板的列表上(鼠标滚轮才会滚列表)。
|
||||
*
|
||||
* 识别方式(不猜名字、也不会误调 addActor):
|
||||
* - 只在 **Stage 自己声明的**方法里找(`uk`/`dt`/`lpt6` 这些层),
|
||||
* `addActor` 声明在 Group 上、不在这一层,所以不会被误选;
|
||||
* - 候选 setter = 「1 个参数是 Actor 类型、返回 void」;候选 getter = 「无参、返回 Actor 类型」;
|
||||
* - 调 setter 后看哪个 getter 变成了我们的面板 —— 那就是 setScrollFocus 那一对,日志会写出来。
|
||||
*/
|
||||
static void grabScrollFocus(final State state) {
|
||||
try {
|
||||
Object stage = SceneObjects.global(state.loader, "uk");
|
||||
if (stage == null) stage = SceneObjects.global(state.loader, "dt");
|
||||
if (stage == null || state.listScroll == null) return;
|
||||
Class<?> actorClass = state.actorClass;
|
||||
java.util.List<Method> getters = new ArrayList<Method>();
|
||||
java.util.List<Method> setters = new ArrayList<Method>();
|
||||
for (Class<?> type = stage.getClass(); type != null && type != Object.class;
|
||||
type = type.getSuperclass()) {
|
||||
if (type.getName().equals(Obf.GROUP)) break; // 到 Group 就停(addActor 在这一层)
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length == 0 && actorClass.isAssignableFrom(method.getReturnType())) {
|
||||
getters.add(method);
|
||||
} else if (types.length == 1 && types[0].isAssignableFrom(actorClass)
|
||||
&& method.getReturnType() == void.class) {
|
||||
setters.add(method);
|
||||
}
|
||||
}
|
||||
if (type.getName().equals("dt")) continue;
|
||||
}
|
||||
for (Method setter : setters) {
|
||||
try {
|
||||
setter.setAccessible(true);
|
||||
setter.invoke(stage, state.listScroll);
|
||||
} catch (Throwable ignored) {
|
||||
continue;
|
||||
}
|
||||
for (Method getter : getters) {
|
||||
try {
|
||||
getter.setAccessible(true);
|
||||
if (getter.invoke(stage) == state.listScroll) {
|
||||
if (state.scrollFocusLogged) return;
|
||||
state.scrollFocusLogged = true;
|
||||
System.err.println("[panel] 滚动焦点已抢到面板:setter=" + setter.getName()
|
||||
+ " getter=" + getter.getName());
|
||||
return;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
// 下一个
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!state.scrollFocusLogged) {
|
||||
state.scrollFocusLogged = true;
|
||||
System.err.println("[panel] 没找到 setScrollFocus(候选 setter " + setters.size()
|
||||
+ " 个 / getter " + getters.size() + " 个)");
|
||||
}
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[panel] 抢滚动焦点失败: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
static void attachEventProbe(final Object actor, final String tag) {
|
||||
try {
|
||||
Class<?> listenerClass = Class.forName("dn", true, actor.getClass().getClassLoader());
|
||||
if (!listenerClass.isInterface()) {
|
||||
System.err.println("[probe] dn 不是接口,跳过");
|
||||
return;
|
||||
}
|
||||
Object listener = java.lang.reflect.Proxy.newProxyInstance(
|
||||
listenerClass.getClassLoader(), new Class<?>[] {listenerClass},
|
||||
new java.lang.reflect.InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
if (method.getName().equals("handle") && args != null && args.length == 1) {
|
||||
Object event = args[0];
|
||||
String name = event == null ? "null" : event.getClass().getName();
|
||||
String type = event == null ? "" : String.valueOf(readField(event, "D"));
|
||||
System.err.println("[probe] " + tag + " event=" + name + " typeField=" + type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
for (Method method : actor.getClass().getMethods()) {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length != 1 || types[0] != listenerClass) continue;
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(actor, listener);
|
||||
System.err.println("[probe] 已给 " + tag + " 挂事件监听: " + method.getName() + " -> " + result);
|
||||
return;
|
||||
}
|
||||
System.err.println("[probe] 找不到 addListener");
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[probe] 挂事件监听失败: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
/** 读一个字段(诊断用,读不到返回 "?")。 */
|
||||
static Object readField(Object target, String name) {
|
||||
for (Class<?> type = target.getClass(); type != null && type != Object.class;
|
||||
type = type.getSuperclass()) {
|
||||
for (java.lang.reflect.Field field : type.getDeclaredFields()) {
|
||||
if (!field.getName().equals(name)) continue;
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return field.get(target);
|
||||
} catch (Throwable ignored) {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void makeTouchable(Object actor) {
|
||||
try {
|
||||
Class<?> enumType = null;
|
||||
@@ -774,6 +899,8 @@ public final class ObjectPanel {
|
||||
private static volatile boolean routerStarted;
|
||||
|
||||
/** 验证用:记住面板上的按钮(标签 → 按钮),探针可以按标签"点"它们。 */
|
||||
private static volatile ObjectPanel.State LAST_STATE;
|
||||
|
||||
private static final java.util.Map<String, Object> BUTTONS =
|
||||
new java.util.LinkedHashMap<String, Object>();
|
||||
|
||||
@@ -883,6 +1010,8 @@ public final class ObjectPanel {
|
||||
}
|
||||
try {
|
||||
((Runnable) route[1]).run();
|
||||
Object st = LAST_STATE;
|
||||
if (st instanceof State) grabScrollFocus((State) st);
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[panel] 动作失败: " + error);
|
||||
error.printStackTrace(System.err);
|
||||
|
||||
Reference in new issue
Block a user