From 7dfd5d168d8936f4468212b267c60bcd2c25dd49 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 7 Jul 2025 20:32:04 +0200 Subject: [PATCH] implement global (menu) event handling --- .../src/main/java/de/unixwork/ui/AppMenu.java | 6 ++- .../main/java/de/unixwork/ui/ArgFuncs.java | 4 ++ .../java/de/unixwork/ui/EventWrapper.java | 44 +++++++++++++++---- .../src/main/java/de/unixwork/ui/Toolkit.java | 12 +++++ .../test/java/de/unixwork/ui/demo/Main.java | 9 +++- 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/ui-java/src/main/java/de/unixwork/ui/AppMenu.java b/ui-java/src/main/java/de/unixwork/ui/AppMenu.java index d98a751..184d088 100644 --- a/ui-java/src/main/java/de/unixwork/ui/AppMenu.java +++ b/ui-java/src/main/java/de/unixwork/ui/AppMenu.java @@ -40,7 +40,11 @@ public class AppMenu { a.menuitem_args_set_icon.invoke(args, cstr); } if(onClick != null) { - // TODO + EventWrapper event = new EventWrapper(onClick); + + // set toolkit args + a.menuitem_args_set_onclick.invoke(args, event.getCallback()); + a.menuitem_args_set_onclickdata.invoke(args, event.getUserData()); } ui.menuitem_create.invoke(args); a.menuitem_args_free.invoke(args); diff --git a/ui-java/src/main/java/de/unixwork/ui/ArgFuncs.java b/ui-java/src/main/java/de/unixwork/ui/ArgFuncs.java index aa70c3e..be12f85 100644 --- a/ui-java/src/main/java/de/unixwork/ui/ArgFuncs.java +++ b/ui-java/src/main/java/de/unixwork/ui/ArgFuncs.java @@ -10,6 +10,8 @@ class ArgFuncs { MethodHandle menuitem_args_set_label; MethodHandle menuitem_args_set_stockid; MethodHandle menuitem_args_set_icon; + MethodHandle menuitem_args_set_onclick; + MethodHandle menuitem_args_set_onclickdata; MethodHandle menuitem_args_free; MethodHandle menutoggleitem_args_new; @@ -310,6 +312,8 @@ class ArgFuncs { menuitem_args_set_label = linker.downcallHandle(ui_menuitem_args_set_label_addr, sigv_mm); menuitem_args_set_stockid = linker.downcallHandle(ui_menuitem_args_set_stockid_addr, sigv_mm); menuitem_args_set_icon = linker.downcallHandle(ui_menuitem_args_set_icon_addr, sigv_mm); + menuitem_args_set_onclick = linker.downcallHandle(ui_menuitem_args_set_onclick_addr, sigv_mm); + menuitem_args_set_onclickdata = linker.downcallHandle(ui_menuitem_args_set_onclickdata_addr, sigv_mm); menuitem_args_free = linker.downcallHandle(ui_menuitem_args_free_addr, sigv_m); menutoggleitem_args_new = linker.downcallHandle(ui_menutoggleitem_args_new_addr, sigm); diff --git a/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java b/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java index a2d34b8..b018454 100644 --- a/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java +++ b/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java @@ -1,5 +1,6 @@ package de.unixwork.ui; +import javax.tools.Tool; import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; @@ -13,11 +14,27 @@ public class EventWrapper { private MemorySegment userdata; public EventWrapper(UiObject obj, EventHandler handler) { - // We need to create an upcall stub for the static ButtonBuilder.eventHandler method + // We need to create an upcall stub for the static EventWrapper.eventHandler method // Also, the event handler must be registered in the object and the returned index // is used as callback userdata. This way we can map the userdata to the java // EventHandler object + Arena objArena = obj.getArena(); // very important to use the obj arena + initCallback(objArena, "eventHandler"); + + long index = obj.addEventHandler(handler); + // use index as callback userdata, like casting it to intptr_t/void* + userdata = MemorySegment.ofAddress(index); + } + + public EventWrapper(EventHandler handler) { + Toolkit toolkit = Toolkit.getInstance(); + initCallback(toolkit.getStaticArena(), "globalEventHandler"); + long index = toolkit.addEventHandler(handler); + userdata = MemorySegment.ofAddress(index); + } + + private void initCallback(Arena arena, String methodName) { Toolkit toolkit = Toolkit.getInstance(); // void callback(UiEvent *event, void *userdata) FunctionDescriptor handlerSig = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS); @@ -26,22 +43,18 @@ public class EventWrapper { try { callbackMethod = MethodHandles.lookup().findStatic( EventWrapper.class, - "eventHandler", + methodName, MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class)); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } - Arena objArena = obj.getArena(); + callback = toolkit.getLinker().upcallStub( callbackMethod, handlerSig, - obj.getArena()); // very important to use the obj arena - - long index = obj.addEventHandler(handler); - // use index as callback userdata, like casting it to intptr_t/void* - userdata = MemorySegment.ofAddress(index); + arena); } public MemorySegment getCallback() { @@ -58,6 +71,19 @@ public class EventWrapper { Event e = new Event(event); UiObject obj = e.getObject(); EventHandler handler = obj.getEventHandler(eventHandlerIndex); - handler.callback(e); + if(handler != null) { + handler.callback(e); + } // else: error? + } + + public static void globalEventHandler(MemorySegment event, MemorySegment userdata) { + int eventHandlerIndex = (int)userdata.address(); + + Event e = new Event(event); + UiObject obj = e.getObject(); + EventHandler handler = Toolkit.getInstance().getEventHandler(eventHandlerIndex); + if(handler != null) { + handler.callback(e); + } // else: error? } } diff --git a/ui-java/src/main/java/de/unixwork/ui/Toolkit.java b/ui-java/src/main/java/de/unixwork/ui/Toolkit.java index 15523e2..1e2b898 100644 --- a/ui-java/src/main/java/de/unixwork/ui/Toolkit.java +++ b/ui-java/src/main/java/de/unixwork/ui/Toolkit.java @@ -4,6 +4,7 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; import java.util.HashMap; public class Toolkit { @@ -29,6 +30,8 @@ public class Toolkit { private SymbolLookup lib; private MethodHandle mainFunc; + private ArrayList eventHandlers = new ArrayList<>(); + protected MemorySegment listFirst; protected MemorySegment listNext; protected MemorySegment listGet; @@ -339,6 +342,15 @@ public class Toolkit { // TODO: remove this notice when subobjects are removed from toolkit } + public long addEventHandler(EventHandler handler) { + eventHandlers.add(handler); + return eventHandlers.size() - 1; + } + + public EventHandler getEventHandler(int index) { + return eventHandlers.get(index); + } + public Context getContext(long address) { return contexts.get(address); } diff --git a/ui-java/src/test/java/de/unixwork/ui/demo/Main.java b/ui-java/src/test/java/de/unixwork/ui/demo/Main.java index 2a6e1c9..60bc75d 100644 --- a/ui-java/src/test/java/de/unixwork/ui/demo/Main.java +++ b/ui-java/src/test/java/de/unixwork/ui/demo/Main.java @@ -51,8 +51,13 @@ public class Main implements Application{ Toolkit.init("testapp"); AppMenu.menu("File", () -> { - AppMenu.menuItem("Open", null); - AppMenu.menuItem("Exit", null); + AppMenu.menuItem("Open", event -> { + System.out.println("Open"); + }); + AppMenu.menuItem("Exit", event -> { + System.out.println("Exit"); + System.exit(0); + }); }); Toolkit.runApplication(new Main()); -- 2.47.3