How to scroll to the selected Item on GtkListView?

I want to use the GtkListView widget on my app. I’ve added some items to it. And, I have set an item as the default selected item of the GtkListView. But the problem is, while running the app, I can’t automatically scroll to the default selected item. Can anybody check it please?
The demo script is given below —

#include <gtk/gtk.h>
static void
setup_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data)             
{
  GtkWidget *lb = gtk_label_new (NULL);
  gtk_list_item_set_child (listitem, lb);
}

static void
bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
  GtkWidget *lb = gtk_list_item_get_child (listitem);
  GtkStringObject *strobj = gtk_list_item_get_item (listitem);
  gtk_label_set_text (GTK_LABEL (lb), gtk_string_object_get_string (strobj));
}

static void
scroll_set(GtkListView *listview, gpointer user_data)
{
  GtkScrollInfo *scrinfo = gtk_scroll_info_new ();
  gtk_scroll_info_set_enable_vertical ((GtkScrollInfo *)scrinfo, TRUE);
  gtk_list_view_scroll_to ((GtkListView *)listview, 10, GTK_LIST_SCROLL_FOCUS, scrinfo); //avilable 4.12
}


static void
app_activate (GApplication *application) {
  GtkApplication *app = GTK_APPLICATION (application);
  GtkWidget *win = gtk_application_window_new (app);
  gtk_window_set_default_size (GTK_WINDOW (win), 200, 100);
  GtkWidget *scr = gtk_scrolled_window_new ();
  gtk_window_set_child (GTK_WINDOW (win), scr);

  char *array[] = {
    "one", "two", "three", "four", "five" ,"six"," seven", 
    "eight" ,"nine", "ten", "eleven", "twelve", "thirteen",NULL
  };

  GtkWidget *lv = gtk_list_view_new(NULL,NULL); 

  GtkStringList *sl =  gtk_string_list_new ((const char * const *) array);
  GtkSingleSelection *selection = gtk_single_selection_new(G_LIST_MODEL(sl));
  gtk_single_selection_set_autoselect(selection, TRUE);
  gtk_list_view_set_model(GTK_LIST_VIEW(lv),GTK_SELECTION_MODEL(selection));

  GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
  g_signal_connect (factory, "setup", G_CALLBACK (setup_cb), NULL);
  g_signal_connect (factory, "bind", G_CALLBACK (bind_cb), NULL);

  gtk_list_view_set_factory (GTK_LIST_VIEW (lv),factory);
  gtk_single_selection_set_selected ((GtkSingleSelection *)selection, 10);

  gtk_widget_set_visible(lv, TRUE);
  /*scroll_set cb not working*/
  g_signal_connect (lv, "show", G_CALLBACK (scroll_set), NULL);

  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
  gtk_window_present (GTK_WINDOW (win));
}


