From 06cea880d38e408e16d375805bcd8c02d5c31e81 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 25 Aug 2025 20:49:08 +0200 Subject: [PATCH] add table view --- .../main/java/de/unixwork/ui/ColumnDef.java | 28 ++ .../main/java/de/unixwork/ui/ColumnType.java | 7 + .../main/java/de/unixwork/ui/ListView.java | 5 +- .../main/java/de/unixwork/ui/TableModel.java | 64 +++++ .../java/de/unixwork/ui/TableViewBuilder.java | 240 ++++++++++++++++++ .../java/de/unixwork/ui/ToolkitFuncs.java | 13 + 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 ui-java/src/main/java/de/unixwork/ui/ColumnDef.java create mode 100644 ui-java/src/main/java/de/unixwork/ui/ColumnType.java create mode 100644 ui-java/src/main/java/de/unixwork/ui/TableModel.java create mode 100644 ui-java/src/main/java/de/unixwork/ui/TableViewBuilder.java diff --git a/ui-java/src/main/java/de/unixwork/ui/ColumnDef.java b/ui-java/src/main/java/de/unixwork/ui/ColumnDef.java new file mode 100644 index 0000000..1a220d4 --- /dev/null +++ b/ui-java/src/main/java/de/unixwork/ui/ColumnDef.java @@ -0,0 +1,28 @@ +package de.unixwork.ui; + +public class ColumnDef { + private String title; + private ColumnType type; + private int width; + + public ColumnDef(String title, ColumnType type) { + this.title = title; + this.type = type; + } + + public ColumnDef(String title, ColumnType type, int width) { + this.title = title; + this.type = type; + this.width = width; + } + + public String getTitle() { + return title; + } + public ColumnType getType() { + return type; + } + public int getWidth() { + return width; + } +} diff --git a/ui-java/src/main/java/de/unixwork/ui/ColumnType.java b/ui-java/src/main/java/de/unixwork/ui/ColumnType.java new file mode 100644 index 0000000..3d9f609 --- /dev/null +++ b/ui-java/src/main/java/de/unixwork/ui/ColumnType.java @@ -0,0 +1,7 @@ +package de.unixwork.ui; + +public enum ColumnType { + STRING, + ICON, + ICON_TEXT +} diff --git a/ui-java/src/main/java/de/unixwork/ui/ListView.java b/ui-java/src/main/java/de/unixwork/ui/ListView.java index e3ea699..5d2aa03 100644 --- a/ui-java/src/main/java/de/unixwork/ui/ListView.java +++ b/ui-java/src/main/java/de/unixwork/ui/ListView.java @@ -1,7 +1,10 @@ package de.unixwork.ui; public class ListView { - + public static TableViewBuilder table(UiObject obj) { + ListFuncs ui = ListFuncs.getInstance(); + return new TableViewBuilder(obj, ui.listview_create); + } public static ListViewBuilder list(UiObject obj) { ListFuncs ui = ListFuncs.getInstance(); diff --git a/ui-java/src/main/java/de/unixwork/ui/TableModel.java b/ui-java/src/main/java/de/unixwork/ui/TableModel.java new file mode 100644 index 0000000..cc767c1 --- /dev/null +++ b/ui-java/src/main/java/de/unixwork/ui/TableModel.java @@ -0,0 +1,64 @@ +package de.unixwork.ui; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; + +public class TableModel { + private ArrayList columns = new ArrayList<>(16); + + public TableModel() { + + } + + public void addColumn(ColumnDef col) { + columns.add(col); + } + + public void addColumn(String title, ColumnType type) { + columns.add(new ColumnDef(title, type)); + } + + public void addColumn(String title, ColumnType type, int width) { + columns.add(new ColumnDef(title, type, width)); + } + + public ArrayList getColumns() { + return columns; + } + + public void clearColumns() { + columns.clear(); + } + + public int getColumnCount() { + return columns.size(); + } + + /* + * convert this TableModel to a native UiModel + */ + protected MemorySegment createModel(Context ctx) { + ToolkitFuncs ui = ToolkitFuncs.getInstance(); + MemorySegment ctxPtr = ctx.getCtx(); + + MemorySegment model; + try(Arena arena = Arena.ofConfined()) { + model = (MemorySegment)ui.model_new.invoke(ctxPtr); + for(var col : columns) { + int type = 0; + switch(col.getType()) { + case ColumnType.STRING: type = 0; break; + case ColumnType.ICON: type = 3; break; + case ColumnType.ICON_TEXT: type = 4; break; + } + MemorySegment cstr = arena.allocateFrom(col.getTitle()); + + ui.model_add_column.invoke(ctxPtr, model, type, cstr, col.getWidth()); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + return model; + } +} diff --git a/ui-java/src/main/java/de/unixwork/ui/TableViewBuilder.java b/ui-java/src/main/java/de/unixwork/ui/TableViewBuilder.java new file mode 100644 index 0000000..5c7cc50 --- /dev/null +++ b/ui-java/src/main/java/de/unixwork/ui/TableViewBuilder.java @@ -0,0 +1,240 @@ +package de.unixwork.ui; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +public class TableViewBuilder extends AbstractWidgetBuilder { + private boolean fill; + private boolean hexpand; + private boolean vexpand; + private boolean hfill; + private boolean vfill; + private boolean overrideDefaults; + private int colspan; + private int rowspan; + private String name; + private String styleClass; + private String varname; + private UiList list; + private TableModel model; + // TODO: static elements + private ListValueConverter getvalue; + private EventHandler onActivate; + private EventHandler onSelection; + private EventHandler onDragStart; + private EventHandler onDragComplete; + private EventHandler onDrop; + private boolean multiselection; + // TODO: contextmenu + private int[] states; + + private MemorySegment modelPtr; + + public TableViewBuilder(UiObject obj, MethodHandle widgetConstructor) { + this.obj = obj; + this.widgetConstructor = widgetConstructor; + this.argsFree = ArgFuncs.getInstance().list_args_free; + } + + public TableViewBuilder fill(boolean fill) { + this.fill = fill; + return this; + } + + public TableViewBuilder hexpand(boolean hexpand) { + this.hexpand = hexpand; + return this; + } + + public TableViewBuilder vexpand(boolean vexpand) { + this.vexpand = vexpand; + return this; + } + + public TableViewBuilder hfill(boolean hfill) { + this.hfill = hfill; + return this; + } + + public TableViewBuilder vfill(boolean vfill) { + this.vfill = vfill; + return this; + } + + public TableViewBuilder overrideDefaults(boolean overrideDefaults) { + this.overrideDefaults = overrideDefaults; + return this; + } + + public TableViewBuilder colspan(int colspan) { + this.colspan = colspan; + return this; + } + + public TableViewBuilder rowspan(int rowspan) { + this.rowspan = rowspan; + return this; + } + + public TableViewBuilder name(String name) { + this.name = name; + return this; + } + + + public TableViewBuilder styleClass(String styleClass) { + this.styleClass = styleClass; + return this; + } + + public TableViewBuilder varname(String varname) { + this.varname = varname; + return this; + } + + public TableViewBuilder value(UiList list) { + this.list = list; + return this; + } + + public TableViewBuilder model(TableModel model) { + this.model = model; + return this; + } + + public TableViewBuilder getvalue(ListValueConverter getvalue) { + this.getvalue = getvalue; + return this; + } + + public TableViewBuilder onActivate(EventHandler onActivate) { + this.onActivate = onActivate; + return this; + } + + public TableViewBuilder onSelection(EventHandler onSelection) { + this.onSelection = onSelection; + return this; + } + + public TableViewBuilder onDragStart(EventHandler onDragStart) { + this.onDragStart = onDragStart; + return this; + } + + public TableViewBuilder onDragComplete(EventHandler onDragComplete) { + this.onDragComplete = onDragComplete; + return this; + } + + public TableViewBuilder onDrop(EventHandler onDrop) { + this.onDrop = onDrop; + return this; + } + + public TableViewBuilder states(int... states) { + this.states = states; + return this; + } + + public MemorySegment createArgs(Arena arena) throws Throwable { + ArgFuncs ui = ArgFuncs.getInstance(); + + args = (MemorySegment) ui.list_args_new.invoke(); + if (fill) { + ui.list_args_set_fill.invoke(args, fill); + } + if (hexpand) { + ui.list_args_set_hexpand.invoke(args, hexpand); + } + if (vexpand) { + ui.list_args_set_vexpand.invoke(args, vexpand); + } + if (hfill) { + ui.list_args_set_hfill.invoke(args, hfill); + } + if (vfill) { + ui.list_args_set_vfill.invoke(args, vfill); + } + if (overrideDefaults) { + ui.list_args_set_override_defaults.invoke(args, overrideDefaults); + } + if (colspan > 0) { + ui.list_args_set_colspan.invoke(args, colspan); + } + if (rowspan > 0) { + ui.list_args_set_rowspan.invoke(args, rowspan); + } + if (name != null) { + MemorySegment cstr = arena.allocateFrom(name); + ui.list_args_set_name.invoke(args, cstr); + } + if (styleClass != null) { + MemorySegment cstr = arena.allocateFrom(styleClass); + ui.list_args_set_style_class.invoke(args, cstr); + } + if(varname != null) { + MemorySegment cstr = arena.allocateFrom(varname); + ui.list_args_set_varname.invoke(args, cstr); + } + if(list != null) { + ui.list_args_set_value.invoke(args, list.valuePtr); + } + if(getvalue != null) { + // always use the Toolkit.getValue wrapper function + ui.list_args_set_getvalue_func2.invoke(args, Toolkit.getInstance().getValue); + MemorySegment userdata = obj.registerValueConverter(getvalue); + ui.list_args_set_getvalue_data.invoke(args, userdata); + } + if(model != null) { + modelPtr = model.createModel(obj); + ui.list_args_set_model.invoke(args, modelPtr); + } + + if (onActivate != null) { + EventWrapper event = new EventWrapper(obj, onActivate); + // set toolkit args + ui.list_args_set_onactivate.invoke(args, event.getCallback()); + ui.list_args_set_onactivatedata.invoke(args, event.getUserData()); + } + if (onSelection != null) { + EventWrapper event = new EventWrapper(obj, onSelection); + // set toolkit args + ui.list_args_set_onselection.invoke(args, event.getCallback()); + ui.list_args_set_onselectiondata.invoke(args, event.getUserData()); + } + if (onDragStart != null) { + EventWrapper event = new EventWrapper(obj, onDragStart); + // set toolkit args + ui.list_args_set_ondragstart.invoke(args, event.getCallback()); + ui.list_args_set_ondragstartdata.invoke(args, event.getUserData()); + } + if (onDragComplete != null) { + EventWrapper event = new EventWrapper(obj, onDragComplete); + // set toolkit args + ui.list_args_set_ondragcomplete.invoke(args, event.getCallback()); + ui.list_args_set_ondragcompletedata.invoke(args, event.getUserData()); + } + if (onDrop != null) { + EventWrapper event = new EventWrapper(obj, onDrop); + // set toolkit args + ui.list_args_set_ondrop.invoke(args, event.getCallback()); + ui.list_args_set_ondropdata.invoke(args, event.getUserData()); + } + + return args; + } + + void freeArgs() { + super.freeArgs(); + ToolkitFuncs ui = ToolkitFuncs.getInstance(); + if(model != null) { + try { + ui.model_free.invoke(obj.getCtx(), modelPtr); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/ui-java/src/main/java/de/unixwork/ui/ToolkitFuncs.java b/ui-java/src/main/java/de/unixwork/ui/ToolkitFuncs.java index a7db8b7..0e44164 100644 --- a/ui-java/src/main/java/de/unixwork/ui/ToolkitFuncs.java +++ b/ui-java/src/main/java/de/unixwork/ui/ToolkitFuncs.java @@ -63,6 +63,10 @@ public class ToolkitFuncs { public MethodHandle text_set; public MethodHandle text_get; + public MethodHandle model_new; + public MethodHandle model_add_column; + public MethodHandle model_free; + public MethodHandle call_mainthread; public MethodHandle ui_malloc; @@ -85,6 +89,7 @@ public class ToolkitFuncs { FunctionDescriptor sigv_mb = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_BOOLEAN); FunctionDescriptor sigv_m = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS); FunctionDescriptor sigm_l = FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG); + FunctionDescriptor sigv_mmimi = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT); MemorySegment object_get_context_addr = lib.find("ui_object_get_context").orElseThrow(); @@ -143,6 +148,10 @@ public class ToolkitFuncs { MemorySegment text_set_addr = lib.find("ui_text_set").orElseThrow(); MemorySegment text_get_addr = lib.find("ui_text_get").orElseThrow(); + MemorySegment model_new_addr = lib.find("ui_model_new").orElseThrow(); + MemorySegment model_add_column_addr = lib.find("ui_model_add_column").orElseThrow(); + MemorySegment model_free_addr = lib.find("ui_model_free").orElseThrow(); + MemorySegment call_mainthread_addr = lib.find("ui_call_mainthread").orElseThrow(); MemorySegment ui_malloc_addr = lib.find("ui_malloc").orElseThrow(); @@ -210,6 +219,10 @@ public class ToolkitFuncs { text_set = linker.downcallHandle(text_set_addr, sigv_mm); text_get = linker.downcallHandle(text_get_addr, sigm_m); + model_new = linker.downcallHandle(model_new_addr, sigm_m); + model_add_column = linker.downcallHandle(model_add_column_addr, sigv_mmimi); + model_free = linker.downcallHandle(model_free_addr, sigv_m); + call_mainthread = linker.downcallHandle(call_mainthread_addr, sigv_mm); ui_malloc = linker.downcallHandle(ui_malloc_addr, sigm_ml); -- 2.47.3