2

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.

2 Answers 2

2

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;
}

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Works great and appreciate your help. I came to this also after some suggestions at gnome discourse when drilling down on the property error. After implementing the boilerplate code for property installation. couldn't figure out why the property install would fail if the first spec wasn't 0. Guess it is just a an artifact of the gobject impelementation. This doc referred to me was very helpful. docs.gtk.org/gobject/concepts.html#object-properties
0

Got a helpful suggestion over at discourse.gnome.org. Have to manually add the sorter (although sorting is built-in in a sense to columnview, the sorter has to be added manually). Also, you can define the column view with a null to allow you to create the column to associated with the sorter, which facilitates putting the sort under the columnview model.

Modified code is here. Still not sorting, but that looks like a problem with the expression evaluating the property, the basic sort function code is there

// 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)
{
}

// 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);

    // 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);
    GtkSorter *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);

    // Setup sort model then overlay with with columnview
    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));

    // 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;
}

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.