From d9771de1a5e6a07aa4a0b3779a986ea41b514592 Mon Sep 17 00:00:00 2001 From: Jared Wahlstrand Date: Sun, 28 Jan 2024 19:13:49 -0500 Subject: [PATCH] parameters argument for GSimpleAction, add_action, add_stateful_action (#51) * allow parameter to be set in "add action" methods * add tests, add GVariant support for tuples * only check tuple length if the GI method works --- Project.toml | 2 +- src/GLib/actions.jl | 66 ++++++++++++++++++++++++++++++++++++-------- src/GLib/gvariant.jl | 58 +++++++++++++++++++++++--------------- test/action-group.jl | 8 ++++++ 4 files changed, 100 insertions(+), 34 deletions(-) diff --git a/Project.toml b/Project.toml index 2ce9a9fd..1d25fb23 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Gtk4" uuid = "9db2cae5-386f-4011-9d63-a5602296539b" -version = "0.6.0" +version = "0.6.1" [deps] BitFlags = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" diff --git a/src/GLib/actions.jl b/src/GLib/actions.jl index 57902583..e2d59f4a 100644 --- a/src/GLib/actions.jl +++ b/src/GLib/actions.jl @@ -1,43 +1,87 @@ # actions GSimpleAction(name::AbstractString; kwargs...) = GSimpleAction(name, nothing; kwargs...) + +function GSimpleAction(name::AbstractString, parameter::Type{T}; kwargs...) where T + pvtype = parameter == Nothing ? nothing : GVariantType(parameter) + GSimpleAction(name, pvtype) +end + +""" + GSimpleAction(name::AbstractString, + [parameter_type::Type{T}, [initial_state]]; kwargs...) where T + +Create an action with a `name` and optionally a `parameter_type` from a Julia +type (only a few simple types are supported) and an `initial_state`. If +`initial_state` is not provided, the action will be stateless. + +Keyword arguments set the action's GObject properties. +""" +function GSimpleAction(name::AbstractString, parameter::Type{T}, + initial_state; kwargs...) where T + pvtype = parameter == Nothing ? nothing : GVariantType(parameter) + GSimpleAction(name, pvtype, GVariant(initial_state)) +end + set_state(m::GSimpleAction, v::GVariant) = G_.set_state(m,v) # action maps push!(m::GActionMap, a::GAction) = (G_.add_action(m,a); m) delete!(m::GActionMap, a::AbstractString) = (G_.remove_action(m, a); m) -function add_action(m::GActionMap, name::AbstractString, handler::Function) - action = GSimpleAction(name) - push!(m,GAction(action)) +function add_action(m::GActionMap, name::AbstractString, + parameter::Type{T}, handler::Function) where T + action = GSimpleAction(name, parameter) + push!(m, GAction(action)) signal_connect(handler, action, :activate) action end -function add_action(m::GActionMap, name::AbstractString, cb, user_data) - action = GSimpleAction(name) +function add_action(m::GActionMap, name::AbstractString, handler::Function) + add_action(m, name, Nothing, handler) +end + +function add_action(m::GActionMap, name::AbstractString, + parameter::Type{T}, cb, user_data) where T + action = GSimpleAction(name, parameter) push!(m,GAction(action)) signal_connect(cb, action, :activate, Nothing, (Ptr{GVariant},), false, user_data) action end -function add_stateful_action(m::GActionMap, name::AbstractString, initial_state, - handler::Function) - action = GSimpleAction(name, nothing, GVariant(initial_state)) - push!(m,GAction(action)) +function add_action(m::GActionMap, name::AbstractString, cb, user_data) + add_action(m, name, Nothing, cb, user_data) +end + +function add_stateful_action(m::GActionMap, name::AbstractString, + parameter::Type{T}, initial_state, + handler::Function) where T + action = GSimpleAction(name, parameter, initial_state) + push!(m, GAction(action)) signal_connect(handler, action, :change_state) action end function add_stateful_action(m::GActionMap, name::AbstractString, initial_state, - cb, user_data) - action = GSimpleAction(name, nothing, GVariant(initial_state)) + handler::Function) + add_stateful_action(m, name, Nothing, initial_state, handler) +end + +function add_stateful_action(m::GActionMap, name::AbstractString, + parameter::Type{T}, initial_state, + cb, user_data) where T + action = GSimpleAction(name, parameter, initial_state) push!(m,GAction(action)) signal_connect(cb, action, :change_state, Nothing, (Ptr{GVariant},), false, user_data) action end +function add_stateful_action(m::GActionMap, name::AbstractString, initial_state, + cb, user_data) + add_stateful_action(m, name, Nothing, initial_state, cb, user_data) +end + # action groups push!(g::GSimpleActionGroup, a) = (push!(GActionMap(g), GAction(a)); g) delete!(g::GSimpleActionGroup, a::AbstractString) = (delete!(GActionMap(g), a); g) diff --git a/src/GLib/gvariant.jl b/src/GLib/gvariant.jl index 97bc7eee..06ed0003 100644 --- a/src/GLib/gvariant.jl +++ b/src/GLib/gvariant.jl @@ -12,6 +12,21 @@ let variant_fns = Expr(:block) Core.eval(GLib, variant_fns) end +# tuples +function GVariant(x::T) where T <: Tuple + vs = [GVariant(xi).handle for xi in x] + G_.Variant_new_tuple(vs) +end +function getindex(gv::GVariant, ::Type{T}) where T <: Tuple + t=fieldtypes(T) + if Sys.WORD_SIZE == 64 # GI method doesn't work on 32 bit CPU's -- should probably wrap the ccall by hand instead of this + n = G_.n_children(gv) + @assert n == length(t) + end + vs = [G_.get_child_value(gv, i-1)[t[i]] for i=1:length(t)] + tuple(vs...) +end + GVariant(::Type{T},x) where T = GVariant(convert(T, x)) Base.:(==)(lhs::GVariant, rhs::GVariant) = G_.equal(lhs,rhs) @@ -20,34 +35,33 @@ Base.:(<=)(lhs::GVariant, rhs::GVariant) = G_.compare(lhs, rhs) <= 0 Base.:(>)(lhs::GVariant, rhs::GVariant) = G_.compare(lhs, rhs) > 0 Base.:(>=)(lhs::GVariant, rhs::GVariant) = G_.compare(lhs, rhs) >= 0 +variant_type_string(::Type{Bool}) = "b" +variant_type_string(::Type{UInt8}) = "y" +variant_type_string(::Type{Int16}) = "n" +variant_type_string(::Type{UInt16}) = "q" +variant_type_string(::Type{Int32}) = "i" +variant_type_string(::Type{UInt32}) = "u" +variant_type_string(::Type{Int64}) = "x" +variant_type_string(::Type{UInt64}) = "t" +variant_type_string(::Type{Float64}) = "d" +variant_type_string(::Type{String}) = "s" +function variant_type_string(::Type{T}) where T <: Tuple + type_string = "(" + for t in fieldtypes(T) + type_string *= variant_type_string(t) + end + type_string *= ")" +end + function variant_type_string(::Type{T}) where T type_string = "" - if T == Bool - type_string = "b" - elseif T == UInt8 - type_string = "y" - elseif T == Int16 - type_string = "n" - elseif T == UInt16 - type_string = "q" - elseif T == Int32 - type_string = "i" - elseif T == UInt32 - type_string = "u" - elseif T == Int64 - type_string = "x" - elseif T == UInt64 - type_string = "t" - elseif T == Float64 - type_string = "d" - elseif T == String - type_string = "s" - elseif T == Any + if T == Any type_string = "*" + else + error("Type not implemented") end # TODO: # array - # tuple # maybe # dictionary type_string diff --git a/test/action-group.jl b/test/action-group.jl index ab029d62..f3145148 100644 --- a/test/action-group.jl +++ b/test/action-group.jl @@ -120,6 +120,7 @@ function cb(ac,va) end add_action(GActionMap(g), "new-action", cb) +add_action(GActionMap(g), "new-action-with-parameter", Bool, cb) end @@ -153,6 +154,8 @@ a5 = add_stateful_action(GActionMap(g), "new-action3", true, cb) GLib.set_state(a5, GVariant(false)) @test a5.state == GVariant(false) +a5 = add_stateful_action(GActionMap(g), "new-action3-par", Bool, true, cb) + end @testset "add stateful action cfunction" begin @@ -219,4 +222,9 @@ gv2 = GLib.GVariant(UInt8,2) @test gv2 > gv1 @test gv2 >= gv1 +# test tuples +gvt = GLib.GVariant((true,3,6.5)) +@test GLib.GVariantType(Tuple{Bool,Int,Float64}) == GLib.G_.get_type(gvt) +@test gvt[Tuple{Bool,Int,Float64}] == (true,3,6.5) + end