Reputation: 2986
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()
}
Upvotes: 5
Views: 1501
Reputation: 2986
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.
Upvotes: 4
Reputation: 9183
So, I had the same issue but managed to make it work in my case. I think there are good chances my solution will work for you too.
Since the grab_focus
method didn't work, I started implementing a workaround solution using listbox_get_row_at_y
. Highly unsatisfying, but hopefully it was going to work. And.. it didn't work, because get_row_at_y
would always return null
, for all the y values I'd feed it. And I knew the listbox wasn't empty. So that made me realize I was trying to focus a row that I had just been adding to the listbox.. The row wasn't realized yet, it couldn't be focused because it wasn't ready for that yet.
So I changed my code to fill the listbox, wait a 100ms timeout, and only then call grab_focus
. And that worked!
I'm actually using a library which is wrapping the timeout call for me, but I think you could use g_timeout_add in 'raw' gtk for that purpose.
Note that this means that calling grab_focus
on a listbox that was already filled beforehand and the items realized on screen should work directly. If that's your situation then this won't help you.
Upvotes: 3