package de.unixwork.ui;
import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
public class ButtonBuilder extends AbstractWidgetBuilder {
private boolean fill;
MemorySegment args = (MemorySegment)ui.button_args_new.invoke();
if(fill) {
- // TODO
+ // TODO: implement after toolkit fill refactoring
}
if(hexpand) {
ui.button_args_set_hexpand.invoke(args, hexpand);
ui.button_args_set_labeltype.invoke(args, labelType);
if(onClick != null) {
- // TODO
+ EventWrapper event = new EventWrapper(obj, onClick);
+
+ // set toolkit args
+ ui.button_args_set_onclick.invoke(args, event.getCallback());
+ ui.button_args_set_onclickdata.invoke(args, event.getUserData());
}
return args;
}
-
- public UiWidget create() {
- UiWidget widget = null;
- try (Arena arena = Arena.ofConfined()) {
- MemorySegment buttonArgs = createArgs(arena);
- widget = new UiWidget((MemorySegment) widgetConstructor.invoke(obj.ptr, buttonArgs));
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- return widget;
- }
}
package de.unixwork.ui;
+import java.lang.foreign.MemorySegment;
+
public class Event {
private UiObject object;
private Object documemt;
}
+ public Event(MemorySegment eventPtr) {
+ Toolkit toolkit = Toolkit.getInstance();
+ ToolkitFuncs ui = ToolkitFuncs.getInstance();
+
+ try {
+ long objAddress = ((MemorySegment) ui.event_get_obj.invoke(eventPtr)).address();
+ object = toolkit.getToplevelObject(objAddress);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public UiObject getObject() {
return object;
}
--- /dev/null
+package de.unixwork.ui;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class EventWrapper {
+ private MemorySegment callback;
+ private MemorySegment userdata;
+
+ public EventWrapper(UiObject obj, EventHandler handler) {
+ // We need to create an upcall stub for the static ButtonBuilder.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
+
+ Toolkit toolkit = Toolkit.getInstance();
+ // void callback(UiEvent *event, void *userdata)
+ FunctionDescriptor handlerSig = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+
+ MethodHandle callbackMethod = null;
+ try {
+ callbackMethod = MethodHandles.lookup().findStatic(
+ EventWrapper.class,
+ "eventHandler",
+ 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);
+ }
+
+ public MemorySegment getCallback() {
+ return callback;
+ }
+
+ public MemorySegment getUserData() {
+ return userdata;
+ }
+
+ public static void eventHandler(MemorySegment event, MemorySegment userdata) {
+ System.out.println("event handler");
+ int eventHandlerIndex = (int)userdata.address();
+
+ Event e = new Event(event);
+ UiObject obj = e.getObject();
+ EventHandler handler = obj.getEventHandler(eventHandlerIndex);
+ handler.callback(e);
+ }
+}
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.HashMap;
public class Toolkit {
private static Toolkit instance;
private Application app;
+ private HashMap<Long, UiObject> toplevelObjects = new HashMap<>();
+
// used for all strings and other memory, that is expected to be const
// and the UI toolkit does not create copies
private final Arena staticArena = Arena.ofShared();
mainFunc = linker.downcallHandle(ui_main_addr, sigv_v);
}
- static Toolkit getInstance() {
+ public static Toolkit getInstance() {
if(Toolkit.instance == null) {
Toolkit.init("app1");
}
throw new RuntimeException(e);
}
}
+
+ public void registerToplevelObject(UiObject obj) {
+ toplevelObjects.put(obj.ptr.address(), obj);
+ }
+
+ public void removeToplevelObject(UiObject obj) {
+ toplevelObjects.remove(obj.ptr.address());
+ }
+
+ public UiObject getToplevelObject(long address) {
+ return toplevelObjects.get(address);
+ }
}
--- /dev/null
+package de.unixwork.ui;
+
+import java.lang.foreign.*;
+import java.lang.invoke.MethodHandle;
+
+public class ToolkitFuncs {
+ static ToolkitFuncs instance;
+
+ public MethodHandle event_get_obj;
+
+ private ToolkitFuncs(Linker linker, SymbolLookup lib) {
+ // void* func(void*)
+ FunctionDescriptor sigm_m = FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+ FunctionDescriptor sigi_m = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+
+ MemorySegment event_get_obj_addr = lib.find("ui_event_get_obj").orElseThrow();
+
+ event_get_obj = linker.downcallHandle(event_get_obj_addr, sigm_m);
+ }
+
+ static ToolkitFuncs getInstance() {
+ if (instance == null) {
+ Toolkit toolkit = Toolkit.getInstance();
+ instance = new ToolkitFuncs(toolkit.getLinker(), toolkit.getSymbolLookup());
+ }
+ return instance;
+ }
+}
package de.unixwork.ui;
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
+import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
+import java.util.function.Function;
public class UiObject {
MemorySegment ptr;
private ArrayList<EventHandler> eventHandlers = new ArrayList<>();
- UiObject(MemorySegment ptr) {
+ private Arena arena;
+
+ public UiObject(MemorySegment ptr) {
this.ptr = ptr;
+ // TODO: maybe the C toolkit code should also handle toplevel object creation and call a callback
+ // because there is some dynamic UiObject creation
+ Toolkit.getInstance().registerToplevelObject(this);
+ // TODO: handle UiObject destroy event
}
public static UiObject createWindow(String title) {
public void show() {
UiObjectFuncs.instance.show(this);
}
+
+ public long addEventHandler(EventHandler handler) {
+ eventHandlers.add(handler);
+ return eventHandlers.size() - 1;
+ }
+
+ public EventHandler getEventHandler(int index) {
+ return eventHandlers.get(index);
+ }
+
+ public Arena getArena() {
+ if (arena == null) {
+ arena = Arena.ofShared();
+ }
+ return arena;
+ }
}
public class Main implements Application{
public void startup() {
UiObject window = UiObject.createWindow("Test Window");
- Button.button(window).label("Click Me").create();
+ Button.button(window).label("Click Me").onClick(event -> {
+ System.out.println("Clicked");
+ }).create();
Button.checkbox(window).label("Checkbox").create();
Button.radioButton(window).label("Radio Button 1").varname("radiobutton").create();
Button.radioButton(window).label("Radio Button 2").varname("radiobutton").create();