Created
May 16, 2017 10:31
-
-
Save kropp/9b8b9578b9421e932f932bb6aed9598a to your computer and use it in GitHub Desktop.
GTK+ Demo Application in Kotlin/Native rewritten in OO-style
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2010-2017 JetBrains s.r.o. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
import kotlinx.cinterop.* | |
import gtk3.* | |
// Note that all callback parameters must be primitive types or nullable C pointers. | |
fun <F : CFunction<*>> g_signal_connect(obj: CPointer<*>, actionName: String, | |
action: CPointer<F>, data: gpointer? = null, connect_flags: Int = 0) { | |
g_signal_connect_data(obj.reinterpret(), actionName, action.reinterpret(), | |
data = data, destroy_data = null, connect_flags = connect_flags) | |
} | |
class Application(id: String) { | |
val app = gtk_application_new(id, G_APPLICATION_FLAGS_NONE)!! | |
fun onActivate(callback: CPointer<CFunction<(CPointer<GtkApplication>?, gpointer?) -> Unit>>) { | |
g_signal_connect(app, "activate", callback) | |
} | |
fun run(args: Array<String>): Int { | |
val status = memScoped { | |
g_application_run(app.reinterpret(), | |
args.size, args.map { it.cstr.getPointer(memScope) }.toCValues()) | |
} | |
g_object_unref(app) | |
return status | |
} | |
} | |
abstract class Widget { | |
abstract val widgetPtr: CPointer<GtkWidget> | |
} | |
abstract class Container: Widget() { | |
fun add(widget: Widget) = gtk_container_add(widgetPtr.reinterpret(), widget.widgetPtr) | |
} | |
class Window(app: CValuesRef<GtkApplication>): Container() { | |
override val widgetPtr = gtk_application_window_new(app)!! | |
private val window get() = widgetPtr.reinterpret<GtkWindow>() | |
var title: String | |
get() = "" | |
set(value) { gtk_window_set_title(window, value) } | |
fun setDefaultSize(width: Int, height: Int) = gtk_window_set_default_size(window, width, height) | |
fun showAll() = gtk_widget_show_all(widgetPtr) | |
} | |
class ButtonBox(orientation: GtkOrientation): Container() { | |
override val widgetPtr = gtk_button_box_new(orientation)!! | |
} | |
fun signalHandler(sender: CPointer<*>?, data: COpaquePointer?) { | |
val button = StableObjPtr.fromValue(data!!).get() as Button | |
button.clicked() | |
} | |
typealias SignalHandler = () -> Unit | |
class Signal { | |
private var handlers = emptyList<SignalHandler>() | |
operator fun plusAssign(handler: SignalHandler) { handlers += handler } | |
operator fun minusAssign(handler: SignalHandler) { handlers -= handler } | |
operator fun invoke() { | |
for (handler in handlers) { | |
try { | |
handler() | |
} catch (e: Throwable) { | |
} | |
} | |
} | |
} | |
class Button(label: String): Widget() { | |
override val widgetPtr = gtk_button_new_with_label(label)!! | |
init { | |
g_signal_connect(widgetPtr, "clicked", staticCFunction(::signalHandler), StableObjPtr.create(this).value) | |
} | |
val clicked = Signal() | |
} | |
fun CPointer<GtkApplication>.window(builder: Window.() -> Unit) { | |
Window(reinterpret()).apply(builder).showAll() | |
} | |
fun Container.buttonBox(builder: ButtonBox.() -> Unit) = add(ButtonBox(GtkOrientation.GTK_ORIENTATION_HORIZONTAL).apply(builder)) | |
fun ButtonBox.button(label: String, builder: Button.() -> Unit) = add(Button(label).apply(builder)) | |
fun gtkMain(args: Array<String>): Int { | |
val app = Application("org.gtk.example")!! | |
app.onActivate(staticCFunction { app, _ -> | |
app!!.window { | |
title = "Kotlin" | |
setDefaultSize(200, 200) | |
buttonBox { | |
button("Hello World!") { | |
clicked += { | |
println("Hello Kotlin!") | |
gtk_widget_destroy([email protected]) | |
} | |
} | |
} | |
} | |
}) | |
return app.run(args) | |
} | |
fun main(args: Array<String>) { | |
gtkMain(args) | |
} |
This is absolutely great. I'd guess a logical next step is to take advantage of GObject Introspection to recreate the class structure in Kotlin.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I like vala, but kotlin even more.
I was thinking about creating anko style DSL for kotlin native with gtk too.
And this is awesome.