added GTK4 support
This commit is contained in:
parent
d7aa177796
commit
fd6cda7fe8
18 changed files with 320 additions and 451 deletions
16
examples/gtk3/app0.nim
Normal file
16
examples/gtk3/app0.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# app0.nim -- minimal application style example
|
||||
# nim c app0.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "GTK3 & Nim"
|
||||
window.defaultSize = (200, 200)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
31
examples/gtk3/builder.nim
Normal file
31
examples/gtk3/builder.nim
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# https://developer.gnome.org/gtk3/stable/ch01s03.html
|
||||
# builder.nim -- application style example using builder/glade xml file for user interface
|
||||
# nim c builder.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
proc hello(b: Button; msg: string) =
|
||||
echo "Hello", msg
|
||||
|
||||
proc quitApp(b: Button; app: Application) =
|
||||
echo "Bye"
|
||||
quit(app)
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let builder = newBuilder()
|
||||
discard builder.addFromFile("builder.ui")
|
||||
let window = builder.getApplicationWindow("window")
|
||||
window.setApplication(app)
|
||||
var button = builder.getButton("button1")
|
||||
button.connect("clicked", hello, "")
|
||||
button = builder.getButton("button2")
|
||||
button.connect("clicked", hello, " again...")
|
||||
button = builder.getButton("quit")
|
||||
button.connect("clicked", quitApp, app)
|
||||
#showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
45
examples/gtk3/builder.ui
Normal file
45
examples/gtk3/builder.ui
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<interface>
|
||||
<object id="window" class="GtkApplicationWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="title">Grid</property>
|
||||
<property name="border-width">10</property>
|
||||
<child>
|
||||
<object id="grid" class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object id="button1" class="GtkButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">Button 1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object id="button2" class="GtkButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">Button 2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object id="quit" class="GtkButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">Quit</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
22
examples/gtk3/button.nim
Normal file
22
examples/gtk3/button.nim
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# nim c button.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
proc buttonClicked (button: Button) =
|
||||
button.label = utf8Strreverse(button.label, -1)
|
||||
|
||||
proc appActivate (app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "GNOME Button"
|
||||
window.defaultSize = (250, 50)
|
||||
let button = newButton("Click Me")
|
||||
window.add(button)
|
||||
button.connect("clicked", buttonClicked)
|
||||
window.showAll
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard app.run
|
||||
|
||||
main()
|
||||
|
||||
44
examples/gtk3/cairo_anim.nim
Normal file
44
examples/gtk3/cairo_anim.nim
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# https://lists.cairographics.org/archives/cairo/2016-October/027791.html
|
||||
# Nim version of that plain cairo animation example
|
||||
|
||||
import gintro/[gtk, glib, gobject, gio, cairo]
|
||||
import math
|
||||
|
||||
const
|
||||
NumPoints = 1000
|
||||
Period = 100.0
|
||||
|
||||
proc invalidateCb(w: Widget): bool =
|
||||
queueDraw(w)
|
||||
return SOURCE_CONTINUE
|
||||
|
||||
proc sineToPoint(x, width, height: int): float =
|
||||
math.sin(x.float * math.TAU / Period) * height.float * 0.5 + height.float * 0.5
|
||||
|
||||
proc drawingAreaDrawCb(widget: DrawingArea; context: Context): bool =
|
||||
var redrawNumber {.global.} : int
|
||||
let width = getAllocatedWidth(widget)
|
||||
let height = getAllocatedHeight(widget)
|
||||
for i in 1 ..< NumPoints:
|
||||
context.lineTo(i.float , sineToPoint(i + redrawNumber, width, height))
|
||||
context.stroke
|
||||
inc(redrawNumber)
|
||||
return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "Drawing example"
|
||||
window.defaultSize = (400, 400)
|
||||
let drawingArea = newDrawingArea()
|
||||
window.add(drawingArea)
|
||||
showAll(window)
|
||||
discard timeoutAdd(1000 div 60, invalidateCb, drawingArea)
|
||||
connect(drawingArea, "draw", drawingAreaDrawCb)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
|
||||
53
examples/gtk3/connect_args.nim
Normal file
53
examples/gtk3/connect_args.nim
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# nim c connect_args.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
type
|
||||
O = object
|
||||
i: int
|
||||
|
||||
proc b1Callback(button: Button; str: string) =
|
||||
echo str
|
||||
|
||||
proc b2Callback(button: Button; o: O) =
|
||||
echo "Value of field i in object o = ", o.i
|
||||
|
||||
proc b3Callback(button: Button; r: ref O) =
|
||||
echo "Value of field i in ref to object O = ", r.i
|
||||
|
||||
proc b4Callback(button: Button; w: ApplicationWindow) =
|
||||
if w.title == "Nim with GTK3":
|
||||
w.title = "GTK3 with Nim"
|
||||
else:
|
||||
w.title = "Nim with GTK3"
|
||||
|
||||
proc appActivate (app: Application) =
|
||||
var o: O
|
||||
var r: ref O
|
||||
new r
|
||||
o.i = 1234567
|
||||
r.i = 7654321
|
||||
let window = newApplicationWindow(app)
|
||||
let box = newBox(Orientation.vertical, 0)
|
||||
window.title = "Parameters for callbacks"
|
||||
let b1 = newButton("Nim with GTK3")
|
||||
let b2 = newButton("Passing an object from stack")
|
||||
let b3 = newButton("Passing an object from heap")
|
||||
let b4 = newButton("Passing a Widget")
|
||||
b1.connect("clicked", b1Callback, "is much fun.")
|
||||
b2.connect("clicked", b2Callback, o)
|
||||
b3.connect("clicked", b3Callback, r)
|
||||
b4.connect("clicked", b4Callback, window)
|
||||
box.add(b1)
|
||||
box.add(b2)
|
||||
box.add(b3)
|
||||
box.add(b4)
|
||||
window.add(box)
|
||||
window.showAll
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard app.run
|
||||
|
||||
main()
|
||||
|
||||
29
examples/gtk3/count_button.nim
Normal file
29
examples/gtk3/count_button.nim
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# nim c count_button.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
type
|
||||
CountButton = ref object of Button
|
||||
counter: int
|
||||
|
||||
proc buttonClicked (button: CountButton; decrement: int) =
|
||||
dec(button.counter, decrement)
|
||||
button.label = "Counter: " & $button.counter
|
||||
echo "Counter is now: ", button.counter
|
||||
|
||||
proc appActivate (app: Application) =
|
||||
var button: CountButton
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "Count Button"
|
||||
initButton(button, "Counting down from 100 by 5")
|
||||
button.counter = 100
|
||||
window.add(button)
|
||||
button.connect("clicked", buttonClicked, 5)
|
||||
window.showAll
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard app.run
|
||||
|
||||
main()
|
||||
|
||||
25
examples/gtk3/darea_test.nim
Normal file
25
examples/gtk3/darea_test.nim
Normal 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()
|
||||
317
examples/gtk3/drawingarea.nim
Normal file
317
examples/gtk3/drawingarea.nim
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
# Plain demo for zooming, panning, scrolling with GTK DrawingArea
|
||||
# (c) S. Salewski, 21-DEC-2010 (initial Ruby version)
|
||||
# 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
|
||||
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...
|
||||
|
||||
|-------------------------|
|
||||
|<-------- 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_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
|
||||
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
|
||||
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
|
||||
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 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) =
|
||||
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 for optimization we should only allocate a new surface when its size has changed.
|
||||
proc paint(this: PDA) =
|
||||
if this.surf != nil:
|
||||
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)
|
||||
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 =
|
||||
#this.updateAdjustments(0, 0)
|
||||
(this.dataX, this.dataY, this.dataWidth,
|
||||
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
|
||||
|
||||
proc hscrollbarSizeAllocateCallback(s: Scrollbar; r: gdk.Rectangle; pda: PDA) =
|
||||
pda.hadjustment.setUpper(r.width.float * pda.userZoom)
|
||||
pda.hadjustment.setPageSize(r.width.float)
|
||||
|
||||
proc vscrollbarSizeAllocateCallback(s: Scrollbar; r: gdk.Rectangle; pda: PDA) =
|
||||
pda.vadjustment.setUpper(r.height.float * pda.userZoom)
|
||||
pda.vadjustment.setPageSize(r.height.float)
|
||||
|
||||
|
||||
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
|
||||
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 *= ZoomFactorMouseWheel
|
||||
elif d == ScrollDirection.down:
|
||||
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))
|
||||
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 < ZoomFactorSelectMax: # 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 = ZoomNearMousepointer # mouse wheel zooming
|
||||
result.userZoom = 1.0
|
||||
result.darea = newDrawingArea()
|
||||
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, 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)
|
||||
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
|
||||
result.vscrollbar.setVExpand
|
||||
result.hscrollbar.connect("size-allocate", hscrollbarSizeAllocateCallback, result)
|
||||
result.vscrollbar.connect("size-allocate", vscrollbarSizeAllocateCallback, result)
|
||||
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; initData: ref PDA_Data) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "Drawing example"
|
||||
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 newDisplay*(initData: PDA_Data) =
|
||||
let app = newApplication("org.gtk.example")
|
||||
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()
|
||||
27
examples/gtk3/gaction.nim
Normal file
27
examples/gtk3/gaction.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# https://wiki.gnome.org/HowDoI/GAction
|
||||
# nim c gaction.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
proc saveCb(action: SimpleAction; v: Variant) =
|
||||
echo "saveCb"
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
let action = newSimpleAction("save")
|
||||
discard action.connect("activate", saveCB)
|
||||
window.actionMap.addAction(action)
|
||||
let button = newButton()
|
||||
button.label = "Save"
|
||||
window.add(button)
|
||||
button.setActionName("win.save")
|
||||
var h: cstringArray = allocCstringArray(["<Control><Shift>S"])
|
||||
setAccelsForAction(app, "win.save", h)
|
||||
deallocCStringArray(h)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
22
examples/gtk3/label.nim
Normal file
22
examples/gtk3/label.nim
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# nim c label.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
let label = newLabel("Yellow text on green background")
|
||||
let cssProvider = newCssProvider()
|
||||
let data = "label {color: yellow; background: green;}"
|
||||
#discard cssProvider.loadFromPath("doesnotexist")
|
||||
discard cssProvider.loadFromData(data)
|
||||
let styleContext = label.getStyleContext
|
||||
assert styleContext != nil
|
||||
addProvider(styleContext, cssProvider, STYLE_PROVIDER_PRIORITY_USER)
|
||||
window.add(label)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
102
examples/gtk3/listview.nim
Normal file
102
examples/gtk3/listview.nim
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# http://zetcode.com/gui/gtk2/gtktreeview/
|
||||
# dynamiclistview.c
|
||||
|
||||
import gintro/[glib, gobject, gtk]
|
||||
import gintro/gio except ListStore
|
||||
|
||||
const
|
||||
LIST_ITEM = 0
|
||||
N_COLUMNS = 1
|
||||
|
||||
var list: TreeView
|
||||
|
||||
# this is copied from gtk.nim
|
||||
#proc getModel*(self: TreeView): TreeModel =
|
||||
# new(result)
|
||||
# result.impl = gtk_tree_view_get_model(cast[ptr TreeView00](self.impl))
|
||||
|
||||
proc getListStore(self: TreeView): ListStore =
|
||||
new(result)
|
||||
result.impl = gtk_tree_view_get_model(cast[ptr TreeView00](self.impl))
|
||||
|
||||
proc appendItem(widget: Button; entry: Entry) =
|
||||
var
|
||||
val: Value
|
||||
iter: TreeIter
|
||||
let store = getListStore(list)
|
||||
let gtype = typeFromName("gchararray")
|
||||
discard gValueInit(val, gtype)
|
||||
gValueSetString(val, entry.text)
|
||||
store.append(iter)
|
||||
store.setValue(iter, LIST_ITEM, val)
|
||||
entry.text = ""
|
||||
|
||||
proc removeItem(widget: Button; selection: TreeSelection) =
|
||||
var
|
||||
ls: ListStore
|
||||
iter: TreeIter
|
||||
let store = getListStore(list)
|
||||
if not store.getIterFirst(iter):
|
||||
return
|
||||
if getSelected(selection, ls, iter):
|
||||
discard store.remove(iter)
|
||||
|
||||
proc onRemoveAll(widget: Button; selection: TreeSelection) =
|
||||
var
|
||||
iter: TreeIter
|
||||
let store = getListStore(list)
|
||||
if not store.getIterFirst(iter):
|
||||
return
|
||||
clear(store)
|
||||
|
||||
proc initList(list: TreeView) =
|
||||
let renderer = newCellRendererText()
|
||||
let column = newTreeViewColumn()
|
||||
column.title = "List Item"
|
||||
column.packStart(renderer, true)
|
||||
column.addAttribute(renderer, "text", LIST_ITEM)
|
||||
discard list.appendColumn(column)
|
||||
let gtype = typeFromName("gchararray")
|
||||
let store = newListStore(N_COLUMNS, cast[pointer]( unsafeaddr gtype)) # cast due to bug in gtk.nim
|
||||
list.setModel(store)
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let
|
||||
window = newApplicationWindow(app)
|
||||
sw = newScrolledWindow()
|
||||
hbox = newBox(Orientation.horizontal, 5)
|
||||
vbox = newBox(Orientation.vertical, 0)
|
||||
add = newButton("Add")
|
||||
remove = newButton("Remove")
|
||||
removeAll = newButton("Remove All")
|
||||
entry = newEntry()
|
||||
window. title = "List view"
|
||||
window.position = WindowPosition.center
|
||||
window.borderWidth = 10
|
||||
window.setSizeRequest(370, 270)
|
||||
list = newTreeView()
|
||||
sw.add(list)
|
||||
sw.setPolicy(PolicyType.automatic, PolicyType.automatic)
|
||||
sw.setShadowType(ShadowType.etchedIn)
|
||||
list.setHeadersVisible(false)
|
||||
vbox.packStart(sw, true, true, 5)
|
||||
entry.setSizeRequest(120, -1)
|
||||
hbox.packStart(add, false, true, 3)
|
||||
hbox.packStart(entry, false, true, 3)
|
||||
hbox.packStart(remove, false, true, 3)
|
||||
hbox.packStart(removeAll, false, true, 3)
|
||||
vbox.packStart(hbox, false, true, 3)
|
||||
window.add(vbox)
|
||||
initList(list)
|
||||
let selection = getSelection(list)
|
||||
connect(add, "clicked", appendItem, entry)
|
||||
connect(remove, "clicked", removeItem, selection)
|
||||
connect(removeAll, "clicked", onRemoveAll, selection)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("org.gtk.example")
|
||||
connect(app, "activate", appActivate)
|
||||
discard run(app)
|
||||
|
||||
main()
|
||||
105
examples/gtk3/menubar.nim
Normal file
105
examples/gtk3/menubar.nim
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# https://developer.gnome.org/glib/stable/glib-GVariant.html
|
||||
# https://developer.gnome.org/glib/stable/glib-GVariantType.html
|
||||
# https://wiki.gnome.org/HowDoI/GMenu
|
||||
# https://wiki.gnome.org/HowDoI/GAction
|
||||
# nim c menubar.nim
|
||||
import gintro/[gtk, glib, gobject, gio]
|
||||
from strutils import `%`, format
|
||||
|
||||
# https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c
|
||||
proc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) =
|
||||
app.hold # hold/release taken over from C example, there may be reasons...
|
||||
block:
|
||||
echo format("action $1 activated", action.name)
|
||||
let state: Variant = action.state
|
||||
let b = state.getBoolean
|
||||
action.state = newVariantBoolean(not b)
|
||||
echo format("state change $1 -> $2", b, not b)
|
||||
app.release
|
||||
|
||||
proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) =
|
||||
app.hold
|
||||
block:
|
||||
echo format("action $1 activated", action.name)
|
||||
let state: Variant = action.state
|
||||
var l: uint64
|
||||
let oldState = state.getString(l) # yes uint64 parameter is a bit ugly
|
||||
let newState = parameter.getString(l)
|
||||
action.state = newVariantString(newState)
|
||||
echo format("state change $1 -> $2", oldState, newState)
|
||||
app.release
|
||||
|
||||
proc quitProgram(action: SimpleAction; parameter: Variant; app: Application) =
|
||||
quit(app)
|
||||
|
||||
proc appStartup(app: Application) =
|
||||
let quit = newSimpleAction("quit") # here we create the actions for whole app
|
||||
connect(quit, "activate", quitProgram, app)
|
||||
app.addAction(quit)
|
||||
|
||||
let menu = gio.newMenu() # root of all menus
|
||||
block: # plain stateless menu
|
||||
let subMenu = gio.newMenu()
|
||||
menu.appendSubMenu("Application", submenu)
|
||||
# let section = gio.newMenu() # no separating section needed here
|
||||
# submenu.appendSection(nil, section)
|
||||
# section.append("Quit", "app.quit")
|
||||
submenu.append("Quit", "app.quit")
|
||||
|
||||
block: #stateful menu with radio items
|
||||
let subMenu = gio.newMenu()
|
||||
menu.appendSubMenu("Layout", submenu)
|
||||
let subMenu2 = gio.newMenu()
|
||||
submenu.appendSubMenu("justify", submenu2)
|
||||
let section = gio.newMenu()
|
||||
submenu2.appendSection(nil, section)
|
||||
section.append("left", "win.justify::left")
|
||||
section.append("center", "win.justify::center")
|
||||
section.append("right", "win.justify::right")
|
||||
|
||||
block: # and finally a toggle menu
|
||||
let subMenu = gio.newMenu()
|
||||
menu.appendSubMenu("Spelling", submenu)
|
||||
let section = gio.newMenu()
|
||||
submenu.appendSection(nil, section)
|
||||
section.append("Check", "win.toggleSpellCheck")
|
||||
# finally add the menubar
|
||||
setMenuBar(app, menu)
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
let window = newApplicationWindow(app)
|
||||
window.title = "GTK3 App with Menubar"
|
||||
window.defaultSize = (500, 200)
|
||||
window.position = WindowPosition.center
|
||||
block: # creat the window related actions
|
||||
let v = newVariantBoolean(true)
|
||||
let spellCheck = newSimpleActionStateful("toggleSpellCheck", nil, v)
|
||||
connect(spellCheck, "activate", activateToggleAction, app)
|
||||
window.actionMap.addAction(spellCheck)
|
||||
block:
|
||||
let v = newVariantString("left") # default value and
|
||||
let vt = newVariantType("s") # string (value type)
|
||||
let justifyAction = newSimpleActionStateful("justify", vt, v)
|
||||
connect(justifyAction, "activate", activateStatefulAction, app)
|
||||
window.actionMap.addAction(justifyAction)
|
||||
let button = newButton()
|
||||
button.label = "Justify Center"
|
||||
#window.add(button) # do not add it here already: (menubar:10010): Gtk-WARNING **: 22:00:33.230: actionhelper:
|
||||
# action win.justify can't be activated due to parameter type mismatch (parameter type s, target type NULL)
|
||||
button.setDetailedActionName("win.justify::center")
|
||||
#button.setActionName("app.quit") # for a stateless action
|
||||
var h: cstringArray = allocCstringArray(["<Control><Shift>R"]) # ugly
|
||||
setAccelsForAction(app, "win.justify::right", h)
|
||||
deallocCStringArray(h)
|
||||
window.add(button)
|
||||
showAll(window)
|
||||
|
||||
proc main =
|
||||
let app = newApplication("app.example")
|
||||
connect(app, "startup", appStartup)
|
||||
connect(app, "activate", appActivate)
|
||||
echo "GTK Version $1.$2.$3" % [$majorVersion(), $minorVersion(), $microVersion()]
|
||||
let status = run(app)
|
||||
quit(status)
|
||||
|
||||
main()
|
||||
16
examples/gtk3/t0.nim
Normal file
16
examples/gtk3/t0.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# nim c t0.nim
|
||||
import gintro/[gtk, gobject]
|
||||
|
||||
proc bye(w: Window) =
|
||||
mainQuit()
|
||||
echo "Bye..."
|
||||
|
||||
proc main =
|
||||
gtk.init()
|
||||
let window = newWindow()
|
||||
window.title = "First Test"
|
||||
window.connect("destroy", bye)
|
||||
window.showAll
|
||||
gtk.main()
|
||||
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue