2022-11-21 14:48:20 +01:00
|
|
|
|
Up: [README.md](../README.md), Prev: [Section 19](sec19.md), Next: [Section 21](sec21.md)
|
2021-01-24 08:50:52 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
# Composite widgets and alert dialog
|
2021-01-24 08:50:52 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
The source files are in the [Gtk4 tutorial GitHub repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
|
|
|
|
Download it and see `src/tfe6` directory.
|
2021-02-19 14:20:23 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
## An outline of new Tfe text editor
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
Tfe text editor will be restructured.
|
|
|
|
|
The program is divided into six parts.
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
- Main program: the C main function.
|
|
|
|
|
- TfeApplication object: It is like GtkApplication but keeps GSettings and CSS Provider.
|
|
|
|
|
- TfeWindow object: It is a window with buttons and a notebook.
|
|
|
|
|
- TfePref object: A preference dialog.
|
|
|
|
|
- TfeAlert object: An alert dialog.
|
|
|
|
|
- pdf2css.h and pdf2css.c: Font and CSS utility functions.
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
This section describes TfeAlert.
|
|
|
|
|
Others will be explained in the following sections.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
## Composite widgets
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
The alert dialog is like this:
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
![Alert dialog](../image/alert.png)
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
Tfe uses it when a user quits the application or closes a notebook without saving data to files.
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
The dialog has a title, buttons, an icon and a message.
|
|
|
|
|
Therefore, it consists of several widgets.
|
|
|
|
|
Such dialog is called a composite widget.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
Composite widgets are defined with template XMLs.
|
|
|
|
|
The class is built in the class initialization function and the instances are built and desposed by the following functions.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
- gtk\_widget\_init\_template
|
|
|
|
|
- gtk\_widget\_dispose\_template
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
TfeAlert is a good example to know composite widgets.
|
|
|
|
|
It is defined with the three files.
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
- tfealert.ui: XML file
|
|
|
|
|
- tfealert.h: Header file
|
|
|
|
|
- tfealert.c: C program file
|
2021-02-19 14:20:23 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
## The XML file
|
2021-01-24 08:50:52 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
A template tag is used in a composite widget XML.
|
2021-01-24 08:50:52 +01:00
|
|
|
|
|
2021-02-19 14:20:23 +01:00
|
|
|
|
~~~xml
|
|
|
|
|
1 <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
2 <interface>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
3 <template class="TfeAlert" parent="GtkWindow">
|
|
|
|
|
4 <property name="resizable">FALSE</property>
|
|
|
|
|
5 <property name="modal">TRUE</property>
|
|
|
|
|
6 <property name="titlebar">
|
|
|
|
|
7 <object class="GtkHeaderBar">
|
|
|
|
|
8 <property name="show-title-buttons">FALSE</property>
|
|
|
|
|
9 <property name="title-widget">
|
|
|
|
|
10 <object class="GtkLabel" id="lb_title">
|
|
|
|
|
11 <property name="label">Are you sure?</property>
|
|
|
|
|
12 <property name="single-line-mode">True</property>
|
|
|
|
|
13 </object>
|
|
|
|
|
14 </property>
|
|
|
|
|
15 <child type="start">
|
|
|
|
|
16 <object class="GtkButton" id="btn_cancel">
|
|
|
|
|
17 <property name="label">Cancel</property>
|
|
|
|
|
18 <style>
|
|
|
|
|
19 <class name="suggested-action"/>
|
|
|
|
|
20 </style>
|
|
|
|
|
21 <signal name="clicked" handler="cancel_cb" swapped="TRUE" object="TfeAlert"></signal>
|
|
|
|
|
22 </object>
|
|
|
|
|
23 </child>
|
|
|
|
|
24 <child type="end">
|
|
|
|
|
25 <object class="GtkButton" id="btn_accept">
|
|
|
|
|
26 <property name="label">Close</property>
|
|
|
|
|
27 <style>
|
|
|
|
|
28 <class name="destructive-action"/>
|
|
|
|
|
29 </style>
|
|
|
|
|
30 <signal name="clicked" handler="accept_cb" swapped="TRUE" object="TfeAlert"></signal>
|
|
|
|
|
31 </object>
|
|
|
|
|
32 </child>
|
|
|
|
|
33 </object>
|
|
|
|
|
34 </property>
|
|
|
|
|
35 <child>
|
|
|
|
|
36 <object class="GtkBox">
|
|
|
|
|
37 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
|
|
|
|
38 <property name="spacing">12</property>
|
|
|
|
|
39 <property name="margin-top">12</property>
|
|
|
|
|
40 <property name="margin-bottom">12</property>
|
|
|
|
|
41 <property name="margin-start">12</property>
|
|
|
|
|
42 <property name="margin-end">12</property>
|
|
|
|
|
43 <child>
|
|
|
|
|
44 <object class="GtkImage">
|
|
|
|
|
45 <property name="icon-name">dialog-warning</property>
|
|
|
|
|
46 <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
|
|
|
|
47 </object>
|
|
|
|
|
48 </child>
|
|
|
|
|
49 <child>
|
|
|
|
|
50 <object class="GtkLabel" id="lb_message">
|
|
|
|
|
51 </object>
|
|
|
|
|
52 </child>
|
|
|
|
|
53 </object>
|
|
|
|
|
54 </child>
|
|
|
|
|
55 </template>
|
|
|
|
|
56 </interface>
|
|
|
|
|
57
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
- 3: A template tag defines a composite widget.
|
|
|
|
|
The class attribute tells the class name of the composite widget.
|
|
|
|
|
The parent attribute tells the parent class of the composite widget.
|
|
|
|
|
So, TfeAlert is a child class of GtkWindow.
|
|
|
|
|
A parent attribute is an option and you can leave it out.
|
|
|
|
|
But it is recommended to write it in the template tag.
|
|
|
|
|
- 4-6: Its three properties are defined.
|
|
|
|
|
These properties are inherited from GtkWindow.
|
|
|
|
|
The titlebar property has a widget for a custom title bar.
|
|
|
|
|
The typical widget is GtkHeaderBar.
|
|
|
|
|
- 8: If the property "show-title-buttons" is TRUE, the title buttons like close, minimize and maximize are shown.
|
|
|
|
|
Otherwise it is not shown.
|
|
|
|
|
The TfeAlert object is not resizable.
|
|
|
|
|
It is closed when either of the two buttons, cancel or accept, is clicked.
|
|
|
|
|
Therefore the title buttons are not necessary and this property is set to FALSE.
|
|
|
|
|
- 9-14: The bar has a title, which is a GtkLabel widget.
|
|
|
|
|
The default title is "Are you sure?" but it can be replaced by an instance method.
|
|
|
|
|
- 15-32: The bar has two buttons, cancel and accept.
|
|
|
|
|
The cancel button is on the left so the child tag has `type="start"` attribute.
|
|
|
|
|
The accept button is on the right so the child tag has `type="end"` attribute.
|
|
|
|
|
The dialog is shown when the user clicked the close button or the quit menu without saving the data.
|
|
|
|
|
Therefore, it is safer for the user to click on the cancel button of the alert dialog.
|
|
|
|
|
So, the cancel button has a "suggested-action" CSS class.
|
|
|
|
|
Ubuntu colors the button green but the color can be blue or other appropriate one defined by the system.
|
|
|
|
|
In the same way the accept button has a "destructive-action" CSS class and is colored red.
|
|
|
|
|
Two buttons have signals which are defined by the signal tags.
|
|
|
|
|
- 35-54: A horizontal box has an image icon and a label.
|
|
|
|
|
- 44-47: The GtkImage widget displays an image.
|
|
|
|
|
The "icon-name" property is an icon name in the icon theme.
|
|
|
|
|
The theme depends on your system.
|
|
|
|
|
You can check it with an icon browser.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
|
|
|
|
~~~
|
|
|
|
|
$ gtk4-icon-browser
|
|
|
|
|
~~~
|
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
|
The "dialog-warning" icon is something like this.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
|
|
|
|
![dialog-warning icon is like ...](../image/dialog_warning.png)
|
|
|
|
|
|
|
|
|
|
These are made by my hand.
|
|
|
|
|
The real image on the alert dialog is nicer.
|
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
It is possible to define the alert widget as a child of GtkDialog.
|
|
|
|
|
But GtkDialog is deprecated since GTK version 4.10.
|
|
|
|
|
And users should use GtkWindow instead of GtkDialog.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
## The header file
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
The header file is similar to the one of TfeTextView.
|
2021-02-19 14:20:23 +01:00
|
|
|
|
|
|
|
|
|
~~~C
|
2023-07-15 10:27:53 +02:00
|
|
|
|
1 #pragma once
|
|
|
|
|
2
|
|
|
|
|
3 #include <gtk/gtk.h>
|
|
|
|
|
4
|
|
|
|
|
5 #define TFE_TYPE_ALERT tfe_alert_get_type ()
|
|
|
|
|
6 G_DECLARE_FINAL_TYPE (TfeAlert, tfe_alert, TFE, ALERT, GtkWindow)
|
|
|
|
|
7
|
|
|
|
|
8 /* "response" signal id */
|
|
|
|
|
9 enum TfeAlertResponseType
|
|
|
|
|
10 {
|
|
|
|
|
11 TFE_ALERT_RESPONSE_ACCEPT,
|
|
|
|
|
12 TFE_ALERT_RESPONSE_CANCEL
|
|
|
|
|
13 };
|
|
|
|
|
14
|
|
|
|
|
15 const char *
|
|
|
|
|
16 tfe_alert_get_title (TfeAlert *alert);
|
|
|
|
|
17
|
|
|
|
|
18 const char *
|
|
|
|
|
19 tfe_alert_get_message (TfeAlert *alert);
|
|
|
|
|
20
|
|
|
|
|
21 const char *
|
|
|
|
|
22 tfe_alert_get_button_label (TfeAlert *alert);
|
|
|
|
|
23
|
|
|
|
|
24 void
|
|
|
|
|
25 tfe_alert_set_title (TfeAlert *alert, const char *title);
|
|
|
|
|
26
|
|
|
|
|
27 void
|
|
|
|
|
28 tfe_alert_set_message (TfeAlert *alert, const char *message);
|
|
|
|
|
29
|
|
|
|
|
30 void
|
|
|
|
|
31 tfe_alert_set_button_label (TfeAlert *alert, const char *btn_label);
|
|
|
|
|
32
|
|
|
|
|
33 GtkWidget *
|
|
|
|
|
34 tfe_alert_new (void);
|
|
|
|
|
35
|
|
|
|
|
36 GtkWidget *
|
|
|
|
|
37 tfe_alert_new_with_data (const char *title, const char *message, const char* btn_label);
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
- 5-6: These two lines are always needed to define a new object.
|
|
|
|
|
`TFE_TYPE_ALERT` is the type of TfeAlert object and it is a macro expanded into `tfe_alert_get_type ()`.
|
|
|
|
|
G\_DECLARE\_FINAL\_TYPE macro is expanded into:
|
|
|
|
|
- The declaration of the function `tfe_alert_get_type`
|
|
|
|
|
- `TfeAlert` is defined as a typedef of `struct _TfeAlert`, which is defined in the C file.
|
|
|
|
|
- `TFE_ALERT` and `TFE_IS_ALERT` macro is defined as a cast and type check function.
|
|
|
|
|
- `TfeAlertClass` structure is defined as a final class.
|
|
|
|
|
- 8-13: The TfeAlert class has a "response" signal.
|
|
|
|
|
It has a parameter and the parameter type is defined as a `TfeAlertResponseType` enumerative constant.
|
|
|
|
|
- 15-31: Getter and setter methods.
|
|
|
|
|
- 33-37: Functions to create a instance.
|
|
|
|
|
The function `tfe_alert_new_with_data` is a convenience function, which creates an instance and sets data at once.
|
|
|
|
|
|
|
|
|
|
## The C file
|
|
|
|
|
|
|
|
|
|
### Functions for composite widgets
|
|
|
|
|
|
|
|
|
|
The following codes are extracted from `tfealert.c`.
|
|
|
|
|
|
|
|
|
|
```C
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
#include "tfealert.h"
|
|
|
|
|
|
|
|
|
|
struct _TfeAlert {
|
|
|
|
|
GtkWindow parent;
|
|
|
|
|
GtkLabel *lb_title;
|
|
|
|
|
GtkLabel *lb_message;
|
|
|
|
|
GtkButton *btn_accept;
|
|
|
|
|
GtkButton *btn_cancel;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DEFINE_FINAL_TYPE (TfeAlert, tfe_alert, GTK_TYPE_WINDOW);
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
static void
|
|
|
|
|
cancel_cb (TfeAlert *alert) {
|
|
|
|
|
... ... ...
|
2023-01-03 07:30:06 +01:00
|
|
|
|
}
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
|
|
|
|
static void
|
2023-07-15 10:27:53 +02:00
|
|
|
|
accept_cb (TfeAlert *alert) {
|
|
|
|
|
... ... ...
|
2021-06-15 04:24:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2023-07-15 10:27:53 +02:00
|
|
|
|
tfe_alert_dispose (GObject *gobject) { // gobject is actually a TfeAlert instance.
|
|
|
|
|
gtk_widget_dispose_template (GTK_WIDGET (gobject), TFE_TYPE_ALERT);
|
|
|
|
|
G_OBJECT_CLASS (tfe_alert_parent_class)->dispose (gobject);
|
2021-06-15 04:24:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2023-07-15 10:27:53 +02:00
|
|
|
|
tfe_alert_init (TfeAlert *alert) {
|
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (alert));
|
2021-06-15 04:24:42 +02:00
|
|
|
|
}
|
2023-07-15 10:27:53 +02:00
|
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
|
static void
|
2023-07-15 10:27:53 +02:00
|
|
|
|
tfe_alert_class_init (TfeAlertClass *class) {
|
|
|
|
|
G_OBJECT_CLASS (class)->dispose = tfe_alert_dispose;
|
|
|
|
|
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfealert.ui");
|
|
|
|
|
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_title);
|
|
|
|
|
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_message);
|
|
|
|
|
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_accept);
|
|
|
|
|
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_cancel);
|
|
|
|
|
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), cancel_cb);
|
|
|
|
|
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), accept_cb);
|
|
|
|
|
... ... ...
|
2021-06-15 04:24:42 +02:00
|
|
|
|
}
|
2023-01-03 07:30:06 +01:00
|
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
|
GtkWidget *
|
|
|
|
|
tfe_alert_new (void) {
|
|
|
|
|
return GTK_WIDGET (g_object_new (TFE_TYPE_ALERT, NULL));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- The macro `G_DEFINE_FINAL_TYPE` is available since GLib version 2.70.
|
|
|
|
|
It is used only for a final type class.
|
|
|
|
|
You can use `G_DEFINE_TYPE` macro instead.
|
|
|
|
|
They are expanded into:
|
|
|
|
|
- The declaration of the functions `tfe_alert_init` and `tfe_alert_class_init`.
|
|
|
|
|
They are defined in the following part of the C program.
|
|
|
|
|
- The definition of the variable `tfe_alert_parent_class`.
|
|
|
|
|
- The definition of the function `tfe_alert_get_type`.
|
|
|
|
|
- The names of the members of `_TfeAlert`, which are `lb_title`, `lb_message`, `btn_accept` and `btn_cancel`,
|
|
|
|
|
must be the same as the id attribute in the XML file `tfealert.ui`.
|
|
|
|
|
- The function `tfe_alert_class_init` initializes the composite widget class.
|
|
|
|
|
- The function `gtk_widget_class_set_template_from_resource` sets the template of the class.
|
|
|
|
|
The template is built from the XML resource "tfealert.ui".
|
|
|
|
|
At this moment no instance is created.
|
|
|
|
|
It just makes the class recognize the structure of the object.
|
|
|
|
|
That’s why the top level tag is not object but template in the XML file.
|
|
|
|
|
- The function macro `gtk_widget_class_bind_template_child` connects the member of TfeAlert and the object class in the template.
|
|
|
|
|
So, for example, you can access to `lb_title` GtkLabel instance via `alert->lb_title` where `alert` is an instance of TfeAlert class.
|
|
|
|
|
- The function `gtk_widget_class_bind_template_callback` connects the callback function and the `handler` attribute of the signal tag in the XML.
|
|
|
|
|
For example, the "clicked" signal on the cancel button has a handler named "cancel\_cb" in the signal tag.
|
|
|
|
|
And the function `cancel_cb` exists in the C file above.
|
|
|
|
|
These two are connected so when the signal is emitted the function `cancel_cb` is called.
|
|
|
|
|
You can add `static` storage class to the callback function thanks to this connection.
|
|
|
|
|
- The function `tfe_alert_init` initializes the newly created instance.
|
|
|
|
|
You need to call `gtk_widget_init_template` to create and initialize the child widgets in the template.
|
|
|
|
|
- The function `tfe_alert_despose` releases objects.
|
|
|
|
|
The function `gtk_widget_despose_template` clears the template children.
|
|
|
|
|
- The function `tfe_alert_new` creates the composite widget TfeAlert instance.
|
|
|
|
|
It creates not only TfeAlert itself but also all the child widgets that the composite widget has.
|
|
|
|
|
|
|
|
|
|
### Other functions
|
|
|
|
|
|
|
|
|
|
The following is the full codes of `tfealert.c`.
|
2021-02-19 14:20:23 +01:00
|
|
|
|
|
|
|
|
|
~~~C
|
2023-07-15 10:27:53 +02:00
|
|
|
|
1 #include <gtk/gtk.h>
|
|
|
|
|
2 #include "tfealert.h"
|
|
|
|
|
3
|
|
|
|
|
4 struct _TfeAlert {
|
|
|
|
|
5 GtkWindow parent;
|
|
|
|
|
6 GtkLabel *lb_title;
|
|
|
|
|
7 GtkLabel *lb_message;
|
|
|
|
|
8 GtkButton *btn_accept;
|
|
|
|
|
9 GtkButton *btn_cancel;
|
|
|
|
|
10 };
|
|
|
|
|
11
|
|
|
|
|
12 G_DEFINE_FINAL_TYPE (TfeAlert, tfe_alert, GTK_TYPE_WINDOW);
|
|
|
|
|
13
|
|
|
|
|
14 enum {
|
|
|
|
|
15 RESPONSE,
|
|
|
|
|
16 NUMBER_OF_SIGNALS
|
|
|
|
|
17 };
|
|
|
|
|
18
|
|
|
|
|
19 static guint tfe_alert_signals[NUMBER_OF_SIGNALS];
|
|
|
|
|
20
|
|
|
|
|
21 static void
|
|
|
|
|
22 cancel_cb (TfeAlert *alert) {
|
|
|
|
|
23 g_signal_emit (alert, tfe_alert_signals[RESPONSE], 0, TFE_ALERT_RESPONSE_CANCEL);
|
|
|
|
|
24 gtk_window_destroy (GTK_WINDOW (alert));
|
|
|
|
|
25 }
|
|
|
|
|
26
|
|
|
|
|
27 static void
|
|
|
|
|
28 accept_cb (TfeAlert *alert) {
|
|
|
|
|
29 g_signal_emit (alert, tfe_alert_signals[RESPONSE], 0, TFE_ALERT_RESPONSE_ACCEPT);
|
|
|
|
|
30 gtk_window_destroy (GTK_WINDOW (alert));
|
|
|
|
|
31 }
|
|
|
|
|
32
|
|
|
|
|
33 const char *
|
|
|
|
|
34 tfe_alert_get_title (TfeAlert *alert) {
|
|
|
|
|
35 return gtk_label_get_text (alert->lb_title);
|
|
|
|
|
36 }
|
|
|
|
|
37
|
|
|
|
|
38 const char *
|
|
|
|
|
39 tfe_alert_get_message (TfeAlert *alert) {
|
|
|
|
|
40 return gtk_label_get_text (alert->lb_message);
|
|
|
|
|
41 }
|
|
|
|
|
42
|
|
|
|
|
43 const char *
|
|
|
|
|
44 tfe_alert_get_button_label (TfeAlert *alert) {
|
|
|
|
|
45 return gtk_button_get_label (alert->btn_accept);
|
|
|
|
|
46 }
|
|
|
|
|
47
|
|
|
|
|
48 void
|
|
|
|
|
49 tfe_alert_set_title (TfeAlert *alert, const char *title) {
|
|
|
|
|
50 gtk_label_set_text (alert->lb_title, title);
|
|
|
|
|
51 }
|
|
|
|
|
52
|
|
|
|
|
53 void
|
|
|
|
|
54 tfe_alert_set_message (TfeAlert *alert, const char *message) {
|
|
|
|
|
55 gtk_label_set_text (alert->lb_message, message);
|
|
|
|
|
56 }
|
|
|
|
|
57
|
|
|
|
|
58 void
|
|
|
|
|
59 tfe_alert_set_button_label (TfeAlert *alert, const char *btn_label) {
|
|
|
|
|
60 gtk_button_set_label (alert->btn_accept, btn_label);
|
|
|
|
|
61 }
|
|
|
|
|
62
|
|
|
|
|
63 static void
|
|
|
|
|
64 tfe_alert_dispose (GObject *gobject) { // gobject is actually a TfeAlert instance.
|
|
|
|
|
65 gtk_widget_dispose_template (GTK_WIDGET (gobject), TFE_TYPE_ALERT);
|
|
|
|
|
66 G_OBJECT_CLASS (tfe_alert_parent_class)->dispose (gobject);
|
|
|
|
|
67 }
|
|
|
|
|
68
|
|
|
|
|
69 static void
|
|
|
|
|
70 tfe_alert_init (TfeAlert *alert) {
|
|
|
|
|
71 gtk_widget_init_template (GTK_WIDGET (alert));
|
|
|
|
|
72 }
|
|
|
|
|
73
|
|
|
|
|
74 static void
|
|
|
|
|
75 tfe_alert_class_init (TfeAlertClass *class) {
|
|
|
|
|
76 G_OBJECT_CLASS (class)->dispose = tfe_alert_dispose;
|
|
|
|
|
77 gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfealert.ui");
|
|
|
|
|
78 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_title);
|
|
|
|
|
79 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_message);
|
|
|
|
|
80 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_accept);
|
|
|
|
|
81 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_cancel);
|
|
|
|
|
82 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), cancel_cb);
|
|
|
|
|
83 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), accept_cb);
|
|
|
|
|
84
|
|
|
|
|
85 tfe_alert_signals[RESPONSE] = g_signal_new ("response",
|
|
|
|
|
86 G_TYPE_FROM_CLASS (class),
|
|
|
|
|
87 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
|
|
|
88 0 /* class offset */,
|
|
|
|
|
89 NULL /* accumulator */,
|
|
|
|
|
90 NULL /* accumulator data */,
|
|
|
|
|
91 NULL /* C marshaller */,
|
|
|
|
|
92 G_TYPE_NONE /* return_type */,
|
|
|
|
|
93 1 /* n_params */,
|
|
|
|
|
94 G_TYPE_INT
|
|
|
|
|
95 );
|
|
|
|
|
96 }
|
|
|
|
|
97
|
|
|
|
|
98 GtkWidget *
|
|
|
|
|
99 tfe_alert_new (void) {
|
|
|
|
|
100 return GTK_WIDGET (g_object_new (TFE_TYPE_ALERT, NULL));
|
|
|
|
|
101 }
|
|
|
|
|
102
|
|
|
|
|
103 GtkWidget *
|
|
|
|
|
104 tfe_alert_new_with_data (const char *title, const char *message, const char* btn_label) {
|
|
|
|
|
105 GtkWidget *alert = tfe_alert_new ();
|
|
|
|
|
106 tfe_alert_set_title (TFE_ALERT (alert), title);
|
|
|
|
|
107 tfe_alert_set_message (TFE_ALERT (alert), message);
|
|
|
|
|
108 tfe_alert_set_button_label (TFE_ALERT (alert), btn_label);
|
|
|
|
|
109 return alert;
|
|
|
|
|
110 }
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
The function `tfe_alert_new_with_data` is used more often than `tfe_alert_new` to create a new instance.
|
|
|
|
|
It creates the instance and sets three data at the same time.
|
|
|
|
|
The following is the common process when you use the TfeAlert class.
|
|
|
|
|
|
|
|
|
|
- Call `tfe_alert_new_with_data` and create an instance.
|
|
|
|
|
- Call `gtk_window_set_transient_for` to set the transient parent window.
|
|
|
|
|
- Call `gtk_window_present` to show the TfeAlert dialog.
|
|
|
|
|
- Connect "response" signal and a handler.
|
|
|
|
|
- The user clicks on the cancel or accept button.
|
|
|
|
|
Then the dialog emits the "response" signal and destroy itself.
|
|
|
|
|
- The user catches the signal and do something.
|
|
|
|
|
|
|
|
|
|
The rest of the program is:
|
|
|
|
|
|
|
|
|
|
- 14-19: An array for a signal id. You can use a variable instead of an array because the class has only one signal.
|
|
|
|
|
But using an array is a common way.
|
|
|
|
|
- 21-31: Signal handlers. They emits the "response" signal and destroy the instance itself.
|
|
|
|
|
- 33-61: Getters and setters.
|
|
|
|
|
- 85-95: Creates the "response" signal.
|
|
|
|
|
- 103-110: A convenience function `tfe_alert_new_with_data` creates an instance and sets labels.
|
|
|
|
|
|
|
|
|
|
## An example
|
|
|
|
|
|
|
|
|
|
There's an example in the `src/tfe6/example` directory.
|
|
|
|
|
It shows how to use TfeAlert.
|
|
|
|
|
The program is `src/example/ex_alert.c`.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
|
|
|
|
~~~C
|
2023-07-15 10:27:53 +02:00
|
|
|
|
1 #include <gtk/gtk.h>
|
|
|
|
|
2 #include "../tfealert.h"
|
|
|
|
|
3
|
|
|
|
|
4 static void
|
|
|
|
|
5 alert_response_cb (TfeAlert *alert, int response, gpointer user_data) {
|
|
|
|
|
6 if (response == TFE_ALERT_RESPONSE_ACCEPT)
|
|
|
|
|
7 g_print ("%s\n", tfe_alert_get_button_label (alert));
|
|
|
|
|
8 else if (response == TFE_ALERT_RESPONSE_CANCEL)
|
|
|
|
|
9 g_print ("Cancel\n");
|
|
|
|
|
10 else
|
|
|
|
|
11 g_print ("Unexpected error\n");
|
|
|
|
|
12 }
|
|
|
|
|
13
|
|
|
|
|
14 static void
|
|
|
|
|
15 app_activate (GApplication *application) {
|
|
|
|
|
16 GtkWidget *alert;
|
|
|
|
|
17 char *title, *message, *btn_label;
|
|
|
|
|
18
|
|
|
|
|
19 title = "Example for TfeAlert"; message = "Click on Cancel or Accept button"; btn_label = "Accept";
|
|
|
|
|
20 alert = tfe_alert_new_with_data (title, message, btn_label);
|
|
|
|
|
21 g_signal_connect (TFE_ALERT (alert), "response", G_CALLBACK (alert_response_cb), NULL);
|
|
|
|
|
22 gtk_window_set_application (GTK_WINDOW (alert), GTK_APPLICATION (application));
|
|
|
|
|
23 gtk_window_present (GTK_WINDOW (alert));
|
|
|
|
|
24 }
|
|
|
|
|
25
|
|
|
|
|
26 static void
|
|
|
|
|
27 app_startup (GApplication *application) {
|
|
|
|
|
28 }
|
|
|
|
|
29
|
|
|
|
|
30 #define APPLICATION_ID "com.github.ToshioCP.example_tfe_alert"
|
|
|
|
|
31
|
|
|
|
|
32 int
|
|
|
|
|
33 main (int argc, char **argv) {
|
|
|
|
|
34 GtkApplication *app;
|
|
|
|
|
35 int stat;
|
|
|
|
|
36
|
|
|
|
|
37 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
|
|
|
|
|
38 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
|
|
|
|
39 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
|
|
|
|
40 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
|
|
|
|
41 g_object_unref (app);
|
|
|
|
|
42 return stat;
|
|
|
|
|
43 }
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
The "activate" signal handler `app_activate` initializes the alert dialog.
|
|
|
|
|
|
|
|
|
|
- A TfeAlert instance is created.
|
|
|
|
|
- Its "response" signal is connected to the handler `alert_response_cb`.
|
|
|
|
|
- TfeAlert class is a sub class of GtkWindow so it can be a top level window that is connected to an application instance.
|
|
|
|
|
The function `gtk_window_set_application` does that.
|
|
|
|
|
- The dialog is shown.
|
|
|
|
|
|
|
|
|
|
A user clicks on either the cancel button or the accept button.
|
|
|
|
|
Then, the "response" signal is emitted and the dialog is destroyed.
|
|
|
|
|
The signal handler `alert_response_cb` checks the response and prints "Accept" or "Cancel".
|
|
|
|
|
If an error happens, it prints "Unexpected error".
|
|
|
|
|
|
|
|
|
|
You can compile it with meson and ninja.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ cd src/tfe6/example
|
|
|
|
|
$ meson setup _build
|
2021-02-19 14:20:23 +01:00
|
|
|
|
$ ninja -C _build
|
2023-07-15 10:27:53 +02:00
|
|
|
|
$ _build/ex_alert
|
|
|
|
|
Accept #<= if you clicked on the accept button
|
|
|
|
|
```
|
2021-01-24 08:50:52 +01:00
|
|
|
|
|
2022-11-21 14:48:20 +01:00
|
|
|
|
Up: [README.md](../README.md), Prev: [Section 19](sec19.md), Next: [Section 21](sec21.md)
|