Debarshi's den

GdMainBox — the new content-view widget in libgd

with 3 comments

Now that I have written at length about the new fluid overview grids in GNOME Photos, it is time to talk a bit about the underlying widgets doing the heavy lifting. Hopefully some of my fellow GNOME developers will find this interesting.

Background

Ever since its incubation inside Documents, libgd has had a widget called GdMainView. It is the one which shows the grid or list of items in the new GNOME applications — Boxes, Photos, Videos, etc.. It is where drag-n-drop, rubber band selection and the selection mode pattern are implemented.

However, as an application developer, I think its greatest value is in making it trivial to switch the main content view from a grid to a list and back. No need to worry about the differences in how the data will be modelled or rendered. No need to worry about all the dozens of little details that arise when the main UI of an application is switched like that. For example, this is all that the JavaScript code in Documents does:

  let view = new Gd.MainView({ shadow_type: Gtk.ShadowType.NONE });
  …
  view.view_type = Gd.MainViewType.LIST; // use a list
  …
  view.view_type = Gd.MainViewType.ICON; // use a grid


Unfortunately, GdMainView is based on GtkIconView and GtkTreeView. By this time we all know that GtkIconView has various performance and visual problems. While GtkTreeView might not be slow, the fact that it uses an entirely separate class of visual elements that are not GtkWidgets limits what one can render using it. That’s where GdMainBox comes in.

GdMainBox

GdMainBox is a replacement for GdMainView that is meant to use GtkFlowBox and GtkListBox instead.

GListModel *model;
GtkWidget *view;

model = /* a GListModel containing GdMainBoxItems */
view = gd_main_box_new (GD_MAIN_BOX_ICON);
gd_main_box_set_model (GD_MAIN_BOX (view), model);
g_signal_connect (view,
                  "item-activated",
                  G_CALLBACK (item_activated_cb),
                  data);
g_signal_connect (view,
                  "selection-mode-request",
                  G_CALLBACK (selection_mode_request_cb),
                  data);
g_signal_connect (view,
                  "selection-changed", /* not view-selection-changed */
                  G_CALLBACK (selection_changed_cb),
                  data);


If you are familiar with with old GdMainView widget, you will notice the striking similarity with it. Except one thing. The data model.

GdMainView expected applications to offer a GtkTreeModel with a certain number of columns arranged in a certain order with certain type of values in them. Nothing surprising since both GtkIconView and GtkTreeView rely on the existence of a GtkTreeModel.

In the world of GtkListBoxes and GtkFlowBoxes, the data model is GListModel, a list-like collection of GObjects [*]. Therefore, instead of columns in a table, they need objects with certain properties, and methods to access them. These are codified in the GdMainBoxItem interface which every rendered object needs to implement. You can look at this commit for an example. A nice side-effect is that an interface is inherently more type-safe than a GtkTreeModel whose expected layout is expressed as enumerated types. The compiler can not assert that a certain column does have the expected data type, so it left us vulnerable to bugs caused by inadvertent changes to either libgd or an application.

But why a new widget?

You can definitely use a GtkFlowBox or GtkListBox directly in an application, if that’s what you prefer. However, the vanilla GTK+ widgets don’t offer all the necessary features. I think there is value in consolidating the implementation of those features in a single place that can be shared across modules. It serves as a staging area for prototyping those features in a reasonably generic way so that they can eventually be moved to GTK+ itself. If nothing else, I didn’t want to duplicate the same code across the two applications that I am responsible for — Documents and Photos.

One particularly hairy thing that I encountered was the difference between how selections are handled by the stock GtkFlowBox and the intended behaviour of the content-view. Other niceties on offer are expanding thumbnails, selection mode, and drag-n-drop.

If you do decide to directly use the GTK+ widgets, then I would suggest that you at least use the same CSS style classes as GdMainBox — “content-view” for the entire view and “tile” for each child.

The future

I mentioned changing lists to grids and vice versa. Currently, GdMainBox only offers a grid of icons because Photos is the only user and it doesn’t offer a list view. That’s going to change when I port Documents to it. When that happens, changing the view is going to be just as easy as it used to be.

gd_main_view_set_view_type (GD_MAIN_BOX (view), GD_MAIN_BOX_LIST);



[*] Yes, it’s possible to use them without a model, but having a GListModel affords important future performance optimizations, so we will ignore that possibility.

Written by Debarshi Ray

29 March, 2017 at 00:06

Posted in Blogroll, C, Documents, GNOME, GTK+, Photos

3 Responses

Subscribe to comments with RSS.

  1. Interesting, thanks for the explanations.

    Sébastien Wilmet

    29 March, 2017 at 07:28

  2. I know this is not the right way, to ask this question here, but since nobody can help me, i try here my luck 🙂

    I have some strange problems with the new MainBox and Vala. I’ve generated a vapi (https://paste.gnome.org/pavk6sgsz) for libgd.

    Now i want to implement the MainBoxItem interface to a test class. If I do this, i get a c error:

    Error: Redefinition of »gradio_test_item_real_get_id«.

    A workaround for this is, remove the getters from the vapi, so I don’t have to implement the getter methods. I think this is because, the property is generating the same C code as the getter method.

    Now I’m getting a strange runtime error from glib:

    GLib-GObject-CRITICAL **: Read-only property ‘icon’ on class ‘GradioTestItem’ has type ‘gpointer’ which is not equal to or more restrictive than the type ‘CairoSurface’ of the property on the interface ‘GdMainBoxItem’

    The rest is working fine (see here: http://i.imgur.com/7lFKnQc.png), I have only problems with the cairo surface icon.

    I have no idea how i can fix this. I’ve tried everything. I hope you can help me.

    my current test class:
    https://paste.gnome.org/pka0wrsro

    and the corresponding vapi:
    https://paste.gnome.org/pujzfdccg

    Felix Häcker

    18 April, 2017 at 15:51


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Facebook photo

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

Connecting to %s

%d bloggers like this: