added support for CellDataFunction
This commit is contained in:
parent
3091b17251
commit
f50ff67b63
3 changed files with 296 additions and 0 deletions
94
README.adoc
94
README.adoc
|
|
@ -1789,6 +1789,100 @@ proc main =
|
||||||
main() # 123 lines
|
main() # 123 lines
|
||||||
----
|
----
|
||||||
|
|
||||||
|
== A Listview example using a CellDataFunction
|
||||||
|
|
||||||
|
This example shows how a CellDataFunction can be used to
|
||||||
|
customize cells of a Tree- or Listview.
|
||||||
|
|
||||||
|
[[celldatafunction.nim]]
|
||||||
|
[source,nim]
|
||||||
|
.celldatafunction.nim
|
||||||
|
----
|
||||||
|
# This example shows how to apply a CellDataFunc to a GtkTreeView
|
||||||
|
# C example code was provided by A.Krause in chapter 8 of his book
|
||||||
|
import gintro/[gtk, gobject, glib]
|
||||||
|
|
||||||
|
const
|
||||||
|
Color = 0
|
||||||
|
Columns = 1
|
||||||
|
clr = ["00", "33", "66", "99", "CC", "FF"]
|
||||||
|
|
||||||
|
proc bye(w: Window) =
|
||||||
|
mainQuit()
|
||||||
|
echo "Bye..."
|
||||||
|
|
||||||
|
proc toStringVal(s: string): Value =
|
||||||
|
let gtype = gStringGetType() # typeFromName("gchararray")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setString(result, s)
|
||||||
|
|
||||||
|
proc toBoolVal(b: bool): Value =
|
||||||
|
let gtype = gBooleanGetType() # typeFromName("gboolean")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setBoolean(result, b)
|
||||||
|
|
||||||
|
# our Nim function
|
||||||
|
proc cellDataFuncN(column: TreeViewColumn; renderer: CellRenderer;
|
||||||
|
model: TreeModel; iter: TreeIter, data: TreeViewColumn) =
|
||||||
|
## Get the color string stored by the column and make it the foreground color.
|
||||||
|
# for testing that optional args work, we pass a TreeViewColumn and echo its title
|
||||||
|
echo data.title
|
||||||
|
var val: Value
|
||||||
|
model.getValue(iter, Color, val)
|
||||||
|
let text = val.getString
|
||||||
|
val.unset # is this necessary?
|
||||||
|
setProperty(renderer, "foreground", toStringVal("#FFFFFF"))
|
||||||
|
setProperty(renderer, "foreground-set", toBoolVal(true))
|
||||||
|
setProperty(renderer, "background", toStringVal(text))
|
||||||
|
setProperty(renderer, "background-set", toBoolVal(true))
|
||||||
|
setProperty(renderer, "text", toStringVal(text))
|
||||||
|
|
||||||
|
## Add three columns to the GtkTreeView. All three of the columns will be
|
||||||
|
## displayed as text, although one is a gboolean value and another is
|
||||||
|
## an integer.
|
||||||
|
proc setupTreeView(treeview: TreeView) =
|
||||||
|
let renderer = gtk.newCellRendererText()
|
||||||
|
let column = newTreeViewColumn()
|
||||||
|
column.title = "Standard Colors"
|
||||||
|
column.packStart(renderer, expand = true)
|
||||||
|
column.addAttribute(renderer, "text", Color)
|
||||||
|
discard treeview.appendColumn(column)
|
||||||
|
column.setCellDataFunc(renderer, cellDataFuncN, column)
|
||||||
|
column.setCellDataFunc(renderer) # test unsetting!
|
||||||
|
column.setCellDataFunc(renderer, nil)
|
||||||
|
column.unsetCellDataFunc(renderer)
|
||||||
|
column.setCellDataFunc(renderer, cellDataFuncN, column)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var iter: TreeIter
|
||||||
|
gtk.init()
|
||||||
|
let window = newWindow()
|
||||||
|
window.setTitle("Color List")
|
||||||
|
window.setBorderWidth(10)
|
||||||
|
window.setSizeRequest(250, 175)
|
||||||
|
window.connect("destroy", bye)
|
||||||
|
let treeview = newTreeView()
|
||||||
|
setupTreeView(treeview)
|
||||||
|
let gtype = typeFromName("gchararray")
|
||||||
|
let store = newListStore(Columns, cast[pointer](unsafeaddr gtype)) # ugly cast
|
||||||
|
## Add all of the products to the GtkListStore.
|
||||||
|
for i in 0 ..< 6:
|
||||||
|
for j in 0 ..< 6:
|
||||||
|
for k in 0 ..< 6:
|
||||||
|
let color: string = "#" & clr[i] & clr[j] & clr[k]
|
||||||
|
store.append(iter)
|
||||||
|
store.setValue(iter, Color, toStringVal(color))
|
||||||
|
treeView.setModel(store)
|
||||||
|
let scrolledWin = newScrolledWindow(nil, nil)
|
||||||
|
scrolledWin.setPolicy(PolicyType.automatic, PolicyType.automatic)
|
||||||
|
scrolledWin.add(treeview)
|
||||||
|
window.add(scrolledWin)
|
||||||
|
window.showAll
|
||||||
|
gtk.main()
|
||||||
|
|
||||||
|
main()
|
||||||
|
----
|
||||||
|
|
||||||
== A more advanced example for cairo drawing with zooming, panning, scrolling
|
== 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).
|
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).
|
||||||
|
|
|
||||||
83
examples/gtk3/celldatafunction.nim
Normal file
83
examples/gtk3/celldatafunction.nim
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# This example shows how to apply a CellDataFunc to a GtkTreeView
|
||||||
|
# C example code was provided by A.Krause in chapter 8 of his book
|
||||||
|
import gintro/[gtk, gobject, glib]
|
||||||
|
|
||||||
|
const
|
||||||
|
Color = 0
|
||||||
|
Columns = 1
|
||||||
|
clr = ["00", "33", "66", "99", "CC", "FF"]
|
||||||
|
|
||||||
|
proc bye(w: Window) =
|
||||||
|
mainQuit()
|
||||||
|
echo "Bye..."
|
||||||
|
|
||||||
|
proc toStringVal(s: string): Value =
|
||||||
|
let gtype = gStringGetType() # typeFromName("gchararray")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setString(result, s)
|
||||||
|
|
||||||
|
proc toBoolVal(b: bool): Value =
|
||||||
|
let gtype = gBooleanGetType() # typeFromName("gboolean")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setBoolean(result, b)
|
||||||
|
|
||||||
|
# our Nim function
|
||||||
|
proc cellDataFuncN(column: TreeViewColumn; renderer: CellRenderer;
|
||||||
|
model: TreeModel; iter: TreeIter, data: TreeViewColumn) =
|
||||||
|
## Get the color string stored by the column and make it the foreground color.
|
||||||
|
# for testing that optional args work, we pass a TreeViewColumn and echo its title
|
||||||
|
echo data.title
|
||||||
|
var val: Value
|
||||||
|
model.getValue(iter, Color, val)
|
||||||
|
let text = val.getString
|
||||||
|
val.unset # is this necessary?
|
||||||
|
setProperty(renderer, "foreground", toStringVal("#FFFFFF"))
|
||||||
|
setProperty(renderer, "foreground-set", toBoolVal(true))
|
||||||
|
setProperty(renderer, "background", toStringVal(text))
|
||||||
|
setProperty(renderer, "background-set", toBoolVal(true))
|
||||||
|
setProperty(renderer, "text", toStringVal(text))
|
||||||
|
|
||||||
|
## Add three columns to the GtkTreeView. All three of the columns will be
|
||||||
|
## displayed as text, although one is a gboolean value and another is
|
||||||
|
## an integer.
|
||||||
|
proc setupTreeView(treeview: TreeView) =
|
||||||
|
let renderer = gtk.newCellRendererText()
|
||||||
|
let column = newTreeViewColumn()
|
||||||
|
column.title = "Standard Colors"
|
||||||
|
column.packStart(renderer, expand = true)
|
||||||
|
column.addAttribute(renderer, "text", Color)
|
||||||
|
discard treeview.appendColumn(column)
|
||||||
|
column.setCellDataFunc(renderer, cellDataFuncN, column)
|
||||||
|
column.setCellDataFunc(renderer) # test unsetting!
|
||||||
|
column.setCellDataFunc(renderer, nil)
|
||||||
|
column.unsetCellDataFunc(renderer)
|
||||||
|
column.setCellDataFunc(renderer, cellDataFuncN, column)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var iter: TreeIter
|
||||||
|
gtk.init()
|
||||||
|
let window = newWindow()
|
||||||
|
window.setTitle("Color List")
|
||||||
|
window.setBorderWidth(10)
|
||||||
|
window.setSizeRequest(250, 175)
|
||||||
|
window.connect("destroy", bye)
|
||||||
|
let treeview = newTreeView()
|
||||||
|
setupTreeView(treeview)
|
||||||
|
let gtype = typeFromName("gchararray")
|
||||||
|
let store = newListStore(Columns, cast[pointer](unsafeaddr gtype)) # ugly cast
|
||||||
|
## Add all of the products to the GtkListStore.
|
||||||
|
for i in 0 ..< 6:
|
||||||
|
for j in 0 ..< 6:
|
||||||
|
for k in 0 ..< 6:
|
||||||
|
let color: string = "#" & clr[i] & clr[j] & clr[k]
|
||||||
|
store.append(iter)
|
||||||
|
store.setValue(iter, Color, toStringVal(color))
|
||||||
|
treeView.setModel(store)
|
||||||
|
let scrolledWin = newScrolledWindow(nil, nil)
|
||||||
|
scrolledWin.setPolicy(PolicyType.automatic, PolicyType.automatic)
|
||||||
|
scrolledWin.add(treeview)
|
||||||
|
window.add(scrolledWin)
|
||||||
|
window.showAll
|
||||||
|
gtk.main()
|
||||||
|
|
||||||
|
main()
|
||||||
119
gintro/gimpl.nim
119
gintro/gimpl.nim
|
|
@ -302,3 +302,122 @@ proc $1(a: $2): culong {.discardable.} =
|
||||||
$1($4)
|
$1($4)
|
||||||
""" % [$procName, ats, $procNameCdecl, $arg]
|
""" % [$procName, ats, $procNameCdecl, $arg]
|
||||||
result = parseStmt(r1s & r2s)
|
result = parseStmt(r1s & r2s)
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
|
||||||
|
### CellDataFunc,
|
||||||
|
# see https://developer.gnome.org/gtk3/stable/GtkTreeViewColumn.html#gtk-tree-view-column-set-cell-data-func
|
||||||
|
|
||||||
|
# the low level GTK interface:
|
||||||
|
# proc cellDataFunc(column00: ptr TreeViewColumn00; renderer00: ptr CellRenderer00;
|
||||||
|
# model00: ptr TreeModel00; iter: TreeIter; data: pointer) {.cdecl.}
|
||||||
|
|
||||||
|
# unset -- the macro can do this already
|
||||||
|
proc unsetCellDataFunc*(column: TreeViewColumn; renderer: CellRenderer) =
|
||||||
|
setCellDataFunc(column, renderer, nil, nil, nil)
|
||||||
|
|
||||||
|
# set a function with this parameter list and one more optional argument
|
||||||
|
# proc cellDataFunc(column: TreeViewColumn; renderer: CellRenderer; model: TreeModel; iter: TreeIter)
|
||||||
|
# if there is no optional arg, then we make it a pointer with nil value.
|
||||||
|
# if arg is a ref, then we apply GC_ref() and pass it
|
||||||
|
# if arg is a ptr, then we pass it. (but we never free that object)
|
||||||
|
# if arg is a value type, then we replace it with a copy of a ref type to guaranty lifetime
|
||||||
|
macro setCellDataFunc*(column: TreeViewColumn; renderer: CellRenderer; fn: untyped = nil; arg: typed = nil): untyped =
|
||||||
|
|
||||||
|
if fn == nil: # unset
|
||||||
|
result = parseStmt("setCellDataFunc($1, $2, nil, nil, nil)" % [$column, $renderer])
|
||||||
|
return
|
||||||
|
|
||||||
|
var CdfID {.compileTime, global.}: int # unique id for our generated cell data functions
|
||||||
|
inc(CdfID)
|
||||||
|
var ats: string # the type of optional argument as string (arg type string)
|
||||||
|
var argStr: string # the value of optional argument as string
|
||||||
|
var atsfix: string # we have to prefix with "ref " if arg is avalue type
|
||||||
|
if arg == nil: # no optional argument, so fake a nil pointer
|
||||||
|
argStr = "nil"
|
||||||
|
ats = "pointer"
|
||||||
|
else:
|
||||||
|
argStr = $(arg.toStrLit)
|
||||||
|
ats = $getTypeInst(arg).toStrLit
|
||||||
|
|
||||||
|
if getTypeInst(arg).typeKind != ntyRef and getTypeInst(arg).typeKind != ntyPtr:
|
||||||
|
atsFix = "ref "
|
||||||
|
|
||||||
|
let procName = "celldatafunc_" & $CdfID # our init code
|
||||||
|
let procNameCdecl = "celldatafunc_cdecl_" & $CdfID # the proc that is called from GTK
|
||||||
|
var procNameDestroy: string # the proc that GTK calls when we unset the cellDataFunc
|
||||||
|
# first we generate the destroy proc
|
||||||
|
var r0s: string
|
||||||
|
|
||||||
|
# GTK should unref the optional argument later, so we generate a destroy proc which GTK can call
|
||||||
|
if arg != nil and getTypeInst(arg).typeKind != ntyPtr:
|
||||||
|
procNameDestroy = "celldatafunc_destroy" & $CdfID
|
||||||
|
r0s ="""
|
||||||
|
proc $1(p: pointer) {.cdecl.} =
|
||||||
|
# echo "GC_Unref(arg)"
|
||||||
|
GC_Unref(cast[$2](p))
|
||||||
|
""" % [$procNameDestroy, atsFix & ats]
|
||||||
|
else:
|
||||||
|
procNameDestroy = "nil"
|
||||||
|
r0s = ""
|
||||||
|
|
||||||
|
# now we generate the proc which GTK will call for each cell of the treeview. g_object_get_qdata() is
|
||||||
|
# the GTK function which retrieves the Nim proxy objects from the plain GTK data objects.
|
||||||
|
var r1s = """
|
||||||
|
proc $1(column00: ptr TreeViewColumn00; renderer00: ptr CellRenderer00;
|
||||||
|
model00: ptr TreeModel00; iter: TreeIter; pdata: pointer) {.cdecl.} =
|
||||||
|
let column = cast[TreeViewColumn](g_object_get_qdata(column00, Quark))
|
||||||
|
let renderer = cast[CellRenderer](g_object_get_qdata(renderer00, Quark))
|
||||||
|
let model = cast[TreeModel](g_object_get_qdata(model00, Quark))
|
||||||
|
""" % [$procNameCdecl]
|
||||||
|
if arg == nil:
|
||||||
|
r1s.add """
|
||||||
|
$1(column, renderer, model, iter) # call the user provided Nim proc
|
||||||
|
""" % [$fn]
|
||||||
|
elif getTypeInst(arg).typeKind == ntyRef or getTypeInst(arg).typeKind == ntyPtr:
|
||||||
|
r1s.add """
|
||||||
|
let a = cast[$2](pdata)
|
||||||
|
$1(column, renderer, model, iter, a) # call the user provided Nim proc
|
||||||
|
""" % [$fn, ats]
|
||||||
|
else: # arg was a value type, but we pass a copy of ref type
|
||||||
|
r1s.add """
|
||||||
|
let a = cast[ref $2](pdata)[]
|
||||||
|
$1(column, renderer, model, iter, a) # call the user provided Nim proc
|
||||||
|
""" % [$fn, ats]
|
||||||
|
|
||||||
|
# this is basically only a code block which is called for initialization, mostly
|
||||||
|
# making a deep copy of optional argument when argumant is not a ref
|
||||||
|
var r2s: string
|
||||||
|
if arg == nil:
|
||||||
|
r2s ="""
|
||||||
|
proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
|
||||||
|
setCellDataFunc($3, $4, $5, nil, nil)
|
||||||
|
$1(column, renderer, $6)
|
||||||
|
""" % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, $procNameDestroy]
|
||||||
|
elif getTypeInst(arg).typeKind == ntyPtr:
|
||||||
|
r2s ="""
|
||||||
|
proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
|
||||||
|
setCellDataFunc($3, $4, $5, cast[pointer](pdata), $7)
|
||||||
|
$1(column, renderer, $6)
|
||||||
|
""" % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, $procNameDestroy]
|
||||||
|
elif getTypeInst(arg).typeKind == ntyRef:
|
||||||
|
r2s ="""
|
||||||
|
proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
|
||||||
|
if not pdata.isNil: # or pdata is pointer):
|
||||||
|
GC_ref(pdata)
|
||||||
|
setCellDataFunc($3, $4, $5, cast[pointer](pdata), $7)
|
||||||
|
$1(column, renderer, $6)
|
||||||
|
""" % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, $procNameDestroy]
|
||||||
|
else: # arg is value object
|
||||||
|
r2s ="""
|
||||||
|
proc $1(column: TreeViewColumn; renderer: CellRenderer; pdata: $2) =
|
||||||
|
var ar: ref $2
|
||||||
|
new(ar)
|
||||||
|
deepCopy(ar[], pdata)
|
||||||
|
GC_ref(ar)
|
||||||
|
setCellDataFunc($3, $4, $5, cast[pointer](ar), $7)
|
||||||
|
$1(column, renderer, $6)
|
||||||
|
""" % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, $procNameDestroy]
|
||||||
|
|
||||||
|
result = parseStmt(r0s & r1s & r2s)
|
||||||
|
# 423 lines
|
||||||
Loading…
Add table
Add a link
Reference in a new issue