new() with typedesc

This commit is contained in:
Stefan Salewski 2020-03-09 12:54:04 +01:00
commit e20c95abf5
4 changed files with 44 additions and 24 deletions

View file

@ -1,6 +1,6 @@
= High level GTK3 (and GTK4) bindings for the Nim programming language
(c) Stefan Salewski
//Version 0.7.2
//Version 0.7.3
:experimental:
:imagesdir: http://ssalewski.de/tmp
:source-highlighter: pygments
@ -25,8 +25,14 @@ with code inserted from files directly.
NOTE: This work is partly based on earlier works of J. Mansour and has been supported by A. Rumpf, E. Bassi and other _Nim_ and _GTK/Gnome_ developers.
The `combinatorics` module was kindly provided by R. Behrends.
NOTE: Starting with current version v0.7.3 we allow passing a type descriptor as first parameter to the
new() procs like `newButton(MyButtonSubclass)` to support subclassing and extending Widgets in OOP style.
See <<Extending or sub-classing Widgets>> for an complete example. The init() procs which were used
before for this task are now deprecated and will be removed later. This new approach generally saves
one line of source code, allows using `let` instead of `var`, and the naming of procs is more consistent.
NOTE: Starting with version v0.7.1 we have added destructor support when compiled with `--gc:arc`,
so we have no memory leak for subclassed objects any more, see the example in `Subclassing` section.
so we have no memory leak for subclassed objects any more, see the example in <<Extending or sub-classing Widgets>> section.
When compiled with default (refc) GC finalizers are used as before. And for objects marked with `nullable`
tag in gobject-introspection we now return nil value for the proxy object when the C lib has returned NULL.
So according to the C API docs, you can check for nil result for the few functions which may return NULL.
@ -597,13 +603,17 @@ Remember the GTK `devhelp` tool, and use also `grep` or the `nimgrep` variant.
== Extending or sub-classing Widgets
I may occur that we want to attach additional information to GTK widgets
by extending or subclassing them. Doing this is supported
It may occur that we want to attach additional information to GTK widgets
by extending or subclassing them. [line-through]#Doing this is supported
by providing for each widget class not only a corresponding new() proc which returns
the newly created widget, but also
a init() proc, which gets an uninitialized variable of the (extended) widget type as argument and
initializes that variable with a newly created
GTK widget . Initializing the added fields is
GTK widget.#
Doing this is supported
by providing for each widget class an additional new() proc which takes an type descriptor
as first argument, like `newButton(CountButton, "Counting down from 100 by 5")` in the example below.
Initializing the added fields is
done separately by the user. The following code shows a GTK button, which is
extended with a counter member field. That counter is decreased for
each button click. The amount of decrease (5) is passed to the callback as a int parameter.
@ -621,29 +631,30 @@ to test the destructor behaviour.)
.count_button.nim
----
# nim c count_button.nim
import gintro/[gtk, glib, gobject, gio]
import gintro/[gtk, gobject, gio]
type
CountButton = ref object of Button
counter: int
when compileOption("gc", "arc"):
when defined(gcDestructors):
proc `=destroy`(x: var typeof(CountButton()[])) =
gtk.`=destroy`(typeof(Button()[])(x))
proc buttonClicked (button: CountButton; decrement: int) =
dec(button.counter, decrement)
button.label = "Counter: " & $button.counter
button.label = "Counter: " & $button.counter
echo "Counter is now: ", button.counter
proc appActivate (app: Application) =
var button: CountButton
#var button: CountButton
let window = newApplicationWindow(app)
window.title = "Count Button"
initButton(button, "Counting down from 100 by 5")
#initButton(button, "Counting down from 100 by 5") # deprecated
let button = newButton(CountButton, "Counting down from 100 by 5")
button.counter = 100
window.add(button)
button.connect("clicked", buttonClicked, 5)
button.connect("clicked", buttonClicked, 5)
window.showAll
proc main =

View file

@ -1,27 +1,28 @@
# nim c count_button.nim
import gintro/[gtk, glib, gobject, gio]
import gintro/[gtk, gobject, gio]
type
CountButton = ref object of Button
counter: int
when compileOption("gc", "arc"):
when defined(gcDestructors):
proc `=destroy`(x: var typeof(CountButton()[])) =
gtk.`=destroy`(typeof(Button()[])(x))
proc buttonClicked (button: CountButton; decrement: int) =
dec(button.counter, decrement)
button.label = "Counter: " & $button.counter
button.label = "Counter: " & $button.counter
echo "Counter is now: ", button.counter
proc appActivate (app: Application) =
var button: CountButton
#var button: CountButton
let window = newApplicationWindow(app)
window.title = "Count Button"
initButton(button, "Counting down from 100 by 5")
#initButton(button, "Counting down from 100 by 5") # deprecated
let button = newButton(CountButton, "Counting down from 100 by 5")
button.counter = 100
window.add(button)
button.connect("clicked", buttonClicked, 5)
button.connect("clicked", buttonClicked, 5)
window.showAll
proc main =

View file

@ -1,6 +1,6 @@
# Package
version = "0.7.2"
version = "0.7.3"
author = "Stefan Salewski"
description = "High level GObject-Introspection based GTK3/GTK4 bindings"
license = "MIT"

View file

@ -1,6 +1,6 @@
# High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language
# nimpretty --maxLineLen:130 gen.nim
# v 0.7.2 2020-FEB-23
# v 0.7.3 2020-MAR-09
# (c) S. Salewski 2018
# https://wiki.gnome.org/Projects/GObjectIntrospection
@ -900,21 +900,29 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
for j in 0.cint ..< gCallableInfoGetNArgs(minfo):
let arg = gCallableInfoGetArg(minfo, j)
assert gArgInfoGetDirection(arg) notin {GIDirection.OUT, INOUT} # so we can ignore this case!
for i in 0 .. 1: # generate the new() and the init() proc
for itt in 0 .. 2: # generate the new(), the new(typedesc) and the init() proc
var hhh = "\nproc " & asym & EM & plist
if i > 0:
if itt == 1: # new(typedesc)
let semi = if hhh.contains("()"): "" else: "; "
let p = hhh.find("): ")
hhh.setLen(p + 1)
hhh = hhh.replace("*(", "*(tdesc: typedesc" & semi)
hhh.add(": tdesc")
if itt == 2: # init(), deprecated
let semi = if hhh.contains("()"): "" else: "; "
let p = hhh.find("): ")
hhh.setLen(p + 1)
hhh = hhh.replace("new", "init")
hhh = hhh.replace("*(", "*[T](result: var T" & semi)
if itt == 2:
depStr = " {.deprecated.}"
methodBuffer.writeLine(hhh & depStr & " =")
methodBuffer.write(arrLex)
methodBuffer.write(sss)
if gCallableInfoCanThrowGerror(minfo):
methodBuffer.writeLine(" var gerror: ptr glib.Error")
if i == 0: arglist.insert(", addr gerror", arglist.high)
if i > 0:
if itt == 0: arglist.insert(", addr gerror", arglist.high)
if itt == 1 or itt == 2:
methodBuffer.writeLine(" assert(result is $1)" % [manglename(gBaseInfoGetName(info))])
if isGObject:
# CAUTION: some procs are advertised as constructor but do not construct new objects,
@ -2604,4 +2612,4 @@ proc launch() =
supmod4.close
launch()
# 2607 lines
# 2615 lines