HowTo: Gtk.Scrollbars and Floating Widgets

Hi All,

So recently with a little project I have been working on, I found myself with a problem.

As shown by the illustration below, I wanted to have a widget (in this case a Gtk.InfoBar) be ‘inside’ the scrollbars of a TreeView I had, so it appeared as though it ‘floated’ on top of the TreeView.

The issue I had

Luckily GTK has widgets to achieve this purpose, however unluckily, there seemed to be a lack of documentation for them:/

However I managed to get my head around them so I thought I’d put this HowTo up for any others who struggle with this in the future (and for me in case I forget :P)

HowTo

The layout of the widgets

To the right you can see the layout needed for this ‘effect’.

Inside a toplevel Gtk.Table (of two columns and two rows), there is a Gtk.VBox, a Gtk.VScrollbar on the right and a Gtk.HScrollbar at the bottom. Inside the Gtk.VBox, there the ‘floating widget’, the widget we want to stay ‘on top’ and a scrollable widget (i.e. Gtk.TreeView, Gtk.IconView).

Essentially what we will do is:

  1. Pack all our widgets into the right places
  2. Synchronise the adjustments for the scrollbars and the scrollable widget
  3. Connect some signals
  4. Done!

NB: All code below is in Vala, however the methods and processes used are the same regardless of language

Okay so first we will create our custom container  ‘ScrolledWindowWithFloatingWidget’.


class ScrolledWindowWithFloatingWidget : Gtk.Table {

    private Gtk.HScrollbar hscrollbar;
    private Gtk.VScrollbar vscrollbar;

    private Gtk.Adjustment hadjustment = new Gtk.Adjustment(0, 0, 0, 0, 0, 0);
    private Gtk.Adjustment vadjustment = new Gtk.Adjustment(0, 0, 0, 0, 0, 0);

    private Gtk.VBox vbox;

As you can see above, this container widget is subclassed from the Gtk.Table that we will use to store all of the widgets. We have made the scrollbars fields of this class, as we are going to use them throughout the code, the same with the VBox. Also we have created two new Gtk.Adjustments which we will use to synchronise the scrolling of our scrollable widget and our scrolbars.

The adjustments simply hold values which will be used by the scrollbars and the scrollable widgets, to show how much has been scrolled, how much is currently showing, the boundaries for scrolling etc.

    
public ScrolledWindowWithFloatingWidget() {
        this.vbox = new Gtk.VBox(false, 0);

        this.hadjustment.changed.connect(this.on_hadjustment_changed);
        this.vadjustment.changed.connect(this.on_vadjustment_changed);

        this.hscrollbar = new Gtk.HScrollbar(this.hadjustment);
        this.vscrollbar = new Gtk.VScrollbar(this.vadjustment);

        this.attach(this.vbox, 0, 1, 0, 1, Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, 0, 0);
        this.attach(hscrollbar, 0, 1, 1, 2, Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0);
        this.attach(vscrollbar, 1, 2, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, 0, 0);
    }

Above, in the construct method of this class, we create the VBox to store our two widgets, create the two scrollbars (using the adjustments we have already created) and pack these widgets into our container. Also we connect some signals to callbacks, which I shall explain later.


    public void add_scrollable_widget(Gtk.Widget widget) {
        widget.set_scroll_adjustments(this.hadjustment, this.vadjustment);
        widget.scroll_event.connect(this.on_widget_scroll_event);
        widget.set_size_request(1, 1);
        this.vbox.pack_start(widget, true, true);
    }

Next we have a public function of this class. This should be called to add the scrollable widget to the container. What it does is set the scroll adjustments of the widget (i.e. the adjustment which the widget to look for so it knows how much to show at any given time), connect a signal to a callback which shall come up later and also packs the widget into the VBox.

(The size request thing is needed for the widget to grow to fit the vbox, but I don’t really know why, all I know is that it doesn’t have any negative effects🙂 )


    public void add_floating_widget(Gtk.Widget widget) {
        this.vbox.pack_start(widget, true, true);
        this.vbox.reorder_child(widget, 0);
    }

Here is a simple function to add our ‘floating widget’. It simply packs the widget into the VBox and makes sure it is at the top.


    private void on_hadjustment_changed() {
        // If the scrollbar is needed, show it, otherwise hide it
        if (this.hadjustment.page_size == this.hadjustment.upper) {
            this.hscrollbar.hide();
        } else {
            this.hscrollbar.show();
        }
    }
    
    private void on_vadjustment_changed() {
        // If the scrollbar is needed, show it, otherwise hide it
        if (this.vadjustment.page_size == this.vadjustment.upper) {
            this.vscrollbar.hide();
        } else {
            this.vscrollbar.show();
        }
    }

These callbacks are connect to the ‘changed’ signal of both Gtk.Adjustments. Basically these callbacks control whether the scrollbars should be shown, based on whether they are needed (i.e. is the scrollable widget too big to fit in the available space and therefore needs to be scrolled.)


    private bool on_widget_scroll_event(Gdk.EventScroll event) {
        if ((event.direction == Gdk.ScrollDirection.UP) || (event.direction == Gdk.ScrollDirection.DOWN)) {
            this.vscrollbar.scroll_event(event);
        } else if ((event.direction == Gdk.ScrollDirection.LEFT) || (event.direction == Gdk.ScrollDirection.RIGHT)) {
            this.hscrollbar.scroll_event(event);
        }
        return true;
    }

This is the final callback and takes care of when a user scrolls the mouse-wheel on the scrollable widget. It checks whether they scrolled horizontally or vertically and passes the event onto the appropriate scrollbar.

The finished product

And that is it! Your floating widget and Gtk.Scrollbars combo should now work a treat! You can download the full code from here: https://code.launchpad.net/~and471/+junk/programming-howtos

3 responses to “HowTo: Gtk.Scrollbars and Floating Widgets

  1. dakira

    Hey.. you’re still alive😉. So IS Kazam still alive, or did you drop it?

    • Well it is pretty much dropped for the moment🙂

      • Thomas Wright

        That is a shame. I might however be able to do some work on the coding side in a few weeks; I have quite a bit of experience programming in Python and it would be a shame to see such a promising project die.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: