package org.openautonomousconnection.luascript.fx; import org.openautonomousconnection.luascript.events.UiEventRegistry; import org.openautonomousconnection.luascript.hosts.EventHost; import org.openautonomousconnection.luascript.runtime.LuaEventRouter; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.events.EventListener; import org.w3c.dom.events.EventTarget; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; /** * EventHost implementation for JavaFX WebView using W3C DOM EventTarget listeners. * *

No JavaScript required. Events are forwarded to Lua via {@link LuaEventRouter}.

*/ public final class FxEventHost implements EventHost { private static final String GLOBAL_TARGET_ID = "__global__"; private final FxDomHost dom; private final LuaEventRouter router; private final ConcurrentHashMap elementListeners = new ConcurrentHashMap<>(); private final ConcurrentHashMap globalListeners = new ConcurrentHashMap<>(); /** * Creates a new FxEventHost. * * @param dom fx dom host * @param router lua router */ public FxEventHost(FxDomHost dom, LuaEventRouter router) { this.dom = Objects.requireNonNull(dom, "dom"); this.router = Objects.requireNonNull(router, "router"); } @Override public void addListener(String elementId, String eventName) { Objects.requireNonNull(elementId, "elementId"); Objects.requireNonNull(eventName, "eventName"); String evt = UiEventRegistry.normalize(eventName); String key = elementId + "\n" + evt; elementListeners.computeIfAbsent(key, k -> { EventListener listener = ev -> { Map payload = FxEventPayloadExtractor.extract(ev); router.emit(elementId, evt, payload); }; FxThreadBridge.runAndWait(() -> { Element el = dom.byId(elementId); ((EventTarget) el).addEventListener(evt, listener, false); }); return listener; }); } @Override public void removeListener(String elementId, String eventName) { Objects.requireNonNull(elementId, "elementId"); Objects.requireNonNull(eventName, "eventName"); String evt = UiEventRegistry.normalize(eventName); String key = elementId + "\n" + evt; EventListener listener = elementListeners.remove(key); if (listener == null) return; FxThreadBridge.runAndWait(() -> { Element el = dom.byId(elementId); ((EventTarget) el).removeEventListener(evt, listener, false); }); } @Override public void addGlobalListener(String eventName) { Objects.requireNonNull(eventName, "eventName"); String evt = UiEventRegistry.normalize(eventName); globalListeners.computeIfAbsent(evt, k -> { EventListener listener = ev -> { Map payload = FxEventPayloadExtractor.extract(ev); router.emit(GLOBAL_TARGET_ID, evt, payload); }; FxThreadBridge.runAndWait(() -> { Document doc = dom.requireDocument(); ((EventTarget) doc).addEventListener(evt, listener, false); }); return listener; }); } @Override public void removeGlobalListener(String eventName) { Objects.requireNonNull(eventName, "eventName"); String evt = UiEventRegistry.normalize(eventName); EventListener listener = globalListeners.remove(evt); if (listener == null) return; FxThreadBridge.runAndWait(() -> { Document doc = dom.requireDocument(); ((EventTarget) doc).removeEventListener(evt, listener, false); }); } }