-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot use GListStore - Boxed Any Type #29
Comments
I've only used the new list views using |
Thanks! Ill try this :) For others pursuing this deeper, I reccommend just creating a custom GListModel that is backed by a Julia Array for better memory & performance. It looks like gtk is having more emphasis towards using their type system so it might be worth it to take a look at the define class macro as I am sure more stuff will use it in the future |
It seems extreme to have to define a new GObject to properly use the new list views, but it looks like that's what you do in the Javascript bindings: https://rmnvgr.gitlab.io/gtk4-gjs-book/application/list-widgets/ Being able to do this would also allow defining custom widgets. I wonder if it could be done in pure Julia... |
I built an efficient manager with what we have with the GString idea. On my computer it can create a 2x100k row columnview in ~.5 seconds. I can PR it if anyone is interested in it. Below is the current version: append!(cv::GtkColumnView, cvc::GtkColumnViewColumn) = G_.append_column(cv, cvc)
GtkNoSelection(model) = G_.NoSelection_new(model)
mutable struct GtkJuliaStore
items::Dict{Ptr{GObject}, Any}
store::GListStore
freeNames::Array{Ptr{GObject}}
GtkJuliaStore() = new(Dict{Ptr{GObject}, Any}(), GLib.GListStore(:GObject), Ptr{GObject}[])
GtkJuliaStore(items::AbstractArray) = (g = GtkJuliaStore(); append!(g, items))
GtkJuliaStore(items...) = GtkJuliaStore(collect(items))
Gtk4.GListModel(g::GtkJuliaStore) = Gtk4.GListModel(g.store)
Base.getindex(g::GtkJuliaStore, i::Integer) = g.items[unsafegetname(g.store, i)]
Base.setindex!(g::GtkJuliaStore, v, i::Integer) = g.items[unsafegetname(g.store, i)] = v
Base.keys(lm::GtkJuliaStore) = keys(g.store)
Base.eltype(::Type{GtkJuliaStore}) = Any
Base.iterate(g::GtkJuliaStore, i=0) = (i == length(g) ? nothing : (getindex(g, i + 1), i + 1))
Base.length(g::GtkJuliaStore) = length(g.store)
Base.empty!(g::GtkJuliaStore) = (empty!(g.items); empty!(g.store); empty!(freeNames))
Base.pushfirst!(g::GtkJuliaStore, item) = insert!(g, 1, item)
Base.append!(g::GtkJuliaStore, items) = foreach(x -> push!(g, x), items)
Base.getindex(g::GtkJuliaStore, i::GtkListItem) = g.items[ccall(("gtk_list_item_get_item", libgtk4), Ptr{GObject}, (Ptr{GObject},), i)]
Base.setindex!(g::GtkJuliaStore, v, i::GtkListItem) = g.items[ccall(("gtk_list_item_get_item", libgtk4), Ptr{GObject}, (Ptr{GObject},), i)] = v
unsafegetname(ls::GListStore, i) = ccall(("g_list_model_get_object", libgio), Ptr{GObject}, (Ptr{GObject}, UInt32), ls, i-1)
function nextname(g::GtkJuliaStore)
name = length(g.freeNames) == 0 ? Symbol("$(length(g))") : pop!(g.freeNames)
return ccall(("gtk_string_object_new", libgtk4), Ptr{GObject}, (Cstring,), name)
end
function Base.push!(g::GtkJuliaStore, item)
name = nextname(g)
ccall(("g_list_store_append", libgio), Nothing, (Ptr{GObject}, Ptr{GObject}), g.store, name)
g.items[name] = item
return nothing
end
function Base.insert!(g::GtkJuliaStore, i::Integer, item)
name = nextname(g)
ccall(("g_list_store_insert", libgio), Nothing, (Ptr{GObject}, UInt32, Ptr{GObject}), g.store, i-1, name)
g.items[name] = item
return nothing
end
function Base.deleteat!(g::GtkJuliaStore, i::Integer)
name = unsafegetname(g.store, i)
push!(freeNames, name)
delete!(g.items, name)
ccall(("g_list_store_remove", libgio), Nothing, (Ptr{GObject}, UInt32), g.store, i-1)
return nothing
end
end
function GtkJuliaColumnViewColumn(store::GtkJuliaStore, name::String, @nospecialize(init_child::Function), @nospecialize(update_child::Function))
factory = GtkSignalListItemFactory()
signal_connect((f, li) -> set_child(li, init_child()), factory, "setup")
signal_connect((f, li) -> update_child(get_child(li), store[li]), factory, "bind")
return GtkColumnViewColumn(name, factory)
end
mutable struct MyTestStruct
num::Integer
name::String
end
##Testing
win = GtkWindow()
sw = GtkScrolledWindow()
win[] = sw
store = GtkJuliaStore()
@time for w in 1:100000
push!(store, MyTestStruct(w, "Number:$w"))
end
name_c = GtkJuliaColumnViewColumn(store, "Name", () -> GtkLabel(""), (c, i) -> c.label = i.name)
num_c = GtkJuliaColumnViewColumn(store, "Number", () -> GtkLabel(""), (c, i) -> c.label = string(i.num))
cv = GtkColumnView(model = GtkSelectionModel(GtkSingleSelection(Gtk4.GListModel(store))))
append!(cv, name_c)
append!(cv, num_c)
sw[] = cv |
What I had in mind was to use
Looking at the constructor for I think the API for |
Ah I see what you meant I think my version my be safe from collecting since I am using handles to the GString objects as the keys. I use unsafe functions to bypass all collecting so its not boxing/unboxing many times between calls. I think the GtkStringStore is also immutable (or atleast not meant to be changed often due to the internal structure) which is why I used the GtkListStore |
Yeah, I think GtkStringList is mostly meant to support simple situations like GtkDropDown. For optimal efficiency it seems like you're supposed to create your own GListModel that produces GObjects on command, rather than creating a ton of GObjects ahead of time. I think the purpose of the new list views is to avoid having to copy your data into a special model data structure (like GtkListStore or GtkTreeStore), which is pretty redundant. Instead you fetch the data needed to render the row in a callback. It sounds nice but they want the data in the form of a GObject... |
I have also been experimenting with building powerful observables. on_update_signal_name(::GtkButton) = "clicked"
on_update_signal_name(::GtkComboBoxText) = "changed"
on_update_signal_name(::GtkAdjustment) = "value-changed"
on_update_signal_name(::GtkEntry) = "activate"
Observables.on(@nospecialize(cb::Function), w::GtkWidget) = signal_connect(cb, w, on_update_signal_name(w))
Observables.connect!(w::GtkWidget, o::AbstractObservable) = on(v -> w[] = v, o)
Base.getindex(g::GtkEntry, ::Type{String}) = g.text
Base.getindex(g::GtkLabel, ::Type{String}) = g.label
Base.getindex(g::GtkComboBoxText, ::Type{String}) = Gtk4.active_text(g)
Base.getindex(g::GtkAdjustment, ::Type{Number}) = Gtk4.value(g)
Base.getindex(g::Union{GtkEntry, GtkLabel, GtkComboBoxText}, t::Type = String) = parse(g, t)
Base.setindex!(g::GtkLabel, v) = g.label = string(v)
Base.setindex!(g::GtkEntry, v) = g.text = string(v)
Base.setindex!(g::GtkAdjustment, v) = Gtk4.value(g, v)
function Gtk4.set_gtk_property!(o::GObject, name::String, value::AbstractObservable)
set_gtk_property!(o, name, value[])
on(v -> set_gtk_property!(o, name, v), value)
end
function Observables.ObservablePair(w::GtkWidget, o::AbstractObservable{T}) where T
done = Ref(false)
on(w) do w
if !done[]
done[] = true
o[] = w[T]
done[] = false
end
end
on(o) do val
if !done[]
done[] = true
w[] = val
done[] = false
end
end
end What this snippet allows you to do is something like this.... w = GtkWindow()
v = GtkEntry()
w[] = v
o = Observable(3)
on(v -> println("Changed:$v"), o)
connect!(w, o) #Update the widget entry when the observable changes (not when widget changes)
Observables.ObservablePair(v, o) #Update the the observable if the entry changes, update the entry if the observable changes.
You can also do stuff on non widgets since I overrided the set_gtk_prop.
my_random_g_object.prop = o #Will update the property when o is updated
Not sure if this is something that others may want to use in the future / something for this library, just something that may be good discussion |
Thanks, PR's are generally welcome. My goal for this package is to keep the layers of stuff on top of GObject introspection relatively light. If we can make the GtkListViews easier to use in Julia without defining new types, that would be ideal IMO. Other packages could do more sophisticated stuff on top of this one. In Gtk.jl, Observable support is in GtkObservables.jl, and I was thinking of keeping the same separation here to keep dependencies to a minimum (I'm not thrilled with the Graphics.jl dependency inherited from Gtk.jl). I have ported GtkObservables.jl to Gtk4 to test Gtk4.jl, but haven't used it much myself yet. |
Gtk4 Utilizes a new storage model GListStore which takes in a GObject.
I cannot find any documentation on how to insert in any data. I was wondering if a boxed julia value could be introduced.
My thinking on implementation is to create a G_TYPE that wraps an integer that points back to an Any[] on the julia side of things.
Let me know if you can do something like this at the moment.
The text was updated successfully, but these errors were encountered: