tfeapplication.c
includes all the code other than tfetxtview.c
and tfenotebook.c
. It does:
The function main
is the first invoked function in C language. It connects the command line given by the user and Gtk application.
#define APPLICATION_ID "com.github.ToshioCP.tfe"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
gtk_application_new
.Startup signal is emitted just after the GtkApplication instance is initialized. What the signal handler needs to do is initialization of the application.
The handler is as follows.
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build;
GtkApplicationWindow *win;
GtkNotebook *nb;
GtkButton *btno;
GtkButton *btnn;
GtkButton *btns;
GtkButton *btnc;
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui");
win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win"));
nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb"));
gtk_window_set_application (GTK_WINDOW (win), app);
btno = GTK_BUTTON (gtk_builder_get_object (build, "btno"));
btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn"));
btns = GTK_BUTTON (gtk_builder_get_object (build, "btns"));
btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc"));
g_signal_connect_swapped (btno, "clicked", G_CALLBACK (open_cb), nb);
g_signal_connect_swapped (btnn, "clicked", G_CALLBACK (new_cb), nb);
g_signal_connect_swapped (btns, "clicked", G_CALLBACK (save_cb), nb);
g_signal_connect_swapped (btnc, "clicked", G_CALLBACK (close_cb), nb);
g_object_unref(build);
GdkDisplay *display;
display = gtk_widget_get_display (GTK_WIDGET (win));
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
gtk_window_set_application
.CSS is an abbreviation of Cascading Style Sheet. It is originally used with HTML to describe the presentation semantics of a document. You might have found that the widgets in Gtk is similar to a window in a browser. It implies that CSS can also be applied to Gtk windowing system.
The syntax of CSS is as follows.
selector { color: yellow; padding-top: 10px; ...}
Every widget has CSS node. For example GtkTextView has textview
node. If you want to set style to GtkTextView, substitute “textview” for the selector.
textview {color: yellow; ...}
Class, ID and some other things can be applied to the selector like Web CSS. Refer to Gtk4 API Reference, CSS in Gtk for further information.
In line 30, the CSS is a string.
textview {padding: 10px; font-family: monospace; font-size: 12pt;}
GtkStyleContext is an object that stores styling information affecting a widget. Each widget is connected to the corresponding GtkStyleContext. You can get the context by gtk_widget_get_style_context
.
GtkCssProvider is an object which parses CSS in order to style widgets.
To apply your CSS to widgets, you need to add GtkStyleProvider (the interface of GtkCSSProvider) to GtkStyleContext. However, instead, you can add it to GdkDisplay of the window (usually top-level window).
Look at the source file of startup
handler again.
gtk_widget_get_display
.gtk_style_context_add_provider_for_display
is the priority of the style provider. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
is a priority for application-specific style information. GTK_STYLE_PROVIDER_PRIORITY_USER
is also often used and it is the highest priority. So, GTK_STYLE_PROVIDER_PRIORITY_USER
is often used to a specific widget.It is possible to add the provider to the context of GtkTextView instead of GdkDiplay. To do so, rewrite tfe_text_view_new
. First, get the GtkStyleContext object of a TfeTextView object. Then adds the CSS provider to the context.
GtkWidget *void) {
tfe_text_view_new (
GtkWidget *tv;
tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
GtkStyleContext *context;
context = gtk_widget_get_style_context (GTK_WIDGET (tv));
GtkCssProvider *provider = gtk_css_provider_new ();"textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_css_provider_load_from_data (provider,
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
return tv;
}
CSS in the context takes precedence over CSS in the display.
The handler of “activate” and “open” signal are app_activate
and app_open
respectively. They just create a new GtkNotebookPage.
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
notebook_page_new (nb);
gtk_widget_show (GTK_WIDGET (win));
}
static void
app_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
int i;
for (i = 0; i < n_files; i++)
notebook_page_new_with_file (nb, files[i]);
if (gtk_notebook_get_n_pages (nb) == 0)
notebook_page_new (nb);
gtk_widget_show (win);
}
app_activate
.app_open
.These codes have become really simple thanks to tfenotebook.c and tfetextview.c.
Only one GApplication instance can be run at a time per session. The session is a bit difficult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login. When you use your PC, you probably login first, then your desktop appears until you log off. This is the session.
However, Linux is multi process OS and you can run two or more instances of the same application. Isn’t it a contradiction?
When first instance is launched, then it registers itself with its application ID (for example, com.github.ToshioCP.tfe
). Just after the registration, startup signal is emitted, then activate or open signal is emitted and the instance’s main loop runs. I wrote “startup signal is emitted just after the application instance is initialized” in the prior subsection. More precisely, it is emitted just after the registration.
If another instance which has the same application ID is invoked, it also tries to register itself. Because this is the second instance, the registration of the ID has already done, so it fails. Because of the failure startup signal isn’t emitted. After that, activate or open signal is emitted in the primary instance, not the second instance. The primary instance receives the signal and its handler is invoked. On the other hand, the second instance doesn’t receive the signal and it immediately quits.
Try to run two instances in a row.
$ ./_build/tfe &
[1] 84453
$ ./build/tfe tfeapplication.c
$
First, the primary instance opens a window. Then, after the second instance is run, a new notebook page with the contents of tfeapplication.c
appears in the primary instance’s window. This is because the open signal is emitted in the primary instance. The second instance immediately quits so shell prompt soon appears.
static void
open_cb (GtkNotebook *nb) {
notebook_page_open (nb);
}
static void
new_cb (GtkNotebook *nb) {
notebook_page_new (nb);
}
static void
save_cb (GtkNotebook *nb) {
notebook_page_save (nb);
}
static void
close_cb (GtkNotebook *nb) {
notebook_page_close (GTK_NOTEBOOK (nb));
}
open_cb
, new_cb
, save_cb
and close_cb
just call corresponding notebook page functions.
project('tfe', 'c')
gtkdep = dependency('gtk4')
gnome=import('gnome')
resources = gnome.compile_resources('resources','tfe.gresource.xml')
sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
executable('tfe', sourcefiles, resources, dependencies: gtkdep)
In this file, just the source file names are modified from the prior version.
The source files of the text editor tfe
will be shown in the next section.
You can also download the files from the repository. There are two options.
If you use git, run the terminal and type the following.
$ git clone https://github.com/ToshioCP/Gtk4-tutorial.git
The source files are under /src/tfe5
directory.