GListModel support modified items?

GListModel supports added and removed items, why not modified items?

Sometimes the child item modifies its properties or child values, like column value in the row,
how to notify the UI refresh its data?

ColumnView, ListStore item changes - Platform / Language bindings - GNOME Discourse

based on these cases, so GListModel should add modified parameter to cover these cases:

void
items_changed (
  GListModel* self,
  guint position,
  guint removed,
  guint added,
  guint modified,
  gpointer user_data
)

Listen to GObject::notify emissions from the items themselves.

1 Like

Hi @taozuhong!

That’s a useful thing, yes. However GListModel holds GObject, unlike GListStore which can hold almost any type (strings, integers and so on). As such one can connect to GObject signals or the notify::property signal.

EDIT: Philip beat me to it :wink:

2 Likes

I might have beat you to it, but you gave a better explanation!

2 Likes

Hi,

If you want to keep a GListModel in sync with GtkListView or GtkColumnView widgets, usually the best solution is to use a GtkExpression, that allows to keep properties in sync.

Do you have a concrete example of what you want to achieve?

1 Like

The GtkExpression and GObject::notify can’t cover the computed or multiple value scenes, for example:
The Row object has many items (column value object), one of them changed its hold value or property, maybe multiple values at time.

In our case, we make many changes at time, then refresh the UI (GtkListView / GtkColumnView) normally, pls see the Vala code as follow:

public class ValueObject : Object, IValue
{
    public virtual string name {
        get { return "UNKNOWN"; }
    }
    public virtual bool is_null {
        get { return false; } 
    }
    public virtual string to_string() {
        return "";
    }
}
public class ValueNull : ValueObject
{
    public const string NULL_LABEL = "(Null)";
    public const string NULL_VALUE = "NULL";
    public const string NULL_EMPTY = "";
    public override string name {
        get { return "NULL"; }
    }
    public string itsvalue {
        get { return NULL_VALUE; }
    }
}

public class RowModel : GLib.Object, GLib.ListModel
{
    private Gee.ArrayList<ValueObject> m_values;
    public RowModel()
    {
        m_values = new Gee.ArrayList<ValueObject>();
    }
}
public class TableModel : GLib.Object, IResult, ListModel
{
    private Gee.ArrayList<RowModel> m_rows;
    private Gee.ArrayList<ColumnModel> m_columns;
    public TableModel()
    {
        m_rows = new Gee.ArrayList<RowModel>();
        m_columns = new Gee.ArrayList<ColumnModel>();
    }
}

public interface IGridable : Object {
    // public abstract int page_active { get; }
    public abstract Gtk.ColumnView grid { get; }
    public void make_grid(TableModel data_model, int[] column_sizes = {})
    {
        (this.grid.columns as GLib.ListStore)?.remove_all();
        for(int index = 0; index < data_model.columns.size; index++) {
            factory = new Gtk.SignalListItemFactory();
            factory.set_data<int>(PROP_INDEX, index);
            factory.set_data<DataType>(PROP_TYPE, data_type);
            factory.setup.connect(column_setup_handler);
            factory.bind.connect(column_bind_handler);

            caption =  column_model.title ?? column_model.name;
            column = new Gtk.ColumnViewColumn(caption, factory);
            column.expand = true;
            column.resizable = true;

            const_expr = new Gtk.ConstantExpression(typeof(int), index);
            if (data_type in INTERGERS) {
                expression = new Gtk.CClosureExpression(typeof(int64), null, { const_expr }, (Callback)get_integer_by_index, null, null);
                column.sorter = new Gtk.NumericSorter(expression);
            } else if (data_type in NUMERICS) {
                expression = new Gtk.CClosureExpression(typeof(double), null, { const_expr }, (Callback)get_double_by_index, null, null);
                column.sorter = new Gtk.NumericSorter(expression);
            } else {
                expression = new Gtk.CClosureExpression(typeof(string), null, { const_expr }, (Callback)get_string_by_index, null, null);
                column.sorter = new Gtk.StringSorter(expression);
            }
            this.grid.append_column(column);
        }
    }

    public virtual void column_setup_handler(Gtk.SignalListItemFactory factory, GLib.Object listitem)
    {
        int index = factory.get_data<int>(PROP_INDEX);
        DataType data_type = factory.get_data<DataType>(PROP_TYPE);

        Gtk.EditableLabel editable = new Gtk.EditableLabel("");
        editable.xalign = 0.0f;
        editable.set_data<int>(PROP_INDEX, index);
        editable.set_data<DataType>(PROP_TYPE, data_type);
        editable.notify["editing"].connect(editable_editing_handler);

        (listitem as Gtk.ListItem).child = editable;
    }

    public virtual void column_bind_handler(Gtk.SignalListItemFactory factory, GLib.Object listitem)
    {
        Gtk.EditableLabel editable = (listitem as Gtk.ListItem).child as Gtk.EditableLabel;
        editable.changed.disconnect(editable_changed_handler);

        RowModel row = (listitem as Gtk.ListItem).item as RowModel;
        editable.set_data<RowModel>(PROP_ROW, row);
        int index = factory.get_data<int>(PROP_INDEX);
        if (index < row.values.size) {
            bool is_null = row.values[index] is ValueNull;
            if (is_null) {
                editable.text = ValueNull.NULL_LABEL;
            } else if (row.values[index] is ValueBinary) {
                var value_binary = row.values[index] as ValueBinary;
                editable.text = DatabaseHelper.summary(value_binary);
            } else {
                editable.text = row.values[index]?.to_string() ?? "";
            }
        }
        editable.changed.connect(editable_changed_handler);
    }

    public virtual void editable_changed_handler(Gtk.Editable sender)
    {
        // Save value

        // Refresh grid
    }
}

@otte @matthiasc @gwillems

GListStore can hold GObject only too, it implements GListModel.

1 Like

Hi @taozuhong

It’s a bit hard to understand the intent of the code…
Can you please provide a real usecase, with on one side the model fields, and on the other side how you plan to combine and display them?
(i.e. the content of each column for a given model?)

Or even better, pictures :slight_smile:

Here is an example to show this, while I update file size in outside, then we need a way to update the size in GtkColumnView
taozuhong/GtkColumnViewDemo: A GtkColumnView demo app (github.com)

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.