fixes for builder get() procs and cairo

This commit is contained in:
Stefan Salewski 2019-04-27 09:02:37 +02:00
commit 4c93a26d04
7 changed files with 3212 additions and 267 deletions

View file

@ -843,7 +843,10 @@ main()
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).
With the latest fix to Nim GTK3 bindings it seems to work fine, but some rewriting for optimized performance
may be needed, I have to investigate that. Cairo surface is currently manually freed, because GC may have a too large delay.
Later we may use Destructors for that.
You can resize the window and zoom in with the mouse wheel. When zoomed in scroll bars appear. You
can hold the middle mouse button pressed while moving the mouse for panning, and you can press left mouse button
and move the mouse to first draw a selection rectangle and zoom into it when releasing the mouse button.
[[drawingarea.nim]]
[source,nim]
@ -851,46 +854,26 @@ Later we may use Destructors for that.
----
# Plain demo for zooming, panning, scrolling with GTK DrawingArea
# (c) S. Salewski, 21-DEC-2010 (initial Ruby version)
# Nim version April 2019 -- well first draft :-)
# Nim version April 2019
# License MIT
# This version of the demo program uses a separate proc paint()
# which allocates a custom surface for buffered drawing.
# That may be not really necessary, for simple drawings doing all
# the drawing in the "draw" call back is easier and faster. But for
# more complicated drawing operations, for example when using a
# background grid, which is a bit larger than the window size and
# is reused when scrolling, a custom surface may be useful.
# And finally that custom surface and custom cairo context is an
# important test for the language bindings.
import gintro/[gtk, gdk, glib, gobject, gio, cairo]
const
DataX = 150.0
DataY = 250.0
DataWidth = 200.0
DataHeight = 120.0
# The following two functions are intended to be user defined -- we have still
# to investigate how to do that best.
# bounding box of user data -- x, y, w, h -- top left corner, width, height
proc getWorldExtends(): (float, float, float, float) =
(DataX, DataY, DataWidth, DataHeight) # current extents of our user world
# draw to cairo context
proc drawWorld(cr: cairo.Context) =
cr.setSource(1, 1, 1)
cr.paint
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
cr.rectangle(DataX, DataY, DataWidth, DataHeight)
var i = 10.0
while true:
if min(DataWidth - 2.0 * i, DataHeight - 2.0 * i) <= 0:
break
cr.rectangle(DataX + i, DataY + i, DataWidth - 2.0 * i,
DataHeight - 2.0 * i)
i += 10.0
cr.stroke
const
ZOOM_FACTOR_MOUSE_WHEEL = 1.1
ZOOM_FACTOR_SELECT_MAX = 10 # ignore zooming in tiny selection
ZOOM_NEAR_MOUSEPOINTER = true # mouse wheel zooming -- to mousepointer or center
SELECT_RECT_COL = [0.0, 0, 1, 0.5] # blue with transparency
ZoomFactorMouseWheel = 1.1
ZoomFactorSelectMax = 10 # ignore zooming in tiny selection
ZoomNearMousepointer = true # mouse wheel zooming -- to mouse-pointer or center
SelectRectCol = [0.0, 0, 1, 0.5] # blue with transparency
discard """
Zooming, scrolling, panning...
@ -919,7 +902,7 @@ and translate the origin of our data to (0, 0)
Zooming: Mouse wheel or selecting a rectangle with left mouse button pressed
Scrolling: Scrollbars
Panning: Moving mouse while middle mouse button pressed
Panning: Moving mouse while middle mouse button pressed
"""
# drawing area and scroll bars in 2x2 table (PDA == Plain Drawing Area)
@ -931,6 +914,12 @@ type
proc newPosAdj: PosAdj =
initAdjustment(result, 0, 0, 1, 1, 10, 1)
type
PDA_Data* = object
draw*: proc (cr: Context)
extents*: proc (): tuple[x, y, w, h: float]
windowSize*: tuple[w, h: int]
type
PDA = ref object of Grid
zoomNearMousepointer: bool
@ -953,6 +942,8 @@ type
lastMousePosY: float
zoomRectX1: float
zoomRectY1: float
drawWorld: proc (cr: Context)
extents: proc (): tuple[x, y, w, h: float]
proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
if this.surf.isNil: return
@ -967,7 +958,8 @@ proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
cr.stroke
return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
return SignalEventStopPropagation
#return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
# clamp to correct values, 0 <= value <= (adj.upper - adj.pageSize), block calling onAdjustmentEvent()
proc updateVal(adj: PosAdj; d: float) =
@ -977,35 +969,30 @@ proc updateVal(adj: PosAdj; d: float) =
proc updateAdjustments(this: PDA; dx, dy: float) =
this.hadjustment.setUpper(this.darea.allocatedWidth.float * this.userZoom)
this.vadjustment.setUpper(this.darea.allocatedHeight.float *
this.userZoom)
this.vadjustment.setUpper(this.darea.allocatedHeight.float * this.userZoom)
this.hadjustment.setPageSize(this.darea.allocatedWidth.float)
this.vadjustment.setPageSize(this.darea.allocatedHeight.float)
updateVal(this.hadjustment, dx)
updateVal(this.vadjustment, dy)
# Maybe we should fix this for latest GTK3, see
# https://developer.gnome.org/gdk3/stable/gdk3-Cairo-Interaction.html
# so use gdk_drawing_context_get_cairo_context ()
# or maybe even better do all drwing from within "draw" callback?
# maybe for optimization we should only allocate a new surface when its size has changed.
proc paint(this: PDA) =
if this.surf != nil:
cairosurfaceDestroy(this.surf.impl) # we may have to fix that later for automatically destroy
destroy(this.surf) # manually destroy surface -- GC would do it for us, but GC is slow...
this.surf = this.darea.window.createSimilarSurface(Content.color,
this.darea.allocatedWidth, this.darea.allocatedHeight)
let cr = newContext(this.surf)
cr.translate(this.hadjustment.upper * 0.5 - this.hadjustment.value, # our origin is the center
this.vadjustment.upper * 0.5 - this.vadjustment.value)
cr.scale(this.fullScale * this.userZoom, this.fullScale * this.userZoom)
cr.translate(-this.dataX - this.dataWidth * 0.5,
-this.dataY - this.dataHeight * 0.5)
drawWorld(cr) # call the user provided drawing fonction
cr.translate(-this.dataX - this.dataWidth * 0.5, -this.dataY - this.dataHeight * 0.5)
this.drawWorld(cr) # call the user provided drawing function
destroy(cr) # we can also manually destroy the context here, but GC would do it for us
proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure;
this: PDA): bool =
proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure; this: PDA): bool =
this.updateAdjustments(0, 0)
(this.dataX, this.dataY, this.dataWidth,
this.dataHeight) = getWorldExtends() # query user defined size
this.dataHeight) = this.extents() # query user defined size
this.fullScale = min(this.darea.allocatedWidth.float / this.dataWidth,
this.darea.allocatedHeight.float / this.dataHeight)
this.paint
@ -1013,8 +1000,7 @@ proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure;
proc updateAdjustmentsAndPaint(this: PDA; dx, dy: float) =
this.updateAdjustments(dx, dy)
this.paint
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth,
this.darea.allocatedHeight)
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth, this.darea.allocatedHeight)
# event coordinates to user space
proc getUserCoordinates(this: PDA; eventX, eventY: float): (float, float) =
@ -1026,16 +1012,13 @@ proc getUserCoordinates(this: PDA; eventX, eventY: float): (float, float) =
proc onMotion(darea: DrawingArea; event: EventMotion; this: PDA): bool =
let state = getState(event)
let (x, y) = event.getCoords
#(x, y) = this.getUserCoordinates(x, y)
if (state.contains(button1)): # selecting
this.selecting = true
this.zoomRectX1 = x
this.zoomRectY1 = y
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth,
this.darea.allocatedHeight)
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth, this.darea.allocatedHeight)
elif button2 in state: # panning
this.updateAdjustmentsAndPaint(this.lastMousePosX - x,
this.lastMousePosY - y)
this.updateAdjustmentsAndPaint(this.lastMousePosX - x, this.lastMousePosY - y)
this.lastMousePosX = x
this.lastMousePosY = y
#event.request # request more motion events ?
@ -1048,25 +1031,20 @@ proc scrollEvent(darea: DrawingArea; event: EventScroll; this: PDA): bool =
var d = getScrollDirection(event)
var (x, y) = event.getCoords
if d == ScrollDirection.up:
this.userZoom *= ZOOM_FACTOR_MOUSE_WHEEL
this.userZoom *= ZoomFactorMouseWheel
elif d == ScrollDirection.down:
this.userZoom /= ZOOM_FACTOR_MOUSE_WHEEL
this.userZoom /= ZoomFactorMouseWheel
if (this.userZoom < 1):
this.userZoom = 1
if this.zoomNearMousepointer:
this.updateAdjustmentsAndPaint((this.hadjustment.value + x) * (
this.userZoom / z0 - 1),
(this.vadjustment.value + y) * (
this.userZoom / z0 - 1))
this.updateAdjustmentsAndPaint((this.hadjustment.value + x) * (this.userZoom / z0 - 1),
(this.vadjustment.value + y) * (this.userZoom / z0 - 1))
else: # zoom to center
this.updateAdjustmentsAndPaint((this.hadjustment.value +
this.darea.allocatedWidth.float * 0.5) * (this.userZoom / z0 - 1),
(this.vadjustment.value +
this.darea.allocatedHeight.float * 0.5) * (
this.userZoom / z0 - 1))
(this.vadjustment.value + this.darea.allocatedHeight.float * 0.5) * (this.userZoom / z0 - 1))
proc buttonPressEvent(darea: DrawingArea; event: EventButton;
this: PDA): bool =
proc buttonPressEvent(darea: DrawingArea; event: EventButton; this: PDA): bool =
var (x, y) = event.getCoords
this.lastMousePosX = x
this.lastMousePosY = y
@ -1077,8 +1055,7 @@ proc buttonPressEvent(darea: DrawingArea; event: EventButton;
echo "User coordinates: ", x, ' ', y, "\n" # to verify getUserCoordinates()
# zoom into selected rectangle and center it
proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
this: PDA): bool =
proc buttonReleaseEvent(darea: DrawingArea; event: EventButton; this: PDA): bool =
var (x, y) = event.getCoords
var b = getButton(event)
if b == 1:
@ -1086,7 +1063,7 @@ proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
let z1 = min(this.darea.allocatedWidth.float / (
this.lastButtonDownPosX - x).abs, this.darea.allocatedHeight.float / (
this.lastButtonDownPosY - y).abs)
if z1 < ZOOM_FACTOR_SELECT_MAX: # else selection rectangle will persist, we may output a message...
if z1 < ZoomFactorSelectMax: # else selection rectangle will persist, we may output a message...
this.userZoom *= z1
this.updateAdjustmentsAndPaint(
((2 * this.hadjustment.value + x + this.lastButtonDownPosX) * z1 -
@ -1096,24 +1073,19 @@ proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
proc onAdjustmentEvent(this: PosAdj; pda: PDA) =
pda.paint
pda.darea.queueDrawArea(0, 0, pda.darea.allocatedWidth,
pda.darea.allocatedHeight)
pda.darea.queueDrawArea(0, 0, pda.darea.allocatedWidth, pda.darea.allocatedHeight)
proc newPDA: PDA =
initGrid(result)
result.zoomNearMousepointer = ZOOM_NEAR_MOUSEPOINTER # mouse wheel zooming
result.selecting = false
result.zoomNearMousepointer = ZoomNearMousepointer # mouse wheel zooming
result.userZoom = 1.0
result.surf = nil
result.darea = newDrawingArea()
result.darea.setHExpand#(true)
result.darea.setVExpand#(true)
result.darea.setHExpand
result.darea.setVExpand
result.darea.connect("draw", drawingAreaDrawCb, result)
result.darea.connect("configure-event", dareaConfigureCallback, result)
result.darea.addEvents({EventFlag.buttonPress, EventFlag.buttonRelease,
EventFlag.scroll,
EventFlag.button1Motion, EventFlag.button2Motion,
EventFlag.pointerMotionHint})
EventFlag.scroll, button1Motion, button2Motion, pointerMotionHint})
result.darea.connect("motion-notify-event", onMotion, result)
result.darea.connect("scroll_event", scrollEvent, result)
result.darea.connect("button_press_event", buttonPressEvent, result)
@ -1126,25 +1098,98 @@ proc newPDA: PDA =
onAdjustmentEvent, result)
result.hscrollbar = newScrollbar(Orientation.horizontal, result.hadjustment)
result.vscrollbar = newScrollbar(Orientation.vertical, result.vadjustment)
result.hscrollbar.setHExpand#(true)
result.vscrollbar.setVExpand#(true)
result.hscrollbar.setHExpand
result.vscrollbar.setVExpand
result.attach(result.darea, 0, 0, 1, 1)
result.attach(result.vscrollbar, 1, 0, 1, 1)
result.attach(result.hscrollbar, 0, 1, 1, 1)
proc appActivate(app: Application) =
proc appActivate(app: Application; initData: ref PDA_Data) =
let window = newApplicationWindow(app)
window.title = "Drawing example"
window.defaultSize = (400, 400)
window.defaultSize = (initData.windowSize[0], initData.windowSize[1])
let pda = newPDA()
pda.drawWorld = initData.draw
pda.extents = initData.extents
window.add(pda)
showAll(window)
proc main =
proc newDisplay*(initData: PDA_Data) =
let app = newApplication("org.gtk.example")
connect(app, "activate", appActivate)
var d = new (ref PDA_Data)
d[] = initData
connect(app, "activate", appActivate, d)
discard run(app)
when isMainModule:
const # arbitrary locations for our data
DataX = 150.0
DataY = 250.0
DataWidth = 200.0
DataHeight = 120.0
# we need two user defined functions -- one gives the extent of the graphics,
# and the other does the cairo drawing using a cairo context.
# bounding box of user data -- x, y, w, h -- top left corner, width, height
proc worldExtents(): (float, float, float, float) =
(DataX, DataY, DataWidth, DataHeight) # current extents of our user world
# draw to cairo context
proc drawWorld(cr: cairo.Context) =
cr.setSource(1, 1, 1)
cr.paint
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
var i = 0.0
while true:
if min(DataWidth - 2.0 * i, DataHeight - 2.0 * i) <= 0:
break
cr.rectangle(DataX + i, DataY + i, DataWidth - 2 * i, DataHeight - 2 * i)
i += 10.0
cr.stroke
proc test =
var data: PDA_Data
data.draw = drawWorld
data.extents = worldExtents
data.windowSize = (800, 600)
newDisplay(data)
test()
----
We can use this module as a library easily and get this simple drawing tool with full zoom and scroll support:
[[darea_test.nim]]
[source,nim]
.darea_test.nim
----
import gintro/cairo
import drawingarea
from math import PI
proc extents(): (float, float, float, float) =
(0.0, 0.0, 100.0, 100.0) # ugly float literals
# draw to cairo context
proc draw(cr: cairo.Context) =
cr.setSource(1, 1, 1) # set background color and paint
cr.paint
cr.setSource(0, 0, 0) # forground color
cr.arc(20, 30, 10, 0, 5) # nearly a circle
cr.newSubPath # do not join the two arcs
cr.arc(70, 60, 20, 0, math.PI)
cr.stroke # finally do it
proc main =
var data: PDA_Data
data.draw = draw
data.extents = extents
data.windowSize = (800, 600)
newDisplay(data)
main()
----

