I just tried my initial idea, and indeed it seems to work not bad.
proc gtk_button_new*(): ptr Button00 {.
importc: "gtk_button_new", libprag.}
proc newButton*(): Button =
new(result, finalizeGObject)
result.impl = gtk_button_new()
GC_ref(result)
g_object_add_toggle_ref(result.impl, toggleNotify, addr(result[]))
assert(g_object_get_qdata(result.impl, Quark) == nil)
g_object_set_qdata(result.impl, Quark, addr(result[]))
proc initButton*[T](result: var T) =
assert(result is Button)
new(result, finalizeGObject)
result.impl = gtk_button_new()
GC_ref(result)
g_object_add_toggle_ref(result.impl, toggleNotify, addr(result[]))
assert(g_object_get_qdata(result.impl, Quark) == nil)
g_object_set_qdata(result.impl, Quark, addr(result[]))
I have supported the automatically generated proc newButton() with a manually
created initButton(), and indeed the test program below works.
# plain test for high level gi based GTK3 Nim wrapper
# https://github.com/StefanSalewski/nim-gi2
import gtk, glib, gobject
import macros
import strutils
type
XButton = ref object of Button
x: int
# TODO: will be moved to library module!
let Quark = g_quark_from_static_string("NimGIQuark")
# TODO: will be moved to library module!
proc initWithArgv*() =
var
cmdLine{.importc.}: cstringArray
cmdCount{.importc.}: cint
gtk.gtk_init(cmdCount, cmdLine)
proc clickd(button: XButton; arg: string) =
echo arg
echo button.x
proc bye(w: Window; arg: string) =
mainQuit()
echo arg
var ProcID: int
# TODO: this macro will be moved to library module!
macro connect(widget: Widget; signal: string; p: typed; arg: typed): typed =
inc(ProcID)
let wt = getType(widget) # widget type
let at = getType(arg) # argument type
let signalName = ($signal).replace("-", "_") # maybe we should just use
plain proc names
let procNameCdecl = newIdentNode("connect_for_signal_cdecl_" & signalName
& $ProcID)
let procName = newIdentNode("connect_for_signal_" & signalName & $ProcID)
let scName = newIdentNode("sc" & signalName)
result = quote do:
proc `procNameCdecl`(button: ptr Object00 , data: pointer) {.cdecl.} =
var h: pointer = g_object_get_qdata(button, Quark)
`p`(cast[`wt`](h), cast[`at`](data))
proc `procName`(self: `wt`; p: proc (self: `wt`, arg: `at`); a: `at`)
=
`scName`(self, `procNameCdecl`, cast[pointer](a))
`procName`(`widget`, `p`, `arg`)
proc main() =
initWithArgv()
var window: Window = newWindow(WindowType.topLevel)
window.setTitle("First Test")
window.setBorderWidth(10)
var
box = newBox(Orientation.vertical, 0)
button1: XButton
button2 = newButtonWithLabel("Wrapper")
initButton(button1)
button1.setLabel("Nim GI")
button1.x = 99
connect(button1, "clicked", clickd, "Hello")
connect(window, "destroy", bye, "Bye")
#button2.newconnect("clicked", (proc(button: Button; arg: string) = echo
arg), "Bye")
box.add(button1)
box.add(button2)
window.add(box)
window.showAll
let p = button1.getParent
assert(p == box)
gtk.gtkMain()
main()
The connect makro is type safe, allows to pass arbitrary arguments and works
with extended objects like XButton from above.