I'm a bit out of ideas here. I want a very simple thing: to be able to select a given GtkListBox
row programmatically and then scroll the list box (which is wrapped in a ScrolledWindow
and a Viewport
).
Selecting a row is trivial (my code is Go & gotk3, but that's not so important):
listBox.SelectRow(row)
But scrolling to the row proved to be a real challenge. Whatever I tried, I failed:
gtk_widget_translate_coordinates()
, but it returns -1 for any rowScrolledWindow
but I can't figure out how to do that.Update: I've tried what's proposed here: Manually scroll to a child in a Gtk.ScrolledWindow, but it didn't work as still no scrolling occurred:
listbox.SelectRow(rowToSelect)
listbox.SetFocusVAdjustment(listbox.GetAdjustment())
if rowToSelect != nil {
rowToSelect.GrabFocus()
}
I also tried the same with rowToSelect
's child using the code below, to no avail:
if c, err := rowToSelect.GetChild(); err == nil {
c.GrabFocus()
}
I've finally nailed it thanks to the hint by Emmanuel Touzery. I didn't have to go as far as to use timers, but the problem was indeed that at the moment of filling of the list box the row hasn't been realised yet so no coordinate translation could possibly happen.
What I did is scheduled the scrolling using GLib's idle_add()
, which makes it happen later downstream, and that seemed to have worked perfectly: see this commit for details.
In short, it all boils down to the following code:
func ListBoxScrollToSelected(listBox *gtk.ListBox) {
// If there's selection
if row := listBox.GetSelectedRow(); row != nil {
// Convert the row's Y coordinate into the list box's coordinate
if _, y, _ := row.TranslateCoordinates(listBox, 0, 0); y >= 0 {
// Scroll the vertical adjustment to center the row in the viewport
if adj := listBox.GetAdjustment(); adj != nil {
_, rowHeight := row.GetPreferredHeight()
adj.SetValue(float64(y) - (adj.GetPageSize()-float64(rowHeight))/2)
}
}
}
}
The above function has to be called using the glib.IdleAdd()
and not in the code that fills the list box.