mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-11 20:03:35 +01:00
Add section 24.
This commit is contained in:
parent
dda0a704cd
commit
a2dca7cfac
18 changed files with 681 additions and 12 deletions
|
@ -37,3 +37,4 @@ You can read it without download.
|
|||
1. [GtkDrawingArea and Cairo](gfm/sec21.md)
|
||||
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec22.md)
|
||||
1. [Tiny turtle graphics interpreter](gfm/sec23.md)
|
||||
1. [List](gfm/sec24.md)
|
||||
|
|
|
@ -198,9 +198,9 @@ For example,
|
|||
gssize size = g_input_stream_read (G_INPUT_STREAM (istream), void *buffer, gsize count, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This function reads data from `istream` and put them into `buffer`.
|
||||
This function reads data from `istream` and puts them into `buffer`.
|
||||
|
||||
GDtaInputStream is used often.
|
||||
GDataInputStream is used often.
|
||||
It can read structured data such as sized data (byte, int16, int32 and int64) and lines (new line terminated string).
|
||||
For example,
|
||||
|
||||
|
|
|
@ -145,9 +145,9 @@ For example,
|
|||
gssize size = g_input_stream_read (G_INPUT_STREAM (istream), void *buffer, gsize count, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This function reads data from `istream` and put them into `buffer`.
|
||||
This function reads data from `istream` and puts them into `buffer`.
|
||||
|
||||
GDtaInputStream is used often.
|
||||
GDataInputStream is used often.
|
||||
It can read structured data such as sized data (byte, int16, int32 and int64) and lines (new line terminated string).
|
||||
For example,
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ textview {color: yellow; ...}
|
|||
~~~
|
||||
|
||||
Class, ID and some other things can be applied to the selector like Web CSS.
|
||||
Refer [GTK4 API reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/theming.html) for further information.
|
||||
Refer [GTK4 API reference](https://developer.gnome.org/gtk4/stable/theming.html) for further information.
|
||||
|
||||
In line 30, the CSS is a string.
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ I installed gtk4 under the directory `$HOME/local`.
|
|||
This is a private user area.
|
||||
|
||||
If you want to install it in the system area, `/opt/gtk4` is one of good choices.
|
||||
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||
[Gtk4 API Reference](https://developer.gnome.org/gtk4/stable/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||
|
||||
Don't install it to `/usr/local` which is the default.
|
||||
It is used by Ubuntu applications, which are not build on gtk4.
|
||||
|
@ -136,7 +136,7 @@ Compile and install it.
|
|||
$ ninja -C _build
|
||||
$ ninja -C _build install
|
||||
|
||||
If you want to know more information, refer to [Gtk4 reference manual](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html).
|
||||
If you want to know more information, refer to [Gtk4 reference manual](https://developer.gnome.org/gtk4/stable/gtk-building.html).
|
||||
|
||||
## Modify env.sh
|
||||
|
||||
|
|
|
@ -442,7 +442,7 @@ This XML file is the same as before except template tag.
|
|||
|
||||
There are three public functions.
|
||||
The function `tfe_window_notebook_page_new` creates a new notebook page.
|
||||
This is a wrapper function of `notebook_page_new`.
|
||||
This is a wrapper function for `notebook_page_new`.
|
||||
It is called by GtkApplication object.
|
||||
The function `tfe_window_notebook_page_new_with_files` creates notebook pages with a contents read from the given files.
|
||||
The function `tfe_window_new` creates a TfeWindow instance.
|
||||
|
|
327
gfm/sec24.md
Normal file
327
gfm/sec24.md
Normal file
|
@ -0,0 +1,327 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md)
|
||||
|
||||
# List
|
||||
|
||||
Gtk4 has added new list objects GtkListView, GtkGridView and GtkColumnView.
|
||||
The new feature is described in the sections "GListModel support" and "List-based widgets" at the top of [the third chapter of the API reference](https://developer.gnome.org/gtk4/stable/gtkobjects.html).
|
||||
|
||||
Gtk4 has other means to implement lists.
|
||||
They are GtkListBox and GtkTreeView which are took over from Gtk3.
|
||||
There's an article in [Gtk Development blog](https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/) about list widgets by Matthias Clasen.
|
||||
He described why GtkListView are developed to replace GtkListBox and GtkTreeView.
|
||||
|
||||
I want to explain GtkListView and its related objects in this tutorial.
|
||||
|
||||
## Outline
|
||||
|
||||
A list is a sequential data structure.
|
||||
For example, an ordered string sequence "one", "two", "three", "four" is a list.
|
||||
Each element of the list is called item.
|
||||
A list is like an array, but in many cases it is implemented with pointers which point to the next item of the list.
|
||||
And it has a start point.
|
||||
So, each item can be referred by the index of the item (first item, second item, ..., nth item, ...).
|
||||
There are two cases.
|
||||
One is the index starts from one (one-based) and the other is it starts from zero (zero-based).
|
||||
|
||||
Gio provides GListModel interface.
|
||||
It is a zero-based list of the same type of GObject objects, or objects that implement the same interface.
|
||||
An object implements GListModel is usually not a widget.
|
||||
So, the list is not displayed on the screen directly.
|
||||
There's another object GtkListView which is a widget to display the list.
|
||||
The items in the list need to be connected to the items in GtkListView.
|
||||
GtkListItemFactory object maps items in the list to GListView.
|
||||
|
||||
![List](../image/list.png)
|
||||
|
||||
The instruction to build the whole list related objects is:
|
||||
|
||||
1. Implement the list object which implements GListModel.
|
||||
2. Build widgets and put GtkListView as a child of GtkScrolledWindow.
|
||||
3. Set GtkListItemFactory.
|
||||
|
||||
## GListModel
|
||||
|
||||
If you want to make a list of strings with GListModel, for example, "one", "two", "three", "four", note that strings can't be items of the list.
|
||||
Because GListModel is a list of GObject objects and strings aren't GObject objects.
|
||||
So, you need a wrapper which is a gobject and contains a string.
|
||||
GtkStringObject is the wrapper object and GStringList, implements GListModel, is a list of GtkStringObject.
|
||||
|
||||
~~~C
|
||||
char *array[] = {"one", "two", "three", "four", NULL};
|
||||
GtkStringList *stringlist = gtk_string_list_new ((const char * const *) array);
|
||||
~~~
|
||||
|
||||
The function `gtk_string_list_new` creates GtkStringList object.
|
||||
Its items are GtkStringObject objects which contain the strings "one", "two", "three" and "four".
|
||||
There are functions to add items to the list or remove items from the list.
|
||||
|
||||
- `gtk_string_list_append` appends an item to the list
|
||||
- `gtk_string_list_remove` removes an item from the list
|
||||
- `gtk_string_list_get_string` gets a string in the list
|
||||
|
||||
See [API document](https://developer.gnome.org/gtk4/stable/GtkStringList.html) for the further information.
|
||||
|
||||
I'll explain the other list objects later.
|
||||
|
||||
## GtkSelectionModel
|
||||
|
||||
GtkSelectionModel is an interface to support for selections.
|
||||
Thanks to this model, user can select items by clicking on them.
|
||||
It is implemented by GtkMultiSelection, GtkNoSelection and GtkSingleSelection objects.
|
||||
These three objects are usually enough to build an application.
|
||||
They are created with GListModel.
|
||||
You can also create them alone and add GListModel later.
|
||||
|
||||
- GtkMultiSelection supports multiple selection.
|
||||
- GtkNoSelection supports no selection. This is a wrapper to GListModel when GtkSelectionModel is needed.
|
||||
- GtkSingleSelection supports single selection.
|
||||
|
||||
## GtkListView
|
||||
|
||||
GtkListView is a widget to show GListModel items.
|
||||
GtkListViewItem is used by GtkListView to represent items of a list model.
|
||||
But, GtkListViewItem itself is not a widget, so a user needs to set a widget, for example GtkLabel, as a child of GtkListView to display an item of the list model.
|
||||
"item" property of GtkListViewItem points an object that belongs to the list model.
|
||||
|
||||
![GtkListViewItem](../image/gtklistviewitem.png)
|
||||
|
||||
In case the number of items is very big, for example more than a thousand, GtkListViewItem is recycled and connected to another item which is newly displayed.
|
||||
This recycle makes the number of GtkListViewItem objects fairly small, less than 200.
|
||||
This is very effective to restrain the growth of memory consumption so that GListModel can contain lots of items, for example, more than a million items.
|
||||
|
||||
## GtkListItemFactory
|
||||
|
||||
GtkListItemFactory creates or recycles GtkListViewItem and connects it with an item of the list model.
|
||||
There are two child objects of this factory, GtkSignalListItemFactory and GtkBuilderListItemFactory.
|
||||
|
||||
### GtkSignalListItemFactory
|
||||
|
||||
GtkSignalListItemFactory provides signals for users to configure a GtkListViewItem object.
|
||||
There are four signals.
|
||||
|
||||
1. "setup" is emitted to set up GtkListViewItem object.
|
||||
A user sets its child widget in the handler.
|
||||
For example, creates a GtkLabel widget and sets the child property of GtkListViewItem object to it.
|
||||
This setting is kept even the GtkListViewItem object is recycled (to bind to another item of GModelList).
|
||||
2. "bind" is emitted to bind an item in the list model to the widget.
|
||||
For example, a user gets the item object from "item" property of the GtkListViewItem object.
|
||||
Then gets the string of the item and sets the label property of the GtkLabel object with the string.
|
||||
This signal is emitted when the GtkListViewItem is newly created and set up, recycled or some changes has happened to the item of the list.
|
||||
3. "unbind" is emitted to unbind an item.
|
||||
A user undoes everything done in step 2 in the signal handler.
|
||||
If some object are created in step 2, they must be destroyed.
|
||||
4. "teardown" is emitted to undo everything done in step 1.
|
||||
So, the widget created in step 1 must be destroyed.
|
||||
After this signal, the list item will be destroyed.
|
||||
|
||||
The following program `list1.c` shows the list of strings "one", "two", "three" and "four".
|
||||
GtkNoSelection is used, so user can't select any item.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 setup_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
|
||||
5 GtkWidget *lb = gtk_label_new ("");
|
||||
6 gtk_list_item_set_child (listitem, lb);
|
||||
7 }
|
||||
8
|
||||
9 static void
|
||||
10 bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
|
||||
11 GtkWidget *lb = gtk_list_item_get_child (listitem);
|
||||
12 GtkStringObject *strobj = gtk_list_item_get_item (listitem);
|
||||
13 const char *text = gtk_string_object_get_string (strobj);
|
||||
14
|
||||
15 gtk_label_set_text (GTK_LABEL (lb), text);
|
||||
16 }
|
||||
17
|
||||
18 static void
|
||||
19 unbind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
|
||||
20 GtkWidget *lb = gtk_list_item_get_child (listitem);
|
||||
21 gtk_label_set_text (GTK_LABEL (lb), "");
|
||||
22 }
|
||||
23
|
||||
24 static void
|
||||
25 teardown_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
|
||||
26 gtk_list_item_set_child (listitem, NULL);
|
||||
27 /* When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed. */
|
||||
28 /* Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code. */
|
||||
29 }
|
||||
30
|
||||
31 /* ----- activate, open, startup handlers ----- */
|
||||
32 static void
|
||||
33 tfe_activate (GApplication *application) {
|
||||
34 GtkApplication *app = GTK_APPLICATION (application);
|
||||
35 GtkWidget *win = gtk_application_window_new (app);
|
||||
36 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
37 GtkWidget *scr = gtk_scrolled_window_new ();
|
||||
38 gtk_window_set_child (GTK_WINDOW (win), scr);
|
||||
39
|
||||
40 char *array[] = {
|
||||
41 "one", "two", "three", "four", NULL
|
||||
42 };
|
||||
43 GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
|
||||
44 GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (sl));
|
||||
45
|
||||
46 GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
|
||||
47 g_signal_connect (factory, "setup", G_CALLBACK (setup_cb), NULL);
|
||||
48 g_signal_connect (factory, "bind", G_CALLBACK (bind_cb), NULL);
|
||||
49 g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cb), NULL);
|
||||
50 g_signal_connect (factory, "teardown", G_CALLBACK (teardown_cb), NULL);
|
||||
51
|
||||
52 GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
|
||||
53 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
54 gtk_widget_show (win);
|
||||
55 }
|
||||
56
|
||||
57 static void
|
||||
58 tfe_startup (GApplication *application) {
|
||||
59 }
|
||||
60
|
||||
61 /* ----- main ----- */
|
||||
62 int
|
||||
63 main (int argc, char **argv) {
|
||||
64 GtkApplication *app;
|
||||
65 int stat;
|
||||
66
|
||||
67 app = gtk_application_new ("com.github.ToshioCP.list1", G_APPLICATION_FLAGS_NONE);
|
||||
68
|
||||
69 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
70 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
71
|
||||
72 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
73 g_object_unref (app);
|
||||
74 return stat;
|
||||
75 }
|
||||
76
|
||||
~~~
|
||||
|
||||
The file `list1.c` is located under the directory [src/misc](../src/misc).
|
||||
Make a shell script below and save it to $HOME/local/bin/comp (comp is the filename).
|
||||
|
||||
~~~Shell
|
||||
gcc `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`
|
||||
~~~
|
||||
|
||||
Change the current directory to the directory includes `list1.c` and type as follows.
|
||||
|
||||
~~~
|
||||
$ chmod 755 $HOME/local/bin/comp
|
||||
$ comp list1
|
||||
$ ./a.out
|
||||
~~~
|
||||
|
||||
Then, `list1.c` has been compiled and executed.
|
||||
|
||||
![list1](../image/list1.png)
|
||||
|
||||
I think the program is not so difficult.
|
||||
If you feel some difficulty, read this section again, especially GtkSignalListItemFactory subsubsection.
|
||||
|
||||
### GtkBuilderListItemFactory
|
||||
|
||||
GtkBuilderListItemFactory is another GtkListItemFactory.
|
||||
Its behavior is defined with ui file.
|
||||
|
||||
~~~xml
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<binding name="label">
|
||||
<lookup name="string" type="GtkStringObject">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
~~~
|
||||
|
||||
Template tag is used to define GtkListItem.
|
||||
And its child property is GtkLabel object.
|
||||
The factory sees this template and creates GtkLabel and sets the child property of GtkListItem.
|
||||
This is the same as what setup handler of GtkSignalListItemFactory did.
|
||||
|
||||
Then, bind the label property of GtkLabel to string property of GtkStringObject.
|
||||
The string object is referred to by item property of GtkListItem.
|
||||
So, the lookup tag is like this:
|
||||
|
||||
~~~
|
||||
string <- GtkStringObject <- item <- GtkListItem
|
||||
~~~
|
||||
|
||||
The source code is as follows.
|
||||
Its name is `list2.c` and located under [src/misc](../src/misc) directory.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 /* ----- activate, open, startup handlers ----- */
|
||||
4 static void
|
||||
5 tfe_activate (GApplication *application) {
|
||||
6 GtkApplication *app = GTK_APPLICATION (application);
|
||||
7 GtkWidget *win = gtk_application_window_new (app);
|
||||
8 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
9 GtkWidget *scr = gtk_scrolled_window_new ();
|
||||
10 gtk_window_set_child (GTK_WINDOW (win), scr);
|
||||
11
|
||||
12 char *array[] = {
|
||||
13 "one", "two", "three", "four", NULL
|
||||
14 };
|
||||
15 GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
|
||||
16 GtkSingleSelection *ss = gtk_single_selection_new (G_LIST_MODEL (sl));
|
||||
17
|
||||
18 const char *ui_string =
|
||||
19 "<interface>"
|
||||
20 "<template class=\"GtkListItem\">"
|
||||
21 "<property name=\"child\">"
|
||||
22 "<object class=\"GtkLabel\">"
|
||||
23 "<binding name=\"label\">"
|
||||
24 "<lookup name=\"string\" type=\"GtkStringObject\">"
|
||||
25 "<lookup name=\"item\">GtkListItem</lookup>"
|
||||
26 "</lookup>"
|
||||
27 "</binding>"
|
||||
28 "</object>"
|
||||
29 "</property>"
|
||||
30 "</template>"
|
||||
31 "</interface>"
|
||||
32 ;
|
||||
33 GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));
|
||||
34 GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
|
||||
35
|
||||
36 GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ss), factory);
|
||||
37 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
38 gtk_widget_show (win);
|
||||
39 }
|
||||
40
|
||||
41 static void
|
||||
42 tfe_startup (GApplication *application) {
|
||||
43 }
|
||||
44
|
||||
45 /* ----- main ----- */
|
||||
46 int
|
||||
47 main (int argc, char **argv) {
|
||||
48 GtkApplication *app;
|
||||
49 int stat;
|
||||
50
|
||||
51 app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
52
|
||||
53 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
54 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
55
|
||||
56 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
57 g_object_unref (app);
|
||||
58 return stat;
|
||||
59 }
|
||||
60
|
||||
~~~
|
||||
|
||||
No signal handler is needed for GtkBulderListItemFactory.
|
||||
GtkSingleSelection is used, so user can select one item at a time.
|
||||
|
||||
Because this is a small program, the ui data is given as strings.
|
||||
However, generally, ui file is better than string and using resource compiler is the best choice.
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md)
|
BIN
image/gtklistviewitem.png
Normal file
BIN
image/gtklistviewitem.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
image/gtklistviewitem.xcf
Normal file
BIN
image/gtklistviewitem.xcf
Normal file
Binary file not shown.
BIN
image/list.png
Normal file
BIN
image/list.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
image/list.xcf
Normal file
BIN
image/list.xcf
Normal file
Binary file not shown.
BIN
image/list1.png
Normal file
BIN
image/list1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
76
src/misc/list1.c
Normal file
76
src/misc/list1.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
setup_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
|
||||
GtkWidget *lb = gtk_label_new ("");
|
||||
gtk_list_item_set_child (listitem, lb);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
|
||||
GtkWidget *lb = gtk_list_item_get_child (listitem);
|
||||
GtkStringObject *strobj = gtk_list_item_get_item (listitem);
|
||||
const char *text = gtk_string_object_get_string (strobj);
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (lb), text);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
|
||||
GtkWidget *lb = gtk_list_item_get_child (listitem);
|
||||
gtk_label_set_text (GTK_LABEL (lb), "");
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
|
||||
gtk_list_item_set_child (listitem, NULL);
|
||||
/* When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed. */
|
||||
/* Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code. */
|
||||
}
|
||||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = gtk_application_window_new (app);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
GtkWidget *scr = gtk_scrolled_window_new ();
|
||||
gtk_window_set_child (GTK_WINDOW (win), scr);
|
||||
|
||||
char *array[] = {
|
||||
"one", "two", "three", "four", NULL
|
||||
};
|
||||
GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
|
||||
GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (sl));
|
||||
|
||||
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_cb), NULL);
|
||||
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cb), NULL);
|
||||
g_signal_connect (factory, "teardown", G_CALLBACK (teardown_cb), NULL);
|
||||
|
||||
GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new ("com.github.ToshioCP.list1", G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
60
src/misc/list2.c
Normal file
60
src/misc/list2.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = gtk_application_window_new (app);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
GtkWidget *scr = gtk_scrolled_window_new ();
|
||||
gtk_window_set_child (GTK_WINDOW (win), scr);
|
||||
|
||||
char *array[] = {
|
||||
"one", "two", "three", "four", NULL
|
||||
};
|
||||
GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
|
||||
GtkSingleSelection *ss = gtk_single_selection_new (G_LIST_MODEL (sl));
|
||||
|
||||
const char *ui_string =
|
||||
"<interface>"
|
||||
"<template class=\"GtkListItem\">"
|
||||
"<property name=\"child\">"
|
||||
"<object class=\"GtkLabel\">"
|
||||
"<binding name=\"label\">"
|
||||
"<lookup name=\"string\" type=\"GtkStringObject\">"
|
||||
"<lookup name=\"item\">GtkListItem</lookup>"
|
||||
"</lookup>"
|
||||
"</binding>"
|
||||
"</object>"
|
||||
"</property>"
|
||||
"</template>"
|
||||
"</interface>"
|
||||
;
|
||||
GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));
|
||||
GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
|
||||
|
||||
GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ss), factory);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ textview {color: yellow; ...}
|
|||
~~~
|
||||
|
||||
Class, ID and some other things can be applied to the selector like Web CSS.
|
||||
Refer [GTK4 API reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/theming.html) for further information.
|
||||
Refer [GTK4 API reference](https://developer.gnome.org/gtk4/stable/theming.html) for further information.
|
||||
|
||||
In line 30, the CSS is a string.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ I installed gtk4 under the directory `$HOME/local`.
|
|||
This is a private user area.
|
||||
|
||||
If you want to install it in the system area, `/opt/gtk4` is one of good choices.
|
||||
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||
[Gtk4 API Reference](https://developer.gnome.org/gtk4/stable/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||
|
||||
Don't install it to `/usr/local` which is the default.
|
||||
It is used by Ubuntu applications, which are not build on gtk4.
|
||||
|
@ -134,7 +134,7 @@ Compile and install it.
|
|||
$ ninja -C _build
|
||||
$ ninja -C _build install
|
||||
|
||||
If you want to know more information, refer to [Gtk4 reference manual](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html).
|
||||
If you want to know more information, refer to [Gtk4 reference manual](https://developer.gnome.org/gtk4/stable/gtk-building.html).
|
||||
|
||||
## Modify env.sh
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ tfe7/tfewindow.h
|
|||
|
||||
There are three public functions.
|
||||
The function `tfe_window_notebook_page_new` creates a new notebook page.
|
||||
This is a wrapper function of `notebook_page_new`.
|
||||
This is a wrapper function for `notebook_page_new`.
|
||||
It is called by GtkApplication object.
|
||||
The function `tfe_window_notebook_page_new_with_files` creates notebook pages with a contents read from the given files.
|
||||
The function `tfe_window_new` creates a TfeWindow instance.
|
||||
|
|
205
src/sec24.src.md
Normal file
205
src/sec24.src.md
Normal file
|
@ -0,0 +1,205 @@
|
|||
# List
|
||||
|
||||
Gtk4 has added new list objects GtkListView, GtkGridView and GtkColumnView.
|
||||
The new feature is described in the sections "GListModel support" and "List-based widgets" at the top of [the third chapter of the API reference](https://developer.gnome.org/gtk4/stable/gtkobjects.html).
|
||||
|
||||
Gtk4 has other means to implement lists.
|
||||
They are GtkListBox and GtkTreeView which are took over from Gtk3.
|
||||
There's an article in [Gtk Development blog](https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/) about list widgets by Matthias Clasen.
|
||||
He described why GtkListView are developed to replace GtkListBox and GtkTreeView.
|
||||
|
||||
I want to explain GtkListView and its related objects in this tutorial.
|
||||
|
||||
## Outline
|
||||
|
||||
A list is a sequential data structure.
|
||||
For example, an ordered string sequence "one", "two", "three", "four" is a list.
|
||||
Each element of the list is called item.
|
||||
A list is like an array, but in many cases it is implemented with pointers which point to the next item of the list.
|
||||
And it has a start point.
|
||||
So, each item can be referred by the index of the item (first item, second item, ..., nth item, ...).
|
||||
There are two cases.
|
||||
One is the index starts from one (one-based) and the other is it starts from zero (zero-based).
|
||||
|
||||
Gio provides GListModel interface.
|
||||
It is a zero-based list of the same type of GObject objects, or objects that implement the same interface.
|
||||
An object implements GListModel is usually not a widget.
|
||||
So, the list is not displayed on the screen directly.
|
||||
There's another object GtkListView which is a widget to display the list.
|
||||
The items in the list need to be connected to the items in GtkListView.
|
||||
GtkListItemFactory object maps items in the list to GListView.
|
||||
|
||||
![List](../image/list.png){width=10cm height=7.5cm}
|
||||
|
||||
The instruction to build the whole list related objects is:
|
||||
|
||||
1. Implement the list object which implements GListModel.
|
||||
2. Build widgets and put GtkListView as a child of GtkScrolledWindow.
|
||||
3. Set GtkListItemFactory.
|
||||
|
||||
## GListModel
|
||||
|
||||
If you want to make a list of strings with GListModel, for example, "one", "two", "three", "four", note that strings can't be items of the list.
|
||||
Because GListModel is a list of GObject objects and strings aren't GObject objects.
|
||||
So, you need a wrapper which is a gobject and contains a string.
|
||||
GtkStringObject is the wrapper object and GStringList, implements GListModel, is a list of GtkStringObject.
|
||||
|
||||
@@@if gfm
|
||||
~~~C
|
||||
@@@elif html
|
||||
~~~{.C}
|
||||
@@@elif latex
|
||||
~~~{.C}
|
||||
@@@else
|
||||
~~~
|
||||
@@@end
|
||||
char *array[] = {"one", "two", "three", "four", NULL};
|
||||
GtkStringList *stringlist = gtk_string_list_new ((const char * const *) array);
|
||||
~~~
|
||||
|
||||
The function `gtk_string_list_new` creates GtkStringList object.
|
||||
Its items are GtkStringObject objects which contain the strings "one", "two", "three" and "four".
|
||||
There are functions to add items to the list or remove items from the list.
|
||||
|
||||
- `gtk_string_list_append` appends an item to the list
|
||||
- `gtk_string_list_remove` removes an item from the list
|
||||
- `gtk_string_list_get_string` gets a string in the list
|
||||
|
||||
See [API document](https://developer.gnome.org/gtk4/stable/GtkStringList.html) for the further information.
|
||||
|
||||
I'll explain the other list objects later.
|
||||
|
||||
## GtkSelectionModel
|
||||
|
||||
GtkSelectionModel is an interface to support for selections.
|
||||
Thanks to this model, user can select items by clicking on them.
|
||||
It is implemented by GtkMultiSelection, GtkNoSelection and GtkSingleSelection objects.
|
||||
These three objects are usually enough to build an application.
|
||||
They are created with GListModel.
|
||||
You can also create them alone and add GListModel later.
|
||||
|
||||
- GtkMultiSelection supports multiple selection.
|
||||
- GtkNoSelection supports no selection. This is a wrapper to GListModel when GtkSelectionModel is needed.
|
||||
- GtkSingleSelection supports single selection.
|
||||
|
||||
## GtkListView
|
||||
|
||||
GtkListView is a widget to show GListModel items.
|
||||
GtkListViewItem is used by GtkListView to represent items of a list model.
|
||||
But, GtkListViewItem itself is not a widget, so a user needs to set a widget, for example GtkLabel, as a child of GtkListView to display an item of the list model.
|
||||
"item" property of GtkListViewItem points an object that belongs to the list model.
|
||||
|
||||
![GtkListViewItem](../image/gtklistviewitem.png){width=10cm height=7.5cm}
|
||||
|
||||
In case the number of items is very big, for example more than a thousand, GtkListViewItem is recycled and connected to another item which is newly displayed.
|
||||
This recycle makes the number of GtkListViewItem objects fairly small, less than 200.
|
||||
This is very effective to restrain the growth of memory consumption so that GListModel can contain lots of items, for example, more than a million items.
|
||||
|
||||
## GtkListItemFactory
|
||||
|
||||
GtkListItemFactory creates or recycles GtkListViewItem and connects it with an item of the list model.
|
||||
There are two child objects of this factory, GtkSignalListItemFactory and GtkBuilderListItemFactory.
|
||||
|
||||
### GtkSignalListItemFactory
|
||||
|
||||
GtkSignalListItemFactory provides signals for users to configure a GtkListViewItem object.
|
||||
There are four signals.
|
||||
|
||||
1. "setup" is emitted to set up GtkListViewItem object.
|
||||
A user sets its child widget in the handler.
|
||||
For example, creates a GtkLabel widget and sets the child property of GtkListViewItem object to it.
|
||||
This setting is kept even the GtkListViewItem object is recycled (to bind to another item of GModelList).
|
||||
2. "bind" is emitted to bind an item in the list model to the widget.
|
||||
For example, a user gets the item object from "item" property of the GtkListViewItem object.
|
||||
Then gets the string of the item and sets the label property of the GtkLabel object with the string.
|
||||
This signal is emitted when the GtkListViewItem is newly created and set up, recycled or some changes has happened to the item of the list.
|
||||
3. "unbind" is emitted to unbind an item.
|
||||
A user undoes everything done in step 2 in the signal handler.
|
||||
If some object are created in step 2, they must be destroyed.
|
||||
4. "teardown" is emitted to undo everything done in step 1.
|
||||
So, the widget created in step 1 must be destroyed.
|
||||
After this signal, the list item will be destroyed.
|
||||
|
||||
The following program `list1.c` shows the list of strings "one", "two", "three" and "four".
|
||||
GtkNoSelection is used, so user can't select any item.
|
||||
|
||||
@@@include
|
||||
misc/list1.c
|
||||
@@@
|
||||
|
||||
The file `list1.c` is located under the directory [src/misc](misc).
|
||||
Make a shell script below and save it to $HOME/local/bin/comp (comp is the filename).
|
||||
|
||||
@@@if gfm
|
||||
~~~Shell
|
||||
@@@elif html
|
||||
~~~{.bash}
|
||||
@@@elif latex
|
||||
~~~{.bash}
|
||||
@@@else
|
||||
~~~
|
||||
@@@end
|
||||
gcc `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`
|
||||
~~~
|
||||
|
||||
Change the current directory to the directory includes `list1.c` and type as follows.
|
||||
|
||||
~~~
|
||||
$ chmod 755 $HOME/local/bin/comp
|
||||
$ comp list1
|
||||
$ ./a.out
|
||||
~~~
|
||||
|
||||
Then, `list1.c` has been compiled and executed.
|
||||
|
||||
![list1](../image/list1.png){width=6.04cm height=4.40cm}
|
||||
|
||||
I think the program is not so difficult.
|
||||
If you feel some difficulty, read this section again, especially GtkSignalListItemFactory subsubsection.
|
||||
|
||||
### GtkBuilderListItemFactory
|
||||
|
||||
GtkBuilderListItemFactory is another GtkListItemFactory.
|
||||
Its behavior is defined with ui file.
|
||||
|
||||
~~~xml
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<binding name="label">
|
||||
<lookup name="string" type="GtkStringObject">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
~~~
|
||||
|
||||
Template tag is used to define GtkListItem.
|
||||
And its child property is GtkLabel object.
|
||||
The factory sees this template and creates GtkLabel and sets the child property of GtkListItem.
|
||||
This is the same as what setup handler of GtkSignalListItemFactory did.
|
||||
|
||||
Then, bind the label property of GtkLabel to string property of GtkStringObject.
|
||||
The string object is referred to by item property of GtkListItem.
|
||||
So, the lookup tag is like this:
|
||||
|
||||
~~~
|
||||
string <- GtkStringObject <- item <- GtkListItem
|
||||
~~~
|
||||
|
||||
The source code is as follows.
|
||||
Its name is `list2.c` and located under [src/misc](misc) directory.
|
||||
|
||||
@@@include
|
||||
misc/list2.c
|
||||
@@@
|
||||
|
||||
No signal handler is needed for GtkBulderListItemFactory.
|
||||
GtkSingleSelection is used, so user can select one item at a time.
|
||||
|
||||
Because this is a small program, the ui data is given as strings.
|
||||
However, generally, ui file is better than string and using resource compiler is the best choice.
|
Loading…
Reference in a new issue