From e89a80ddcdc6e19d8e4c680abe9530536ed8596b Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sun, 20 Jul 2025 12:48:39 +0200 Subject: [PATCH] add kotlin dispatcher --- .../main/kotlin/de/unixwork/rssreader/App.kt | 4 ++ .../de/unixwork/rssreader/MainWindow.kt | 3 ++ .../src/main/java/de/unixwork/ui/Toolkit.java | 2 +- ui-kotlin/pom.xml | 5 ++ .../ui/kotlin/BlockingToolkitDispatcher.kt | 23 +++++++++ .../unixwork/ui/kotlin/ToolkitDispatcher.kt | 14 ++++++ .../kotlin/de/unixwork/ui/kotlin/Toplevel.kt | 49 +++++++++---------- .../kotlin/de/unixwork/ui/kotlin/demo/Test.kt | 20 ++++++-- 8 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/BlockingToolkitDispatcher.kt create mode 100644 ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/ToolkitDispatcher.kt diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt index 4d1b0a5..744fc91 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt @@ -8,6 +8,10 @@ class App : Application { val window = MainWindow() window.show() } + + override fun shutdown() { + System.exit(0) + } } fun main() { 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 094bfab..f3c1a65 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt @@ -10,6 +10,7 @@ class MainWindow { init { window = sidebarWindow("RSS Reader") { + sidebar { vbox(fill = true) { sourcelist { elm: Object -> @@ -23,6 +24,7 @@ class MainWindow { } } + hsplitpane(initialPosition = 300) { vbox { listview(varname = "items", fill = true) { elm, col -> @@ -34,6 +36,7 @@ class MainWindow { webview(varname = "webview", fill = true) } } + } } 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 90d2a5d..b2c7f9f 100644 --- a/ui-java/src/main/java/de/unixwork/ui/Toolkit.java +++ b/ui-java/src/main/java/de/unixwork/ui/Toolkit.java @@ -183,7 +183,7 @@ public class Toolkit { // threadfunc wrapper try { MethodHandle threadFuncCallback = MethodHandles.lookup().findStatic( - Thread.class, + Toolkit.class, "threadFunc", MethodType.methodType(int.class, MemorySegment.class)); FunctionDescriptor threadFuncDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS); diff --git a/ui-kotlin/pom.xml b/ui-kotlin/pom.xml index df08ad2..03f2d81 100644 --- a/ui-kotlin/pom.xml +++ b/ui-kotlin/pom.xml @@ -81,6 +81,11 @@ kotlin-stdlib 2.1.20 + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.10.2 + de.unixwork diff --git a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/BlockingToolkitDispatcher.kt b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/BlockingToolkitDispatcher.kt new file mode 100644 index 0000000..2066fe1 --- /dev/null +++ b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/BlockingToolkitDispatcher.kt @@ -0,0 +1,23 @@ +package de.unixwork.ui.kotlin + +import de.unixwork.ui.Toolkit +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import java.util.concurrent.Executor + +object BlockingToolkitDispatcher : CoroutineDispatcher() { + override fun dispatch(context: kotlin.coroutines.CoroutineContext, block: Runnable) { + val latch = java.util.concurrent.CountDownLatch(1) + + Toolkit.getInstance().invokeMainThread { + try { + block.run() + } finally { + latch.countDown() + } + } + + // Block the calling thread until execution finishes + latch.await() + } +} \ No newline at end of file diff --git a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/ToolkitDispatcher.kt b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/ToolkitDispatcher.kt new file mode 100644 index 0000000..aca7b7e --- /dev/null +++ b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/ToolkitDispatcher.kt @@ -0,0 +1,14 @@ +package de.unixwork.ui.kotlin + +import de.unixwork.ui.Toolkit +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import java.util.concurrent.Executor + +object ToolkitDispatcher : CoroutineDispatcher() { + override fun dispatch(context: kotlin.coroutines.CoroutineContext, block: Runnable) { + Toolkit.getInstance().invokeMainThread { + block.run() + } + } +} \ No newline at end of file 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 6fe4517..20bfbb9 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 @@ -2,12 +2,10 @@ package de.unixwork.ui.kotlin import de.unixwork.ui.Alignment import de.unixwork.ui.Button -import de.unixwork.ui.ButtonBuilder import de.unixwork.ui.Container import de.unixwork.ui.ContainerBuilder import de.unixwork.ui.ContainerUI import de.unixwork.ui.EventHandler -import de.unixwork.ui.EventWrapper.eventHandler import de.unixwork.ui.LabelBuilder import de.unixwork.ui.LabelStyle import de.unixwork.ui.ListValueConverter @@ -24,13 +22,12 @@ import de.unixwork.ui.UiString import de.unixwork.ui.UiWidget import de.unixwork.ui.WebView import de.unixwork.ui.Label -import java.lang.foreign.MemorySegment class Toplevel(obj: UiObject) { - private val obj: UiObject = obj + val ui: UiObject = obj fun show() { - obj.show() + ui.show() } operator fun invoke(block: Toplevel.() -> Unit) { @@ -124,7 +121,7 @@ class Toplevel(obj: UiObject) { ui: ContainerUI? = null ): UiWidget { return createContainer( - container = Container.hbox(obj), + container = Container.hbox(this@Toplevel.ui), fill = fill, hexpand = hexpand, vexpand = vexpand, @@ -158,7 +155,7 @@ class Toplevel(obj: UiObject) { ui: ContainerUI? = null ): UiWidget { return createContainer( - container = Container.vbox(obj), + container = Container.vbox(this@Toplevel.ui), fill = fill, hexpand = hexpand, vexpand = vexpand, @@ -198,7 +195,7 @@ class Toplevel(obj: UiObject) { ui: ContainerUI? = null ): UiWidget { return createContainer( - container = Container.grid(obj), + container = Container.grid(this@Toplevel.ui), fill = fill, hexpand = hexpand, vexpand = vexpand, @@ -225,7 +222,7 @@ class Toplevel(obj: UiObject) { ui: ContainerUI? = null ) { ui?.callback() - Container.newline(obj) + Container.newline(this@Toplevel.ui) } private fun createSplitpane( @@ -249,9 +246,9 @@ class Toplevel(obj: UiObject) { ui: ContainerUI? = null ): UiWidget { val container = if(horizontal) { - Container.hsplitview(obj) + Container.hsplitview(this@Toplevel.ui) } else { - Container.vsplitview(obj) + Container.vsplitview(this@Toplevel.ui) } if(hexpand) { @@ -392,7 +389,7 @@ class Toplevel(obj: UiObject) { spacing: Int = -1, ui: ContainerUI? = null ): UiWidget { - val container = Container.sidebar(obj) + val container = Container.sidebar(this@Toplevel.ui) if(margin > 0) { container.margin(margin) @@ -426,7 +423,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, onClick: EventHandler? = null ): UiWidget { - val button = Button.button(obj) + val button = Button.button(ui) label?.let { button.label(it) } @@ -561,7 +558,7 @@ class Toplevel(obj: UiObject) { onChange: EventHandler? = null ): UiWidget { return createToggleButton( - button = Button.toggleButton(obj), + button = Button.toggleButton(ui), label = label, stockId = stockId, icon = icon, @@ -600,7 +597,7 @@ class Toplevel(obj: UiObject) { onChange: EventHandler? = null ): UiWidget { return createToggleButton( - button = Button.checkbox(obj), + button = Button.checkbox(ui), label = label, stockId = stockId, icon = icon, @@ -639,7 +636,7 @@ class Toplevel(obj: UiObject) { onChange: EventHandler? = null ): UiWidget { return createToggleButton( - button = Button.switchButton(obj), + button = Button.switchButton(ui), label = label, stockId = stockId, icon = icon, @@ -678,7 +675,7 @@ class Toplevel(obj: UiObject) { onChange: EventHandler? = null ): UiWidget { return createToggleButton( - button = Button.radioButton(obj), + button = Button.radioButton(ui), label = label, stockId = stockId, icon = icon, @@ -713,7 +710,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, getvalue: ListValueConverter? = null ): UiWidget { - val list = ListView.list(obj) + val list = ListView.list(ui) varname?.let { list.varname(varname) } @@ -771,7 +768,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, getvalue: SubListValueConverter? = null ): UiWidget { - val list = ListView.sourcelist(obj) + val list = ListView.sourcelist(ui) varname?.let { list.varname(varname) } @@ -887,7 +884,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, onChange: EventHandler? = null ): UiWidget { - val textfield = Text.textfield(obj) + val textfield = Text.textfield(ui) return createTextField( textfield = textfield, varname = varname, @@ -921,7 +918,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, onChange: EventHandler? = null ): UiWidget { - val textfield = Text.passwordfield(obj) + val textfield = Text.passwordfield(ui) return createTextField( textfield = textfield, varname = varname, @@ -955,7 +952,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, onChange: EventHandler? = null ): UiWidget { - val textfield = Text.framelessTextfield(obj) + val textfield = Text.framelessTextfield(ui) return createTextField( textfield = textfield, varname = varname, @@ -989,7 +986,7 @@ class Toplevel(obj: UiObject) { styleClass: String? = null, onChange: EventHandler? = null ): UiWidget { - val webview = WebView.webview(obj) + val webview = WebView.webview(ui) varname?.let { webview.varname(it) } @@ -1114,7 +1111,7 @@ class Toplevel(obj: UiObject) { name: String? = null, styleClass: String? = null ): UiWidget { - val labelBuilder = Label.label(obj) + val labelBuilder = Label.label(ui) return createLabel( label = labelBuilder, labelStr = label, @@ -1151,7 +1148,7 @@ class Toplevel(obj: UiObject) { name: String? = null, styleClass: String? = null ): UiWidget { - val labelBuilder = Label.llabel(obj) + val labelBuilder = Label.llabel(ui) return createLabel( label = labelBuilder, labelStr = label, @@ -1187,7 +1184,7 @@ class Toplevel(obj: UiObject) { name: String? = null, styleClass: String? = null ): UiWidget { - val labelBuilder = Label.rlabel(obj) + val labelBuilder = Label.rlabel(ui) return createLabel( label = labelBuilder, labelStr = label, diff --git a/ui-kotlin/src/test/kotlin/de/unixwork/ui/kotlin/demo/Test.kt b/ui-kotlin/src/test/kotlin/de/unixwork/ui/kotlin/demo/Test.kt index 2da3998..04b4e67 100644 --- a/ui-kotlin/src/test/kotlin/de/unixwork/ui/kotlin/demo/Test.kt +++ b/ui-kotlin/src/test/kotlin/de/unixwork/ui/kotlin/demo/Test.kt @@ -4,18 +4,28 @@ import de.unixwork.ui.Application import de.unixwork.ui.ToolbarPosition import de.unixwork.ui.Toolkit import de.unixwork.ui.kotlin.* - +import kotlinx.coroutines.* class Test : Application { override fun startup() { toolbarItem(name = "button1", label = "Test") addToolbarDefault("button1", ToolbarPosition.RIGHT); - val window = window("Test Window") { + val win = window("Test Window") { + val mystr = ui.string() grid { vbox(vexpand = true, vfill = true) { - button("B1") - button("B2") + button("B1") { + CoroutineScope(Dispatchers.IO).launch { + repeat(100) { index -> + delay(5000L) + withContext(BlockingToolkitDispatcher) { + mystr.setString("iteration " + index) + } + } + } + } + textfield(value = mystr) button("B3") } grid(columnspacing = 10, rowspacing = 10, hexpand = true, hfill = true, vexpand = true) { @@ -35,7 +45,7 @@ class Test : Application { } } - window.show() + win.show() } } -- 2.47.3