Adding items to the start of a ListBox without moving the scroll position?

Is it possible to add items to the start of a GTK ListBox without affecting the widgets that are visible?*

What I have is a list of time-ordered items and a ListBoxRow per item. At start I load a batch of the newest ones and scroll to the bottom. When the user scrolls to the top again then I want to load a batch of older items. I can do that by connecting to “value_changed” on the vadjustment and looking for very small values (<5).

BUT if I insert items on that event then I still end up with a vadjust of ~0, which is now lots of items older than the user was looking at a second ago.

What I’d like is to be able to keep the same item visible when the user scrolls so that the new items are available before it in the list and now be scrolled back through. (i.e. if the user scrolls back from [latest] to item [latest - 20] and the app loads 10 items earlier then I want to still be showing [latest-20] at the top of the list but with [latest - 21] to [latest - 30] now available to scroll to before it).

I’ve tried adding the new widget’s height to the vadjust as it is loaded, but it hasn’t been properly allocated by then and so its height is 1. Even if it was properly allocated, I suspect the UI would still jump around as it adds the item, moves them all down, and then adjust the scroll position. Unless there’s a way to freeze/ignore repaints.

So, it is possible or are GTK ListViews not designed this way?

.

* I want to say “without affecting the scroll position”, but that confuses matters because it’s not affecting the scroll position as the user sees it in relation to the widgets they can see but it is affecting it in relation to the actual vadjustment value

2 Likes

I don’t know how I didn’t realise earlier, but I’ve already got the answer! I’m maintaining an app that someone else built and after a month or more of pondering this problem, within hours of posting the question I suddenly noticed that another bit of the app already does this!

The answer is a custom ScrolledWindow that connects to the upper and value change events, tracks the old values, and then you can set a mode that appropriately adjusts the position when it changes (and it then reverts the mode back to default so that it doesn’t get into a loop).

So, to keep the position consistent while adding to the top:

const int TOP    = 1;
const int BOTTOM = 2;
const int NONE   = 0;

⋮

  construct {
    vadjustment.notify["upper"].connect (keep_upper_func);
  }

  private void keep_upper_func() {
    double upper = vadjustment.upper;
    if (this.balance == TOP){
      double inc = (upper - upper_cache);

      this.vadjustment.value += inc;
      this.balance = NONE;
    }
    this.upper_cache = vadjustment.upper;
    this.value_cache = vadjustment.value;
  }

Then add your item to the list within the ScrolledWindow, set this.balance = TOP; and it should all just work (but I’m pulling this out of code, not writing a minimal example, so I may have missed something!)

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