Simplest way to make sortable columns in ColumnView

Recently completed my first table implementation with ColumnView widget, now I think about sort ordering from the box. By reviewing some pages in docs and few examples in C, not sure yet how to better implement sorting there on click on the table header rows.

I believe it’s build-in feature, it’s maybe something about sorter getter or maybe I should define some factory, to make it react and show the order direction arrows.

Could somebody please explain in any language, what is the simpler way to implement this feature, without lot of extra code I currently plan.

I had exactly this problem recently and was offered this solution, which works.

// gcc -o main main.c pkg-config --cflags --libs gtk4

#include <gtk/gtk.h>

void object_free(gpointer data)
{
  g_print("free object\n");
  g_free(data);
}

static void add_item(GListStore* store ) {
  GObject* obj = g_object_new(G_TYPE_OBJECT, NULL);
  for (int j = 0; j < 3; j++) {
      gint * value = g_new(gint, 1); 
      *value = g_random_int_range(1, 100);  
      g_object_set_data_full(obj, g_strdup_printf("item-object-%d", j), value, object_free);
  }
  g_list_store_append(store, obj);
  g_object_unref(obj);
}

static gint compareFunc(gconstpointer a, gconstpointer b, gpointer user_data) {
    int column_index = GPOINTER_TO_INT(user_data);
    gint *int_a = g_object_get_data(G_OBJECT(a), g_strdup_printf("item-object-%d", column_index));
    gint *int_b = g_object_get_data(G_OBJECT(b), g_strdup_printf("item-object-%d", column_index));
    return *int_a - *int_b;
}

static void setup_cb(GtkSignalListItemFactory* factory, GtkListItem* list_item, gpointer user_data) {
    GtkWidget* label = gtk_label_new(NULL);
    gtk_list_item_set_child(list_item, label);
}

static void bind_number_cb(GtkSignalListItemFactory* factory, GtkListItem* list_item, gpointer user_data) {
    int column_index = GPOINTER_TO_INT(user_data);
    GtkWidget* label = gtk_list_item_get_child(list_item);
    GObject* item = gtk_list_item_get_item(list_item);
    gint *value = g_object_get_data(item, g_strdup_printf("item-object-%d", column_index));
    char buffer[32];
    snprintf(buffer, sizeof(buffer), "%d", *value);
    gtk_label_set_text(GTK_LABEL(label), buffer);
}

static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget* window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Sort Example");
    gtk_window_set_default_size(GTK_WINDOW(window), 600, 300);

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

    GtkWidget* column_view = gtk_column_view_new(NULL);
    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window), column_view);

    GListStore* store = g_list_store_new(G_TYPE_OBJECT);

    GtkSorter *view_sorter = g_object_ref (gtk_column_view_get_sorter(GTK_COLUMN_VIEW(column_view)));
    GtkSortListModel *sort_model = gtk_sort_list_model_new(G_LIST_MODEL(store), view_sorter);
    GtkSelectionModel* selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(sort_model)));
    gtk_column_view_set_model(GTK_COLUMN_VIEW(column_view), selection_model);
    g_object_unref(selection_model);

    const char* column_titles[] = {"Number 1", "Number 2", "Number 3"};
    for (int i = 0; i < 3; i++) {
        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_number_cb), GINT_TO_POINTER(i));

        GtkColumnViewColumn* column = gtk_column_view_column_new(column_titles[i], factory);
        gtk_column_view_append_column(GTK_COLUMN_VIEW(column_view), column);

        GtkSorter *column_sorter = GTK_SORTER(gtk_custom_sorter_new(compareFunc, GINT_TO_POINTER(i), NULL));
        gtk_column_view_column_set_sorter(column, column_sorter);
        g_object_unref(column_sorter);
        g_object_unref(column);
    }

    add_item(store);
    add_item(store);
    add_item(store);

    gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char** argv) {
    GtkApplication* app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    int status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);
    return status;
}
2 Likes