Skip to content

Commit

Permalink
GtkFlowBox, more tests for actions, misc.
Browse files Browse the repository at this point in the history
  • Loading branch information
jwahlstrand committed Sep 24, 2023
1 parent b450b3d commit b61e49c
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
36 changes: 32 additions & 4 deletions docs/src/manual/gettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down Expand Up @@ -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
```

40 changes: 37 additions & 3 deletions docs/src/manual/layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
```
2 changes: 1 addition & 1 deletion docs/src/manual/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
| :--- | :--- | :--- |
Expand Down
12 changes: 11 additions & 1 deletion src/GLib/actions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/layout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion src/lists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
24 changes: 23 additions & 1 deletion test/action-group.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions test/gui/examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 35 additions & 2 deletions test/gui/listviews.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -48,7 +49,7 @@ destroy(win)

end

@testset "Listbox" begin
@testset "ListBox" begin
win = GtkWindow("ListBox demo with filter")
box = GtkBox(:v)
entry = GtkSearchEntry()
Expand Down Expand Up @@ -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

2 changes: 2 additions & 0 deletions test/gui/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ for child in g1
i += 1
end

@test length(Gtk4.toplevels()) == 1

destroy(w)
end

Expand Down
3 changes: 1 addition & 2 deletions test/gui/window.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b61e49c

Please sign in to comment.