How to make GtkScrolledWindow work together with GtkFixed inside GtkOverlay

Viewed 117

The basic problem is this:

I have a bunch of widgets that the user can move around. This is achieved by using a GtkOverlay as the outer container, and inside this, is a bunch of GtkFixed, each containing the corresponding widget:

  • GtkOverlay
    • GtkFixed
      • Widget A
    • GtkFixed
      • Widget B
    • ...

The reason for the outer GtkOverlay is that GtkFixed lacks support for z-index. Now each GtkFixed covers the entire area. Thus, to make it possible to activate a widget that is currently below, the pass-through property has to be set on each child of the GtkOverlay. The system works for the leaf widgets GtkScale and GtkDrawingArea. However, If I put something inside a GtkScrolledWindow in GTK assumes that the GtkScrolledWindow is located in the upper left corner of the GtkFixed, which in general is wrong. Unsetting the pass-through property solves the problem, but then other widgets become inaccessible. Is there any workaround for this.

I think the proper solution would be to have each child of the GtkOverlay not to occupy the whole area. Then pass-through would not be needed, but I think it is not possible to do this with GTK (or maybe you could if you write your own layout container, which behaves like a web page with free floating layers.

Minimal non-working example:

//@ {
//@  "targets":[{"name":"minimal-nonworking-example-so-64090741","type":"application", "pkgconfig_libs":["gtk+-3.0"]}]
//@ }

#include <gtk/gtk.h>
#include <string>
#include <vector>
#include <algorithm>

// To be able to feed the list box
template<class Callback>
void readlines(FILE* src, Callback&& cb)
{
    std::string buffer;
    while(true)
    {
        auto ch_in = getc(src);
        if(ch_in == -1)
        {
            if(buffer.size() != 0)
            {
                cb(buffer);
            }
            return;
        }

        if(ch_in=='\n')
        {
            cb(buffer);
            buffer.clear();
        }
        else
        {
            buffer+=ch_in;
        }
    }
}

int main(int argc, char** argv)
{
    gtk_init(&argc, &argv);
    auto mainwin = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
    gtk_window_set_title(mainwin, "ListBoxTest");
    gtk_window_set_default_size(mainwin, 800, 500);
    auto overlay = GTK_OVERLAY(gtk_overlay_new());
    gtk_container_add(GTK_CONTAINER(mainwin), GTK_WIDGET(overlay));

    gtk_widget_set_size_request(GTK_WIDGET(overlay), 500, 300);

    auto frame = GTK_FRAME(gtk_frame_new(nullptr));
    auto fixed = GTK_FIXED(gtk_fixed_new());
    gtk_fixed_put(fixed, GTK_WIDGET(frame), 60, 60);
    gtk_overlay_add_overlay(overlay, GTK_WIDGET(fixed));

    // {
    // Without this line, GtkScrolledWindow works as expected, but if there were more layers
    // other layers would become inaccessible
    gtk_overlay_set_overlay_pass_through(overlay, GTK_WIDGET(fixed), TRUE);
    // }
    gtk_overlay_reorder_overlay(overlay, GTK_WIDGET(fixed), -1);

    auto scrolled_window = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(nullptr, nullptr));
    gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(scrolled_window));
    gtk_widget_set_size_request(GTK_WIDGET(frame), 128, 128);
    auto listbox = GTK_LIST_BOX(gtk_list_box_new());
    gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(listbox));

    gtk_widget_show_all(GTK_WIDGET(mainwin));

    readlines(stdin, [listbox](auto&& item) {
        auto label = GTK_LABEL(gtk_label_new(item.c_str()));
        gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
        gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START);
        gtk_list_box_insert(listbox, GTK_WIDGET(label), -1);
    });

    gtk_widget_show_all(GTK_WIDGET(fixed));

    gtk_main();
    gtk_widget_destroy(GTK_WIDGET(mainwin));
    return 0;
}
0 Answers
Related