kotlin-libui

Kotlin/Native interop to libui: a portable GUI library

Github星跟蹤圖

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.

Windows

Unix

macOS

#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;
}
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.

主要指標

概覽
名稱與所有者msink/kotlin-libui
主編程語言Kotlin
編程語言Kotlin (語言數: 2)
平台
許可證MIT License
所有者活动
創建於2018-05-12 10:15:44
推送於2023-10-24 02:42:33
最后一次提交2022-09-30 12:55:41
發布數21
最新版本名稱0.1.8 (發布於 )
第一版名稱alpha-3.5-1 (發布於 )
用户参与
星數668
關注者數18
派生數51
提交數507
已啟用問題?
問題數30
打開的問題數19
拉請求數18
打開的拉請求數1
關閉的拉請求數3
项目设置
已啟用Wiki?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?