int
main (int argc, char **argv) {
  GtkApplication *app;
  int stat;
  app = gtk_application_new ("com.gtk.listview.test", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
  stat =g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
 return stat;
}

Note: Here item number 10 is set as selected item. While running the app, to the item number 10 is not scrolling.

6

Here is slightly changed application in Python:

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

def on_list_item_factory_setup(signal_list_item_factory, objekt):
    objekt.set_child(Gtk.Label())

def on_list_item_factory_bind(signal_list_item_factory, objekt):
    objekt.get_child().set_label(objekt.get_item().get_string())

def on_selection_model_selection_changed(selection_model, position, n_items, user_data):
    list_view = user_data
    scroll_info = Gtk.ScrollInfo.new()
    scroll_info.set_enable_vertical(True)
    list_view.scroll_to(selection_model.get_selected(), Gtk.ListScrollFlags.FOCUS, scroll_info)

def on_spin_button_value_changed(spin_button, user_data):
    selection_model = user_data
    selection_model.set_selected(spin_button.get_value_as_int() - 1)

def on_application_activate(app):
    arr = [
        'one', 'two', 'three', 'four', 'five' ,'six',' seven', 
        'eight' ,'nine', 'ten', 'eleven', 'twelve', 'thirteen'
    ]

    window = Gtk.ApplicationWindow(application=app)
    window.set_default_size(200, 100)

    header_bar = Gtk.HeaderBar.new()
    window.set_titlebar(header_bar)

    adjustment = Gtk.Adjustment.new(1, 1, len(arr), 1, 1, 1)
    spin_button = Gtk.SpinButton.new(adjustment, 0, 0)
    header_bar.pack_end(spin_button)

    scrolled = Gtk.ScrolledWindow.new()
    window.set_child(scrolled)

    list_model = Gtk.StringList.new(arr)
    selection_model = Gtk.SingleSelection.new(list_model)
    selection_model.set_autoselect(True)
    spin_button.connect('value_changed', on_spin_button_value_changed, selection_model)

    list_item_factory = Gtk.SignalListItemFactory.new()
    list_item_factory.connect('setup', on_list_item_factory_setup)
    list_item_factory.connect('bind', on_list_item_factory_bind)

    list_view = Gtk.ListView.new(selection_model, list_item_factory)
    selection_model.connect('selection-changed', on_selection_model_selection_changed, list_view)

    scrolled.set_child(list_view)

    window.present()


if __name__ == '__main__':
    import sys

    app = Gtk.Application(application_id='com.example.ScrollTestApp')
    app.connect('activate', on_application_activate)

    app.run(sys.argv)

and its C version:

#include <gtk/gtk.h>

static void
on_list_item_factory_setup (GtkSignalListItemFactory *item_factory, GObject* objekt, gpointer user_data)
{
  GtkListItem *list_item = GTK_LIST_ITEM (objekt);
  GtkWidget *label = gtk_label_new (NULL);
  gtk_list_item_set_child (list_item, label);
}

static void
on_list_item_factory_bind (GtkSignalListItemFactory *item_factory, GObject *objekt, gpointer user_data)
{
  GtkListItem *list_item = GTK_LIST_ITEM (objekt);
  GtkWidget *label = gtk_list_item_get_child (list_item);
  GtkStringObject *string_object = gtk_list_item_get_item (list_item);
  gtk_label_set_text (GTK_LABEL (label), gtk_string_object_get_string (string_object));
}

static void
on_selection_model_selection_changed (GtkSelectionModel* selection_model, guint position, guint n_items, gpointer user_data)
{
  GtkListView *list_view = GTK_LIST_VIEW (user_data);
  GtkScrollInfo *scroll_info = gtk_scroll_info_new ();
  gtk_scroll_info_set_enable_vertical (scroll_info, TRUE);
  int selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection_model));
  gtk_list_view_scroll_to (list_view, selected, GTK_LIST_SCROLL_FOCUS, scroll_info);
}

static void
on_spin_button_value_changed (GtkSpinButton *spin_button, gpointer user_data)
{
  GtkSingleSelection *selection_model = user_data;
  int pos = gtk_spin_button_get_value_as_int(spin_button) - 1;
  gtk_single_selection_set_selected (selection_model, pos);
}

static void
on_application_activate (GApplication *application, gpointer user_data)
{
  char *array[] = {
    "one", "two", "three", "four", "five" , "six", "seven", 
    "eight" ,"nine", "ten", "eleven", "twelve", "thirteen",
    NULL
  };

  GtkWidget *window = gtk_application_window_new (GTK_APPLICATION (application));
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);

  GtkWidget *header_bar = gtk_header_bar_new ();
  gtk_window_set_titlebar (GTK_WINDOW (window), header_bar);

  GtkAdjustment *adjustment = gtk_adjustment_new (1, 1, G_N_ELEMENTS (array), 1, 1, 1);
  GtkWidget *spin_button = gtk_spin_button_new(adjustment, 0, 0);
  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), spin_button);

  GtkWidget *scrolled = gtk_scrolled_window_new ();
  gtk_window_set_child (GTK_WINDOW (window), scrolled);

  GtkStringList *list_model = gtk_string_list_new ((const char * const *) array);
  GtkSingleSelection *selection_model = gtk_single_selection_new (G_LIST_MODEL (list_model));
  gtk_single_selection_set_autoselect(selection_model, TRUE);
  g_signal_connect(spin_button, "value_changed", G_CALLBACK (on_spin_button_value_changed), selection_model);

  GtkListItemFactory *list_item_factory = gtk_signal_list_item_factory_new ();
  g_signal_connect (list_item_factory, "setup", G_CALLBACK (on_list_item_factory_setup), NULL);
  g_signal_connect (list_item_factory, "bind", G_CALLBACK (on_list_item_factory_bind), NULL);

  GtkWidget *list_view = gtk_list_view_new (GTK_SELECTION_MODEL (selection_model), list_item_factory); 
  g_signal_connect(selection_model, "selection-changed", G_CALLBACK (on_selection_model_selection_changed), list_view);

  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), list_view);
  gtk_window_present (GTK_WINDOW (window));
}


int
main (int argc, char **argv)
{
  GtkApplication *application = gtk_application_new ("com.example.ScrollTestApp", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (application, "activate", G_CALLBACK (on_application_activate), NULL);
  int stat = g_application_run (G_APPLICATION (application), argc, argv);
  g_object_unref (application);
  return stat;
}

I don’t know why on application initialization, some signals not emitted. Needs a GTK expert here to answer. But as you see, the signal system works correctly afterwards.

As you see, for small testing purposes, prototyping in Python, Javascript, even in Vala may help.

EDIT:

In comments, I wrote about early setting the selected row and scrolling to selected row, while GTK does not know widget sizes. Here I added a simple loop after gtk_window_present to ask Gtk to complete all its event handling operations (e.g. calculating sizes, handling input events, drawings, …), then set the row. It works as intended and list view scrolled correctly and shows selected row:

#include <gtk/gtk.h>

static void
on_list_item_factory_setup (GtkSignalListItemFactory *item_factory, GObject* objekt, gpointer user_data)
{
  GtkListItem *list_item = GTK_LIST_ITEM (objekt);
  GtkWidget *label = gtk_label_new (NULL);
  gtk_list_item_set_child (list_item, label);
}

static void
on_list_item_factory_bind (GtkSignalListItemFactory *item_factory, GObject *objekt, gpointer user_data)
{
  GtkListItem *list_item = GTK_LIST_ITEM (objekt);
  GtkWidget *label = gtk_list_item_get_child (list_item);
  GtkStringObject *string_object = gtk_list_item_get_item (list_item);
  gtk_label_set_text (GTK_LABEL (label), gtk_string_object_get_string (string_object));
}

static void
on_selection_model_selection_changed (GtkSelectionModel* selection_model, guint position, guint n_items, gpointer user_data)
{
  GtkListView *list_view = GTK_LIST_VIEW (user_data);
  GtkScrollInfo *scroll_info = gtk_scroll_info_new ();
  gtk_scroll_info_set_enable_vertical (scroll_info, TRUE);
  int selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection_model));
  gtk_list_view_scroll_to (list_view, selected, GTK_LIST_SCROLL_FOCUS, scroll_info);
}

static void
on_application_activate (GApplication *application, gpointer user_data)
{
  char *array[] = {
    "one", "two", "three", "four", "five" , "six", "seven", 
    "eight" ,"nine", "ten", "eleven", "twelve", "thirteen",
    NULL
  };

  GtkWidget *window = gtk_application_window_new (GTK_APPLICATION (application));
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);

  GtkWidget *scrolled = gtk_scrolled_window_new ();
  gtk_window_set_child (GTK_WINDOW (window), scrolled);

  GtkStringList *list_model = gtk_string_list_new ((const char * const *) array);
  GtkSingleSelection *selection_model = gtk_single_selection_new (G_LIST_MODEL (list_model));
  gtk_single_selection_set_autoselect(selection_model, TRUE);

  GtkListItemFactory *list_item_factory = gtk_signal_list_item_factory_new ();
  g_signal_connect (list_item_factory, "setup", G_CALLBACK (on_list_item_factory_setup), NULL);
  g_signal_connect (list_item_factory, "bind", G_CALLBACK (on_list_item_factory_bind), NULL);

  GtkWidget *list_view = gtk_list_view_new (GTK_SELECTION_MODEL (selection_model), list_item_factory); 
  g_signal_connect(selection_model, "selection-changed", G_CALLBACK (on_selection_model_selection_changed), list_view);

  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), list_view);

  gtk_window_present (GTK_WINDOW (window));

  while (g_main_context_pending (g_main_context_default ()))
    g_main_context_iteration (NULL, TRUE);

  gtk_single_selection_set_selected (selection_model, 10);
}


int
main (int argc, char **argv)
{
  GtkApplication *application = gtk_application_new ("com.example.ScrollTestApp", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (application, "activate", G_CALLBACK (on_application_activate), NULL);
  int stat = g_application_run (G_APPLICATION (application), argc, argv);
  g_object_unref (application);
  return stat;
}

Gtk and also its object system, GObject, GType, … are amazing. I strongly recommend every C developer to study its internals and codes. If you develop Gtk based widgets, libraries, …, you should learn its internals.

5

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật