Skip to content

Instantly share code, notes, and snippets.

@kropp
Created May 16, 2017 10:31
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save kropp/9b8b9578b9421e932f932bb6aed9598a to your computer and use it in GitHub Desktop.
GTK+ Demo Application in Kotlin/Native rewritten in OO-style
/*
* 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(this@window.widgetPtr)
}
}
}
}
})
return app.run(args)
}
fun main(args: Array<String>) {
gtkMain(args)
}
@itisrazza
Copy link

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