2021-01-25 10:35:49 +01:00
|
|
|
# Ui file and GtkBuilder
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
## New, open and save button
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
We made the simplest editor in the previous section.
|
2021-02-06 09:26:57 +01:00
|
|
|
It reads the files in `on_open` function at start-up and writes them when closing the window.
|
2021-01-13 05:29:35 +01:00
|
|
|
It works but is not good.
|
|
|
|
It is better to make "New", "Open", "Save" and "Close" buttons.
|
|
|
|
This section describes how to put those buttons into the window.
|
|
|
|
Signals and handlers will be explained later.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
![Screenshot of the file editor](../image/screenshot_tfe2.png){width=9.3cm height=6.825cm}
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
The screenshot above shows the layout.
|
|
|
|
The function `on_open` in the source code `tfe2.c` is as follows.
|
2021-01-07 15:48:03 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@include
|
|
|
|
tfe/tfe2.c on_open
|
|
|
|
@@@
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
The point is how to build the window.
|
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
- 25-27: Generates GtkApplicationWindow and sets the title and default size.
|
|
|
|
- 29-30: Generates GtkBox `boxv`.
|
2021-01-13 05:29:35 +01:00
|
|
|
It is a vertical box and a child of GtkApplicationWindow.
|
|
|
|
It has two children.
|
|
|
|
The first child is a horizontal box includes buttons.
|
|
|
|
The second child is GtkNotebook.
|
2021-02-06 09:26:57 +01:00
|
|
|
- 32-33: Generates GtkBox `boxh` and appends it to 'boxv' as a first child.
|
|
|
|
- 35-40: Generates three dummy labels.
|
2021-01-13 05:29:35 +01:00
|
|
|
The labels `dmy1` and `dmy3` has a character width of ten.
|
2021-02-06 09:26:57 +01:00
|
|
|
The other label `dmy2` has hexpand property which is set to be TRUE.
|
2021-01-13 05:29:35 +01:00
|
|
|
This makes the label expands horizontally as long as possible.
|
2021-02-06 09:26:57 +01:00
|
|
|
- 41-44: Generates four buttons.
|
|
|
|
- 46-52: Appends these GtkLabel and GtkButton to `boxh`.
|
|
|
|
- 54-57: Generates GtkNotebook and sets hexpand and vexpand properties TRUE.
|
|
|
|
This makes it expand horizontally and vertically as big as possible.
|
2021-01-13 05:29:35 +01:00
|
|
|
It is appended to `boxv` as the second child.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
The number of lines is 33(=57-25+1) to build the widgets.
|
|
|
|
And we needed many variables (boxv, boxh, dmy1 ...).
|
|
|
|
Most of them aren't necessary except building the widgets.
|
|
|
|
Are there any good solution to reduce these work?
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Gtk provides GtkBuilder.
|
|
|
|
It reads ui data and builds a window.
|
2021-02-06 09:26:57 +01:00
|
|
|
It reduces the cumbersome work.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
## Ui file
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets.
|
2021-01-07 15:48:03 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@include
|
|
|
|
tfe/tfe3.ui
|
|
|
|
@@@
|
2021-01-13 05:29:35 +01:00
|
|
|
|
|
|
|
This is coded with XML structure.
|
2021-02-06 09:26:57 +01:00
|
|
|
Constructs beginning with `<` and ending with `>` are called tags.
|
|
|
|
And there are two types of tags, start tag and end tag.
|
2021-01-13 05:29:35 +01:00
|
|
|
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
|
|
|
Ui file begins and ends with interface tags.
|
2021-02-17 15:59:50 +01:00
|
|
|
Some tags, for example, object tags can have a class and id attributes in the start tag.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-17 15:59:50 +01:00
|
|
|
- 1: The first line is XML declaration.
|
|
|
|
It specifies that the version of XML is 1.0 and the encoding is UTF-8.
|
|
|
|
Even if the line is left out, GtkBuilder builds objects from the ui file.
|
|
|
|
But ui files must use UTF-8 encoding, or GtkBuilder can't recognize it and fatal error occurs.
|
|
|
|
- 3-6: An object with `GtkApplicationWindow` class and `win` id is defined.
|
2021-01-13 05:29:35 +01:00
|
|
|
This is the top level window.
|
|
|
|
And the three properties of the window are defined.
|
|
|
|
`title` property is "file editor", `default-width` property is 400 and `default-height` property is 300.
|
2021-02-17 15:59:50 +01:00
|
|
|
- 7: child tag means a child of the object above.
|
2021-01-13 05:29:35 +01:00
|
|
|
For example, line 7 tells us that GtkBox object which id is "boxv" is a child of `win`.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Compare this ui file and the lines 25-57 in the source code of `on_open` function.
|
2021-02-06 09:26:57 +01:00
|
|
|
Those two describe the same structure of widgets.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-17 15:59:50 +01:00
|
|
|
You can check the ui file with `gtk4-builder-tool`.
|
|
|
|
|
|
|
|
- `gtk4-builder-tool validate <ui file name>` validates the ui file.
|
|
|
|
If the ui file includes some syntactical error, `gtk4-builder-tool` prints the error.
|
|
|
|
- `gtk4-builder-tool simplify <ui file name>` simplifies the ui file and prints the result.
|
|
|
|
If `--replace` option is given, it replaces the ui file with the simplified one.
|
|
|
|
If the ui file specifies a value of property but it is default, then it will be removed.
|
|
|
|
Anf some values are simplified.
|
|
|
|
For example, "TRUE"and "FALSE" becomes "1" and "0" respectively.
|
|
|
|
However, "TRUE" or "FALSE" is better for maintenance.
|
|
|
|
|
|
|
|
It is a good idea to check your ui file before compiling.
|
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
## GtkBuilder
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
GtkBuilder builds widgets based on the ui file.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-25 10:35:49 +01:00
|
|
|
~~~C
|
|
|
|
GtkBuilder *build;
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-25 10:35:49 +01:00
|
|
|
build = gtk_builder_new_from_file ("tfe3.ui");
|
|
|
|
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
|
|
|
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
|
|
|
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
|
|
|
~~~
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
The function `gtk_builder_new_from_file` reads the file given as an argument.
|
|
|
|
Then, it builds the widgets and pointers to them, creates GtkBuilder object and put the pointers in it.
|
2021-01-13 05:29:35 +01:00
|
|
|
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
|
|
|
All the widgets are connected based on the parent-children relationship described in the ui file.
|
|
|
|
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets.
|
|
|
|
This reduces lines in the C source file.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@shell
|
2021-01-13 05:29:35 +01:00
|
|
|
cd tfe; diff tfe2.c tfe3.c
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
`60,103c61,65` means 42 (=103-60+1) lines change to 5 (=65-61+1) lines.
|
|
|
|
Therefore 37 lines are reduced.
|
|
|
|
Using ui file not only shortens C source files, but also makes the widgets' structure clear.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
Now I'll show you `on_open` function in the C source code `tfe3.c`.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@include
|
|
|
|
tfe/tfe3.c on_open
|
|
|
|
@@@
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
The whole source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
2021-01-13 05:29:35 +01:00
|
|
|
If you want to see it, click the link above.
|
2021-02-06 09:26:57 +01:00
|
|
|
You can also get the source files below.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
### Using ui string
|
|
|
|
|
|
|
|
GtkBuilder can build widgets using string.
|
|
|
|
Use the function gtk\_builder\_new\_from\_string instead of gtk\_builder\_new\_from\_file.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-25 10:35:49 +01:00
|
|
|
~~~C
|
|
|
|
char *uistring;
|
|
|
|
|
|
|
|
uistring =
|
|
|
|
"<interface>"
|
|
|
|
"<object class="GtkApplicationWindow" id="win">"
|
|
|
|
"<property name=\"title\">file editor</property>"
|
|
|
|
"<property name=\"default-width\">600</property>"
|
|
|
|
"<property name=\"default-height\">400</property>"
|
|
|
|
"<child>"
|
|
|
|
"<object class=\"GtkBox\" id=\"boxv\">"
|
|
|
|
"<property name="orientation">GTK_ORIENTATION_VERTICAL</property>"
|
|
|
|
... ... ...
|
|
|
|
... ... ...
|
|
|
|
"</interface>";
|
|
|
|
|
|
|
|
build = gtk_builder_new_from_stringfile (uistring);
|
|
|
|
~~~
|
2021-01-13 05:29:35 +01:00
|
|
|
|
|
|
|
This method has an advantage and disadvantage.
|
|
|
|
The advantage is that the ui string is written in the source code.
|
|
|
|
So ui file is not necessary on runtime.
|
|
|
|
The disadvantage is that writing C string is a bit bothersome because of the double quotes.
|
|
|
|
If you want to use this method, you should write a script that transforms ui file into C-string.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
- Add backslash before each double quote.
|
|
|
|
- Add double quote at the left and right.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
### Using Gresource
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Using Gresource is similar to using string.
|
|
|
|
But Gresource is compressed binary data, not text data.
|
|
|
|
And there's a compiler that compiles ui file into Gresource.
|
|
|
|
It can compile not only text files but also binary files such as images, sounds and so on.
|
|
|
|
And after compilation, it bundles them up into one Gresource object.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
An xml file is necessary for the resource compiler `glib-compile-resources`.
|
|
|
|
It describes resource files.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@include
|
|
|
|
tfe/tfe3.gresource.xml
|
|
|
|
@@@
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-06 09:26:57 +01:00
|
|
|
- 2: `gresources` tag can include multiple gresources (gresource tags).
|
2021-01-13 05:29:35 +01:00
|
|
|
However, this xml has only one gresource.
|
|
|
|
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
2021-02-06 09:26:57 +01:00
|
|
|
- 4: The gresource has `tfe3.ui`.
|
2021-01-13 05:29:35 +01:00
|
|
|
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
|
|
|
If you want to add more files, then insert them between line 4 and 5.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Save this xml text to `tfe3.gresource.xml`.
|
|
|
|
The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@shell
|
2021-01-13 05:29:35 +01:00
|
|
|
LANG=C glib-compile-resources --help
|
2021-02-08 14:24:54 +01:00
|
|
|
@@@
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Now run the compiler.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Then a C source file `resources.c` is generated.
|
2021-02-06 09:26:57 +01:00
|
|
|
Modify `tfe3.c` and save it as `tfe3_r.c`.
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-25 10:35:49 +01:00
|
|
|
~~~C
|
|
|
|
#include "resources.c"
|
|
|
|
... ... ...
|
|
|
|
... ... ...
|
|
|
|
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
|
|
|
|
... ... ...
|
|
|
|
... ... ...
|
|
|
|
~~~
|
2020-12-21 13:12:05 +01:00
|
|
|
|
2021-01-13 05:29:35 +01:00
|
|
|
Then, compile and run it.
|
|
|
|
The window appears and it is the same as the screenshot at the beginning of this page.
|
2020-12-21 13:12:05 +01:00
|
|
|
|