Clemapfel / Mousetrap.jl

Finally, a GUI Engine made for Julia
https://clemens-cords.com/mousetrap
GNU Lesser General Public License v3.0
387 stars 10 forks source link

Scrollable Tree View #47

Open rafael-guerra-www opened 7 months ago

rafael-guerra-www commented 7 months ago

How can we produce a scrollable Tree View using Mousetrap.jl, as in this Gtk.jl example

Clemapfel commented 7 months ago

Hi, sorry for the late reply. TreeView was deprecated in GTK4, but mousetrap has ColumnView which works almost identically.

image

To make any widget scrollable, you insert it into a Viewport, see here for more information.

To create the above, first update mousetrap (as there was a bug with ColumnView that was only fixed recently):

import Pkg; Pkg.update("Mousetrap")

Then run:

using Mousetrap

main() do app::Application
    window = Window(app)

    # create column view with columns
    column_view = ColumnView()

    row_index_column = push_back_column!(column_view, " ")
    count_column = push_back_column!(column_view, "#")
    name_column = push_back_column!(column_view, "Name")
    weight_column = push_back_column!(column_view, "Weight")
    unit_column = push_back_column!(column_view, "Units")

    # fill columns with text
    for i in 1:100
        row = [
            Label(string(i)),           # row index
            Label(string(rand(0:99))),  # count
            Label(rand(["Apple", "Orange", "Banana", "Kumquat", "Durian", "Mangosteen"])), # name
            Label(string(rand(0:100))), # weight
            Label(string(rand(["mg", "g", "kg", "ton"]))) # unit
        ]

        set_horizontal_alignment!.(row, ALIGNMENT_START)
        push_back_row!(column_view, row...)
    end

    # create viewport, this will add a scrollbar
    scrolled_viewport = Viewport()
    set_propagate_natural_width!(scrolled_viewport, true) # hides horizontal scrollbar
    set_child!(scrolled_viewport, column_view)

    # show both in window
    set_child!(window, scrolled_viewport)
    present!(window)
end
rafael-guerra-www commented 7 months ago

@Clemapfel, thanks for your time and nice example, which ran fine.

How can we add to the Mousetrap.jl code above some subtitles (or submenus) that expand when clicked to show their respective column view items?

See the example in the linked original Gtk code:

Gtk_TreeView

Clemapfel commented 7 months ago

ColumnView does not support nested rows, I removed that feature because it would've made filtering rows, which is a feature scheduled for a future version of mousetrap, impossible.

You can still kinda do this by inserting a widget that has the expandable element as a row, keeping everything but the first column empty.

There are two options for the widget, you can either use Expander, which has a label and a child:

expander = Expander()
set_label_widget!(expander, Label("Expander VEGETABLE"))
set_child!(expander, Label("Nested Tomato"))
set_widget_at!(column_view, row_index_column, 5, expander)

Or you can use a nested ListView, which is a little more complicated, but it auto-indents the nested elements, and you can have more than one nested child:

expander_list = ListView(ORIENTATION_VERTICAL)
nested_it = push_back!(expander_list, Label("ListView VEGETABLE"))
push_back!(expander_list, Label("Nested Tomato"), nested_it)
push_back!(expander_list, Label("Nested Apple"), nested_it)
set_widget_at!(column_view, row_index_column, 6, expander_list)

Here the outermost listview has only one child, which acts as the label "ListView VEGETABLE", while the inner listview has the actual elements "Nested Tomato", "Nested Apple".

image

You can see how the first column is stretched to fit the entire expander, which isn't ideal, but its the closest you can get using ColumnView.

Full main.jl for the image above is:

using Mousetrap

main() do app::Application
    window = Window(app)

    # create column view with columns
    column_view = ColumnView()

    row_index_column = push_back_column!(column_view, " ")
    count_column = push_back_column!(column_view, "#")
    name_column = push_back_column!(column_view, "Name")
    weight_column = push_back_column!(column_view, "Weight")
    unit_column = push_back_column!(column_view, "Units")

    set_expand!.((row_index_column, count_column, name_column, weight_column, unit_column), true)

    # fill columns with text
    for i in 1:100
        row = [
            Label(string(i)),           # row index
            Label(string(rand(0:99))),  # count
            Label(rand(["Apple", "Orange", "Banana", "Kumquat", "Durian", "Mangosteen"])), # name
            Label(string(rand(0:100))), # weight
            Label(string(rand(["mg", "g", "kg", "ton"]))) # unit
        ]

        # insert empty row, then set first element to expandable item, while leaving the other columns empty
        if i == 5 # using `Expander`
            expander = Expander()
            set_label_widget!(expander, Label("Expander VEGETABLE"))
            set_child!(expander, Label("Nested Tomato"))
            set_widget_at!(column_view, row_index_column, 5, expander)

        elseif i == 6 # using nested `ListView`
            expander_list = ListView(ORIENTATION_VERTICAL)
            nested_it = push_back!(expander_list, Label("ListView VEGETABLE"))
            push_back!(expander_list, Label("Nested Tomato"), nested_it)
            push_back!(expander_list, Label("Nested Apple"), nested_it)
            set_widget_at!(column_view, row_index_column, 6, expander_list)

        else # else fill all columns
            set_horizontal_alignment!.(row, ALIGNMENT_START)
            push_back_row!(column_view, row...)
        end
    end

    # create viewport, this will add a scrollbar
    scrolled_viewport = Viewport()
    set_propagate_natural_width!(scrolled_viewport, true) # hides horizontal scrollbar
    set_child!(scrolled_viewport, column_view)

    # show both in window
    set_child!(window, scrolled_viewport)
    present!(window)
end

In my personal opinion, if the nested elements are the last rows of the column view like in the GTK picture you send, I would do this by doing a vertical box whose first element is the column view, and the last two elements are ListViews. That way the first column doesn't get stretched.

image

# after `column_view` was filled

vbox = Box(ORIENTATION_VERTICAL)
push_back!(vbox, column_view)

expander_list = ListView(ORIENTATION_VERTICAL)
nested_it = push_back!(expander_list, Label("ListView VEGETABLE"))
push_back!(expander_list, Label("Nested Tomato"), nested_it)
push_back!(expander_list, Label("Nested Apple"), nested_it)
push_back!(vbox, expander_list)

scrolled_viewport = Viewport()
set_propagate_natural_width!(scrolled_viewport, true) # hides horizontal scrollbar
set_child!(scrolled_viewport, vbox) # child is now vbox, which contains column_view

But this way you can't have rows after the nested elements, since the end of the column_view is above them

rafael-guerra-www commented 7 months ago

Thank you for your feedback and I am sorry, but I could not test your latest code as I am now experiencing errors:

(julia.exe:20440): GLib-GIO-WARNING **: 21:13:43.406: C:\Users\jguerra\.julia\artifacts\13606487e48c4dea9d20813adf4f03a3edea59fd\bin\gdbus.exe dbus binary failed to launch bus, maybe incompatible version
[ERROR] In Mousetrap.main: MethodError: no method matching set_expand!(::ColumnViewColumn, ::Bool)

I will get back to you later when this has been sorted out.

PS: Meanwhile your snapshots do not seem to match the Gtk example? Tbc.

Clemapfel commented 7 months ago

My bad, I was using the development version of Mousetrap, simply remove the line 16 with set_expand! and it should work.

It doesn't match your picture because ColumnView does not support expanded rows, like I said the closest you can do is inserting a row that has an expander. The picture is using Gtk.TreeView, which was deprecated in 4.10 and is not part of mousetrap.