GtkSignalListItemFactory
GtkSignalListItemFactory vs GtkBuilderListItemFactory
GtkBuilderlistItemFactory is convenient when GtkListView just shows the contents of a list model. Its binding direction is always from an item of the list to a child of GtkListItem.
However, it is insufficient for dynamic connections. For example, suppose you want to edit the contents of a list. You can set a GtkText widget as the child of a GtkListItem to allow users to edit text. You need to update the list item when the text in the GtkText is edited. The data flow is in the opposite direction compared to GtkBuilderListItemFactory—it goes from the GtkText widget back to the list item. You can achieve this using GtkSignalListItemFactory, which provides more flexibility.
This section shows just some parts of the source file listeditor.c. If you want to see the whole codes, see src/listeditor directory of the Gtk4 tutorial repository.
List Editor
The sample program is a list editor and the data in the list are strings. It’s similar to a line editor. It reads a text file line by line. Each line is an item of the list. The list is displayed with a GtkListView. The list view contains strings which are the contents of the corresponding items in the list model.
You can compile the source file and execute it as follows.
- Download the program from the repository.
- Change your current directory to
src/listeditor. - Type the following on your commandline.
$ meson setup _build
$ ninja -C _build
$ _build/listeditor
The window has buttons:
- Append button: appends a data after the current item, or at the end of the model if no current item exists.
- Insert button: inserts a row before the current item, or at the top of the model if no current item exists.
- Remove button: removes the current item.
- Read button: reads a file.
- Write button: Writes the contents to the file shown on the toolbar. If no filename is set, a file dialog opens to let you choose one.
- Close button: remove all the items from the model.
- Quit button: quits the application.
You can click on the content area and select a row. The corresponding item to the row will be the current item. The current position (zero-based) is shown at the left of the tool bar.
Connect a GtkText Instance and an Item in the Model
The ListView sets its factory property to
GtkSignalListItemFactory. Its two signals “setup” and “bind” are
connected to the handlers setup_cb and
bind_cb respectively. The remaining two signals
“unbind” and “teardown” aren’t used in this program. The
following shows the signal handlers.
static void
setup_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GtkWidget *text = gtk_text_new ();
gtk_list_item_set_child (listitem, GTK_WIDGET (text));
gtk_editable_set_alignment (GTK_EDITABLE (text), 0.0);
gtk_widget_set_can_target (GTK_WIDGET (text), FALSE);
g_signal_connect (GTK_EDITABLE (text), "changed", G_CALLBACK (text_changed_cb), listitem);
g_signal_connect (listitem, "notify::selected", G_CALLBACK (item_selected_cb), text);
}
static void
bind_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GtkWidget *text = gtk_list_item_get_child (listitem);
LeData *data = LE_DATA (gtk_list_item_get_item(listitem));
if (data) {
g_signal_handlers_block_by_func (text, (gpointer) text_changed_cb, listitem);
gtk_editable_set_text (GTK_EDITABLE (text), le_data_look_string (data));
gtk_editable_set_position (GTK_EDITABLE (text), -1);
g_signal_handlers_unblock_by_func (text, (gpointer) text_changed_cb, listitem);
}
}5-7:
setup_cbcreates the child widget of the list item. This widget will be a row of the GtkListView instance.9-10:
setup_cbconnects the “changed” signal on the GtkText instance to the handlertext_changed_cb. This handler copies the text in the GtkText to the item when the GtkText is changed.setup_cbalso connects the “notify::selected” signal on the GtkListItem to the handleritem_selected_cb. The handler make the GtkText grab the keyboard focus to prepare keyboard input.These instance and signals created by
setup_cbwill be automatically destroyed when the list item disposes itself. So, teardown signal handler is not necessary.83-86:
bind_cbjust copy the string in the item in the model to the GtkText. The macro functiong_signal_block_by_funcavoids unnecessary copy from GtkText to item at the update of the GtkText. No instances are created in this process, so “unbind” signal handler is not necessary.5-7:
setup_cbcreates the child widget for the list item, which serves as a row in the GtkListView.9-10:
setup_cbconnects the GtkText’s “changed” signal to thetext_changed_cbhandler. This handler updates the item in the model with the new text whenever the GtkText is modified. Additionally, it connects the “notify::selected” signal of the GtkListItem toitem_selected_cb, which makes the GtkText grab keyboard focus to prepare for immediate input. The “notify” signal is inherited from GObject class.The instances and signal connections created in
setup_cbare automatically cleaned up when the list item is disposed. Therefore, a “teardown” signal handler is unnecessary.19-22:
bind_cbsimply copies the string from the model’s item to the GtkText. Theg_signal_handlers_block_by_funcmacro prevents an unnecessary write-back from the GtkText to the item during this update. Since no new resources are allocated in this step, an “unbind” signal handler is not needed.
The two signal handlers text_changed_cb and
item_selected_cb are shown below.
static void
text_changed_cb (GtkEditable *self, gpointer user_data) {
GtkListItem *listitem = GTK_LIST_ITEM (user_data);
LeData *data = LE_DATA (gtk_list_item_get_item(listitem));
const char *s;
if (data) {
s = gtk_editable_get_text (self);
le_data_set_string (data, s);
}
}
static void
item_selected_cb (GtkListItem *listitem, GParamSpec *pspec, gpointer user_data) {
GtkWidget *text = GTK_WIDGET (user_data);
if (gtk_list_item_get_selected (listitem)) {
gtk_widget_grab_focus (text);
}
}GtkSingleSelection
In the liststore.ui file, the GtkListView’s
model property is set to a GtkSingleSelection, which in turn
wraps the underlying GListModel.
Clicking a row in the list view selects and highlights it, automatically focusing the GtkText widget for immediate editing. Since GtkSingleSelection handles all the selection logic internally, no additional code is required for this behavior.
Another Possible Implementation
Alternatively, you can use a GBinding object to bind the GtkText’s “text” property to the LeData’s “string” property. To do this, you first need to implement the “string” property in the LeData class.
The code for this version is available in the src/listeditor_binding/
directory. The GBinding instance is created in the “bind”
handler and unbound in the “unbind” handler. We use the
g_object_set_data function to store the binding
object within the list item.
This is just one example, and other implementations are possible. For instance, you could subclass GtkText and store the binding instance directly inside the subclass.
However, these alternatives require more code. Ultimately, using the “changed” signal remains the simplest and most straightforward approach.