connectAfter() and new example

This commit is contained in:
Stefan Salewski 2019-09-21 21:10:59 +02:00
commit f3a8dcfdbe
6 changed files with 310 additions and 27 deletions

View file

@ -1638,6 +1638,144 @@ make editing the program name entries permanent. That proc needs cstring paramet
may be surprising, as we generally use Nim strings. Not a big problem, maybe intended, we may have to
check the connect() macro in gimpl.nim.
== And one more Listview example -- with custom cairo drawing
This example is again a Nim version of a C example from C. Eric Cashon
provided at https://discourse.gnome.org/t/gtk-how-to-draw-on-top-of-gtktreeview/1783/2.
It draws an rectangular frame on a selected listview cell. For that to work
connectAfter() is used to ensure that the custom cairo drawing occurs after
the widget is drawn by GTK.
[[overlay_tree1.nim]]
[source,nim]
.overlay_tree1.nim
----
# nim c overlayTree1.nim
import gintro/[gtk, gdk, glib, gobject, cairo]
import strformat
from strutils import parseInt
const
Id = 0
Program = 1
Color = 2
Color2 = 3
Columns = 4
var
rowG = 0
columnG = 1
proc bye(w: gtk.Window) =
mainQuit()
echo "Bye..."
proc toStringVal(s: string): Value =
let gtype = typeFromName("gchararray")
discard init(result, gtype)
setString(result, s)
proc toUIntVal(i: int): Value =
let gtype = typeFromName("guint")
discard init(result, gtype)
setUint(result, i)
proc toBoolVal(b: bool): Value =
let gtype = typeFromName("gboolean")
discard init(result, gtype)
setBoolean(result, b)
proc selectCell(treeView: TreeView; path: TreePath; column: TreeViewColumn) =
let str = toString(path)
echo fmt"{str} {getTitle(column)}"
rowG = parseInt(str)
queueDraw(treeView)
proc drawRectangle(overlay: Overlay; cr: cairo.Context; treeView: TreeView): bool =
echo fmt"Draw Rectangle {rowG} {columnG}"
let path = newTreePathFromIndices(@[rowG.int32])
echo path.toString
let column = getColumn(treeView, columnG)
var rect: gdk.Rectangle
var x, y: int
treeView.convertBinWindowToWidgetCoords(0, 0, x, y)
cr.save
cr.translate(x.float, y.float)
cr.setLineWidth(2)
cr.setSource(0, 0, 0, 1)
treeView.getCellArea(path, column, rect)
cr.rectangle(rect.x.float + 1, rect.y.float + 1, rect.width.float - 1, rect.height.float - 1)
cr.stroke
cr.restore
return EVENT_PROPAGATE # false
proc main =
gtk.init()
let window = newWindow()
window.setTitle("Overlay Tree")
window.setPosition(WindowPosition.center)
window.setDefaultSize(500, 500)
window.setBorderWidth(20)
window.connect("destroy", bye)
var iter: TreeIter
let h = [typeFromName("guint"), typeFromName("gchararray"), typeFromName("gchararray"),
typeFromName("gchararray")]
let store = newListStore(Columns, cast[pointer](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
let progNames = ["Gedit", "Gimp", "Inkscape", "Firefox", "Calculator", "Devhelp"]
for i, n in progNames:
store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original
store.setValue(iter, Id, toUIntVal(i))
store.setValue(iter, Program, toStringVal(n))
store.setValue(iter, Color, toStringVal("SpringGreen"))
store.setValue(iter, Color2, toStringVal("cyan"))
let tree = newTreeViewWithModel(store)
tree.setHexpand
tree.setVexpand
tree.setProperty("activate-on-single-click", toBoolVal(true))
let selection = tree.getSelection
selection.setMode(SelectionMode.single)
let renderer1 = newCellRendererText()
renderer1.setProperty("editable", toBoolVal(false))
let renderer2 = newCellRendererText()
renderer2.setProperty("editable", toBoolVal(true))
tree.connect("row-activated", selectCell)
## Bind the COLOR column to the "cell-background" property.
let column1 = newTreeViewColumn()
column1.setTitle("ID")
column1.packStart(renderer1, true)
column1.addAttribute(renderer1, "text", Id)
column1.addAttribute(renderer1, "cell-background", Color)
discard tree.appendColumn(column1)
let column2 = newTreeViewColumn()
column2.setTitle("Program")
column2.packStart(renderer2, true)
column2.addAttribute(renderer2, "text", Program)
column2.addAttribute(renderer2, "cell-background", Color2)
discard tree.appendColumn(column2)
## For drawing the outline of the cell.
let overlay = newOverlay()
overlay.setHexpand
overlay.setVexpand
overlay.setAppPaintable
overlay.addOverlay(tree)
overlay.setOverlayPassThrough(tree, true)
overlay.connectAfter("draw", drawRectangle, tree)
let grid = newGrid()
grid.attach(overlay, 0, 0, 1, 1)
window.add(grid)
const cssString =
"""treeview{background-color: rgba(0,255,255,1.0);
font-size:30pt} treeview:selected{background-color:rgba(0,255,255,1.0);
color: rgba(0,0,255,1.0);}"""
let provider = newCssProvider()
discard provider.loadFromData(cssString)
getDefaultScreen().addProviderForScreen(provider, STYLE_PROVIDER_PRIORITY_APPLICATION)
window.showAll
gtk.main()
main() # 123 lines
----
== A more advanced example for cairo drawing with zooming, panning, scrolling
The following code is a plain Nim version of a drawing demo which I wrote some years ago in Ruby (http://ssalewski.de/PetEd-Demo.html.en).

View file

@ -2,8 +2,7 @@
## C. Eric Cashon
# nim c css_colored_listview.nim
import gintro/[gtk, glib, gobject]
import gintro/gdk except Window # there is a problem with gdk.Window -- we have to investigate!
import gintro/[gtk, gdk, glib, gobject]
const # maybe we should use Nim's enum here?
Id = 0
Program = 1
@ -11,7 +10,7 @@ const # maybe we should use Nim's enum here?
Color2 = 3
Columns = 4
proc bye(w: Window) =
proc bye(w: gtk.Window) =
mainQuit()
echo "Bye..."

View file

@ -0,0 +1,124 @@
# nim c overlayTree1.nim
# https://discourse.gnome.org/t/gtk-how-to-draw-on-top-of-gtktreeview/1783/2
import gintro/[gtk, gdk, glib, gobject, cairo]
import strformat
from strutils import parseInt
const
Id = 0
Program = 1
Color = 2
Color2 = 3
Columns = 4
var
rowG = 0
columnG = 1
proc bye(w: gtk.Window) =
mainQuit()
echo "Bye..."
proc toStringVal(s: string): Value =
let gtype = typeFromName("gchararray")
discard init(result, gtype)
setString(result, s)
proc toUIntVal(i: int): Value =
let gtype = typeFromName("guint")
discard init(result, gtype)
setUint(result, i)
proc toBoolVal(b: bool): Value =
let gtype = typeFromName("gboolean")
discard init(result, gtype)
setBoolean(result, b)
proc selectCell(treeView: TreeView; path: TreePath; column: TreeViewColumn) =
let str = toString(path)
echo fmt"{str} {getTitle(column)}"
rowG = parseInt(str)
queueDraw(treeView)
proc drawRectangle(overlay: Overlay; cr: cairo.Context; treeView: TreeView): bool =
echo fmt"Draw Rectangle {rowG} {columnG}"
let path = newTreePathFromIndices(@[rowG.int32])
echo path.toString
let column = getColumn(treeView, columnG)
var rect: gdk.Rectangle
var x, y: int
treeView.convertBinWindowToWidgetCoords(0, 0, x, y)
cr.save
cr.translate(x.float, y.float)
cr.setLineWidth(2)
cr.setSource(0, 0, 0, 1)
treeView.getCellArea(path, column, rect)
cr.rectangle(rect.x.float + 1, rect.y.float + 1, rect.width.float - 1, rect.height.float - 1)
cr.stroke
cr.restore
return EVENT_PROPAGATE # false
proc main =
gtk.init()
let window = newWindow()
window.setTitle("Overlay Tree")
window.setPosition(WindowPosition.center)
window.setDefaultSize(500, 500)
window.setBorderWidth(20)
window.connect("destroy", bye)
var iter: TreeIter
let h = [typeFromName("guint"), typeFromName("gchararray"), typeFromName("gchararray"),
typeFromName("gchararray")]
let store = newListStore(Columns, cast[pointer](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
let progNames = ["Gedit", "Gimp", "Inkscape", "Firefox", "Calculator", "Devhelp"]
for i, n in progNames:
store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original
store.setValue(iter, Id, toUIntVal(i))
store.setValue(iter, Program, toStringVal(n))
store.setValue(iter, Color, toStringVal("SpringGreen"))
store.setValue(iter, Color2, toStringVal("cyan"))
let tree = newTreeViewWithModel(store)
tree.setHexpand
tree.setVexpand
tree.setProperty("activate-on-single-click", toBoolVal(true))
let selection = tree.getSelection
selection.setMode(SelectionMode.single)
let renderer1 = newCellRendererText()
renderer1.setProperty("editable", toBoolVal(false))
let renderer2 = newCellRendererText()
renderer2.setProperty("editable", toBoolVal(true))
tree.connect("row-activated", selectCell)
## Bind the COLOR column to the "cell-background" property.
let column1 = newTreeViewColumn()
column1.setTitle("ID")
column1.packStart(renderer1, true)
column1.addAttribute(renderer1, "text", Id)
column1.addAttribute(renderer1, "cell-background", Color)
discard tree.appendColumn(column1)
let column2 = newTreeViewColumn()
column2.setTitle("Program")
column2.packStart(renderer2, true)
column2.addAttribute(renderer2, "text", Program)
column2.addAttribute(renderer2, "cell-background", Color2)
discard tree.appendColumn(column2)
## For drawing the outline of the cell.
let overlay = newOverlay()
overlay.setHexpand
overlay.setVexpand
overlay.setAppPaintable
overlay.addOverlay(tree)
overlay.setOverlayPassThrough(tree, true)
overlay.connectAfter("draw", drawRectangle, tree)
let grid = newGrid()
grid.attach(overlay, 0, 0, 1, 1)
window.add(grid)
const cssString =
"""treeview{background-color: rgba(0,255,255,1.0);
font-size:30pt} treeview:selected{background-color:rgba(0,255,255,1.0);
color: rgba(0,0,255,1.0);}"""
let provider = newCssProvider()
discard provider.loadFromData(cssString)
getDefaultScreen().addProviderForScreen(provider, STYLE_PROVIDER_PRIORITY_APPLICATION)
window.showAll
gtk.main()
main() # 123 lines

View file

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

View file

@ -37,31 +37,45 @@ proc findSignal(name, obj: NimNode): string =
n = getType(n)[1]
n = getType(n)[1]
#var ProcID {.compileTime, global.}: int
# from file gisup.nim we have:
# "remove_editable!CellArea!2!(self: CellArea; renderer: CellRenderer; editable: CellEditable | SpinButton | ComboBox | ...
# so for signal remove_editable third parameter of handler proc can be CellEditable OR SpinButton OR ComboBox OR ...
# This is because SpinButton, ComboBox provides the CellEditable interface!
# https://mail.gnome.org/archives/gtk-list/2017-July/msg00018.html
# This interface parameter is in most cases the second parameter of handler proc, but can be the third also.
macro mconnect(widget: gobject.Object; signal: string; p: typed; arg: typed; ignoreArg: bool): untyped =
macro mconnect(widget: gobject.Object; signal: string; p: typed; arg: typed; ignoreArg: bool; sf: static[gobject.ConnectFlags]): untyped =
#echo p.symbol.getImpl().toStrLit
#echo p.symbol.getImpl().params.toStrLit
#echo p.symbol.getImpl().params[2][1].toStrLit
var ProcID {.compileTime, global.}: int
var # position and actual type of interface provider, if any
ipos = -1
ipro: string #= nil
sfstr = $sf
#if sfstr == "{after}":
# sfstr = "{gobject.ConnectFlag.after}"
sfstr = sfstr.replace("after", "gobject.ConnectFlag.after")
sfstr = sfstr.replace("swapped", "gobject.ConnectFlag.swapped")
inc(ProcID)
let wt = getType(widget)
let at = getTypeInst(arg)
# echo getType(widget)[0].strVal # ref
# echo getType(widget)[1].strVal # Window:ObjectType
# echo getTypeInst(widget).strVal # Window
#echo at.repr
#echo at.typeKind == ntyRef
let wts = ($(wt[1].toStrLit)).replace(":ObjectType")
let ats = $at.toStrLit
# let wts = getTypeInst(widget).strVal
let wts = widget.getTypeInst.owner.strVal & '.' & widget.getTypeInst.strVal # new in v0.5.5
let ats = at.toStrLit.strVal
#assert ats == $(at.toStrLit)
#assert ats == at.toStrLit.strVal
let signalName = ($signal).replace("-", "_") # maybe we should just use plain proc names
let procNameCdecl = "connect_for_signal_cdecl_" & signalName & $ProcID
let procName = "connect_for_signal_" & signalName & $ProcID
@ -142,7 +156,6 @@ proc $1$2 {.cdecl.} =
r1s.add(" new " & names[i] & "1" & "\n")
r1s.add(" " & names[i] & "1.impl = " & names[i] & "\n")
if not ignoreArg.boolVal:
# resu.add(", cast[$5](data)")
if getTypeInst(arg).typeKind == ntyRef:
resu.add(", cast[$5](data)")
else:
@ -190,16 +203,16 @@ $1($7, $8, $9)
if ignoreArg.boolVal:
"""
proc $1(self: $2; p: proc $3): culong {.discardable.} =
sc$4(self, $5, nil)
sc$4(self, $5, nil, $8)
$1($6, $7)
""" % [$procName, wts, ahl, signalName, $procNameCdecl, $(widget.toStrLit), $p]
""" % [$procName, wts, ahl, signalName, $procNameCdecl, $(widget.toStrLit), $p, sfstr]
elif getTypeInst(arg).typeKind == ntyRef:
"""
proc $1(self: $2; p: proc $3; a: $4): culong {.discardable.} =
GC_ref(a)
sc$5(self, $6, cast[pointer](a))
sc$5(self, $6, cast[pointer](a), $10)
$1($7, $8, $9)
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit)]
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit), sfstr]
else:
"""
proc $1(self: $2; p: proc $3; a: $4): culong {.discardable.} =
@ -208,20 +221,30 @@ proc $1(self: $2; p: proc $3; a: $4): culong {.discardable.} =
deepCopy(ar[], a)
GC_ref(ar)
# sc$5(self, $6, cast[pointer](ar[]))
sc$5(self, $6, cast[pointer](ar))
sc$5(self, $6, cast[pointer](ar), $10)
$1($7, $8, $9)
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit)]
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit), sfstr]
# echo r2s
result.add(parseStmt(r2s))
template connect*(widget: gobject.Object; signal: string; p: typed; arg: typed): untyped =
mconnect(widget, signal, p, arg, false)
mconnect(widget, signal, p, arg, false, {})
template connect*(widget: gobject.Object; signal: string; p: typed): untyped =
mconnect(widget, signal, p, "", true)
mconnect(widget, signal, p, "", true, {})
template connectAfter*(widget: gobject.Object; signal: string; p: typed; arg: typed): untyped =
mconnect(widget, signal, p, arg, false, {gobject.ConnectFlag.after})
template connectAfter*(widget: gobject.Object; signal: string; p: typed): untyped =
mconnect(widget, signal, p, "", true, {gobject.ConnectFlag.after})
template connectSwapped*(widget: gobject.Object; signal: string; p: typed; arg: typed): untyped =
mconnect(widget, signal, p, arg, false, {gobject.ConnectFlag.swapped})
template connectSwapped*(widget: gobject.Object; signal: string; p: typed): untyped =
mconnect(widget, signal, p, "", true, {gobject.ConnectFlag.swapped})
#var TimeoutID {.compileTime, global.}: int
#
macro timeoutAdd*(interval: Natural; p: untyped; arg: typed): untyped =
var TimeoutID {.compileTime, global.}: int
inc(TimeoutID)
@ -251,8 +274,6 @@ $1($5)
""" % [$procName, ats, $interval.toStrLit, $procNameCdecl, $arg]
result = parseStmt(r1s & r2s)
#var IdleID {.compileTime, global.}: int
#
macro idleAdd*(p: untyped; arg: typed): untyped =
var IdleID {.compileTime, global.}: int
inc(IdleID)
@ -281,4 +302,3 @@ proc $1(a: $2): culong {.discardable.} =
$1($4)
""" % [$procName, ats, $procNameCdecl, $arg]
result = parseStmt(r1s & r2s)

View file

@ -1,5 +1,5 @@
# High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language
# v 0.5.3 2019-AUG-13
# v 0.5.5 2019-SEP-21
# (c) S. Salewski 2018
# https://wiki.gnome.org/Projects/GObjectIntrospection
@ -1174,12 +1174,14 @@ template writeSignal() =
xxx = xxx & " | " & provider.join(" | ")
signalbuffer.write("proc " & mangleName("sc_" & $gBaseInfoGetName(signalInfo)) & EM & "(self: " & xxx & "; ")
if gCallableInfoGetNArgs(signalInfo) > 0 or gTypeInfoGetTag(zzzu) != GITypeTag.VOID:
signalbuffer.writeLine(" p: proc (self: ptr " & yyy & "00; " & h & " {.cdecl.}, xdata: pointer = nil): culong =")
#signalbuffer.writeLine(" p: proc (self: ptr " & yyy & "00; " & h & " {.cdecl.}, xdata: pointer = nil, cf: gobject.ConnectFlags = {}): culong =")
signalbuffer.writeLine(" p: proc (self: ptr " & yyy & "00; " & h & " {.cdecl.}, xdata: pointer, cf: gobject.ConnectFlags): culong =")
else:
signalbuffer.writeLine(" p: proc (self: ptr gobject.Object00; " & h & " {.cdecl.}, xdata: pointer = nil): culong =")
#signalbuffer.writeLine(" p: proc (self: ptr gobject.Object00; " & h & " {.cdecl.}, xdata: pointer = nil, cf: gobject.ConnectFlags = {}): culong =")
signalbuffer.writeLine(" p: proc (self: ptr gobject.Object00; " & h & " {.cdecl.}, xdata: pointer, cf: gobject.ConnectFlags): culong =")
signalbuffer.write(" g_signal_connect_data(self.impl, \"")
signalbuffer.write($gBaseInfoGetName(signalInfo))
signalbuffer.writeLine("\", cast[GCallback](p), xdata, nil, cast[ConnectFlags](0))")
signalbuffer.writeLine("\", cast[GCallback](p), xdata, nil, cf)")
proc writeInterface(info: GIInterfaceInfo) =
if not suppressType: