I'm testing the GTK4 columnview list model for displaying multiple columns. Coding in C. I have that code working (see code below). I am running into trouble adding the standard columnview sort function, using the C snippet referenced in the GTK 4.0 documentation as a guide
C snippet is shown here: https://docs.gtk.org/gtk4/method.ColumnView.get_sorter.html
Working code, pre-sort attempt, is below. The app displays the columns, no errors to std out, working as expected.
// Use for testing columnview
#include <gtk/gtk.h>
// Setup custom type - each item has two strings which will be displayed in two columns
#define TESTCOL_TYPE_ITEM (testcol_item_get_type())
G_DECLARE_FINAL_TYPE (TestColItem, testcol_item, TESTCOL, ITEM, GObject)
struct _TestColItem
{
GObject parent_instance;
const char *str1;
const char *str2;
};
struct _TestColItemClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (TestColItem, testcol_item, G_TYPE_OBJECT)
static void testcol_item_init(TestColItem *item)
{
}
static void testcol_item_class_init(TestColItemClass *class)
{
}
enter image description here
// Set label for factory widget
static void setup_cb(GtkSignalListItemFactory *factory,GObject *listitem)
{
GtkWidget *label =gtk_label_new(NULL);
gtk_list_item_set_child(GTK_LIST_ITEM(listitem),label);
}
// Set text from the column1 passed item for label
static void bind_col1_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
GtkWidget *label = gtk_list_item_get_child(listitem);
TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
const char *string = item->str1;
g_print("value is %s\n",string); // Show on std out
gtk_label_set_text(GTK_LABEL (label), string);
}
// Set text from the column2 passed item for label
static void bind_col2_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
GtkWidget *label = gtk_list_item_get_child(listitem);
TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
const char *string = item->str2;
g_print("value is %s\n",string); // Show on std out
gtk_label_set_text(GTK_LABEL (label), string);
}
// Create the custom object
static TestColItem * testcol_item_new(const char *str1, const char *str2)
{
TestColItem *item = g_object_new(TESTCOL_TYPE_ITEM, NULL);
item->str1 = g_strdup(str1);
item->str2 = g_strdup(str2);
return item;
}
// Activate
static void activate (GtkApplication* app, gpointer user_data)
{
// Create and populate custom list model
GListStore *store = g_list_store_new(G_TYPE_OBJECT);
GListModel *model = G_LIST_MODEL(store);
g_list_store_append(store,testcol_item_new("abc1","ghi1"));
g_list_store_append(store,testcol_item_new("def2","aaa2"));
g_list_store_append(store,testcol_item_new("aaa3","jkl3"));
// Set selection for custom model
GtkSingleSelection *selection = gtk_single_selection_new(G_LIST_MODEL(model));
gtk_single_selection_set_autoselect(selection,TRUE);
// Set Windows in child scrolled window
GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Test");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
GtkWidget *scrolled_window = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW(window), scrolled_window);
// Add column view. Put in scrolled window.
GtkWidget *cv = gtk_column_view_new(GTK_SELECTION_MODEL (selection));
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),cv);
gtk_column_view_set_show_column_separators (GTK_COLUMN_VIEW (cv),TRUE);
// Factory setup and bind for column view
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_col1_cb),NULL);
GtkColumnViewColumn *column = gtk_column_view_column_new("Col1", factory);
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
// Factory setup and bind for column view
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_col2_cb),NULL);
column = gtk_column_view_column_new("Col2", factory);
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
// Show window
gtk_window_present (GTK_WINDOW (window));
}
// Init application
int main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
PROBLEM
I tried adding sort capability using the C snippet above as a guide. When working we see a sort icon in the column header that, when clicked, activates a sort. Many code test variations, this is one:
First I commented out the gtk_column_view_append_column and then added:
// First comment out append and then add
GtkSorter *sorter = gtk_column_view_get_sorter ((GtkColumnView *) cv); // get the sorter for col
gtk_column_view_column_set_sorter(column, sorter); // set the sorter on column
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column); // put col into colview
GtkSortListModel *smodel = gtk_sort_list_model_new ((GListModel *) model, sorter);
selection = gtk_single_selection_new(G_LIST_MODEL(smodel));
gtk_column_view_set_model ((GtkColumnView *)cv, (GtkSelectionModel *)selection);
Std out showed.
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.759: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.797: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
(out:22870): GLib-GIO-CRITICAL **: 21:39:59.807: g_list_model_get_n_items: assertion 'G_IS_LIST_MODEL (list)' failed
Clearly I'm not understanding this. Continuing to experiment, but would appreciate any suggestions on how to add C code to my working example that introduces the sort capability on the two columns.
As the error message shows, str1 and str2 have yet to be implemented as properties. I have added this accordingly in the listing. Now it works.
// Use for testing columnview
#include <gtk/gtk.h>
// Setup custom type - each item has two strings which will be displayed in two columns
#define TESTCOL_TYPE_ITEM (testcol_item_get_type())
G_DECLARE_FINAL_TYPE (TestColItem, testcol_item, TESTCOL, ITEM, GObject)
struct _TestColItem
{
GObject parent_instance;
char *str1;
char *str2;
};
struct _TestColItemClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (TestColItem, testcol_item, G_TYPE_OBJECT)
enum {
PROP_STR1 = 1,
PROP_STR2 = 2,
N_PROPERTIES
};
static void
testcol_item_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
TestColItem *self = (TestColItem *)object;
switch (prop_id) {
case PROP_STR1:
g_value_set_string(value, self->str1);
break;
case PROP_STR2:
g_value_set_string(value, self->str2);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void
testcol_item_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
TestColItem *self = (TestColItem *)object;
switch (prop_id) {
case PROP_STR1:
g_free(self->str1);
self->str1 = g_value_dup_string(value);
break;
case PROP_STR2:
g_free(self->str2);
self->str2 = g_value_dup_string(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void testcol_item_init(TestColItem *item)
{
item->str1 = NULL;
item->str2 = NULL;
}
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
static void testcol_item_class_init(TestColItemClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
gobject_class->get_property = testcol_item_get_property;
gobject_class->set_property = testcol_item_set_property;
obj_properties[PROP_STR1] = g_param_spec_string("str1",
"Str1",
"first column",
NULL,
G_PARAM_READWRITE);
obj_properties[PROP_STR2] = g_param_spec_string("str2",
"Str2",
"second column",
NULL,
G_PARAM_READWRITE);
g_object_class_install_properties(gobject_class, N_PROPERTIES, obj_properties);
}
// Set label for factory widget
static void setup_cb(GtkSignalListItemFactory *factory,GObject *listitem)
{
GtkWidget *label =gtk_label_new(NULL);
gtk_list_item_set_child(GTK_LIST_ITEM(listitem),label);
}
// Set text from the column1 passed item for label
static void bind_col1_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
GtkWidget *label = gtk_list_item_get_child(listitem);
TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
const char *string = item->str1;
g_print("value is %s\n",string); // Show on std out
gtk_label_set_text(GTK_LABEL (label), string);
}
// Set text from the column2 passed item for label
static void bind_col2_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
GtkWidget *label = gtk_list_item_get_child(listitem);
TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
const char *string = item->str2;
g_print("value is %s\n",string); // Show on std out
gtk_label_set_text(GTK_LABEL (label), string);
}
// Create the custom object
static TestColItem * testcol_item_new(const char *str1, const char *str2)
{
TestColItem *item = g_object_new(TESTCOL_TYPE_ITEM, NULL);
item->str1 = g_strdup(str1);
item->str2 = g_strdup(str2);
return item;
}
// Activate
static void activate (GtkApplication* app, gpointer user_data)
{
// Create and populate custom list model
GListStore *store = g_list_store_new(G_TYPE_OBJECT);
g_list_store_append(store,testcol_item_new("abc1","ghi1"));
g_list_store_append(store,testcol_item_new("def2","aaa2"));
g_list_store_append(store,testcol_item_new("aaa3","jkl3"));
// Set Windows in child scrolled window
GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Test");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
GtkWidget *scrolled_window = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW(window), scrolled_window);
// Add column view. Put in scrolled window.
GtkWidget *cv = gtk_column_view_new(NULL); // Null for now want to add sorter
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),cv);
gtk_column_view_set_show_column_separators (GTK_COLUMN_VIEW (cv),TRUE);
// Setup sort model then overlay with with columnview
GtkSorter *sorter = g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (cv)));
GtkSortListModel *model = gtk_sort_list_model_new (G_LIST_MODEL (store), sorter);
GtkSingleSelection *selection = gtk_single_selection_new (G_LIST_MODEL (model));
gtk_single_selection_set_autoselect (selection, TRUE);
gtk_column_view_set_model (GTK_COLUMN_VIEW (cv), GTK_SELECTION_MODEL (selection));
// Factory setup and bind for column 1
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_col1_cb),NULL);
GtkColumnViewColumn *column = gtk_column_view_column_new("Col1", factory);
sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (TESTCOL_TYPE_ITEM,NULL,"str1")));
gtk_column_view_column_set_sorter (column, GTK_SORTER (sorter));
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
// Factory setup and bind for column 2
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_col2_cb),NULL);
column = gtk_column_view_column_new("Col2", factory);
sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (TESTCOL_TYPE_ITEM, NULL, "str2")));
gtk_column_view_column_set_sorter (column, GTK_SORTER (sorter));
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
// Show window
gtk_window_present (GTK_WINDOW (window));
}
// Init application
int main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}