GTK: how should one edit a TreeListModel?

I would like some advice about how one is supposed to edit a GTK TreeListModel.

Problem statement

I want to create a UI for a drag-and-drop list of layers, like one has in e.g. GIMP or Inkscape. Layers can be put into groups and then one can drag the whole group.

My approach

I am using TreeListModel to store the layers. I have a custom object whose private data is:

  • for a layer, a unique identifier for that layer together with some additional properties, such as a name (to display in the UI)
  • for a group, a unique identifier, together with its children (if any)

The recursive structure of this data (with a group containing all its direct children, and thus recursively all descendants) means I can create a GTK TreeListModel, with the child model for a group being a simple GIO ListModel.

Now, the difficulty comes after a drag and drop operation has happened and I need to move data around in the model. It isn’t clear at all to me how to proceed.

Suppose for example I have the following layer structure

  • G1
    • G2
      • L3
      • L4
  • G5
    • L6

If I want to move L3 out of its parent groups and say put it inside G5 below L6, it seems I have to edit the list model in many places:

  • I have to remove the 3rd list item (L3), and put it back elsewhere (after L6). So far, so good.
  • I also have to edit the parent of L3, G2, because the private data for object G2 refers to L3, when L3 will no longer be one of its children.
  • I ALSO have to edit the parent of G2, G1, because as noted above its private data stored all descendants, while I’m removing L3 (and moving it elsewhere).
  • Conversely, I also need to update G5 to set its private data to add L3 as its second child.

This seems like I am going round the houses editing this list model, which leads me to believe I am going about this wrong and overcomplicating things.

Another issue is if I want to move a whole group around, say G2. I don’t think I can easily move the whole thing in one operation, I have to do a ListStore splice operation where I remove all the items and put them back elsewhere. This again suggests I’m going about this wrong, as I would naively think it should be possible to move an entire group with its descendants in one go, but perhaps that requires an approach where ListModels are nested inside other ListModels?

Summary

Can someone please advise how I should set up the ListModel for tree-like data like the layers and groups I am dealing with above, in such a way which facilitates updating the list model when data gets moved around?

1 Like

After struggling for quite a while, I have come to understand a few things.

Firstly, the TreeListModel provides a flattened view of the whole hierarchy, and it only supports querying at these “flattened” indices. So the way to perform the above update is:

  • Find out the indices of the source and target. This includes two things:
    • The parent indices. The source parent is G2 at (0-indexed) position 1. The destination parent is G5 at position 4. These can be computed using gtk_tree_list_row_get_position from the list items themselves (e.g. in the signal list item factory “on bind” action).
    • The indices within the parents, e.g. we are removing from position 1 in the child list of G2, and adding at position 1 in the child list of G5.
  • If the source position is smaller, do the insertion first, and then the deletion; otherwise the other way around. (This avoids having to shift indices due to items moving around.)
  • To remove L3 from G2, query the TreeListModel (using gtk_tree_list_model_get_row) with the source parent index to obtain the appropriate list model (with gtk_tree_list_row_get_children), and perform the deletion at the source child index.
  • To add L3 to G6, do the same, except perform an insertion instead of a deletion.

This allows moving a single layer or group with one insertion + one deletion.

As for the point about the recursive structure of the data, one must make sure that the data one puts in the model only ever stores one layer deep. I suppose this isn’t a problem in C as one can simply mutate things, but if trying to use immutable data structures one should carry the sibling information in a separate piece of mutable state so that, when we insert or delete from some list models, GTK properly sees the latest view of the data.
More specifically, the private data is simply the unique identifier; the information about children is kept in a separate mutable variable.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.