Code Monkey home page Code Monkey logo

kotlin-libui's Introduction

kotlin-libui

Kotlin/Native bindings to the libui C library.

Build Status Build status

libui is a C lightweight multi-platform UI library using native widgets on Linux (Gtk3), macOS, and Windows. Using this bindings you can develop cross-platform but native-looking GUI programs, written in Kotlin, and compiled to small native executable file.

Using

To use this library in your project you can clone Hello World application and use it as starting point.

Building

Cross-platform build is automated using Travis for Linux and macOS targets, and AppVeyor for Windows targets. Just create release on GitHub, and executable files for all 3 major desktop platforms will be compiled and attached to release.

For local build use ./gradlew build on Linux or macOS, or gradlew build on Windows. In this case only one - native for your platform - file will be built.

The script below builds kotlin-libui then builds and runs the sample hello-ktx:

#clone this project
git clone https://github.com/msink/kotlin-libui.git

#build libui.klib
cd kotlin-libui/libui
../gradlew build

#build and run the hello-ktx sample
cd ../samples/hello-ktx/
../../gradlew run

You can use IntelliJ IDEA CE/UE or CLion EAP for code navigation and code completion, debugging works only in CLion.

Status

Warning: currently it is just a prototype - works in most cases, but not protected from errors. And as both libui and Kotlin/Native are currently in alpha stage, anything can change.

Well, I'm also not sure about DSL syntax - it works, and for now is good enough. Let's leave it as is for a while.

If anyone have ideas - Issues and PullRequests are welcome.

Hello World

Let's start from minimal sample application - single button and single scrollable text area.

Screenshots:

Windows

Unix

macOS


C implementation:
#include "ui.h"

static int onClosing(uiWindow *window, void *data)
{
    uiQuit();
    return 1;
}

static void saySomething(uiButton *button, void *data)
{
    uiMultilineEntryAppend(uiMultilineEntry(data),
        "Hello, World!  Ciao, mondo!\n"
        "Привет, мир!  你好,世界!\n\n");
}

int main(void)
{
    uiInitOptions options;
    uiWindow *window;
    uiBox *box;
    uiButton *button;
    uiMultilineEntry *scroll;

    memset(&options, 0, sizeof(options));
    if (uiInit(&options) != NULL)
        abort();

    window = uiNewWindow("Hello", 320, 240, 0);
    uiWindowSetMargined(window, 1);

    box = uiNewVerticalBox();
    uiBoxSetPadded(box, 1);
    uiWindowSetChild(window, uiControl(box));

    scroll = uiNewMultilineEntry();
    uiMultilineEntrySetReadOnly(scroll, 1);

    button = uiNewButton("libui говорит: click me!");
    uiButtonOnClicked(button, saySomething, scroll);
    uiBoxAppend(box, uiControl(button), 0);

    uiBoxAppend(box, uiControl(scroll), 1);

    uiWindowOnClosing(window, onClosing, NULL);
    uiControlShow(uiControl(window));
    uiMain();
    return 0;
}

Direct translation to Kotlin:
import kotlinx.cinterop.*
import libui.*

fun main(args: Array<String>) = memScoped {
    val options = alloc<uiInitOptions>()
    val error = uiInit(options.ptr)
    if (error != null) throw Error("Error: '${error.toKString()}'")

    val window = uiNewWindow("Hello", 320, 240, 0)
    uiWindowSetMargined(window, 1)

    val box = uiNewVerticalBox()
    uiBoxSetPadded(box, 1)
    uiWindowSetChild(window, box?.reinterpret())

    val scroll = uiNewMultilineEntry()
    uiMultilineEntrySetReadOnly(scroll, 1)
    val button = uiNewButton("libui говорит: click me!")
    fun saySomething(button: CPointer<uiButton>?, data: COpaquePointer?) {
        uiMultilineEntryAppend(data?.reinterpret(),
            "Hello, World!  Ciao, mondo!\n" +
            "Привет, мир!  你好,世界!\n\n")
    }
    uiButtonOnClicked(button, staticCFunction(::saySomething), scroll)
    uiBoxAppend(box, button?.reinterpret(), 0)
    uiBoxAppend(box, scroll?.reinterpret(), 1)

    fun onClosing(window: CPointer<uiWindow>?, data: COpaquePointer?): Int {
        uiQuit()
        return 1
    }
    uiWindowOnClosing(window, staticCFunction(::onClosing), null)
    uiControlShow(window?.reinterpret())
    uiMain()
    uiUninit()
}

While this works, it's far from idiomatic Kotlin.

OK, let's wrap all that noisy function calls, with final goal to get something similar to TornadoFX:

import libui.*

fun main(args: Array<String>) = appWindow(
    title = "Hello",
    width = 320,
    height = 240
) {
    vbox {
        lateinit var scroll: TextArea

        button("libui говорит: click me!") {
            action {
                scroll.append("""
                    |Hello, World!  Ciao, mondo!
                    |Привет, мир!  你好,世界!
                    |
                    |""".trimMargin())
            }
        }
        scroll = textarea {
            readonly = true
            stretchy = true
        }
    }
}

More samples

Documentation

See autogenerated documentation, samples and comments in source code.

Lifecycle management

Kotlin memory management differs from native C model, so all libui objects are wrapped in Kotlin objects inherited from Disposable, and direct using of libui functions is not recommended in most cases.

Disposable objects must be disposed by calling dispose() method, before program ends. Most objects are attached as a child to some other object, in this case parent is responsible to dispose all its children, recursively. As DSL builders automatically add created object to some container - in most cases you do not have to worry about lifecycle management. But if you want to do something not supported by DSL builders - you can create Disposable object directly, and in this case you are responsible to dispose or attach it at some point.

kotlin-libui's People

Contributors

andreas-mausch avatar araujojordan avatar creeperface01 avatar gildor avatar lucasvsme avatar mervyn-mccreight avatar molexx avatar msink avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlin-libui's Issues

Can't use MsgBox in worker

I'm trying to run a background task using a worker and display a message when an exception is caught.

Here's a minimal reproducible example:

package example

import kotlin.native.concurrent.TransferMode
import kotlin.native.concurrent.Worker
import libui.ktx.*

fun main() = appWindow("Test", 800, 600) {
    vbox {
        button("Test") {
            action {
                Worker.start().execute(TransferMode.UNSAFE, {}) {
                    MsgBox("This doesn't work")
                }
            }
        }
    }
}

That fails with this error when clicking the button:

Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: Trying to access top level value not marked as @ThreadLocal or @SharedImmutable from non-main thread
        at ThrowIncorrectDereferenceException (0x232c5c)
        at kfun:example.$<bridge-UNNN>main$lambda-2$<anonymous>_3_6$<anonymous>_5_8(kotlin.Unit)#internal (0x2485c0)
        at _ZN6Worker19processQueueElementEb (0x25d2ac)
        at _ZN12_GLOBAL__N_113workerRoutineEPv (0x25d96d)
        at  (0x7f70ac741422)
        at clone (0x7f70ac656bf3)
        at  ((nil))

  • platform: Linux x86_64
  • kotlin: 1.3.72
  • kotlin-libui: 0.1.7
  • coroutines: 1.3.5-native-mt

User guide/Documentation

Despite two years have passed since the moment when this problem had been outlined for the first time, there is still no documentation.
I'm a newbie to both Kotlin|NAtive and GUI.
I found that I simply cannot do even the simplest things without documents.

Can anybody assist me in providing at least full-functional (and not few simple samples) examples of coding GUI using libui?
I need to build a very simple application:

  1. Initialize window
  2. Initialize menu
  3. Input several common project data (the algorithm is to check the input and do not allow incorrect data)
  4. Output these data to the screen
  5. Switch to the next screen
  6. Input several particular project item data
  7. Output them to the table on the screen
  8. Perform calculations.
  9. Switch to the next screen
  10. Output the results to the screen
  11. Calculate one more version?
  12. If Yes - goto 6
  13. If No - do nothing

Thank you.

libtinfo.so.5: cannot open shared object file: No such file or directory

▶ ./gradlew build

Welcome to Gradle 5.5-rc-3!

Here are the highlights of this release:
 - Kickstart Gradle plugin development with gradle init
 - Distribute organization-wide Gradle properties in custom Gradle distributions
 - Transform dependency artifacts on resolution

For more details see https://docs.gradle.org/5.5-rc-3/release-notes.html

Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Task :buildSrc:compileKotlin
The `kotlin-dsl` plugin applied to project ':buildSrc' enables experimental Kotlin compiler features. For more information see https://docs.gradle.org/5.5-rc-3/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin

> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources NO-SOURCE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:inspectClassesForKotlinIC UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :buildSrc:assemble UP-TO-DATE
> Task :buildSrc:compileTestKotlin NO-SOURCE
> Task :buildSrc:pluginUnderTestMetadata UP-TO-DATE
> Task :buildSrc:compileTestJava NO-SOURCE
> Task :buildSrc:compileTestGroovy NO-SOURCE
> Task :buildSrc:processTestResources NO-SOURCE
> Task :buildSrc:testClasses UP-TO-DATE
> Task :buildSrc:test NO-SOURCE
> Task :buildSrc:validateTaskProperties UP-TO-DATE
> Task :buildSrc:check UP-TO-DATE
> Task :buildSrc:build UP-TO-DATE

> Configure project :libui
Kotlin Multiplatform Projects are an experimental feature.
publishModeEnabled: false

The Kotlin source set commonTest was configured but not added to any Kotlin compilation. You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#connecting-source-sets

> Task :libui:downloadArchive UP-TO-DATE
> Task :libui:unpackArchive UP-TO-DATE

> Task :libui:cinteropLibuiLinux FAILED
Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/bruno/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/lib/libclang.so.6.0: libtinfo.so.5: cannot open shared object file: No such file or directory
        at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
        at java.lang.Runtime.load0(Runtime.java:809)
        at java.lang.System.load(System.java:1086)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.prepareTool(main.kt:297)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:176)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.interop(main.kt:38)
        at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:70)
        at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:18)

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':libui:cinteropLibuiLinux'.
> Process 'command '/usr/lib/jvm/java-8-openjdk/bin/java'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.5-rc-3/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 8s
3 actionable tasks: 1 executed, 2 up-to-date

os: MANJARO linux 5.1.15

Any ways to manually close the window?

Hi,
I'm using this library to build a desktop project but I need to close the window manually and re-create it later. Now I'm using the ktx library but I cannot find any methods to do this. Are there any methods for me to use in order to close the window? My code is attached below. Thanks.

fun main(args: Array<String>?) {
    appMain() // Display the UI and block
    // To do some extra work.
}

fun appMain() = appWindow(
    title = "Sample",
    width = 320,
    height = 240
) {
    vbox {
        button ("Quit") {
            action {
                // uiControlDestroy
                // I tried it but the window closes without continuing the statements in main().
                this@appWindow.dispose()
            }
        }
    }
}

Integrate with Jetbrains Compose

I noticed in this project's README that you weren't sure about the DSL.

Well, what if we changed the DSL over to Android/Jetbrains Compose? It seems like fun idea because:

  • Take advantage of the tree-building and reactivity built into compose
  • Compose is gaining traction & mind-share, so this project might be more approachable by sharing concepts
  • Improved code sharing; e.g. you could use expect/actual to build common interfaces that work across both Android and native desktop.
  • Jetbrains is already working on Compose Desktop, but some devs (myself included) disapprove of using Skia canvas instead of native platform widgets for reasons of accessibility/etc.. Plus, it's on the JVM and not Kotlin/native. So we could create a nice alternative to fill a different niche.

I'm just brainstorming, but I'm hoping to have some time to explore this myself, thought the idea was worth sharing.

Missing support for `linux_arm32_hfp`

With Kotlin 1.3 the support for raspberry was added (which is a linuxArm32Hfp plattform).
It would be great to extend the project for this platform.

I already compiled the libui manually and added it, but it should be done directly in this project instead of doing it manualy.

Can we have a OpenFolderDialog()?

I found OpenFileDialog very useful and would like to have the same for folders. If you don't have time for it I can try to dig into it.

Control.dispose() is never called

Hi, me again. :)

I've noticed the dispose() method is never called for me in the examples.
I've tried hello-ktx and controlgallery, OS is Manjaro (4.19 Kernel).

The callback from uiOnShouldQuit is never reached.

Even when I dispose the mainWindow manually, I would have expected all children's dispose() method to be called - which was not the case!

I'm not sure if I missed something, or maybe it's a bug?

Andreas

Common UI for native and JVM ?

I'm thinking about writing applications that run natively, but also on the JVM.
Is it possible to write one single gui code, using the kotlin-libui syntax ?
Can the kotlin-libui library be extended, so it uses e.g. Tornado-FX, but still provide the same programming interface to the developer?

Release on maven central?

This is awesome that you've put this project together and continue to maintain it. I'm trying to think of an easy way to add it to an app, and it's not an easy process. It'd be amazing if this were on maven central so we could pull it naturally as a dependency!

Depends on old version of libcrypt

When executing an application created with this library on linux, it looks for libcrypt.so.1 which on Arch Linux has been deprecated and replaced with libcrypt.so.2. This issue can be solved simply by creating a symlink, which means theres no compatibility issues requiring the older version of the library, so I would recommend updating the dependency to the newer version.

"ld.lld: error: unable to find library" on CentOS7

Hello.
I tried to work hello-ktx sample according to README in this project. But it doesn't work and output this error.

Task :samples:controlgallery:linkReleaseExecutableLinux FAILED
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lgtk-3
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lgdk-3
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -latk-1.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lgio-2.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lpangocairo-1.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lgdk_pixbuf-2.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lcairo-gobject
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lpango-1.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lcairo
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lgobject-2.0
/home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld: error: unable to find library -lglib-2.0
e: /home/komkomh/.konan/dependencies/clang-llvm-6.0.1-linux-x86-64/bin/ld.lld invocation reported errors
FAILURE: Build failed with an exception.

My os version is
CentOS Linux release 7.6.1810 (Core)

Please give me a hint. Thanks

32-bit windows target

As Kotlin 1.3.31 now can build for 32-bit windows - I decided just switch windows build to 32-bit, and drop support for 64-bit windows, at least for now.
Do not see any reasons for 64-bit windows executable for simple utilites.
If anyone have significant reasons - please comment here, and I will create new target windows_x64

To migrate - just replace in your build scripts mingwX64 to mingwX86, and msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 to msys2-mingw-w64-i686-gcc-7.4.0-clang-llvm-6.0.1.
Gradle should be upgraded to 5.4 - gradle wrapper --gradle-version 5.4 --distribution-type all

Had to change linkerOpts.linux on Manjaro

libui/libui.def

-linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu \
+linkerOpts.linux = -L/usr/lib \

Not sure if this can be set automatically to the correct directory on all systems. Maybe just document somewhere that you might need to adjust the value?

gradle.output.txt

Switch to kotlin-multiplatform plugin

Hi, this is awesome!

Is there an easy way to add kotlin-libui as a dependency when I create a new project, or does kotlin-libui have to be in the parent gradle project?

Documentation

For now there is almost no documentation, except for autogenerated.
Any contributions will be very helpful.

Data-binding

Do you plan to support some kind of data-binding?

I think of it in a way that you pass a model object (Observable?) to a TextField, and each time the string is changed, the UI is changed as well.

And, additionally, each time the user types into the TextField, the model object get's changed automatically.

Does not compile

➜  kotlin-libui git:(master) ./gradlew build
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :libui:compileKonanLibuiLinux_x64 FAILED
Exception in thread "main" java.lang.Error: /tmp/tmp4030171382076881273.c:1:10: fatal error: 'ui.h' file not found
        at org.jetbrains.kotlin.native.interop.indexer.UtilsKt.ensureNoCompileErrors(Utils.kt:145)
        at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.indexDeclarations(Indexer.kt:916)
        at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.buildNativeIndexImpl(Indexer.kt:906)
        at org.jetbrains.kotlin.native.interop.indexer.NativeIndexKt.buildNativeIndex(NativeIndex.kt:56)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:242)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.interop(main.kt:36)
        at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:88)
        at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:18)

FAILURE: Build failed with an exception.

Looks like I am missing a library?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.