25
examples/darea_test.nim Normal file
View file

@ -0,0 +1,25 @@
import gintro/cairo
import drawingarea
from math import PI
proc extents(): (float, float, float, float) =
(0.0, 0.0, 100.0, 100.0) # ugly float literals
# draw to cairo context
proc draw(cr: cairo.Context) =
cr.setSource(1, 1, 1) # set background color and paint
cr.paint
cr.setSource(0, 0, 0) # forground color
cr.arc(20, 30, 10, 0, 5) # nearly a circle
cr.newSubPath # do not join the two arcs
cr.arc(70, 60, 20, 0, math.PI)
cr.stroke # finally do it
proc main =
var data: PDA_Data
data.draw = draw
data.extents = extents
data.windowSize = (800, 600)
newDisplay(data)
main()

View file

@ -1,45 +1,25 @@
# Plain demo for zooming, panning, scrolling with GTK DrawingArea
# (c) S. Salewski, 21-DEC-2010 (initial Ruby version)
# Nim version April 2019 -- well first draft :-)
# Nim version April 2019
# License MIT
# This version of the demo program uses a separate proc paint()
# which allocates a custom surface for buffered drawing.
# That may be not really necessary, for simple drawings doing all
# the drawing in the "draw" call back is easier and faster. But for
# more complicated drawing operations, for example when using a
# background grid, which is a bit larger than the window size and
# is reused when scrolling, a custom surface may be useful.
# And finally that custom surface and custom cairo context is an
# important test for the language bindings.
import gintro/[gtk, gdk, glib, gobject, gio, cairo]
const
DataX = 150.0
DataY = 250.0
DataWidth = 200.0
DataHeight = 120.0
# The following two functions are intended to be user defined -- we have still
# to investigate how to do that best.
# bounding box of user data -- x, y, w, h -- top left corner, width, height
proc getWorldExtends(): (float, float, float, float) =
(DataX, DataY, DataWidth, DataHeight) # current extents of our user world
# draw to cairo context
proc drawWorld(cr: cairo.Context) =
cr.setSource(1, 1, 1)
cr.paint
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
cr.rectangle(DataX, DataY, DataWidth, DataHeight)
var i = 10.0
while true:
if min(DataWidth - 2.0 * i, DataHeight - 2.0 * i) <= 0:
break
cr.rectangle(DataX + i, DataY + i, DataWidth - 2.0 * i,
DataHeight - 2.0 * i)
i += 10.0
cr.stroke
const
ZOOM_FACTOR_MOUSE_WHEEL = 1.1
ZOOM_FACTOR_SELECT_MAX = 10 # ignore zooming in tiny selection
ZOOM_NEAR_MOUSEPOINTER = true # mouse wheel zooming -- to mousepointer or center
SELECT_RECT_COL = [0.0, 0, 1, 0.5] # blue with transparency
ZoomFactorMouseWheel = 1.1
ZoomFactorSelectMax = 10 # ignore zooming in tiny selection
ZoomNearMousepointer = true # mouse wheel zooming -- to mouse-pointer or center
SelectRectCol = [0.0, 0, 1, 0.5] # blue with transparency
discard """
Zooming, scrolling, panning...
@ -68,7 +48,7 @@ and translate the origin of our data to (0, 0)
Zooming: Mouse wheel or selecting a rectangle with left mouse button pressed
Scrolling: Scrollbars
Panning: Moving mouse while middle mouse button pressed
Panning: Moving mouse while middle mouse button pressed
"""
# drawing area and scroll bars in 2x2 table (PDA == Plain Drawing Area)
@ -80,6 +60,12 @@ type
proc newPosAdj: PosAdj =
initAdjustment(result, 0, 0, 1, 1, 10, 1)
type
PDA_Data* = object
draw*: proc (cr: Context)
extents*: proc (): tuple[x, y, w, h: float]
windowSize*: tuple[w, h: int]
type
PDA = ref object of Grid
zoomNearMousepointer: bool
@ -102,6 +88,8 @@ type
lastMousePosY: float
zoomRectX1: float
zoomRectY1: float
drawWorld: proc (cr: Context)
extents: proc (): tuple[x, y, w, h: float]
proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
if this.surf.isNil: return
@ -116,7 +104,8 @@ proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
cr.stroke
return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
return SignalEventStopPropagation
#return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
# clamp to correct values, 0 <= value <= (adj.upper - adj.pageSize), block calling onAdjustmentEvent()
proc updateVal(adj: PosAdj; d: float) =
@ -126,35 +115,30 @@ proc updateVal(adj: PosAdj; d: float) =
proc updateAdjustments(this: PDA; dx, dy: float) =
this.hadjustment.setUpper(this.darea.allocatedWidth.float * this.userZoom)
this.vadjustment.setUpper(this.darea.allocatedHeight.float *
this.userZoom)
this.vadjustment.setUpper(this.darea.allocatedHeight.float * this.userZoom)
this.hadjustment.setPageSize(this.darea.allocatedWidth.float)
this.vadjustment.setPageSize(this.darea.allocatedHeight.float)
updateVal(this.hadjustment, dx)
updateVal(this.vadjustment, dy)
# Maybe we should fix this for latest GTK3, see
# https://developer.gnome.org/gdk3/stable/gdk3-Cairo-Interaction.html
# so use gdk_drawing_context_get_cairo_context ()
# or maybe even better do all drwing from within "draw" callback?
# maybe for optimization we should only allocate a new surface when its size has changed.
proc paint(this: PDA) =
if this.surf != nil:
cairosurfaceDestroy(this.surf.impl) # we may have to fix that later for automatically destroy
destroy(this.surf) # manually destroy surface -- GC would do it for us, but GC is slow...
this.surf = this.darea.window.createSimilarSurface(Content.color,
this.darea.allocatedWidth, this.darea.allocatedHeight)
let cr = newContext(this.surf)
cr.translate(this.hadjustment.upper * 0.5 - this.hadjustment.value, # our origin is the center
this.vadjustment.upper * 0.5 - this.vadjustment.value)
cr.scale(this.fullScale * this.userZoom, this.fullScale * this.userZoom)
cr.translate(-this.dataX - this.dataWidth * 0.5,
-this.dataY - this.dataHeight * 0.5)
drawWorld(cr) # call the user provided drawing fonction
cr.translate(-this.dataX - this.dataWidth * 0.5, -this.dataY - this.dataHeight * 0.5)
this.drawWorld(cr) # call the user provided drawing function
destroy(cr) # we can also manually destroy the context here, but GC would do it for us
proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure;
this: PDA): bool =
proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure; this: PDA): bool =
this.updateAdjustments(0, 0)
(this.dataX, this.dataY, this.dataWidth,
this.dataHeight) = getWorldExtends() # query user defined size
this.dataHeight) = this.extents() # query user defined size
this.fullScale = min(this.darea.allocatedWidth.float / this.dataWidth,
this.darea.allocatedHeight.float / this.dataHeight)
this.paint
@ -162,8 +146,7 @@ proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure;
proc updateAdjustmentsAndPaint(this: PDA; dx, dy: float) =
this.updateAdjustments(dx, dy)
this.paint
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth,
this.darea.allocatedHeight)
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth, this.darea.allocatedHeight)
# event coordinates to user space
proc getUserCoordinates(this: PDA; eventX, eventY: float): (float, float) =
@ -175,16 +158,13 @@ proc getUserCoordinates(this: PDA; eventX, eventY: float): (float, float) =
proc onMotion(darea: DrawingArea; event: EventMotion; this: PDA): bool =
let state = getState(event)
let (x, y) = event.getCoords
#(x, y) = this.getUserCoordinates(x, y)
if (state.contains(button1)): # selecting
this.selecting = true
this.zoomRectX1 = x
this.zoomRectY1 = y
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth,
this.darea.allocatedHeight)
this.darea.queueDrawArea(0, 0, this.darea.allocatedWidth, this.darea.allocatedHeight)
elif button2 in state: # panning
this.updateAdjustmentsAndPaint(this.lastMousePosX - x,
this.lastMousePosY - y)
this.updateAdjustmentsAndPaint(this.lastMousePosX - x, this.lastMousePosY - y)
this.lastMousePosX = x
this.lastMousePosY = y
#event.request # request more motion events ?
@ -197,25 +177,20 @@ proc scrollEvent(darea: DrawingArea; event: EventScroll; this: PDA): bool =
var d = getScrollDirection(event)
var (x, y) = event.getCoords
if d == ScrollDirection.up:
this.userZoom *= ZOOM_FACTOR_MOUSE_WHEEL
this.userZoom *= ZoomFactorMouseWheel
elif d == ScrollDirection.down:
this.userZoom /= ZOOM_FACTOR_MOUSE_WHEEL
this.userZoom /= ZoomFactorMouseWheel
if (this.userZoom < 1):
this.userZoom = 1
if this.zoomNearMousepointer:
this.updateAdjustmentsAndPaint((this.hadjustment.value + x) * (
this.userZoom / z0 - 1),
(this.vadjustment.value + y) * (
this.userZoom / z0 - 1))
this.updateAdjustmentsAndPaint((this.hadjustment.value + x) * (this.userZoom / z0 - 1),
(this.vadjustment.value + y) * (this.userZoom / z0 - 1))
else: # zoom to center
this.updateAdjustmentsAndPaint((this.hadjustment.value +
this.darea.allocatedWidth.float * 0.5) * (this.userZoom / z0 - 1),
(this.vadjustment.value +
this.darea.allocatedHeight.float * 0.5) * (
this.userZoom / z0 - 1))
(this.vadjustment.value + this.darea.allocatedHeight.float * 0.5) * (this.userZoom / z0 - 1))
proc buttonPressEvent(darea: DrawingArea; event: EventButton;
this: PDA): bool =
proc buttonPressEvent(darea: DrawingArea; event: EventButton; this: PDA): bool =
var (x, y) = event.getCoords
this.lastMousePosX = x
this.lastMousePosY = y
@ -226,8 +201,7 @@ proc buttonPressEvent(darea: DrawingArea; event: EventButton;
echo "User coordinates: ", x, ' ', y, "\n" # to verify getUserCoordinates()
# zoom into selected rectangle and center it
proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
this: PDA): bool =
proc buttonReleaseEvent(darea: DrawingArea; event: EventButton; this: PDA): bool =
var (x, y) = event.getCoords
var b = getButton(event)
if b == 1:
@ -235,7 +209,7 @@ proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
let z1 = min(this.darea.allocatedWidth.float / (
this.lastButtonDownPosX - x).abs, this.darea.allocatedHeight.float / (
this.lastButtonDownPosY - y).abs)
if z1 < ZOOM_FACTOR_SELECT_MAX: # else selection rectangle will persist, we may output a message...
if z1 < ZoomFactorSelectMax: # else selection rectangle will persist, we may output a message...
this.userZoom *= z1
this.updateAdjustmentsAndPaint(
((2 * this.hadjustment.value + x + this.lastButtonDownPosX) * z1 -
@ -245,24 +219,19 @@ proc buttonReleaseEvent(darea: DrawingArea; event: EventButton;
proc onAdjustmentEvent(this: PosAdj; pda: PDA) =
pda.paint
pda.darea.queueDrawArea(0, 0, pda.darea.allocatedWidth,
pda.darea.allocatedHeight)
pda.darea.queueDrawArea(0, 0, pda.darea.allocatedWidth, pda.darea.allocatedHeight)
proc newPDA: PDA =
initGrid(result)
result.zoomNearMousepointer = ZOOM_NEAR_MOUSEPOINTER # mouse wheel zooming
result.selecting = false
result.zoomNearMousepointer = ZoomNearMousepointer # mouse wheel zooming
result.userZoom = 1.0
result.surf = nil
result.darea = newDrawingArea()
result.darea.setHExpand#(true)
result.darea.setVExpand#(true)
result.darea.setHExpand
result.darea.setVExpand
result.darea.connect("draw", drawingAreaDrawCb, result)
result.darea.connect("configure-event", dareaConfigureCallback, result)
result.darea.addEvents({EventFlag.buttonPress, EventFlag.buttonRelease,
EventFlag.scroll,
EventFlag.button1Motion, EventFlag.button2Motion,
EventFlag.pointerMotionHint})
EventFlag.scroll, button1Motion, button2Motion, pointerMotionHint})
result.darea.connect("motion-notify-event", onMotion, result)
result.darea.connect("scroll_event", scrollEvent, result)
result.darea.connect("button_press_event", buttonPressEvent, result)
@ -275,23 +244,63 @@ proc newPDA: PDA =
onAdjustmentEvent, result)
result.hscrollbar = newScrollbar(Orientation.horizontal, result.hadjustment)
result.vscrollbar = newScrollbar(Orientation.vertical, result.vadjustment)
result.hscrollbar.setHExpand#(true)
result.vscrollbar.setVExpand#(true)
result.hscrollbar.setHExpand
result.vscrollbar.setVExpand
result.attach(result.darea, 0, 0, 1, 1)
result.attach(result.vscrollbar, 1, 0, 1, 1)
result.attach(result.hscrollbar, 0, 1, 1, 1)
proc appActivate(app: Application) =
proc appActivate(app: Application; initData: ref PDA_Data) =
let window = newApplicationWindow(app)
window.title = "Drawing example"
window.defaultSize = (400, 400)
window.defaultSize = (initData.windowSize[0], initData.windowSize[1])
let pda = newPDA()
pda.drawWorld = initData.draw
pda.extents = initData.extents
window.add(pda)
showAll(window)
proc main =
proc newDisplay*(initData: PDA_Data) =
let app = newApplication("org.gtk.example")
connect(app, "activate", appActivate)
var d = new (ref PDA_Data)
d[] = initData
connect(app, "activate", appActivate, d)
discard run(app)
main()
when isMainModule:
const # arbitrary locations for our data
DataX = 150.0
DataY = 250.0
DataWidth = 200.0
DataHeight = 120.0
# we need two user defined functions -- one gives the extent of the graphics,
# and the other does the cairo drawing using a cairo context.
# bounding box of user data -- x, y, w, h -- top left corner, width, height
proc worldExtents(): (float, float, float, float) =
(DataX, DataY, DataWidth, DataHeight) # current extents of our user world
# draw to cairo context
proc drawWorld(cr: cairo.Context) =
cr.setSource(1, 1, 1)
cr.paint
cr.setSource(0, 0, 0)
cr.setLineWidth(2)
var i = 0.0
while true:
if min(DataWidth - 2.0 * i, DataHeight - 2.0 * i) <= 0:
break
cr.rectangle(DataX + i, DataY + i, DataWidth - 2 * i, DataHeight - 2 * i)
i += 10.0
cr.stroke
proc test =
var data: PDA_Data
data.draw = drawWorld
data.extents = worldExtents
data.windowSize = (800, 600)
newDisplay(data)
test()

