diff --git a/Project.toml b/Project.toml index 97b9729c..c23a2e3c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Gtk4" uuid = "9db2cae5-386f-4011-9d63-a5602296539b" -version = "0.5.2" +version = "0.5.3" [deps] BitFlags = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" diff --git a/docs/src/manual/gettingStarted.md b/docs/src/manual/gettingStarted.md index 79de0d27..a953e4ba 100644 --- a/docs/src/manual/gettingStarted.md +++ b/docs/src/manual/gettingStarted.md @@ -16,10 +16,6 @@ We will now go through this example step by step. First the package is loaded `u ```julia push!(win,b) ``` -Since a `GtkWindow` can have only one child widget, we could have added the button to the window using -```julia -win[] = b -``` Finally, `show(win)` makes the window visible. This could also have been accomplished using the `visible` property (properties of "GObjects" like `GtkWindow` are discussed on the [Properties](../manual/properties.md) section of this manual). @@ -49,3 +45,35 @@ function on_button_clicked(w) end signal_connect(on_button_clicked, b, "clicked") ``` + +## The hierarchy of widgets + +In the example above, `GtkWindow` and `GtkButton` are GTK "widgets", which represent GUI elements. +Widgets are arranged in a hierarchy, with a `GtkWindow` at the top level (typically), inside which are widgets that contain other widgets. +A widget in this hierarchy can have child widgets and a parent widget. +The parent widget can be found using the method `parent`: +```julia +julia> parent(b) == win +true +``` +The toplevel widget in a particular widget's hierarchy can be found using the method `toplevel`: +```julia +julia> toplevel(b) == win +true +``` +Iterating over a widget gives you its child widgets: +```julia +for child in widget + myfunc(child) +end +``` + +Widgets can be added and removed using interface methods defined by Gtk4.jl. +For many widgets that can contain children, `push!` is defined to append a widget to another's children. +Some widget types can only have one child. +For this situation, Gtk4.jl defines `setindex!(w,x)` and `getindex(w)` methods with no arguments, which can be written as `w[] = x` and `output = w[]`, respectively. +For example, a `GtkWindow` can have only one child widget, so we could have added the button to the window in our example using +```julia +win[] = b +``` + diff --git a/docs/src/manual/layout.md b/docs/src/manual/layout.md index d97444e8..564e1d5f 100644 --- a/docs/src/manual/layout.md +++ b/docs/src/manual/layout.md @@ -7,7 +7,7 @@ Gtk4 provides many layout widgets for arranging widgets in a window. ## GtkBox -The simplest layout widget is the `GtkBox`. It is one-dimensional and can be either be horizontally or vertical aligned. +The layout widget used most often is `GtkBox`. It is one-dimensional and can be either be horizontally or vertical aligned. ```julia win = GtkWindow("New title") hbox = GtkBox(:h) # :h makes a horizontal layout, :v a vertical layout @@ -143,6 +143,17 @@ push!(nb, vbox, "Vertical") # here "Vertical" is the label for the tab push!(nb, hbox, "Horizontal") ``` +Julia interface methods defined for `GtkNotebook`: + +| method | what it does | +| :--- | :--- | +| `push!(n::GtkNotebook, x::GtkWidget, label::AbstractString)` | Appends a widget with a label | +| `push!(n::GtkNotebook, x::GtkWidget, label::GtkWidget)` | Appends a widget with a widget to be shown in the tab | +| `pushfirst!(n::GtkNotebook, x::GtkWidget, label::AbstractString)` | Prepends a widget with a label | +| `pushfirst!(n::GtkNotebook, x::GtkWidget, label::GtkWidget)` | Prepends a widget with a widget to be shown in the tab | +| `deleteat!(n::GtkNotebook, x::GtkWidget)` | Removes a widget from the notebook | +| `empty!(n::GtkNotebook)` | Removes all widgets from the notebook | + ## GtkStack The `GtkStack` widget is a lot like `GtkNotebook`, but a separate widget `GtkStackSwitcher` controls what page is shown. @@ -157,11 +168,34 @@ push!(s, GtkLabel("First label"), "id1", "Label 1") # first string is an id, se push!(s, GtkLabel("Second label"), "id2", "Label 2") # widget can be retrieved using s[id] ``` +Julia interface methods defined for `GtkStack`: + +| method | what it does | +| :--- | :--- | +| `getindex(s::GtkStack, name::AbstractString)` or `s[name]` | Gets a widget by name | +| `setindex!(s::GtkStack, x::GtkWidget, name::AbstractString)` or `s[name] = x` | Sets a widget by name | +| `push!(s::GtkStack, x::GtkWidget)` | Appends a widget | +| `push!(s::GtkStack, x::GtkWidget, name::AbstractString)` | Appends a widget with a name | +| `push!(s::GtkStack, x::GtkWidget, name::AbstractString, title::AbstractString)` | Appends a widget with a name and a title | +| `delete!(s::GtkStack, x::GtkWidget)` | Removes a widget from the stack | +| `empty!(s::GtkStack)` | Removes all widgets from the stack | + +## GtkFrame, GtkAspectFrame, and GtkExpander + +These widgets hold one child widget. `GtkFrame` and `GtkAspectFrame` display them in a decorative frame with an optional label. `GtkExpander` allows the user to hide the child. + +Julia interface methods defined for `GtkFrame`, `GtkAspectFrame`, and `GtkExpander`: + +| method | what it does | +| :--- | :--- | +| `getindex(f)` or `f[]` | Gets the child widget | +| `setindex!(f, w::Union{GtkWidget,Nothing})` or `f[] = w` | Sets or clears the child widget | + ## Iterating over child widgets For any of the widgets described above (or any `GtkWidget` that has children), you can iterate over all child widgets using ```julia -for w in widget - myfunc(w) +for child in widget + myfunc(child) end ``` diff --git a/docs/src/manual/methods.md b/docs/src/manual/methods.md index da00a97a..feafef32 100644 --- a/docs/src/manual/methods.md +++ b/docs/src/manual/methods.md @@ -22,7 +22,7 @@ Constants and struct definitions are also generated using GObject introspection. ## Constructors -Constructor methods in `G_` are treated a little differently. They are named according to GObject_constructor_name, as in the following table: +Constructor methods in `G_` are treated a little differently. They are named according to `GObject_$constructor_name`, as in the following table: | C function | Gtk4.G_ Julia method | Comments | | :--- | :--- | :--- | diff --git a/src/GLib/actions.jl b/src/GLib/actions.jl index 4a2241b2..57902583 100644 --- a/src/GLib/actions.jl +++ b/src/GLib/actions.jl @@ -21,13 +21,23 @@ function add_action(m::GActionMap, name::AbstractString, cb, user_data) action end -function add_stateful_action(m::GActionMap, name::AbstractString, initial_state, handler::Function) +function add_stateful_action(m::GActionMap, name::AbstractString, initial_state, + handler::Function) action = GSimpleAction(name, nothing, GVariant(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)) + push!(m,GAction(action)) + signal_connect(cb, action, :change_state, Nothing, + (Ptr{GVariant},), false, user_data) + action +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/base.jl b/src/base.jl index 5233700d..3141ad24 100644 --- a/src/base.jl +++ b/src/base.jl @@ -121,6 +121,13 @@ Related GTK function: [`gtk_widget_activate`()]($(gtkdoc_method_url("gtk4","Widg """ activate(w::GtkWidget) = G_.activate(w) +""" + toplevels() + +Returns a GListModel of all toplevel widgets (i.e. windows) known to GTK4. +""" +toplevels() = G_.get_toplevels() + @doc """ display(w::GtkWidget) diff --git a/src/layout.jl b/src/layout.jl index e4e41d0e..3880721e 100644 --- a/src/layout.jl +++ b/src/layout.jl @@ -226,6 +226,10 @@ function splice!(w::GtkNotebook, i::Integer) G_.remove_page(w, i - 1) w end +function deleteat!(w::GtkNotebook, i::Integer) + G_.remove_page(w, i - 1) + w +end pagenumber(w::GtkNotebook, child::GtkWidget) = G_.page_num(w, child) + 1 diff --git a/src/lists.jl b/src/lists.jl index 90e324cd..fc769c17 100644 --- a/src/lists.jl +++ b/src/lists.jl @@ -108,6 +108,23 @@ function set_filter_func(lb::GtkListBox, match::Function) return nothing end +## GtkFlowBox +setindex!(fb::GtkFlowBox, w::GtkWidget, i::Integer) = (G_.insert(fb, w, i - 1); fb[i]) +getindex(fb::GtkFlowBox, i::Integer) = G_.get_child_at_index(fb, i - 1) + +push!(fb::GtkFlowBox, w::GtkWidget) = (G_.append(fb, w); fb) +pushfirst!(fb::GtkFlowBox, w::GtkWidget) = (G_.prepend(fb, w); fb) +insert!(fb::GtkFlowBox, i::Integer, w::GtkWidget) = (G_.insert(fb, w, i - 1); fb) + +delete!(fb::GtkFlowBox, w::GtkWidget) = (G_.remove(fb, w); fb) + +function set_filter_func(fb::GtkFlowBox, match::Function) + cfunc = @cfunction(GtkFlowBoxFilterFunc, Cint, (Ptr{GObject}, Ref{Function})) + ref, deref = GLib.gc_ref_closure(match) + ccall(("gtk_flow_box_set_filter_func", libgtk4), Nothing, (Ptr{GObject}, Ptr{Nothing}, Ptr{Nothing}, Ptr{Nothing}), fb, cfunc, ref, deref) + return nothing +end + ## GtkCustomFilter @@ -119,7 +136,7 @@ function GtkCustomFilter(match::Function) end function set_filter_func(cf::GtkCustomFilter, match::Function) - cfunc = @cfunction(GtkListBoxFilterFunc, Cint, (Ptr{GObject}, Ref{Function})) + cfunc = @cfunction(GtkCustomFilterFunc, Cint, (Ptr{GObject}, Ref{Function})) ref, deref = GLib.gc_ref_closure(match) ccall(("gtk_custom_filter_set_filter_func", libgtk4), Nothing, (Ptr{GObject}, Ptr{Nothing}, Ptr{Nothing}, Ptr{Nothing}), cf, cfunc, ref, deref) return nothing diff --git a/test/action-group.jl b/test/action-group.jl index 9808aba6..956a4254 100644 --- a/test/action-group.jl +++ b/test/action-group.jl @@ -61,11 +61,33 @@ end delete!(g, "do-something") +# test the more sophisticated `signal_connect` signal_connect(on_action_added2, g, "action_added", Nothing, (String,), false, 3) -push!(g,a) +# test `add_action` +function cb(a,v) + nothing +end + +add_action(GActionMap(g), "new-action", cb) + @test extra_arg_ref[] == 3 +function cb2(a,v,user_data) + nothing +end + +add_action(GActionMap(g), "new-action2", cb2, 4) + +# test `add_stateful_action` + +a5 = add_stateful_action(GActionMap(g), "new-action3", true, cb) +add_stateful_action(GActionMap(g), "new-action4", true, cb2, 5) + +@test a5.state == GVariant(true) +GLib.set_state(a5, GVariant(false)) +@test a5.state == GVariant(false) + # test keyword constructor a2 = GSimpleAction("do-something-else";enabled=false) diff --git a/test/gui/examples.jl b/test/gui/examples.jl index a05e3859..c6cd197d 100644 --- a/test/gui/examples.jl +++ b/test/gui/examples.jl @@ -38,6 +38,7 @@ end @testset "Filtered List View" begin include(joinpath(@__DIR__, "..", "..", "examples", "filteredlistview.jl")) + @test Gtk4.G_.match(filter,Gtk4.GLib.G_.get_item(GListModel(model),0)) destroy(win) end diff --git a/test/gui/listviews.jl b/test/gui/listviews.jl index e500a993..b2d2602f 100644 --- a/test/gui/listviews.jl +++ b/test/gui/listviews.jl @@ -30,7 +30,8 @@ function bind_cb(f, li) end -list = GtkListView(GtkSelectionModel(GtkSingleSelection(model)), factory) +list = GtkListView(GtkSelectionModel(GtkSingleSelection(model))) +Gtk4.factory(list,factory) signal_connect(setup_cb, factory, "setup") signal_connect(bind_cb, factory, "bind") @@ -48,7 +49,7 @@ destroy(win) end -@testset "Listbox" begin +@testset "ListBox" begin win = GtkWindow("ListBox demo with filter") box = GtkBox(:v) entry = GtkSearchEntry() @@ -93,3 +94,35 @@ destroy(win) end +@testset "FlowBox" begin +win = GtkWindow("FlowBox demo with filter") +box = GtkBox(:v) +entry = GtkSearchEntry() +sw = GtkScrolledWindow() +push!(box, entry) +push!(box, sw) +push!(win, box) + +listBox = GtkFlowBox() +l=GtkLabel("widget 1") +push!(listBox, l) +@test listBox[1].child == l +l0=GtkLabel("widget 0") +pushfirst!(listBox, l0) +@test listBox[1].child == l0 +lmiddle=GtkLabel("widget 0.5") +insert!(listBox,2,lmiddle) +@test listBox[2].child == lmiddle + +delete!(listBox, listBox[1]) +@test listBox[2].child == l + +listBox[1] = GtkLabel("widget 2") +@test listBox[2].child != l +sw[] = listBox +listBox.vexpand = true + +destroy(win) + +end + diff --git a/test/gui/misc.jl b/test/gui/misc.jl index baf6684d..7e087cd4 100644 --- a/test/gui/misc.jl +++ b/test/gui/misc.jl @@ -95,6 +95,8 @@ for child in g1 i += 1 end +@test length(Gtk4.toplevels()) == 1 + destroy(w) end diff --git a/test/gui/window.jl b/test/gui/window.jl index f81a8772..9358a451 100644 --- a/test/gui/window.jl +++ b/test/gui/window.jl @@ -28,12 +28,11 @@ if m!==nothing r = Gtk4.G_.get_geometry(m) end -#r2 = m.geometry - hide(w) show(w) grab_focus(w) close(w) +sleep(0.2) destroy(w) end