From 702d2f3531e24cb6a3d71902cd78e2fb0e7d938d Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Tue, 12 Aug 2025 14:04:08 +0200 Subject: [PATCH] add ui for creating categories and feeds --- .../de/unixwork/rssreader/MainWindow.kt | 67 ++++++++- .../de/unixwork/ui/DialogWindowBuilder.java | 5 +- .../java/de/unixwork/ui/EventWrapper.java | 2 +- .../main/java/de/unixwork/ui/UiObject.java | 4 + .../java/de/unixwork/ui/UiObjectFuncs.java | 9 ++ .../kotlin/de/unixwork/ui/kotlin/Toplevel.kt | 137 +++++++++++++++++- .../kotlin/de/unixwork/ui/kotlin/Window.kt | 4 + 7 files changed, 219 insertions(+), 9 deletions(-) diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt index 473b015..1b8d9df 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt @@ -1,10 +1,9 @@ package de.unixwork.rssreader import de.unixwork.ui.SubListItem -import de.unixwork.ui.UiObject -import de.unixwork.ui.UiSourceList import de.unixwork.ui.kotlin.Toplevel import de.unixwork.ui.kotlin.sidebarWindow +import de.unixwork.ui.kotlin.dialogWindow class MainWindow { val window : Toplevel @@ -20,10 +19,10 @@ class MainWindow { } hbox(margin = 4, spacing = 4) { button(icon = "list-add") { - + createFeedDialog() } button(icon = "folder-new") { - + createCategoryDialog() } } } @@ -44,7 +43,67 @@ class MainWindow { } } + private fun createFeedDialog() { + val w = dialogWindow( + parent = window.ui, + title = "Add Feed", + lbutton1 = "Add", + rbutton4 = "Cancel", + onClick = { ev -> + ev.`object`.close() + }, + modal = true) + { + grid( + margin = 12, + columnspacing = 8, + rowspacing = 8, + defhfill = true, + defvfill = true, + fill = true) + { + row { + rlabel("Category") + dropdown(varname = "group", hexpand = true) { elm, column -> + "" + } + } + row { + rlabel("Name") + textfield(varname = "name", hexpand = true) + } + row { + rlabel("URLs", overrideDefaults = true, hfill = true) // overrideDefaults for disabling default vfill + textarea(varname = "url", hexpand = true, vexpand = true, vfill = true) + } + } + } + w.show() + } + private fun createCategoryDialog() { + val w = dialogWindow( + parent = window.ui, + title = "Add Category", + lbutton1 = "Add", + modal = true) + { + grid( + margin = 12, + columnspacing = 8, + rowspacing = 8, + defhfill = true, + defvfill = true, + fill = true) + { + row { + rlabel("Name") + textfield(varname = "name", hexpand = true) + } + } + } + w.show() + } fun show() { window.show() diff --git a/ui-java/src/main/java/de/unixwork/ui/DialogWindowBuilder.java b/ui-java/src/main/java/de/unixwork/ui/DialogWindowBuilder.java index 0efdae6..40a3857 100644 --- a/ui-java/src/main/java/de/unixwork/ui/DialogWindowBuilder.java +++ b/ui-java/src/main/java/de/unixwork/ui/DialogWindowBuilder.java @@ -125,13 +125,14 @@ public class DialogWindowBuilder { ui.dialogwindow_args_set_width.invoke(args, width); ui.dialogwindow_args_set_height.invoke(args, height); - UiObject obj = UiObjectFuncs.instance.dialogWindow(parent.ptr, args); if(onClick != null) { - EventWrapper event = new EventWrapper(obj, onClick); + EventWrapper event = new EventWrapper(onClick); ui.dialogwindow_args_set_onclick.invoke(args, event.getCallback()); ui.dialogwindow_args_set_onclickdata.invoke(args, event.getUserData()); } + UiObject obj = UiObjectFuncs.instance.dialogWindow(parent.ptr, args); + ui.dialogwindow_args_free.invoke(args); return obj; } catch (Throwable e) { 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 4d3af33..5f72a7b 100644 --- a/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java +++ b/ui-java/src/main/java/de/unixwork/ui/EventWrapper.java @@ -34,7 +34,7 @@ public class EventWrapper { long index = toolkit.addOneshotEventHandler(handler); userdata = MemorySegment.ofAddress(index); } else { - callback = toolkit.eventHandler; + callback = toolkit.globalEventHandler; long index = toolkit.addEventHandler(handler); userdata = MemorySegment.ofAddress(index); } diff --git a/ui-java/src/main/java/de/unixwork/ui/UiObject.java b/ui-java/src/main/java/de/unixwork/ui/UiObject.java index 7a88793..bfcafae 100644 --- a/ui-java/src/main/java/de/unixwork/ui/UiObject.java +++ b/ui-java/src/main/java/de/unixwork/ui/UiObject.java @@ -49,6 +49,10 @@ public class UiObject extends Context { UiObjectFuncs.instance.show(this); } + public void close() { + UiObjectFuncs.instance.close(this); + } + public long addEventHandler(EventHandler handler) { eventHandlers.add(handler); return eventHandlers.size() - 1; diff --git a/ui-java/src/main/java/de/unixwork/ui/UiObjectFuncs.java b/ui-java/src/main/java/de/unixwork/ui/UiObjectFuncs.java index faeb691..c5190fb 100644 --- a/ui-java/src/main/java/de/unixwork/ui/UiObjectFuncs.java +++ b/ui-java/src/main/java/de/unixwork/ui/UiObjectFuncs.java @@ -7,6 +7,7 @@ class UiObjectFuncs { static UiObjectFuncs instance; public MethodHandle ui_show; + public MethodHandle ui_close; public MethodHandle ui_window; public MethodHandle ui_sidebar_window; public MethodHandle ui_simple_window; @@ -27,6 +28,7 @@ class UiObjectFuncs { FunctionDescriptor sigv_mmmm = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS); MemorySegment ui_show_addr = lib.find("ui_show").orElseThrow(); + MemorySegment ui_close_addr = lib.find("ui_close").orElseThrow(); MemorySegment ui_window_addr = lib.find("ui_window").orElseThrow(); MemorySegment ui_sidebar_window_addr = lib.find("ui_sidebar_window").orElseThrow(); MemorySegment ui_simple_window_addr = lib.find("ui_simple_window").orElseThrow(); @@ -36,6 +38,7 @@ class UiObjectFuncs { MemorySegment ui_savefiledialog_addr = lib.find("ui_savefiledialog").orElseThrow(); ui_show = linker.downcallHandle(ui_show_addr, sigv_m); + ui_close = linker.downcallHandle(ui_close_addr, sigv_m); ui_window = linker.downcallHandle(ui_window_addr, sigm_mm); ui_sidebar_window = linker.downcallHandle(ui_sidebar_window_addr, sigm_mm); ui_simple_window = linker.downcallHandle(ui_simple_window_addr, sigm_mm); @@ -106,6 +109,12 @@ class UiObjectFuncs { } } + void close(UiObject obj) { + try { + ui_close.invoke(obj.ptr); + } catch (Throwable e) {} + } + public void openFileDialog(UiObject obj, int mode, EventHandler callback) { EventWrapper ew = new EventWrapper(callback, true); try { diff --git a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt index d337cb3..3180c88 100644 --- a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt +++ b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt @@ -22,6 +22,9 @@ import de.unixwork.ui.UiString import de.unixwork.ui.UiWidget import de.unixwork.ui.WebView import de.unixwork.ui.Label +import de.unixwork.ui.ListViewBuilder +import de.unixwork.ui.UiText +import java.awt.TextArea class Toplevel(obj: UiObject) { val ui: UiObject = obj @@ -30,6 +33,10 @@ class Toplevel(obj: UiObject) { ui.show() } + fun close() { + ui.close() + } + operator fun invoke(block: Toplevel.() -> Unit) { block() } @@ -700,7 +707,8 @@ class Toplevel(obj: UiObject) { ) } - fun listview( + fun createListView( + list: ListViewBuilder, varname: String? = null, value: UiList? = null, fill: Boolean = false, @@ -715,7 +723,6 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, getvalue: ListValueConverter? = null ): UiWidget { - val list = ListView.list(ui) varname?.let { list.varname(varname) } @@ -758,6 +765,74 @@ class Toplevel(obj: UiObject) { return list.create() } + fun listview( + varname: String? = null, + value: UiList? = null, + fill: Boolean = false, + hexpand: Boolean = false, + vexpand: Boolean = false, + hfill: Boolean = false, + vfill: Boolean = false, + overrideDefaults: Boolean = false, + colspan: Int = -1, + rowspan: Int = -1, + name: String? = null, + styleClass: String? = null, + getvalue: ListValueConverter? = null + ): UiWidget { + val list = ListView.list(ui) + return createListView( + list = list, + varname = varname, + value = value, + fill = fill, + hexpand = hexpand, + vexpand = vexpand, + hfill = hfill, + vfill = vfill, + overrideDefaults = overrideDefaults, + colspan = colspan, + rowspan = rowspan, + name = name, + styleClass = styleClass, + getvalue = getvalue + ) + } + + fun dropdown( + varname: String? = null, + value: UiList? = null, + fill: Boolean = false, + hexpand: Boolean = false, + vexpand: Boolean = false, + hfill: Boolean = false, + vfill: Boolean = false, + overrideDefaults: Boolean = false, + colspan: Int = -1, + rowspan: Int = -1, + name: String? = null, + styleClass: String? = null, + getvalue: ListValueConverter? = null + ): UiWidget { + val list = ListView.dropdown(ui) + return createListView( + list = list, + varname = varname, + value = value, + fill = fill, + hexpand = hexpand, + vexpand = vexpand, + hfill = hfill, + vfill = vfill, + overrideDefaults = overrideDefaults, + colspan = colspan, + rowspan = rowspan, + name = name, + styleClass = styleClass, + getvalue = getvalue + ) + } + fun sourcelist( varname: String? = null, sourceList: UiSourceList? = null, @@ -816,6 +891,64 @@ class Toplevel(obj: UiObject) { return list.create() } + fun textarea( + varname: String? = null, + value: UiText? = null, + fill: Boolean = false, + hexpand: Boolean = false, + vexpand: Boolean = false, + hfill: Boolean = false, + vfill: Boolean = false, + overrideDefaults: Boolean = false, + colspan: Int = -1, + rowspan: Int = -1, + name: String? = null, + styleClass: String? = null, + onChange: EventHandler? = null + ): UiWidget { + val textarea = Text.textarea(ui) + varname?.let { + textarea.varname(it) + } + value?.let { + textarea.value(it) + } + if(fill) { + textarea.fill(fill); + } + if(hexpand) { + textarea.hexpand(true) + } + if(vexpand) { + textarea.vexpand(true) + } + if(hfill) { + textarea.hfill(true) + } + if(vfill) { + textarea.vfill(true) + } + if(colspan > 0) { + textarea.colspan(colspan) + } + if(rowspan > 0) { + textarea.rowspan(rowspan) + } + if(overrideDefaults) { + textarea.overrideDefaults(true) + } + name?.let { + textarea.name(it) + } + styleClass?.let { + textarea.styleClass(it) + } + onChange?.let { + textarea.onChange(onChange) + } + return textarea.create() + } + private fun createTextField( textfield: TextFieldBuilder, varname: String? = null, diff --git a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Window.kt b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Window.kt index a1c048f..a45d720 100644 --- a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Window.kt +++ b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Window.kt @@ -67,6 +67,7 @@ fun dialog( fun dialogWindow( parent: UiObject, + title: String? = null, modal: Boolean? = null, titlebarButtons: Boolean? = null, showCloseButton: Boolean? = null, @@ -81,6 +82,9 @@ fun dialogWindow( ui: (Toplevel.() -> Unit)?): Toplevel { val builder = DialogWindowBuilder(parent) + title?.let { + builder.title(it) + } modal?.let { builder.modal(it) } -- 2.47.3