项目作者: msink

项目描述 :
Kotlin/Native interop to libui: a portable GUI library
高级语言: Kotlin
项目地址: git://github.com/msink/kotlin-libui.git
创建时间: 2018-05-12T10:15:44Z
项目社区:https://github.com/msink/kotlin-libui

开源协议:MIT License

下载


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:

  1. #clone this project
  2. git clone https://github.com/msink/kotlin-libui.git
  3. #build libui.klib
  4. cd kotlin-libui/libui
  5. ../gradlew build
  6. #build and run the hello-ktx sample
  7. cd ../samples/hello-ktx/
  8. ../../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:

  1. #include "ui.h"
  2. static int onClosing(uiWindow *window, void *data)
  3. {
  4. uiQuit();
  5. return 1;
  6. }
  7. static void saySomething(uiButton *button, void *data)
  8. {
  9. uiMultilineEntryAppend(uiMultilineEntry(data),
  10. "Hello, World! Ciao, mondo!\n"
  11. "Привет, мир! 你好,世界!\n\n");
  12. }
  13. int main(void)
  14. {
  15. uiInitOptions options;
  16. uiWindow *window;
  17. uiBox *box;
  18. uiButton *button;
  19. uiMultilineEntry *scroll;
  20. memset(&options, 0, sizeof(options));
  21. if (uiInit(&options) != NULL)
  22. abort();
  23. window = uiNewWindow("Hello", 320, 240, 0);
  24. uiWindowSetMargined(window, 1);
  25. box = uiNewVerticalBox();
  26. uiBoxSetPadded(box, 1);
  27. uiWindowSetChild(window, uiControl(box));
  28. scroll = uiNewMultilineEntry();
  29. uiMultilineEntrySetReadOnly(scroll, 1);
  30. button = uiNewButton("libui говорит: click me!");
  31. uiButtonOnClicked(button, saySomething, scroll);
  32. uiBoxAppend(box, uiControl(button), 0);
  33. uiBoxAppend(box, uiControl(scroll), 1);
  34. uiWindowOnClosing(window, onClosing, NULL);
  35. uiControlShow(uiControl(window));
  36. uiMain();
  37. return 0;
  38. }



Direct translation to Kotlin:

  1. import kotlinx.cinterop.*
  2. import libui.*
  3. fun main(args: Array<String>) = memScoped {
  4. val options = alloc<uiInitOptions>()
  5. val error = uiInit(options.ptr)
  6. if (error != null) throw Error("Error: '${error.toKString()}'")
  7. val window = uiNewWindow("Hello", 320, 240, 0)
  8. uiWindowSetMargined(window, 1)
  9. val box = uiNewVerticalBox()
  10. uiBoxSetPadded(box, 1)
  11. uiWindowSetChild(window, box?.reinterpret())
  12. val scroll = uiNewMultilineEntry()
  13. uiMultilineEntrySetReadOnly(scroll, 1)
  14. val button = uiNewButton("libui говорит: click me!")
  15. fun saySomething(button: CPointer<uiButton>?, data: COpaquePointer?) {
  16. uiMultilineEntryAppend(data?.reinterpret(),
  17. "Hello, World! Ciao, mondo!\n" +
  18. "Привет, мир! 你好,世界!\n\n")
  19. }
  20. uiButtonOnClicked(button, staticCFunction(::saySomething), scroll)
  21. uiBoxAppend(box, button?.reinterpret(), 0)
  22. uiBoxAppend(box, scroll?.reinterpret(), 1)
  23. fun onClosing(window: CPointer<uiWindow>?, data: COpaquePointer?): Int {
  24. uiQuit()
  25. return 1
  26. }
  27. uiWindowOnClosing(window, staticCFunction(::onClosing), null)
  28. uiControlShow(window?.reinterpret())
  29. uiMain()
  30. uiUninit()
  31. }


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:

  1. import libui.*
  2. fun main(args: Array<String>) = appWindow(
  3. title = "Hello",
  4. width = 320,
  5. height = 240
  6. ) {
  7. vbox {
  8. lateinit var scroll: TextArea
  9. button("libui говорит: click me!") {
  10. action {
  11. scroll.append("""
  12. |Hello, World! Ciao, mondo!
  13. |Привет, мир! 你好,世界!
  14. |
  15. |""".trimMargin())
  16. }
  17. }
  18. scroll = textarea {
  19. readonly = true
  20. stretchy = true
  21. }
  22. }
  23. }

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.