View file

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

View file

@ -1,10 +1,12 @@
# this is the high level cairo module for Nim -- based on the low level ngtk3 module, manually tuned.
# S. Salewski 2017, cairo 1.15.6
# This is the high level cairo module for Nim -- based on the low level ngtk3 module, manually tuned.
# (c) S. Salewski 2017, cairo 1.15.6
# v0.4.18
# 24-APR-2019
# cairo does not really support gobject introspection and gobject's toggle references.
# so code for memory management is a bit different compared to other gtk modules.
# Cairo does not really support gobject introspection and gobject's toggle references.
# So code for memory management is a bit different compared to other gtk modules.
# This is the template for creation of new objects:
# This was the initial template for creation of new objects :
# proc newImageSurface*(format: Format; width, height: int): Surface =
# new(result, surfaceDestroy)
@ -12,7 +14,6 @@
# discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
# with
# proc gcuref(o: pointer) {.cdecl.} = GC_unref(cast[RootRef](o))
# proc surfaceDestroy*(surface: Surface) = cairo_surface_destroy(surface.impl)
@ -24,17 +25,44 @@
# result.impl = cairo_create(target.impl)
# discard cairo_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
# we have to call GC_ref() because we store no direct reference to the proxy object.
# for this example cairo_create() references the surface.
# when ref count of surface is decreased, maybe because context is destroyed, then gcuref is called
# We have to call GC_ref() because we store no direct reference to the proxy object.
# For this example cairo_create() references the surface.
# When ref count of surface is decreased, maybe because context is destroyed, then gcuref is called
# which calls GC_unref() on proxy. When there is no other ref to proxy GC can free proxy and surface
# by calling surfaceDestroy(). This works also when surface is newer used for something.
# currently this is still nearly untested!
# one problem may be procs like cairo_surface_create_similar() when called frequently, maybe
# But there is at least one design flaw:
# When we create for example a surface and GC releases it, then gcunref() is called on it while
# gc_ref() was newer called. When we consider that the cairo context is the only element which
# stores references to other objects, then we can simplify the code to
# proc newImageSurface*(format: Format; width, height: int): Surface =
# new(result, surfaceDestroy)
# result.impl = cairo_image_surface_create(format, width.cint, height.cint)
# with
# proc surfaceDestroy*(surface: Surface) = cairo_surface_destroy(surface.impl)
# and this is the template for refering such an object:
# proc newContext*(target: Surface): Context =
# new(result, destroy)
# GC_ref(target)
# result.impl = cairo_create(target.impl)
# discard cairo_surface_set_user_data(target.impl, NUDK, cast[pointer](target), gcuref)
# with
# proc gcuref(o: pointer) {.cdecl.} = GC_unref(cast[RootRef](o))
# Currently this is all not really tested.
# One problem may be procs like cairo_surface_create_similar() when called frequently, maybe
# for each frame in animations. GC may be too slow in freeing the memory.
# We should try to avoid such usage, or we may add a way to manually free memory.
# Maybe later destructors can be used?
# Maybe later destructors can be used, or newruntime with owned refs?
# Maybe currently the best fix is to allow manually calling destroy() procs, which then ignore
# the later call from GC because impl field is already nil.
# Some procs are marked with label TODO -- because I have no real idea what to do with them
@ -238,7 +266,9 @@ proc setUserData*(cr: Context; key: ptr UserDataKey; userData: pointer; destroy:
proc cairo_destroy*(cr: ptr Context00) {.importc, libcairo.}
#
proc destroy*(cr: Context) =
cairo_destroy(cr.impl)
if cr != nil and cr.impl != nil:
cairo_destroy(cr.impl)
cr.impl = nil
proc cairo_surface_reference*(surface: ptr Surface00): ptr Surface00 {.importc, libcairo.}
#
@ -246,13 +276,22 @@ proc surfaceReference*(surface: Surface): Surface =
discard cairo_surface_reference(surface.impl)
return surface
proc cairo_surface_set_user_data*(surface: ptr Surface00;
key: ptr UserDataKey; userData: pointer; destroy: DestroyFunc00): Status {.importc, libcairo.}
#
proc setUserData*(surface: Surface; key: ptr UserDataKey; userData: pointer; destroy: DestroyFunc00): Status =
cairo_surface_set_user_data(surface.impl, key, userData, destroy)
proc cairo_create*(target: ptr Surface00): ptr Context00 {.importc, libcairo.}
#
proc newContext*(target: Surface): Context =
new(result, destroy)
GC_ref(target)
result.impl = cairo_create(target.impl)
discard cairo_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
discard cairo_surface_set_user_data(target.impl, NUDK, cast[pointer](target), gcuref)
proc cairo_reference*(cr: ptr Context00): ptr Context00 {.importc, libcairo.}
#
@ -1002,6 +1041,7 @@ proc getFontFace*(cr: Context): FontFace =
#assert false # may this happen?
new(result, fontFaceDestroy)
result.impl = h
# TODO check
discard cairo_font_face_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
discard cairo_font_face_reference(result.impl)
else:
@ -1234,7 +1274,7 @@ proc cairo_toy_font_face_create*(family: cstring; slant: FontSlant;
proc toyFontFaceCreate*(family: string; slant: FontSlant; weight: FontWeight): FontFace =
new(result, fontFaceDestroy)
result.impl = cairo_toy_font_face_create(family, slant, weight)
discard cairo_font_face_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_font_face_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
# returns: The family name. This string is owned by the font face and remains valid as long as the font face is alive (referenced).
proc cairo_toy_font_face_get_family*(fontFace: ptr Font_face00): cstring {.importc, libcairo.}
@ -1258,7 +1298,7 @@ proc userFontFaceCreate*(): FontFace =
new(result, fontFaceDestroy)
result.impl = cairo_user_font_face_create()
# there is no cairo_user_font_face_set_user_data()
discard cairo_font_face_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
# discard cairo_font_face_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
type
UserScaledFontInitFunc00* = proc (scaledFont: ptr ScaledFont00;
@ -1434,19 +1474,17 @@ proc getMatrix*(cr: Context; matrix: var Matrix) =
proc cairo_surface_destroy*(surface: ptr Surface00) {.importc, libcairo.}
#
proc surfaceDestroy(surface: Surface) =
cairo_surface_destroy(surface.impl)
proc destroy*(surface: Surface) =
if surface != nil and surface.impl != nil:
cairo_surface_destroy(surface.impl)
surface.impl = nil
proc cairo_surface_get_user_data*(surface: ptr Surface00; key: ptr UserDataKey): pointer {.importc, libcairo.}
#
proc getUserData*(surface: Surface; key: ptr UserDataKey): pointer =
cairo_surface_get_user_data(surface.impl, key)
proc cairo_surface_set_user_data*(surface: ptr Surface00;
key: ptr UserDataKey; userData: pointer; destroy: DestroyFunc00): Status {.importc, libcairo.}
#
proc setUserData*(surface: Surface; key: ptr UserDataKey; userData: pointer; destroy: DestroyFunc00): Status =
cairo_surface_set_user_data(surface.impl, key, userData, destroy)
proc cairo_get_target*(cr: ptr Context00): ptr Surface00 {.importc, libcairo.}
#
@ -1456,7 +1494,7 @@ proc getTarget*(cr: Context): Surface =
let d = cairo_surface_get_user_data(h, NUDK)
if d.isNil:
assert false # may this happen?
new(result, surfaceDestroy)
new(result, destroy)
result.impl = h
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
discard cairo_surface_reference(result.impl)
@ -1473,10 +1511,10 @@ proc getGroupTarget*(cr: Context): Surface =
let d = cairo_surface_get_user_data(h, NUDK)
if d.isNil:
#assert false # may this happen?
new(result, surfaceDestroy)
new(result, destroy)
result.impl = h
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
discard cairo_surface_reference(result.impl)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_reference(result.impl)
else:
result = cast[Surface](d)
assert(result.impl == h)
@ -1586,17 +1624,17 @@ proc cairo_surface_create_similar*(other: ptr Surface00; content: Content; width
ptr Surface00 {.importc, libcairo.}
proc createSimilar*(other: Surface; content: Content; width, height: int): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_surface_create_similar(other.impl, content, width.cint, height.cint)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_surface_create_similar_image*(other: ptr Surface00; format: Format; width, height: cint):
ptr Surface00 {.importc, libcairo.}
#
proc createSimilarImage*(other: Surface; format: Format; width, height: int): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_surface_create_similar_image(other.impl, format, width.cint, height.cint)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_surface_unmap_image*(surface: ptr Surface00; image: ptr Surface00) {.importc, libcairo.}
#
@ -1606,7 +1644,7 @@ proc unmapImage*(surface: Surface; image: Surface) =
proc cairo_surface_map_to_image*(surface: ptr Surface00; extents: RectangleInt): ptr Surface00 {.importc, libcairo.}
#
proc mapToImage*(surface: Surface; extents: RectangleInt): Surface =
#new(result, surfaceDestroy) # no idea currently
#new(result, destroy) # no idea currently
new(result)
result.impl = cairo_surface_map_to_image(surface.impl, extents)
@ -1614,9 +1652,9 @@ proc cairo_surface_create_for_rectangle*(target: ptr Surface00; x, y, width, hei
ptr Surface00 {.importc, libcairo.}
#
proc surfaceCreateForRectangle*(target: Surface; x, y, width, height: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_surface_create_for_rectangle(target.impl, x.cdouble, y.cdouble, width.cdouble, height.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
type
SurfaceObserverMode* {.size: sizeof(cint), pure.} = enum
@ -1627,9 +1665,9 @@ proc cairo_surface_create_observer*(target: ptr Surface00;
mode: SurfaceObserverMode): ptr Surface00 {.importc, libcairo.}
#
proc createObserver*(target: Surface; mode: SurfaceObserverMode): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_surface_create_observer(target.impl, mode)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
type
SurfaceObserverCallback00* = proc (observer: ptr Surface00;
@ -1841,9 +1879,9 @@ proc hasShowTextGlyphs*(surface: Surface): bool =
proc cairo_image_surface_create*(format: Format; width, height: cint): ptr Surface00 {.importc, libcairo.}
#
proc imageSurfaceCreate*(format: Format; width, height: int): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_image_surface_create(format, width.cint, height.cint)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_format_stride_for_width*(format: Format; width: cint): cint {.importc, libcairo.}
#
@ -1880,24 +1918,24 @@ when CAIRO_HAS_PNG_FUNCTIONS:
proc cairo_image_surface_create_from_png*(filename: cstring): ptr Surface00 {.importc, libcairo.}
proc imageSurfaceCreateFromPng*(filename: string): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_image_surface_create_from_png(filename)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_image_surface_create_from_png_stream*(read_func: Read_func00;
closure: pointer): ptr Surface00 {.importc, libcairo.}
#
proc imageSurfaceCreateFromPngStream*(readFunc: ReadFunc00; closure: pointer): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_image_surface_create_from_png_stream(readFunc, closure)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_recording_surface_create*(content: Content; extents: Rectangle00): ptr Surface00 {.importc, libcairo.}
#
proc recordingSurfaceCreate*(content: Content; extents: Rectangle00): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_recording_surface_create(content, extents)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_recording_surface_ink_extents*(surface: ptr Surface00; x0, y0, width, height: var cdouble) {.importc, libcairo.}
#
@ -1943,7 +1981,7 @@ proc cairo_pattern_create_raster_source*(userData: pointer; content: Content; wi
proc patternCreateRasterSource*(userData: pointer; content: Content; width, height: int): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_raster_source(userData, content, width.cint, height.cint)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_raster_source_pattern_set_callback_data*(pattern: ptr Pattern00; data: pointer) {.importc,libcairo.}
#
@ -2002,42 +2040,42 @@ proc cairo_pattern_create_rgb*(red, green, blue: cdouble): ptr Pattern00 {.impor
proc patternCreateRgb*(red, green, blue: float): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_rgb(red.cdouble, green.cdouble, blue.cdouble)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_create_rgba*(red, green, blue, alpha: cdouble): ptr Pattern00 {.importc, libcairo.}
#
proc patternCreateRgba*(red, green, blue, alpha: float): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_rgba(red.cdouble, green.cdouble, blue.cdouble, alpha.cdouble)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_create_for_surface*(surface: ptr Surface00): ptr Pattern00 {.importc, libcairo.}
#
proc patternCreateForSurface*(surface: Surface): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_for_surface(surface.impl)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_create_linear*(x0, y0, x1, y1: cdouble): ptr Pattern00 {.importc, libcairo.}
#
proc patternCreateLinear*(x0, y0, x1, y1: float): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_linear(x0.cdouble, y0.cdouble, x1.cdouble, y1.cdouble)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_create_radial*(cx0, cy0, radius0, cx1, cy1, radius1: cdouble): ptr Pattern00 {.importc, libcairo.}
#
proc patternCreateRadial*(cx0, cy0, radius0, cx1, cy1, radius1: float): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_radial(cx0.cdouble, cy0.cdouble, radius0.cdouble, cx1.cdouble, cy1.cdouble, radius1.cdouble)
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_create_mesh*(): ptr Pattern00 {.importc, libcairo.}
#
proc patternCreateMesh*(): Pattern =
new(result, patternDestroy)
result.impl = cairo_pattern_create_mesh()
discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_pattern_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pattern_reference*(pattern: ptr Pattern00): ptr Pattern00 {.importc, libcairo.}
#
@ -2166,6 +2204,7 @@ proc getRgba*(pattern: Pattern; red, green, blue, alpha: var float): Status =
blue = blue0.float
alpha = alpha0.float
# TODO this seems to be wrong
proc cairo_pattern_get_surface*(pattern: ptr Pattern00; surface: var ptr Surface00): Status {.importc, libcairo.}
#
proc getSurface*(pattern: Pattern; surface: var Surface): Status =
@ -2175,7 +2214,7 @@ proc getSurface*(pattern: Pattern; surface: var Surface): Status =
let d = cairo_surface_get_user_data(h, NUDK)
if d.isNil:
assert false # may this happen?
new(surface, surfaceDestroy)
new(surface, destroy)
surface.impl = h
discard cairo_surface_set_user_data(surface.impl, NUDK, cast[pointer](surface), gcuref)
discard cairo_surface_reference(surface.impl)
@ -2324,21 +2363,21 @@ type
proc cairo_region_destroy*(region: ptr Region00) {.importc, libcairo.}
#
proc regionDestroy*(region: Region) =
proc destroy*(region: Region) =
cairo_region_destroy(region.impl)
# there is no cairo_region_set_user_data()
proc cairo_region_create*(): ptr Region00 {.importc, libcairo.}
#
proc regionCreate*(): Region =
new(result, regionDestroy)
new(result, destroy)
result.impl = cairo_region_create()
#discard cairo_region_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_region_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_region_create_rectangle*(rectangle: RectangleInt): ptr Region00 {.importc, libcairo.}
#
proc regionCreateRectangle*(rectangle: RectangleInt): Region =
new(result, regionDestroy)
new(result, destroy)
result.impl = cairo_region_create_rectangle(rectangle)
# TODO
@ -2348,7 +2387,7 @@ proc cairo_region_create_rectangles*(rects: RectangleInt; count: cint): ptr Regi
proc cairo_region_copy*(original: ptr Region00): ptr Region00 {.importc, libcairo.}
#
proc regionCopy*(original: Region): Region =
new(result, regionDestroy)
new(result, destroy)
result.impl = cairo_region_copy(original.impl)
proc cairo_region_reference*(region: ptr Region00): ptr Region00 {.importc, libcairo.}
@ -2456,17 +2495,17 @@ when CAIRO_HAS_PDF_SURFACE:
importc, libcairo.}
#pong
proc pdfSurfaceCreate*(filename: string; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_pdf_surface_create(filename, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pdf_surface_create_for_stream*(writeFunc: WriteFunc00;
closure: pointer; widthInPoints, heightInPoints: cdouble): ptr Surface00 {.importc, libcairo.}
#
proc pdfSurfaceCreateForStream*(writeFunc: WriteFunc00; closure: pointer; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_pdf_surface_create_for_stream(writeFunc, closure, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_pdf_surface_restrict_to_version*(surface: ptr Surface00;
version: PdfVersion) {.importc, libcairo.}
@ -2537,17 +2576,17 @@ when CAIRO_HAS_PS_SURFACE:
importc, libcairo.}
#
proc psSurfaceCreate*(filename: string; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_ps_surface_create(filename, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_ps_surface_create_for_stream*(writeFunc: WriteFunc00; closure: pointer;
widthInPoints, heightInPoints: cdouble): ptr Surface00 {.importc, libcairo.}
#
proc psSurfaceCreateForStream*(writeFunc: WriteFunc00; closure: pointer; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_ps_surface_create_for_stream(writeFunc, closure, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_ps_surface_restrict_to_level*(surface: ptr Surface00; level: PsLevel) {.importc, libcairo.}
#
@ -2604,17 +2643,17 @@ when CAIRO_HAS_SVG_SURFACE:
importc, libcairo.}
#
proc svgSurfaceCreate*(filename: string; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_svg_surface_create(filename, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_svg_surface_create_for_stream*(write_func: Write_func00;
closure: pointer; widthInPoints, heightInPoints: cdouble): ptr Surface00 {.importc, libcairo.}
#
proc svgSurfaceCreateForstream*(writeFunc: WriteFunc00; closure: pointer; widthInPoints, heightInPoints: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_svg_surface_create_for_stream(writeFunc, closure, widthInPoints.cdouble, heightInPoints.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_svg_surface_restrict_to_version*(surface: ptr Surface00; version: SvgVersion) {.importc, libcairo.}
#
@ -2638,22 +2677,22 @@ when CAIRO_HAS_XML_SURFACE:
proc xml_create*(filename: string): Device =
new(result, deviceDestroy)
result.impl = cairo_xml_create(filename)
discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_xml_create_for_stream*(writeFunc: WriteFunc00; closure: pointer): ptr Device00 {.importc, libcairo.}
#
proc xmlCreateForStream*(writeFunc: WriteFunc00; closure: pointer): Device =
new(result, deviceDestroy)
result.impl = cairo_xml_create_for_stream(writeFunc, closure)
discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_xml_surface_create*(xml: ptr Device00; content: Content; width, height: cdouble): ptr Surface00 {.
importc, libcairo.}
#
proc xmlSurfaceCreate*(xml: Device; content: Content; width, height: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_xml_surface_create(xml.impl, content, width.cdouble, height.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_xml_for_recording_surface*(xml: ptr Device00; surface: ptr Surface00): Status {.importc, libcairo.}
#
@ -2670,14 +2709,14 @@ when CAIRO_HAS_SCRIPT_SURFACE:
proc scriptCreate*(filename: string): Device =
new(result, deviceDestroy)
result.impl = cairo_script_create(filename)
discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_script_create_for_stream*(writeFunc: WriteFunc00; closure: pointer): ptr Device00 {.importc, libcairo.}
#
proc scriptCcreateForStream*(writeFunc: WriteFunc00; closure: pointer): Device =
proc scriptCreateForStream*(writeFunc: WriteFunc00; closure: pointer): Device =
new(result, deviceDestroy)
result.impl = cairo_script_create_for_stream(writeFunc, closure)
discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_device_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_script_write_comment*(script: ptr Device00; comment: cstring; len: cint) {.importc, libcairo.}
#
@ -2698,17 +2737,17 @@ when CAIRO_HAS_SCRIPT_SURFACE:
importc, libcairo.}
#
proc scriptSurfaceCreate*(script: Device; content: Content; width, height: float): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_script_surface_create(script.impl, content, width.cdouble, height.cdouble)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_script_surface_create_for_target*(script: ptr Device00;
target: ptr Surface00): ptr Surface00 {.importc, libcairo.}
#
proc scriptSurfaceCreateForTarget*(script: Device; target: Surface): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_script_surface_create_for_target(script.impl, target.impl)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_script_from_recording_surface*(script: ptr Device00;
recordingSurface: ptr Surface00): Status {.importc, libcairo.}
@ -2720,9 +2759,9 @@ when CAIRO_HAS_SKIA_SURFACE:
proc cairo_skia_surface_create*(format: Format; width, height: cint): ptr Surface00 {.importc, libcairo.}
#
proc skiaSurfaceCreate*(format: Format; width, height: int): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_skia_surface_create(format, width.cint, height.cint)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
#TODO
proc cairo_skia_surface_create_for_data*(data: ptr cuchar; format: Format; width, height, stride: cint):
@ -2762,9 +2801,9 @@ when CAIRO_HAS_TEE_SURFACE:
proc cairo_tee_surface_create*(master: ptr Surface00): ptr Surface00 {.importc, libcairo.}
#
proc teeSurfaceCreate*(master: Surface): Surface =
new(result, surfaceDestroy)
new(result, destroy)
result.impl = cairo_tee_surface_create(master.impl)
discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
### discard cairo_surface_set_user_data(result.impl, NUDK, cast[pointer](result), gcuref)
proc cairo_tee_surface_add*(surface: ptr Surface00; target: ptr Surface00) {.importc, libcairo.}
#

2785
gintro/oldcairo.nim Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
# High level gobject-introspection based GTK3 bindings for the Nim programming language
# v 0.4.17 2019-APR-16
# v 0.4.18 2019-APR-27
# (c) S. Salewski 2018
# https://wiki.gnome.org/Projects/GObjectIntrospection
@ -106,6 +106,7 @@ proc renumber(s: var string; i: int) =
var defaultParameters = newTable[string, string]()
var fixedProcNames = newTable[string, string]()
var fixedDestroyNames = newTable[string, string]()
var mangledNames = newTable[string, string]()
var gdkKeys = newTable[string, seq[string]]()
var allSyms: HashSet[string]
@ -124,6 +125,11 @@ var delayedMethods: seq[(GIBaseInfo, GIFunctionInfo)]
var classList: seq[GIBaseInfo]
var ct: CountTable[string]
fixedDestroyNames.add("gdk_window_create_similar_surface", "cairo.destroy")
fixedDestroyNames.add("gdk_window_create_similar_image_surface", "cairo.destroy")
fixedDestroyNames.add("gdk_cairo_region_create_from_surface", "cairo.destroy")
fixedDestroyNames.add("gdk_cairo_surface_create_from_pixbuf", "cairo.destroy")
fixedProcNames.add("gtk_button_new_with_label", "newButton")
fixedProcNames.add("pango_font_description_from_string", "newFontDescription")
fixedProcNames.add("pango_language_from_string", "languageFromString")
@ -722,8 +728,12 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
freeMe = gStructInfoFindMethod(info, "unref")
var freeMeName: string
if freeMe == nil:
echo "Caution: No free/unref found for ", ' ', " (", sym, ')' # Mostly missing cairo functions...
if fixedDestroyNames.contains(sym):
freeMeName = fixedDestroyNames[sym]
else:
echo "Caution: No free/unref found for ", ' ', " (", sym, ')' # Mostly missing cairo functions...
else:
assert not fixedDestroyNames.contains(sym)
freeMeName = $gBaseInfoGetName(freeMe)
if sym == "g_closure_new_simple" or sym == "g_closure_new_object": freeMeName = "unref" # TODO GI bug?
assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.EVERYTHING, GITransfer.NOTHING})
@ -797,8 +807,16 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
elif h.endsWith("unref"):
freeMeName = "unref"
break
if fixedDestroyNames.contains(sym):
freeMeName = fixedDestroyNames[sym]
#else:
if freeMeName == "":
# assert false # TODO we have to fix this case manually
echo "Caution: No free/unref found for ", ' ', gBaseInfoGetName(iface), " (", sym, ')'
methodBuffer.writeLine(" new(result)")
else:
@ -847,8 +865,17 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
freeMe = gStructInfoFindMethod(info, "free")
if freeMe.isNil:
freeMe = gStructInfoFindMethod(info, "unref")
if freeMe == nil:
echo "Caution: No free/unref found for ", ' ', " (", sym, ')' # Mostly missing cairo functions...
if fixedDestroyNames.contains(sym):
freeMeName = fixedDestroyNames[sym]
else:
echo "Caution: No free/unref found for ", ' ', " (", sym, ')' # Mostly missing cairo functions...
else:
freeMeName = $gBaseInfoGetName(freeMe)
let h2 = mangleName(gBaseInfoGetName(arg))
@ -1907,6 +1934,10 @@ proc main(namespace: string) =
output.writeLine("type\n ColorArray* = pointer")
output.writeLine("type\n Point00Array* = pointer")
elif namespace == "Gtk":
output.writeLine("# https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-draw")
output.writeLine("# TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.")
output.writeLine("const\n SignalEventStopPropagation* = true")
output.writeLine("const\n SignalEventContinuePropagation* = false")
output.writeLine("type\n TargetEntry00Array* = pointer")
output.writeLine("type\n AccelGroupEntry00Array* = pointer")
output.writeLine("type\n BindingArg00Array* = pointer")
@ -2115,10 +2146,21 @@ proc main(namespace: string) =
if i == "": continue
output.write("""
proc get$1*(builder: Builder; name: string): $1 =
new result
let gt = g_type_from_name("Gtk$1")
assert(gt != 0)
result.impl = gtk_builder_get_object(cast[ptr Builder00](builder.impl), name)
let gobj = gtk_builder_get_object(cast[ptr Builder00](builder.impl), name)
if g_object_get_qdata(gobj, Quark) != nil:
result = cast[type(result)](g_object_get_qdata(gobj, Quark))
assert(result.impl == gobj)
else:
new(result, finalizeGObject)
result.impl = gobj
#GC_ref(result) # we should not need this for builder, as returned objects are not floating
#discard g_object_ref_sink(result.impl)
g_object_add_toggle_ref(result.impl, toggleNotify, addr(result[]))
#g_object_unref(result.impl)
assert(g_object_get_qdata(result.impl, Quark) == nil)
g_object_set_qdata(result.impl, Quark, addr(result[]))
assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](result.impl), gt)))
""" % [i])
@ -2176,4 +2218,4 @@ o.close()
supmod.close
#for i in callerAllocCollector: echo i
# 1743 lines
# 2233 lines