Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 116 additions & 25 deletions src/cairo.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Cairo: CairoSurface, CairoContext, CairoARGBSurface
import Cairo: CairoSurface, CairoContext, CairoARGBSurface, set_source_surface, paint
import ..GLib: _GObjectClass

using Cairo_jll
using ..Pango.Cairo
Expand Down Expand Up @@ -89,6 +90,118 @@ function resize(config::Function, widget::GtkCanvas)
nothing
end

### CairoCanvas is experimental and unstable

mutable struct CairoCanvas <: GtkWidget
handle::Ptr{GObject}
content_size::Tuple{Int,Int}
is_sized::Bool
resize::Union{Function, Nothing}
draw::Union{Function, Nothing}
back::CairoSurface # backing store
backcc::CairoContext

function CairoCanvas(handle::Ptr{GObject}, w::Integer = -1, h::Integer = -1, init_back::Bool = false)
content_size = (0,0)
if w > 0 && h > 0
content_size = (w,h)
elseif init_back
error("Width and height arguments must be provided to immediately initialize CairoCanvas.")
end
widget = new(handle, content_size, false, nothing, nothing)
if init_back
_init_canvas!(widget, w, h)
end

return widget
end
end

"""
getgc(c::GtkCanvas)

Return the CairoContext of the `CairoSurface` backing store of a `GtkCanvas`.
"""
function getgc(c::Union{GtkCanvas,CairoCanvas})
isdefined(c,:backcc) || error("GtkCanvas not yet initialized.")
return c.backcc
end

CairoContext(cr::cairoContext) = CairoContext(Ptr{Nothing}(cr.handle))
Cairo.cairoContext(cr::CairoContext) = cairoContext(Ptr{cairoContext}(cr.ptr),false)

function widget_measure(widget_ptr::Ptr{GObject}, orientation::Cint, for_size::Cint, minimum::Ptr{Cint}, natural::Ptr{Cint}, minimum_baseline::Ptr{Cint}, natural_baseline::Ptr{Cint})
canvas = convert(CairoCanvas, widget_ptr)
s = if orientation == Orientation_HORIZONTAL
canvas.content_size[1]
else
canvas.content_size[2]
end
unsafe_store!(minimum, s)
unsafe_store!(natural, s)
unsafe_store!(natural_baseline, Cint(-1))
nothing
end

function widget_size_allocate(widget_ptr::Ptr{GObject}, width::Cint, height::Cint, baseline::Cint)
canvas = convert(CairoCanvas, widget_ptr)
_init_canvas!(canvas, width, height)
canvas.is_sized = true

if isa(canvas.resize, Function)
canvas.resize(canvas)
end
draw(canvas)
nothing
end

function widget_snapshot(widget_ptr::Ptr{GObject}, snapshot_ptr::Ptr{GObject})
canvas = convert(CairoCanvas, widget_ptr)
snapshot = convert(GtkSnapshot, snapshot_ptr)
w,h = size(canvas)
cr = Gtk4.G_.append_cairo(snapshot, Ref(_GrapheneRect(0,0,w,h)))
cc = CairoContext(Ptr{Nothing}(cr.handle))
set_source_surface(cc, canvas.back)
paint(cc)
nothing
end

function object_class_init(class::Ptr{_GObjectClass}, user_data)
widget_klass_ptr = Ptr{_GtkWidgetClass}(class)
widget_klass = unsafe_load(widget_klass_ptr)
widget_klass.snapshot = @cfunction(widget_snapshot, Cvoid, (Ptr{GObject}, Ptr{GObject}))
widget_klass.measure = @cfunction(widget_measure, Cvoid, (Ptr{GObject}, Cint, Cint, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}))
widget_klass.size_allocate = @cfunction(widget_size_allocate, Cvoid, (Ptr{GObject}, Cint, Cint, Cint))
unsafe_store!(widget_klass_ptr, widget_klass)
nothing
end

function GLib.g_type(::Type{T}) where T <: CairoCanvas
gt = GLib.g_type_from_name(:CairoCanvas)
if gt > 0
return gt
else
object_class_init_cfunc = @cfunction(object_class_init, Cvoid, (Ptr{_GObjectClass}, Ptr{Cvoid}))
return GLib.register_subtype(GtkWidget, :CairoCanvas, object_class_init_cfunc)
end
end

function CairoCanvas(w::Integer = -1, h::Integer = -1, init_back::Bool = false; kwargs...)
widget = GLib.gobject_new(CairoCanvas, w, h, init_back)
GLib.setproperties!(widget; kwargs...)
widget
end

"""
cairo_surface(c::GtkCanvas)

Return the image `CairoSurface` backing store for a `GtkCanvas`.
"""
function cairo_surface(c::Union{GtkCanvas,CairoCanvas})
isdefined(c,:back) || error("Canvas not yet initialized.")
return c.back
end

"""
draw(redraw::Function, widget::GtkCanvas)

Expand All @@ -97,7 +210,7 @@ needs to be redrawn. The function should have a single argument, the
`GtkCanvas`, from which the `CairoSurface` can be retrieved using
[`getgc`](@ref).
"""
function draw(redraw::Function, widget::GtkCanvas)
function draw(redraw::Function, widget::Union{GtkCanvas,CairoCanvas})
widget.draw = redraw
draw(widget)
nothing
Expand All @@ -108,7 +221,7 @@ end

Triggers a redraw of the canvas using a previously set `redraw` function.
"""
function draw(widget::GtkCanvas)
function draw(widget::Union{GtkCanvas,CairoCanvas})
if !isdefined(widget, :back)
#@warn("backing store not defined")
return
Expand All @@ -119,25 +232,3 @@ function draw(widget::GtkCanvas)
G_.queue_draw(widget)
end

"""
getgc(c::GtkCanvas)

Return the CairoContext of the `CairoSurface` backing store of a `GtkCanvas`.
"""
function getgc(c::GtkCanvas)
isdefined(c,:backcc) || error("GtkCanvas not yet initialized.")
return c.backcc
end

"""
cairo_surface(c::GtkCanvas)

Return the image `CairoSurface` backing store for a `GtkCanvas`.
"""
function cairo_surface(c::GtkCanvas)
isdefined(c,:back) || error("GtkCanvas not yet initialized.")
return c.back
end

CairoContext(cr::cairoContext) = CairoContext(Ptr{Nothing}(cr.handle))
Cairo.cairoContext(cr::CairoContext) = cairoContext(Ptr{cairoContext}(cr.ptr),false)
26 changes: 26 additions & 0 deletions test/gui/canvas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,29 @@ c = GtkCanvas(10,10,true)
@test isdefined(c,:back)
end

@testset "CairoCanvas initialization" begin
# must provide a nonzero size to use `init_back`
@test_throws ErrorException c = Gtk4.CairoCanvas(0,0,true)
c = Gtk4.CairoCanvas(10,10,true)
@test isdefined(c,:back)
end

@testset "SetCoordinates CairoCanvas" begin
cnvs = Gtk4.CairoCanvas(300, 280)
sleep(0.2)
draw(cnvs) do c
set_coordinates(getgc(c), BoundingBox(0, 1, 0, 1))
end
win = GtkWindow(cnvs)
sleep(1.0)
s = size(cnvs)
@test s[1] == 300
@test s[2] == 280
mtrx = Cairo.get_matrix(getgc(cnvs))
@test mtrx.xx == 300
@test mtrx.yy == 280
@test mtrx.xy == mtrx.yx == mtrx.x0 == mtrx.y0 == 0
surf = Gtk4.cairo_surface(cnvs)
destroy(win)
end

Loading