滚动到 GtkListBox 中的选定行
Scroll to selected row in GtkListBox
我有点没脑子了。我想要一个非常简单的事情:能够以编程方式 select 给定的 GtkListBox
行,然后滚动列表框(包含在 ScrolledWindow
和 Viewport
中) .
选择一行很简单(我的代码是 Go & gotk3,但这并不重要):
listBox.SelectRow(row)
但事实证明,滚动到该行是一项真正的挑战。无论我尝试过什么,我都失败了:
- 我试图聚焦该行,但没有任何帮助
- 我试图使用
gtk_widget_translate_coordinates()
找出该行的 Y-coordinate,但对于任何行 returns -1
- 也许我可以找出列表框顶部和底部的哪一行并使用它来滚动
ScrolledWindow
但我不知道该怎么做。
更新: 我已经尝试了这里提出的建议:,但没有成功,因为仍然没有滚动发生:
listbox.SelectRow(rowToSelect)
listbox.SetFocusVAdjustment(listbox.GetAdjustment())
if rowToSelect != nil {
rowToSelect.GrabFocus()
}
我也使用下面的代码对 rowToSelect
的 child 进行了同样的尝试,但无济于事:
if c, err := rowToSelect.GetChild(); err == nil {
c.GrabFocus()
}
所以,我遇到了同样的问题,但设法让它在我的案例中起作用。我认为我的解决方案很有可能也适用于您。
由于 grab_focus
方法不起作用,我开始使用 listbox_get_row_at_y
实施解决方案。非常不满意,但希望它会起作用。而且.. 它没有用,因为 get_row_at_y
总是 return null
,对于我提供的所有 y 值。而且我知道列表框不是空的。所以这让我意识到我正在尝试聚焦我刚刚添加到列表框中的行。该行尚未实现,它无法聚焦,因为它还没有准备好。
所以我更改了代码以填充列表框,等待 100 毫秒超时,然后才调用 grab_focus
。这奏效了!
我实际上正在使用一个为我包装超时调用的库,但我认为您可以为此目的在 'raw' gtk 中使用 g_timeout_add。
请注意,这意味着在预先已填充的列表框中调用 grab_focus
并且在屏幕上实现的项目应该可以直接工作。如果这是你的情况,那么这对你没有帮助。
感谢 Emmanuel Touzery 的提示,我终于成功了。我不必使用计时器,但问题确实是在填充列表框时该行尚未实现,因此不可能发生坐标转换。
我所做的是使用 GLib 的 idle_add()
安排滚动,这使得它稍后在下游发生,并且似乎效果很好:请参阅 this commit 了解详细信息。
简而言之,这一切都归结为以下代码:
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)
}
}
}
}
必须使用 glib.IdleAdd()
而非在填充列表框的代码中调用 上述函数 。
我有点没脑子了。我想要一个非常简单的事情:能够以编程方式 select 给定的 GtkListBox
行,然后滚动列表框(包含在 ScrolledWindow
和 Viewport
中) .
选择一行很简单(我的代码是 Go & gotk3,但这并不重要):
listBox.SelectRow(row)
但事实证明,滚动到该行是一项真正的挑战。无论我尝试过什么,我都失败了:
- 我试图聚焦该行,但没有任何帮助
- 我试图使用
gtk_widget_translate_coordinates()
找出该行的 Y-coordinate,但对于任何行 returns -1 - 也许我可以找出列表框顶部和底部的哪一行并使用它来滚动
ScrolledWindow
但我不知道该怎么做。
更新: 我已经尝试了这里提出的建议:
listbox.SelectRow(rowToSelect)
listbox.SetFocusVAdjustment(listbox.GetAdjustment())
if rowToSelect != nil {
rowToSelect.GrabFocus()
}
我也使用下面的代码对 rowToSelect
的 child 进行了同样的尝试,但无济于事:
if c, err := rowToSelect.GetChild(); err == nil {
c.GrabFocus()
}
所以,我遇到了同样的问题,但设法让它在我的案例中起作用。我认为我的解决方案很有可能也适用于您。
由于 grab_focus
方法不起作用,我开始使用 listbox_get_row_at_y
实施解决方案。非常不满意,但希望它会起作用。而且.. 它没有用,因为 get_row_at_y
总是 return null
,对于我提供的所有 y 值。而且我知道列表框不是空的。所以这让我意识到我正在尝试聚焦我刚刚添加到列表框中的行。该行尚未实现,它无法聚焦,因为它还没有准备好。
所以我更改了代码以填充列表框,等待 100 毫秒超时,然后才调用 grab_focus
。这奏效了!
我实际上正在使用一个为我包装超时调用的库,但我认为您可以为此目的在 'raw' gtk 中使用 g_timeout_add。
请注意,这意味着在预先已填充的列表框中调用 grab_focus
并且在屏幕上实现的项目应该可以直接工作。如果这是你的情况,那么这对你没有帮助。
感谢 Emmanuel Touzery 的提示,我终于成功了。我不必使用计时器,但问题确实是在填充列表框时该行尚未实现,因此不可能发生坐标转换。
我所做的是使用 GLib 的 idle_add()
安排滚动,这使得它稍后在下游发生,并且似乎效果很好:请参阅 this commit 了解详细信息。
简而言之,这一切都归结为以下代码:
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)
}
}
}
}
必须使用 glib.IdleAdd()
而非在填充列表框的代码中调用 上述函数 。