various fixes
This commit is contained in:
parent
fd90029090
commit
8a59ea33d0
5 changed files with 668 additions and 50 deletions
85
README.adoc
85
README.adoc
|
|
@ -841,14 +841,13 @@ main()
|
|||
== 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).
|
||||
With the latest fix to Nim GTK3 bingings it seems to work fine, but some rewriting for optimized performance
|
||||
may be needed, I have to investigate that.
|
||||
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.
|
||||
|
||||
NOTE: The latest needed gintro patch is not yet available, I will provide it soon...
|
||||
|
||||
[[cairo2.nim]]
|
||||
[[drawingarea.nim]]
|
||||
[source,nim]
|
||||
.cairo2.nim
|
||||
.drawingarea.nim
|
||||
----
|
||||
# Plain demo for zooming, panning, scrolling with GTK DrawingArea
|
||||
# (c) S. Salewski, 21-DEC-2010 (initial Ruby version)
|
||||
|
|
@ -879,7 +878,7 @@ proc drawWorld(cr: cairo.Context) =
|
|||
cr.rectangle(DataX, DataY, DataWidth, DataHeight)
|
||||
var i = 10.0
|
||||
while true:
|
||||
if [DataWidth - 2.0 * i, DataHeight - 2.0 * i].min <= 0:
|
||||
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)
|
||||
|
|
@ -933,7 +932,7 @@ proc newPosAdj: PosAdj =
|
|||
initAdjustment(result, 0, 0, 1, 1, 10, 1)
|
||||
|
||||
type
|
||||
PDA = ref object of Table
|
||||
PDA = ref object of Grid
|
||||
zoomNearMousepointer: bool
|
||||
selecting: bool
|
||||
userZoom: float
|
||||
|
|
@ -973,7 +972,7 @@ proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
|
|||
# clamp to correct values, 0 <= value <= (adj.upper - adj.pageSize), block calling onAdjustmentEvent()
|
||||
proc updateVal(adj: PosAdj; d: float) =
|
||||
adj.signalHandlerBlock(adj.handlerID)
|
||||
adj.setValue([0.0, [adj.value + d, adj.upper - adj.pageSize].min].max)
|
||||
adj.setValue(max(0.0, min(adj.value + d, adj.upper - adj.pageSize)))
|
||||
adj.signalHandlerUnblock(adj.handlerID)
|
||||
|
||||
proc updateAdjustments(this: PDA; dx, dy: float) =
|
||||
|
|
@ -1007,8 +1006,8 @@ proc dareaConfigureCallback(darea: DrawingArea; event: EventConfigure;
|
|||
this.updateAdjustments(0, 0)
|
||||
(this.dataX, this.dataY, this.dataWidth,
|
||||
this.dataHeight) = getWorldExtends() # query user defined size
|
||||
this.fullScale = [this.darea.allocatedWidth.float / this.dataWidth,
|
||||
this.darea.allocatedHeight.float / this.dataHeight].min
|
||||
this.fullScale = min(this.darea.allocatedWidth.float / this.dataWidth,
|
||||
this.darea.allocatedHeight.float / this.dataHeight)
|
||||
this.paint
|
||||
|
||||
proc updateAdjustmentsAndPaint(this: PDA; dx, dy: float) =
|
||||
|
|
@ -1017,11 +1016,17 @@ proc updateAdjustmentsAndPaint(this: PDA; dx, dy: float) =
|
|||
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) =
|
||||
((eventX - this.hadjustment.upper * 0.5 + this.hadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataX + this.dataWidth * 0.5,
|
||||
(eventY - this.vadjustment.upper * 0.5 + this.vadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataY + this.dataHeight * 0.5)
|
||||
|
||||
proc onMotion(darea: DrawingArea; event: EventMotion; this: PDA): bool =
|
||||
var state: ModifierType
|
||||
discard event.state(state)
|
||||
var x, y: float
|
||||
discard event.getCoords(x, y)
|
||||
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
|
||||
|
|
@ -1040,10 +1045,8 @@ proc onMotion(darea: DrawingArea; event: EventMotion; this: PDA): bool =
|
|||
# is the relative movement caused by zooming
|
||||
proc scrollEvent(darea: DrawingArea; event: EventScroll; this: PDA): bool =
|
||||
let z0 = this.userZoom
|
||||
var d: ScrollDirection
|
||||
discard scrollDirection(event, d)
|
||||
var x, y: float
|
||||
discard event.getCoords(x, y)
|
||||
var d = getScrollDirection(event)
|
||||
var (x, y) = event.getCoords
|
||||
if d == ScrollDirection.up:
|
||||
this.userZoom *= ZOOM_FACTOR_MOUSE_WHEEL
|
||||
elif d == ScrollDirection.down:
|
||||
|
|
@ -1059,38 +1062,30 @@ proc scrollEvent(darea: DrawingArea; event: EventScroll; this: PDA): bool =
|
|||
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))
|
||||
|
||||
# event coordinates to user space
|
||||
proc getUserCoordinates(this: PDA; eventX, eventY: float): (float, float) =
|
||||
((eventX - this.hadjustment.upper * 0.5 + this.hadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataX + this.dataWidth * 0.5,
|
||||
(eventY - this.vadjustment.upper * 0.5 + this.vadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataY + this.dataHeight * 0.5)
|
||||
this.darea.allocatedHeight.float * 0.5) * (
|
||||
this.userZoom / z0 - 1))
|
||||
|
||||
proc buttonPressEvent(darea: DrawingArea; event: EventButton;
|
||||
this: PDA): bool =
|
||||
var x, y: float
|
||||
discard event.getCoords(x, y)
|
||||
(x, y) = this.getUserCoordinates(x, y)
|
||||
echo "User coordinates: ", x, ' ', y, "\n" # to verify getUserCoordinates()
|
||||
var (x, y) = event.getCoords
|
||||
this.lastMousePosX = x
|
||||
this.lastMousePosY = y
|
||||
this.lastButtonDownPosX = x
|
||||
this.lastButtonDownPosY = y
|
||||
echo "buttonPressEvent", x, " ", y
|
||||
(x, y) = this.getUserCoordinates(x, y)
|
||||
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 =
|
||||
var x, y: float
|
||||
discard event.getCoords(x, y)
|
||||
var b: int
|
||||
discard button(event, b)
|
||||
var (x, y) = event.getCoords
|
||||
var b = getButton(event)
|
||||
if b == 1:
|
||||
this.selecting = false
|
||||
let z1 = [this.darea.allocatedWidth.float / (
|
||||
let z1 = min(this.darea.allocatedWidth.float / (
|
||||
this.lastButtonDownPosX - x).abs, this.darea.allocatedHeight.float / (
|
||||
this.lastButtonDownPosY - y).abs].min
|
||||
this.lastButtonDownPosY - y).abs)
|
||||
if z1 < ZOOM_FACTOR_SELECT_MAX: # else selection rectangle will persist, we may output a message...
|
||||
this.userZoom *= z1
|
||||
this.updateAdjustmentsAndPaint(
|
||||
|
|
@ -1105,13 +1100,14 @@ proc onAdjustmentEvent(this: PosAdj; pda: PDA) =
|
|||
pda.darea.allocatedHeight)
|
||||
|
||||
proc newPDA: PDA =
|
||||
initTable(result, 2, 2, false)
|
||||
initGrid(result)
|
||||
result.zoomNearMousepointer = ZOOM_NEAR_MOUSEPOINTER # mouse wheel zooming
|
||||
result.selecting = false
|
||||
result.userZoom = 1.0
|
||||
result.surf = nil
|
||||
result.darea = newDrawingArea()
|
||||
|
||||
result.darea.setHExpand#(true)
|
||||
result.darea.setVExpand#(true)
|
||||
result.darea.connect("draw", drawingAreaDrawCb, result)
|
||||
result.darea.connect("configure-event", dareaConfigureCallback, result)
|
||||
result.darea.addEvents({EventFlag.buttonPress, EventFlag.buttonRelease,
|
||||
|
|
@ -1130,12 +1126,11 @@ proc newPDA: PDA =
|
|||
onAdjustmentEvent, result)
|
||||
result.hscrollbar = newScrollbar(Orientation.horizontal, result.hadjustment)
|
||||
result.vscrollbar = newScrollbar(Orientation.vertical, result.vadjustment)
|
||||
result.attach(result.darea, 0, 1, 0, 1, {AttachFlag.expand, AttachFlag.fill}, {
|
||||
AttachFlag.expand, AttachFlag.fill}, 0, 0)
|
||||
result.attach(result.hscrollbar, 0, 1, 1, 2, {AttachFlag.expand,
|
||||
AttachFlag.fill}, {}, 0, 0)
|
||||
result.attach(result.vscrollbar, 1, 2, 0, 1, {}, {AttachFlag.expand,
|
||||
AttachFlag.fill}, 0, 0)
|
||||
result.hscrollbar.setHExpand#(true)
|
||||
result.vscrollbar.setVExpand#(true)
|
||||
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) =
|
||||
let window = newApplicationWindow(app)
|
||||
|
|
|
|||
297
examples/drawingarea.nim
Normal file
297
examples/drawingarea.nim
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
# 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 :-)
|
||||
# License MIT
|
||||
|
||||
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
|
||||
|
||||
discard """
|
||||
Zooming, scrolling, panning...
|
||||
|
||||
|-------------------------|
|
||||
|<-------- A ------------>|
|
||||
| |
|
||||
| |---------------| |
|
||||
| | <---- a ----->| |
|
||||
| | visible | |
|
||||
| |---------------| |
|
||||
| |
|
||||
| |
|
||||
|-------------------------|
|
||||
|
||||
a is the visible, zoomed in area == darea.allocatedWidth
|
||||
A is the total data range
|
||||
A/a == userZoom >= 1
|
||||
For horizontal adjustment we use
|
||||
hadjustment.setUpper(darea.allocatedWidth * userZoom) == A
|
||||
hadjustment.setPageSize(darea.allocatedWidth) == a
|
||||
So hadjustment.value == left side of visible area
|
||||
|
||||
Initially, we set userZoom = 1, scale our data to fit into darea.allocatedWidth
|
||||
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
|
||||
"""
|
||||
|
||||
# drawing area and scroll bars in 2x2 table (PDA == Plain Drawing Area)
|
||||
|
||||
type
|
||||
PosAdj = ref object of Adjustment
|
||||
handlerID: uint64
|
||||
|
||||
proc newPosAdj: PosAdj =
|
||||
initAdjustment(result, 0, 0, 1, 1, 10, 1)
|
||||
|
||||
type
|
||||
PDA = ref object of Grid
|
||||
zoomNearMousepointer: bool
|
||||
selecting: bool
|
||||
userZoom: float
|
||||
surf: Surface
|
||||
darea: DrawingArea
|
||||
hadjustment: PosAdj
|
||||
vadjustment: PosAdj
|
||||
hscrollbar: Scrollbar
|
||||
vscrollbar: Scrollbar
|
||||
fullScale: float
|
||||
dataX: float
|
||||
dataY: float
|
||||
dataWidth: float
|
||||
dataHeight: float
|
||||
lastButtonDownPosX: float
|
||||
lastButtonDownPosY: float
|
||||
lastMousePosX: float
|
||||
lastMousePosY: float
|
||||
zoomRectX1: float
|
||||
zoomRectY1: float
|
||||
|
||||
proc drawingAreaDrawCb(darea: DrawingArea; cr: Context; this: PDA): bool =
|
||||
if this.surf.isNil: return
|
||||
cr.setSource(patternCreateForSurface(this.surf))
|
||||
cr.paint
|
||||
if this.selecting:
|
||||
cr.rectangle(this.lastButtonDownPosX, this.lastButtonDownPosY,
|
||||
this.zoomRectX1 - this.lastButtonDownPosX,
|
||||
this.zoomRectY1 - this.lastButtonDownPosY)
|
||||
cr.setSource(0, 0, 1, 0.5) # SELECT_RECT_COL) # 0, 0, 1, 0.5
|
||||
cr.fillPreserve
|
||||
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.
|
||||
|
||||
# clamp to correct values, 0 <= value <= (adj.upper - adj.pageSize), block calling onAdjustmentEvent()
|
||||
proc updateVal(adj: PosAdj; d: float) =
|
||||
adj.signalHandlerBlock(adj.handlerID)
|
||||
adj.setValue(max(0.0, min(adj.value + d, adj.upper - adj.pageSize)))
|
||||
adj.signalHandlerUnblock(adj.handlerID)
|
||||
|
||||
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.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?
|
||||
proc paint(this: PDA) =
|
||||
if this.surf != nil:
|
||||
cairosurfaceDestroy(this.surf.impl) # we may have to fix that later for automatically destroy
|
||||
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
|
||||
|
||||
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.fullScale = min(this.darea.allocatedWidth.float / this.dataWidth,
|
||||
this.darea.allocatedHeight.float / this.dataHeight)
|
||||
this.paint
|
||||
|
||||
proc updateAdjustmentsAndPaint(this: PDA; dx, dy: float) =
|
||||
this.updateAdjustments(dx, dy)
|
||||
this.paint
|
||||
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) =
|
||||
((eventX - this.hadjustment.upper * 0.5 + this.hadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataX + this.dataWidth * 0.5,
|
||||
(eventY - this.vadjustment.upper * 0.5 + this.vadjustment.value) / (
|
||||
this.fullScale * this.userZoom) + this.dataY + this.dataHeight * 0.5)
|
||||
|
||||
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)
|
||||
elif button2 in state: # panning
|
||||
this.updateAdjustmentsAndPaint(this.lastMousePosX - x,
|
||||
this.lastMousePosY - y)
|
||||
this.lastMousePosX = x
|
||||
this.lastMousePosY = y
|
||||
#event.request # request more motion events ?
|
||||
|
||||
# zooming with mouse wheel -- data near mouse pointer should not move if possible!
|
||||
# hadjustment.value + event.x is the position in our zoomed_in world, (userZoom / z0 - 1)
|
||||
# is the relative movement caused by zooming
|
||||
proc scrollEvent(darea: DrawingArea; event: EventScroll; this: PDA): bool =
|
||||
let z0 = this.userZoom
|
||||
var d = getScrollDirection(event)
|
||||
var (x, y) = event.getCoords
|
||||
if d == ScrollDirection.up:
|
||||
this.userZoom *= ZOOM_FACTOR_MOUSE_WHEEL
|
||||
elif d == ScrollDirection.down:
|
||||
this.userZoom /= ZOOM_FACTOR_MOUSE_WHEEL
|
||||
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))
|
||||
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))
|
||||
|
||||
proc buttonPressEvent(darea: DrawingArea; event: EventButton;
|
||||
this: PDA): bool =
|
||||
var (x, y) = event.getCoords
|
||||
this.lastMousePosX = x
|
||||
this.lastMousePosY = y
|
||||
this.lastButtonDownPosX = x
|
||||
this.lastButtonDownPosY = y
|
||||
echo "buttonPressEvent", x, " ", y
|
||||
(x, y) = this.getUserCoordinates(x, y)
|
||||
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 =
|
||||
var (x, y) = event.getCoords
|
||||
var b = getButton(event)
|
||||
if b == 1:
|
||||
this.selecting = false
|
||||
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...
|
||||
this.userZoom *= z1
|
||||
this.updateAdjustmentsAndPaint(
|
||||
((2 * this.hadjustment.value + x + this.lastButtonDownPosX) * z1 -
|
||||
this.darea.allocatedWidth.float) * 0.5 - this.hadjustment.value,
|
||||
((2 * this.vadjustment.value + y + this.lastButtonDownPosY) * z1 -
|
||||
this.darea.allocatedHeight.float) * 0.5 - this.vadjustment.value)
|
||||
|
||||
proc onAdjustmentEvent(this: PosAdj; pda: PDA) =
|
||||
pda.paint
|
||||
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.userZoom = 1.0
|
||||
result.surf = nil
|
||||
result.darea = newDrawingArea()
|
||||
result.darea.setHExpand#(true)
|
||||
result.darea.setVExpand#(true)
|
||||
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})
|
||||
result.darea.connect("motion-notify-event", onMotion, result)
|
||||
result.darea.connect("scroll_event", scrollEvent, result)
|
||||
result.darea.connect("button_press_event", buttonPressEvent, result)
|
||||
result.darea.connect("button_release_event", buttonReleaseEvent, result)
|
||||
result.hadjustment = newPosAdj()
|
||||
result.hadjustment.handlerID = result.hadjustment.connect("value-changed",
|
||||
onAdjustmentEvent, result)
|
||||
result.vadjustment = newPosAdj()
|
||||
result.vadjustment.handlerID = result.vadjustment.connect("value-changed",
|
||||
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.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) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "Drawing example"
|
||||
window.defaultSize = (400, 400)
|
||||
let pda = newPDA()
|
||||
window.add(pda)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
author = "Stefan Salewski"
|
||||
description = "High level GObject-Introspection based GTK3 bindings"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ proc $1$2 {.cdecl.} =
|
|||
r1s.add(resu & "\n")
|
||||
all = all.replace(")", "; data: pointer)")
|
||||
r1s = r1s % [$procNameCdecl, all, $p, wts, ats]
|
||||
echo r1s
|
||||
#echo r1s
|
||||
result = parseStmt(r1s)
|
||||
if not ignoreArg.boolVal:
|
||||
ahl = ahl.replace(")", "; arg: " & ats & ")")
|
||||
|
|
@ -175,7 +175,7 @@ proc $1(self: $2; p: proc $3; a: $4): culong {.discardable.} =
|
|||
sc$5(self, $6, cast[pointer](ar[]))
|
||||
$1($7, $8, $9)
|
||||
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit)]
|
||||
echo r2s
|
||||
#echo r2s
|
||||
result.add(parseStmt(r2s))
|
||||
|
||||
template connect*(widget: gobject.Object; signal: string; p: typed; arg: typed): untyped =
|
||||
|
|
|
|||
328
tests/gen.nim
328
tests/gen.nim
|
|
@ -1,5 +1,5 @@
|
|||
# High level gobject-introspection based GTK3 bindings for the Nim programming language
|
||||
# v 0.4.12 2019-APR-11
|
||||
# v 0.4.12 2019-APR-14
|
||||
# (c) S. Salewski 2018
|
||||
|
||||
# https://wiki.gnome.org/Projects/GObjectIntrospection
|
||||
|
|
@ -460,6 +460,12 @@ proc genP(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil): (str
|
|||
if genProxy and (str == "string" or str == "cstring") and mayBeNil: # v0.4.11
|
||||
#str.add(" = nil")
|
||||
str.add(" = \"\"")
|
||||
if genProxy and m == 0:
|
||||
if sym.contains("_set_") and str == "bool":
|
||||
#echo "TTTTTTTTTT", sym
|
||||
str.add(" = true")
|
||||
|
||||
|
||||
if genProxy and isProxyCandidate(t) and mayBeNil:
|
||||
#if not str.contains("|") and (gArgInfoGetDirection(arg) == GIDirection.OUT or gArgInfoGetDirection(arg) == GIDirection.INOUT):
|
||||
# str.add(" = cast[" & str & "](nil)")
|
||||
|
|
@ -573,6 +579,15 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
|
|||
var ret2 = gCallableInfoGetReturnType(minfo)
|
||||
#var ret22 = gCallableInfoGetReturnType(minfo)
|
||||
for run in 0 .. 1:
|
||||
|
||||
#if run == 0:
|
||||
# if asym.startsWith("set"):
|
||||
# if gCallableInfoGetNArgs(minfo) == 1 or (gCallableInfoIsMethod(minfo) and gCallableInfoGetNArgs(minfo) == 2):
|
||||
# echo "XXXXXXXXXX", asym, " ", gCallableInfoGetNArgs(minfo)
|
||||
|
||||
|
||||
|
||||
|
||||
if run == 1:
|
||||
|
||||
#if not (asym != "errorQuark" and asym != "getPlugin" and asym != "quark"): continue
|
||||
|
|
@ -589,6 +604,10 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
|
|||
asym = asym[3 .. ^1]
|
||||
asym[0] = asym[0].toLowerAscii
|
||||
if asym.startsWith("set"):
|
||||
#if plist.contains(" = true"):
|
||||
#echo "ZZZZZZ ", plist, " ", arglist
|
||||
plist = plist.replace(": bool = true", ": bool")
|
||||
|
||||
asym = asym[3 .. ^1]
|
||||
asym[0] = asym[0].toLowerAscii
|
||||
asym = '`' & asym & '=' & '`'
|
||||
|
|
@ -601,12 +620,18 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo; genProxy = false) =
|
|||
if gBaseInfoGetType(iface) == GIInfoType.Object and gBaseInfoGetName(iface) != "ParamSpec":
|
||||
isGObject = true
|
||||
(plist, arglist, replist) = genP(mInfo, true, info)
|
||||
if asym[^2] == '=':
|
||||
plist = plist.replace(": bool = true", ": bool")
|
||||
if (gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) != 0:
|
||||
if info != nil:
|
||||
asym = asym.replace("new", "new" & manglename(gBaseInfoGetName(info)))
|
||||
if fixedProcNames.contains(sym):
|
||||
asym = fixedProcNames[sym]
|
||||
for i in 0 .. 1:
|
||||
|
||||
#if i > 0 and isGObject:
|
||||
# echo "XYZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ", asym
|
||||
|
||||
var hhh = "\nproc " & asym & EM & plist
|
||||
if i > 0:
|
||||
let semi = if hhh.contains("()"): "" else: "; "
|
||||
|
|
@ -1401,6 +1426,237 @@ proc processInfo(i: GIBaseInfo) =
|
|||
writeConst(i)
|
||||
else: assert(false)
|
||||
|
||||
|
||||
# Gtk objects builder may provide. Only for Gtk module, so Gtk prefix is assume.
|
||||
# Other objects loke gtkSourceView are separated!
|
||||
# This a guessed list, so it may be inaccurate.
|
||||
const GtkBuilderObjects = """
|
||||
|
||||
AboutDialog
|
||||
AccelLabel
|
||||
ActionBar
|
||||
ActionGroup
|
||||
Alignment
|
||||
AppChooserButton
|
||||
AppChooserDialog
|
||||
AppChooserWidget
|
||||
Application
|
||||
ApplicationWindow
|
||||
Arrow
|
||||
AspectFrame
|
||||
Assistant
|
||||
Box
|
||||
Builder
|
||||
Button
|
||||
ButtonBox
|
||||
Calendar
|
||||
CellAreaBox
|
||||
CellView
|
||||
CheckButton
|
||||
CheckMenuItem
|
||||
ColorButton
|
||||
ColorChooserDialog
|
||||
ColorChooserWidget
|
||||
ColorSelectionDialog
|
||||
ComboBox
|
||||
ComboBoxText
|
||||
Dialog
|
||||
DrawingArea
|
||||
Entry
|
||||
EntryCompletion
|
||||
EventBox
|
||||
Expander
|
||||
FileChooserButton
|
||||
FileChooserNative
|
||||
FileChooserWidget
|
||||
FileFilter
|
||||
Fixed
|
||||
FlowBox
|
||||
FontButton
|
||||
FontChooserDialog
|
||||
FontChooserWidget
|
||||
FontSelection
|
||||
FontSelectionDialog
|
||||
Frame
|
||||
GLArea
|
||||
Grid
|
||||
HandleBox
|
||||
HBox
|
||||
HButtonBox
|
||||
HeaderBar
|
||||
HPaned
|
||||
HScale
|
||||
HScrollbar
|
||||
HSeparator
|
||||
IconView
|
||||
ImageMenuItem
|
||||
InfoBar
|
||||
Invisible
|
||||
Label
|
||||
Layout
|
||||
LevelBar
|
||||
LinkButton
|
||||
ListBox
|
||||
ListBoxRow
|
||||
ListStore
|
||||
LockButton
|
||||
Menu
|
||||
MenuBar
|
||||
MenuButton
|
||||
MenuItem
|
||||
MenuToolButton
|
||||
ModelButton
|
||||
Notebook
|
||||
OffscreenWindow
|
||||
Overlay
|
||||
Paned
|
||||
PlacesSidebar
|
||||
Popover
|
||||
PopoverMenu
|
||||
ProgressBar
|
||||
RadioButton
|
||||
RadioMenuItem
|
||||
RadioToolButton
|
||||
RecentChooserMenu
|
||||
RecentChooserWidget
|
||||
RecentManager
|
||||
Revealer
|
||||
Scale
|
||||
ScaleButton
|
||||
Scrollbar
|
||||
ScrolledWindow
|
||||
SearchBar
|
||||
SearchEntry
|
||||
Separator
|
||||
SeparatorMenuItem
|
||||
SeparatorToolItem
|
||||
ShortcutLabel
|
||||
SizeGroup
|
||||
SpinButton
|
||||
Spinner
|
||||
Stack
|
||||
StackSidebar
|
||||
StackSwitcher
|
||||
Statusbar
|
||||
Style
|
||||
Switch
|
||||
Table
|
||||
TearoffMenuItem
|
||||
TextView
|
||||
ToggleButton
|
||||
ToggleToolButton
|
||||
Toolbar
|
||||
ToolButton
|
||||
ToolItem
|
||||
ToolItemGroup
|
||||
ToolPalette
|
||||
TreeStore
|
||||
TreeView
|
||||
TreeViewColumn
|
||||
VBox
|
||||
VButtonBox
|
||||
Viewport
|
||||
VolumeButton
|
||||
VPaned
|
||||
VScrollbar
|
||||
VSeparator
|
||||
Window
|
||||
|
||||
"""
|
||||
|
||||
const GTK_SOURCE_EPI = """
|
||||
|
||||
proc getView(builder: Builder; name: string): View =
|
||||
new result
|
||||
let gt = g_type_from_name("GSource")
|
||||
assert(gt != 0)
|
||||
result.impl = gtk_builder_get_object(cast[ptr Builder00](builder.impl), name)
|
||||
assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](result.impl), gt)))
|
||||
|
||||
proc completionContextGetType*(): GType {.importc: "gtk_source_completion_context_get_type", libprag.}
|
||||
|
||||
proc completionProviderGetType*(): GType {.importc: "gtk_source_completion_provider_get_type", libprag.}
|
||||
|
||||
proc completionWordsGetType*(): GType {.importc: "gtk_source_completion_words_get_type", libprag.}
|
||||
|
||||
proc bufferGetType*(): GType {.importc: "gtk_source_buffer_get_type", libprag.}
|
||||
|
||||
proc completionGetType*(): GType {.importc: "gtk_source_completion_get_type", libprag.}
|
||||
|
||||
proc completionInfoGetType*(): GType {.importc: "gtk_source_completion_info_get_type", libprag.}
|
||||
|
||||
proc completionItemGetType*(): GType {.importc: "gtk_source_completion_item_get_type", libprag.}
|
||||
|
||||
proc completionProposalGetType*(): GType {.importc: "gtk_source_completion_proposal_get_type", libprag.}
|
||||
|
||||
proc encodingGetType*(): GType {.importc: "gtk_source_encoding_get_type", libprag.}
|
||||
|
||||
proc fileGetType*(): GType {.importc: "gtk_source_file_get_type", libprag.}
|
||||
|
||||
proc fileLoaderGetType*(): GType {.importc: "gtk_source_file_loader_get_type", libprag.}
|
||||
|
||||
proc fileSaverGetType*(): GType {.importc: "gtk_source_file_saver_get_type", libprag.}
|
||||
|
||||
proc gutterGetType*(): GType {.importc: "gtk_source_gutter_get_type", libprag.}
|
||||
|
||||
proc gutterRendererGetType*(): GType {.importc: "gtk_source_gutter_renderer_get_type", libprag.}
|
||||
|
||||
proc gutterRendererTextGetType*(): GType {.importc: "gtk_source_gutter_renderer_text_get_type", libprag.}
|
||||
|
||||
proc gutterRendererPixbufGetType*(): GType {.importc: "gtk_source_gutter_renderer_pixbuf_get_type", libprag.}
|
||||
|
||||
proc languageGetType*(): GType {.importc: "gtk_source_language_get_type", libprag.}
|
||||
|
||||
proc languageManagerGetType*(): GType {.importc: "gtk_source_language_manager_get_type", libprag.}
|
||||
|
||||
proc viewGetType*(): GType {.importc: "gtk_source_view_get_type", libprag.}
|
||||
|
||||
proc mapGetType*(): GType {.importc: "gtk_source_map_get_type", libprag.}
|
||||
|
||||
proc markGetType*(): GType {.importc: "gtk_source_mark_get_type", libprag.}
|
||||
|
||||
proc markAttributesGetType*(): GType {.importc: "gtk_source_mark_attributes_get_type", libprag.}
|
||||
|
||||
proc printCompositorGetType*(): GType {.importc: "gtk_source_print_compositor_get_type", libprag.}
|
||||
|
||||
proc regionGetType*(): GType {.importc: "gtk_source_region_get_type", libprag.}
|
||||
|
||||
proc searchContextGetType*(): GType {.importc: "gtk_source_search_context_get_type", libprag.}
|
||||
|
||||
proc searchSettingsGetType*(): GType {.importc: "gtk_source_search_settings_get_type", libprag.}
|
||||
|
||||
proc spaceDrawerGetType*(): GType {.importc: "gtk_source_space_drawer_get_type", libprag.}
|
||||
|
||||
proc styleGetType*(): GType {.importc: "gtk_source_style_get_type", libprag.}
|
||||
|
||||
proc styleSchemeGetType*(): GType {.importc: "gtk_source_style_scheme_get_type", libprag.}
|
||||
|
||||
proc styleSchemeChooserGetType*(): GType {.importc: "gtk_source_style_scheme_chooser_get_type", libprag.}
|
||||
|
||||
proc styleSchemeChooserButtonGetType*(): GType {.importc: "gtk_source_style_scheme_chooser_button_get_type", libprag.}
|
||||
|
||||
proc styleSchemeChooserWidgetGetType*(): GType {.importc: "gtk_source_style_scheme_chooser_widget_get_type", libprag.}
|
||||
|
||||
proc styleSchemeManagerGetType*(): GType {.importc: "gtk_source_style_scheme_manager_get_type", libprag.}
|
||||
|
||||
proc tagGetType*(): GType {.importc: "gtk_source_tag_get_type", libprag.}
|
||||
|
||||
proc undoManagerGetType*(): GType {.importc: "gtk_source_undo_manager_get_type", libprag.}
|
||||
|
||||
proc completionWordsProposalGetType*(): GType {.importc: "gtk_source_completion_words_proposal_get_type", libprag.}
|
||||
|
||||
proc completionWordsLibraryGetType*(): GType {.importc: "gtk_source_completion_words_library_get_type", libprag.}
|
||||
|
||||
proc completionWordsBufferGetType*(): GType {.importc: "gtk_source_completion_words_buffer_get_type", libprag.}
|
||||
|
||||
# register the GObject types so builder can use them, see
|
||||
# https://mail.gnome.org/archives/gtk-list/2015-March/msg00016.html
|
||||
discard viewGetType()
|
||||
discard completionInfoGetType()
|
||||
discard styleSchemeChooserButtonGetType()
|
||||
|
||||
"""
|
||||
|
||||
const GDK_EPI = """
|
||||
|
||||
proc gdk_window_invalidate_nilrect(self: ptr Window00; rect: ptr Rectangle; invalidateChildren: gboolean) {.
|
||||
|
|
@ -1419,6 +1675,60 @@ proc fixednewCursorFromName*(display: Display; name: string): Cursor =
|
|||
#assert(g_object_get_qdata(result.impl, Quark) == nil)
|
||||
g_object_set_qdata(result.impl, Quark, addr(result[]))
|
||||
|
||||
proc getAngle*(self: SomeEvent; event2: SomeEvent): cdouble =
|
||||
if not toBool(gdk_events_get_angle(cast[ptr Event00](self.impl), cast[ptr Event00](event2.impl), result)):
|
||||
raise newException(Defect, "Events do not support getAngle().")
|
||||
|
||||
proc getCenter*(self: SomeEvent; event2: SomeEvent): (cdouble, cdouble) =
|
||||
if not toBool(gdk_events_get_center(cast[ptr Event00](self.impl), cast[ptr Event00](event2.impl), result[0], result[1])):
|
||||
raise newException(Defect, "Events do not support getCenter().")
|
||||
|
||||
proc getDistance*(self: SomeEvent; event2: SomeEvent): cdouble =
|
||||
if not toBool(gdk_events_get_distance(cast[ptr Event00](self.impl), cast[ptr Event00](event2.impl), result)):
|
||||
raise newException(Defect, "Events do not support getDistance().")
|
||||
|
||||
proc getAxis*(self: SomeEvent; axisUse: AxisUse): cdouble =
|
||||
if not toBool(gdk_event_get_axis(cast[ptr Event00](self.impl), axisUse, result)):
|
||||
raise newException(Defect, "Event does not support getAxis().")
|
||||
|
||||
proc getButton*(self: SomeEvent): int =
|
||||
var button: uint32
|
||||
if not toBool(gdk_event_get_button(cast[ptr Event00](self.impl), button)):
|
||||
raise newException(Defect, "Event don't has a button field.")
|
||||
return int(button)
|
||||
|
||||
proc getClickCount*(self: SomeEvent): int =
|
||||
var clickCount_00: uint32
|
||||
if not toBool(gdk_event_get_click_count(cast[ptr Event00](self.impl), clickCount_00)):
|
||||
raise newException(Defect, "Event does not support getClickCount().")
|
||||
return int(clickCount_00)
|
||||
|
||||
proc getCoords*(self: SomeEvent): (cdouble, cdouble) =
|
||||
if not toBool(gdk_event_get_coords(cast[ptr Event00](self.impl), result[0], result[1])):
|
||||
raise newException(Defect, "This event don't has a coordinate field.")
|
||||
|
||||
proc getKeyval*(self: SomeEvent): int =
|
||||
var keyval_00 = uint32
|
||||
if not toBool(gdk_event_get_keyval(cast[ptr Event00](self.impl), keyval_00)):
|
||||
raise newException(Defect, "This event don't has a keyval field.")
|
||||
return int(keyval_00)
|
||||
|
||||
proc getRootCoords*(self: SomeEvent): (cdouble, cdouble) =
|
||||
if not toBool(gdk_event_get_root_coords(cast[ptr Event00](self.impl), result[0], result[1])):
|
||||
raise newException(Defect, "This event don't has a rootCoordinate field.")
|
||||
|
||||
proc getScrollDeltas*(self: SomeEvent): (cdouble, cdouble) =
|
||||
if not toBool(gdk_event_get_scroll_deltas(cast[ptr Event00](self.impl), result[0], result[1])):
|
||||
raise newException(Defect, "This event don't has a scrollDeltas field.")
|
||||
|
||||
proc getScrollDirection*(self: SomeEvent): ScrollDirection =
|
||||
if not toBool(gdk_event_get_scroll_direction(cast[ptr Event00](self.impl), result)):
|
||||
raise newException(Defect, "Event don't has a scrollDirection field.")
|
||||
|
||||
proc getState*(self: SomeEvent): ModifierType =
|
||||
if not toBool(gdk_event_get_state(cast[ptr Event00](self.impl), result)):
|
||||
raise newException(Defect, "Event don't has a state field.")
|
||||
|
||||
"""
|
||||
|
||||
const GIO_EPI = """
|
||||
|
|
@ -1801,12 +2111,28 @@ proc main(namespace: string) =
|
|||
if namespace == "Gtk":
|
||||
output.write(GTK_EPI)
|
||||
|
||||
for i in GtkBuilderObjects.split:
|
||||
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)
|
||||
assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](result.impl), gt)))
|
||||
|
||||
""" % [i])
|
||||
output.write("")
|
||||
|
||||
if namespace == "Gio":
|
||||
output.write(GIO_EPI)
|
||||
|
||||
if namespace == "Gdk":
|
||||
output.write(GDK_EPI)
|
||||
|
||||
if namespace == "GtkSource":
|
||||
output.write(GTK_SOURCE_EPI)
|
||||
|
||||
output.flush
|
||||
var o = open("nim_gi" / (namespace.toLowerAscii) & ".nim", fmWrite)
|
||||
o.write(output.data)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue