mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-17 06:11:38 +01:00
Upgrade tfe. It's in src/tfe7. Insert sec20.
This commit is contained in:
parent
2b2c26a1ca
commit
bb632f024d
28 changed files with 2851 additions and 751 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ src/tfe4/_build
|
|||
src/tfe5/_build
|
||||
src/tfe5/hello.txt
|
||||
src/tfe6/_build
|
||||
src/tfe7/_build
|
||||
src/menu/a.out
|
||||
src/color/_build
|
||||
src/turtle/_build
|
||||
|
|
|
@ -32,6 +32,7 @@ You can read it without download.
|
|||
1. [Menu and action](gfm/sec16.md)
|
||||
1. [Stateful action](gfm/sec17.md)
|
||||
1. [Ui file for menu and action entries](gfm/sec18.md)
|
||||
1. [Upgrade text file editor](gfm/sec19.md)
|
||||
1. [GtkDrawingArea and Cairo](gfm/sec20.md)
|
||||
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec21.md)
|
||||
1. [GtkMenuButton, accelerators, font, pango and gsettings](gfm/sec19.md)
|
||||
1. [Template XML](gfm/sec20.md)
|
||||
1. [GtkDrawingArea and Cairo](gfm/sec21.md)
|
||||
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec22.md)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md), Next: [Section 20](sec20.md)
|
||||
|
||||
# Upgrade text file editor
|
||||
# GtkMenuButton, accelerators, font, pango and gsettings
|
||||
|
||||
Traditional menu structure is fine.
|
||||
However, Buttons or menu items we often use are not so many.
|
||||
|
|
994
gfm/sec20.md
994
gfm/sec20.md
File diff suppressed because it is too large
Load diff
524
gfm/sec21.md
524
gfm/sec21.md
|
@ -1,373 +1,201 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 20](sec20.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 20](sec20.md), Next: [Section 22](sec22.md)
|
||||
|
||||
# Combine GtkDrawingArea and TfeTextView
|
||||
# GtkDrawingArea and Cairo
|
||||
|
||||
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
|
||||
Its name is "color".
|
||||
If you write a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
|
||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
||||
You can draw or redraw an image in this widget freely.
|
||||
It is called custom drawing.
|
||||
|
||||
![color](../image/color.png)
|
||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
||||
In this section, I will explain:
|
||||
|
||||
The following colors are available.
|
||||
1. Cairo, but briefly.
|
||||
2. GtkDrawingArea with very simple example.
|
||||
|
||||
- white
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
- blue
|
||||
## Cairo
|
||||
|
||||
In addition the following two options are also available.
|
||||
Cairo is a two dimensional graphics library.
|
||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||
|
||||
- light: Make the color of the drawing area lighter.
|
||||
- dark: Make the color of the drawing area darker.
|
||||
- Surface represents an image.
|
||||
It is like a canvas.
|
||||
We can draw shapes and images with different colors on surfaces.
|
||||
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
||||
- Mask is image mask used in the transference.
|
||||
- Destination is a target surface.
|
||||
- Cairo context manages the transference from source to destination through mask with its functions.
|
||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||
- Transformation is applied before the transfer completes.
|
||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||
In this section, we don't use it.
|
||||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
|
||||
This application can only do very simple things.
|
||||
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
|
||||
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
|
||||
![Stroke a rectangle](../image/cairo.png)
|
||||
|
||||
In this section, we focus on how to bind the two objects.
|
||||
The instruction is as follows:
|
||||
|
||||
## Color.ui and color.gresource.xml
|
||||
1. Create a surface.
|
||||
This will be a destination.
|
||||
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
||||
3. Create a source pattern within the context.
|
||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||
6. Save the destination surface to a file if necessary.
|
||||
|
||||
First, We need to make the ui file of the widgets.
|
||||
The image in the previous subsection gives us the structure of the widgets.
|
||||
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
|
||||
The ui file is as follows.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <object class="GtkApplicationWindow" id="win">
|
||||
4 <property name="title">color changer</property>
|
||||
5 <property name="default-width">600</property>
|
||||
6 <property name="default-height">400</property>
|
||||
7 <child>
|
||||
8 <object class="GtkBox" id="boxv">
|
||||
9 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
10 <child>
|
||||
11 <object class="GtkBox" id="boxh1">
|
||||
12 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
13 <child>
|
||||
14 <object class="GtkLabel" id="dmy1">
|
||||
15 <property name="width-chars">10</property>
|
||||
16 </object>
|
||||
17 </child>
|
||||
18 <child>
|
||||
19 <object class="GtkButton" id="btnr">
|
||||
20 <property name="label">Run</property>
|
||||
21 <signal name="clicked" handler="run_cb"></signal>
|
||||
22 </object>
|
||||
23 </child>
|
||||
24 <child>
|
||||
25 <object class="GtkButton" id="btno">
|
||||
26 <property name="label">Open</property>
|
||||
27 <signal name="clicked" handler="open_cb"></signal>
|
||||
28 </object>
|
||||
29 </child>
|
||||
30 <child>
|
||||
31 <object class="GtkLabel" id="dmy2">
|
||||
32 <property name="hexpand">TRUE</property>
|
||||
33 </object>
|
||||
34 </child>
|
||||
35 <child>
|
||||
36 <object class="GtkButton" id="btns">
|
||||
37 <property name="label">Save</property>
|
||||
38 <signal name="clicked" handler="save_cb"></signal>
|
||||
39 </object>
|
||||
40 </child>
|
||||
41 <child>
|
||||
42 <object class="GtkButton" id="btnc">
|
||||
43 <property name="label">Close</property>
|
||||
44 <signal name="clicked" handler="close_cb"></signal>
|
||||
45 </object>
|
||||
46 </child>
|
||||
47 <child>
|
||||
48 <object class="GtkLabel" id="dmy3">
|
||||
49 <property name="width-chars">10</property>
|
||||
50 </object>
|
||||
51 </child>
|
||||
52 </object>
|
||||
53 </child>
|
||||
54 <child>
|
||||
55 <object class="GtkBox" id="boxh2">
|
||||
56 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
57 <property name="homogeneous">TRUE</property>
|
||||
58 <child>
|
||||
59 <object class="GtkScrolledWindow" id="scr">
|
||||
60 <property name="hexpand">TRUE</property>
|
||||
61 <property name="vexpand">TRUE</property>
|
||||
62 <child>
|
||||
63 <object class="TfeTextView" id="tv">
|
||||
64 <property name="wrap-mode">GTK_WRAP_WORD_CHAR</property>
|
||||
65 </object>
|
||||
66 </child>
|
||||
67 </object>
|
||||
68 </child>
|
||||
69 <child>
|
||||
70 <object class="GtkDrawingArea" id="da">
|
||||
71 <property name="hexpand">TRUE</property>
|
||||
72 <property name="vexpand">TRUE</property>
|
||||
73 </object>
|
||||
74 </child>
|
||||
75 </object>
|
||||
76 </child>
|
||||
77 </object>
|
||||
78 </child>
|
||||
79 </object>
|
||||
80 </interface>
|
||||
~~~
|
||||
|
||||
- 10-53: This part describes the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
|
||||
This is similar to the toolbar of tfe text editor in [Section 8](sec8.md).
|
||||
There are two differences.
|
||||
`Run` button replaces `New` button.
|
||||
A signal element is added to each button object.
|
||||
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function.
|
||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||
And be careful that the handler must be defined without 'static' class.
|
||||
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
|
||||
TfeTextView is a child of GtkScrolledWindow.
|
||||
|
||||
The xml file for the resource compiler is almost same as before.
|
||||
Just substitute "color" for "tfe".
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/color">
|
||||
4 <file>color.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
## Tfetextview.h, tfetextview.c and color.h
|
||||
|
||||
First two files are the same as before.
|
||||
Color.h just includes tfetextview.h.
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "../tfetextview/tfetextview.h"
|
||||
~~~
|
||||
|
||||
## Colorapplication.c
|
||||
|
||||
This is the main file.
|
||||
It deals with:
|
||||
|
||||
- Building widgets by GtkBuilder.
|
||||
- Seting a drawing function of GtkDrawingArea.
|
||||
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||
- Implementing each call back functions.
|
||||
Particularly, `Run` signal handler is the point in this program.
|
||||
|
||||
The following is `colorapplication.c`.
|
||||
|
||||
~~~C
|
||||
1 #include "color.h"
|
||||
2
|
||||
3 static GtkWidget *win;
|
||||
4 static GtkWidget *tv;
|
||||
5 static GtkWidget *da;
|
||||
6
|
||||
7 static cairo_surface_t *surface = NULL;
|
||||
8
|
||||
9 static void
|
||||
10 run (void) {
|
||||
11 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
12 GtkTextIter start_iter;
|
||||
13 GtkTextIter end_iter;
|
||||
14 char *contents;
|
||||
15 cairo_t *cr;
|
||||
16
|
||||
17 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
18 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||
19 if (surface) {
|
||||
20 cr = cairo_create (surface);
|
||||
21 if (g_strcmp0 ("red", contents) == 0)
|
||||
22 cairo_set_source_rgb (cr, 1, 0, 0);
|
||||
23 else if (g_strcmp0 ("green", contents) == 0)
|
||||
24 cairo_set_source_rgb (cr, 0, 1, 0);
|
||||
25 else if (g_strcmp0 ("blue", contents) == 0)
|
||||
26 cairo_set_source_rgb (cr, 0, 0, 1);
|
||||
27 else if (g_strcmp0 ("white", contents) == 0)
|
||||
28 cairo_set_source_rgb (cr, 1, 1, 1);
|
||||
29 else if (g_strcmp0 ("black", contents) == 0)
|
||||
30 cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
31 else if (g_strcmp0 ("light", contents) == 0)
|
||||
32 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
|
||||
33 else if (g_strcmp0 ("dark", contents) == 0)
|
||||
34 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
|
||||
35 else
|
||||
36 cairo_set_source_surface (cr, surface, 0, 0);
|
||||
37 cairo_paint (cr);
|
||||
38 cairo_destroy (cr);
|
||||
39 }
|
||||
40 }
|
||||
41
|
||||
42 void
|
||||
43 run_cb (GtkWidget *btnr) {
|
||||
44 run ();
|
||||
45 gtk_widget_queue_draw (GTK_WIDGET (da));
|
||||
46 }
|
||||
47
|
||||
48 void
|
||||
49 open_cb (GtkWidget *btno) {
|
||||
50 tfe_text_view_open (TFE_TEXT_VIEW (tv), win);
|
||||
51 }
|
||||
52
|
||||
53 void
|
||||
54 save_cb (GtkWidget *btns) {
|
||||
55 tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||
56 }
|
||||
57
|
||||
58 void
|
||||
59 close_cb (GtkWidget *btnc) {
|
||||
60 if (surface)
|
||||
61 cairo_surface_destroy (surface);
|
||||
62 gtk_window_destroy (GTK_WINDOW (win));
|
||||
63 }
|
||||
64
|
||||
65 static void
|
||||
66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
|
||||
67 if (surface)
|
||||
68 cairo_surface_destroy (surface);
|
||||
69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
70 run ();
|
||||
71 }
|
||||
72
|
||||
73 static void
|
||||
74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||
75 if (surface) {
|
||||
76 cairo_set_source_surface (cr, surface, 0, 0);
|
||||
77 cairo_paint (cr);
|
||||
78 }
|
||||
79 }
|
||||
80
|
||||
81 static void
|
||||
82 activate (GApplication *application) {
|
||||
83 gtk_widget_show (win);
|
||||
84 }
|
||||
85
|
||||
86 static void
|
||||
87 startup (GApplication *application) {
|
||||
88 GtkApplication *app = GTK_APPLICATION (application);
|
||||
89 GtkBuilder *build;
|
||||
90
|
||||
91 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
|
||||
92 win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
93 gtk_window_set_application (GTK_WINDOW (win), app);
|
||||
94 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv"));
|
||||
95 da = GTK_WIDGET (gtk_builder_get_object (build, "da"));
|
||||
96 g_object_unref(build);
|
||||
97 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
|
||||
98 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
|
||||
99
|
||||
100 GdkDisplay *display;
|
||||
101
|
||||
102 display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||
103 GtkCssProvider *provider = gtk_css_provider_new ();
|
||||
104 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
|
||||
105 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
106 }
|
||||
107
|
||||
108 int
|
||||
109 main (int argc, char **argv) {
|
||||
110 GtkApplication *app;
|
||||
111 int stat;
|
||||
112
|
||||
113 app = gtk_application_new ("com.github.ToshioCP.color", G_APPLICATION_FLAGS_NONE);
|
||||
114
|
||||
115 g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
|
||||
116 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
|
||||
117
|
||||
118 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
119 g_object_unref (app);
|
||||
120 return stat;
|
||||
121 }
|
||||
122
|
||||
~~~
|
||||
|
||||
- 108-121: The function `main` is almost same as before but there are some differences.
|
||||
The application ID is "com.github.ToshioCP.color".
|
||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||
- 86-106: Startup handler.
|
||||
- 91-96: Builds widgets.
|
||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||
This is because these objects are often used in handlers.
|
||||
They never be rewritten so they're thread safe.
|
||||
- 97: connects "resize" signal and the handler.
|
||||
- 98: sets the drawing function.
|
||||
- 81-84: Activates handler, which just shows the widgets.
|
||||
- 73-79: The drawing function.
|
||||
It just copies `surface` to destination.
|
||||
- 65-71: Resize handler.
|
||||
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||
- 58-63: Closes the handler.
|
||||
It destroys `surface` if it exists.
|
||||
Then it destroys the top window and quits the application.
|
||||
- 48-56: Open and save handler.
|
||||
They just call the corresponding functions of TfeTextView.
|
||||
- 42-46: Run handler.
|
||||
It calls run function to paint the surface.
|
||||
After that `gtk_widget_queue_draw` is called.
|
||||
This fhunction adds the widget (GtkDrawingArea) to the queue to be redrawn.
|
||||
It is important to know that the drawing function is called when it is necessary.
|
||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||
But repaint of `surface` is not automatically notified to gtk.
|
||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||
- 9-40: Run function paints the surface.
|
||||
First, it gets the contents of GtkTextBuffer.
|
||||
Then it compares it to "red", "green" and so on.
|
||||
If it matches the color, then the surface is painted the color.
|
||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||
Alpha channel is used.
|
||||
|
||||
## Meson.build
|
||||
|
||||
This file is almost same as before.
|
||||
An argument "export_dynamic: true" is added to executable function.
|
||||
|
||||
~~~meson
|
||||
1 project('color', 'c')
|
||||
1 #include <cairo.h>
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
4
|
||||
5 gnome=import('gnome')
|
||||
6 resources = gnome.compile_resources('resources','color.gresource.xml')
|
||||
7
|
||||
8 sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
|
||||
9
|
||||
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
||||
3 int
|
||||
4 main (int argc, char **argv)
|
||||
5 {
|
||||
6 cairo_surface_t *surface;
|
||||
7 cairo_t *cr;
|
||||
8 int width = 100;
|
||||
9 int height = 100;
|
||||
10
|
||||
11 /* Generate surface and cairo */
|
||||
12 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
|
||||
13 cr = cairo_create (surface);
|
||||
14
|
||||
15 /* Drawing starts here. */
|
||||
16 /* Paint the background white */
|
||||
17 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
|
||||
18 cairo_paint (cr);
|
||||
19 /* Draw a black rectangle */
|
||||
20 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||
21 cairo_set_line_width (cr, 2.0);
|
||||
22 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
23 cairo_stroke (cr);
|
||||
24
|
||||
25 /* Write the surface to a png file and clean up cairo and surface. */
|
||||
26 cairo_surface_write_to_png (surface, "rectangle.png");
|
||||
27 cairo_destroy (cr);
|
||||
28 cairo_surface_destroy (surface);
|
||||
29
|
||||
30 return 0;
|
||||
31 }
|
||||
~~~
|
||||
|
||||
## Compile and execute it
|
||||
- 1: Includes the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||
Each data has 8 bit quantity.
|
||||
Modern displays have this type of color depth.
|
||||
Width and height are pixels and given as integers.
|
||||
- 13: Creates cairo context.
|
||||
The surface given as an argument will be the destination of the context.
|
||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||
The second to fourth argument is red, green and blue color depth respectively.
|
||||
Their type is float and the values are between zero and one.
|
||||
(0,0,0) is black and (1,1,1) is white.
|
||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||
The destination is filled with white pixels by this command.
|
||||
- 20: Sets the source color to black.
|
||||
- 21: `cairo_set_line_width` set the width of lines.
|
||||
In this case, the line width is set to two pixels.
|
||||
(It is because the transformation is identity.
|
||||
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||
- 22: Draws a rectangle (square).
|
||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||
- 23: `cairo_stroke` transfer the source to destination through the rectangle in mask.
|
||||
- 26: Outputs the image to a png file `rectangle.png`.
|
||||
- 27: Destroys the context. At the same time the source is destroyed.
|
||||
- 28: Destroys the destination surface.
|
||||
|
||||
First you need to export some variables (refer to [Section 2](sec2.md)).
|
||||
To compile this, type the following.
|
||||
|
||||
$ . env.sh
|
||||
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
|
||||
|
||||
Then type the following to compile it.
|
||||
![rectangle.png](../src/misc/rectangle.png)
|
||||
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
||||
If you aren't familiar with cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
||||
|
||||
The application is made in `_build` directory.
|
||||
Type the following to execute it.
|
||||
## GtkDrawingArea
|
||||
|
||||
$ _build/color
|
||||
The following is a very simple example.
|
||||
|
||||
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
|
||||
Then, click on `Run` button.
|
||||
Make sure the color of GtkDrawingArea changes.
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||
5 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */
|
||||
6 cairo_paint (cr);
|
||||
7 cairo_set_line_width (cr, 2.0);
|
||||
8 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
||||
9 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
10 cairo_stroke (cr);
|
||||
11 }
|
||||
12
|
||||
13 static void
|
||||
14 on_activate (GApplication *app, gpointer user_data) {
|
||||
15 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
16 GtkWidget *area = gtk_drawing_area_new ();
|
||||
17
|
||||
18 gtk_window_set_title (GTK_WINDOW (win), "da1");
|
||||
19 /* Set initial size of width and height */
|
||||
20 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (area), 100);
|
||||
21 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (area), 100);
|
||||
22 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
|
||||
23 gtk_window_set_child (GTK_WINDOW (win), area);
|
||||
24
|
||||
25 gtk_widget_show (win);
|
||||
26 }
|
||||
27
|
||||
28 int
|
||||
29 main (int argc, char **argv) {
|
||||
30 GtkApplication *app;
|
||||
31 int stat;
|
||||
32
|
||||
33 app = gtk_application_new ("com.github.ToshioCP.da1", G_APPLICATION_FLAGS_NONE);
|
||||
34 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
|
||||
35 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
36 g_object_unref (app);
|
||||
37 return stat;
|
||||
38 }
|
||||
39
|
||||
~~~
|
||||
|
||||
In this program TfeTextView is used to change the color.
|
||||
You can use buttons or menus instead of textview.
|
||||
Probably it is more appropriate.
|
||||
Using textview is unnatural.
|
||||
It is a good practice to make such application by yourself.
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 20](sec20.md)
|
||||
- 16: Generates a GtkDrawingArea object.
|
||||
- 20,21: Sets the width and height of the contents of the GtkDrawingArea widget.
|
||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||
- 22: Sets a drawing function of the widget.
|
||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||
Then, the whole window needs to be redrawn.
|
||||
|
||||
The drawing function has five parameters.
|
||||
|
||||
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
|
||||
gpointer user_data);
|
||||
|
||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||
The second parameter is a cairo context given by the widget.
|
||||
The destination surface of the context is connected to the contents of the widget.
|
||||
What you draw to this surface will appear in the widget on the screen.
|
||||
The third and fourth parameters are the size of the destination surface.
|
||||
|
||||
- 3-11: The drawing function.
|
||||
- 4-5: Sets the source to be white and paint the destination white.
|
||||
- 7: Sets the line width to be 2.
|
||||
- 8: Sets the source to be black.
|
||||
- 9: Adds a rectangle to the mask.
|
||||
- 10: Draws the rectangle with black color to the destination.
|
||||
|
||||
Compile and run it, then a window with a black rectangle (square) appears.
|
||||
Try resizing the window.
|
||||
The square always appears at the center of the window because the drawing function is invoked every moment the window is resized.
|
||||
|
||||
![Square in the window](../image/da1.png)
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 20](sec20.md), Next: [Section 22](sec22.md)
|
||||
|
|
373
gfm/sec22.md
Normal file
373
gfm/sec22.md
Normal file
|
@ -0,0 +1,373 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md)
|
||||
|
||||
# Combine GtkDrawingArea and TfeTextView
|
||||
|
||||
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
|
||||
Its name is "color".
|
||||
If you write a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
|
||||
|
||||
![color](../image/color.png)
|
||||
|
||||
The following colors are available.
|
||||
|
||||
- white
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
- blue
|
||||
|
||||
In addition the following two options are also available.
|
||||
|
||||
- light: Make the color of the drawing area lighter.
|
||||
- dark: Make the color of the drawing area darker.
|
||||
|
||||
This application can only do very simple things.
|
||||
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
|
||||
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
|
||||
|
||||
In this section, we focus on how to bind the two objects.
|
||||
|
||||
## Color.ui and color.gresource.xml
|
||||
|
||||
First, We need to make the ui file of the widgets.
|
||||
The image in the previous subsection gives us the structure of the widgets.
|
||||
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
|
||||
The ui file is as follows.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <object class="GtkApplicationWindow" id="win">
|
||||
4 <property name="title">color changer</property>
|
||||
5 <property name="default-width">600</property>
|
||||
6 <property name="default-height">400</property>
|
||||
7 <child>
|
||||
8 <object class="GtkBox" id="boxv">
|
||||
9 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
10 <child>
|
||||
11 <object class="GtkBox" id="boxh1">
|
||||
12 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
13 <child>
|
||||
14 <object class="GtkLabel" id="dmy1">
|
||||
15 <property name="width-chars">10</property>
|
||||
16 </object>
|
||||
17 </child>
|
||||
18 <child>
|
||||
19 <object class="GtkButton" id="btnr">
|
||||
20 <property name="label">Run</property>
|
||||
21 <signal name="clicked" handler="run_cb"></signal>
|
||||
22 </object>
|
||||
23 </child>
|
||||
24 <child>
|
||||
25 <object class="GtkButton" id="btno">
|
||||
26 <property name="label">Open</property>
|
||||
27 <signal name="clicked" handler="open_cb"></signal>
|
||||
28 </object>
|
||||
29 </child>
|
||||
30 <child>
|
||||
31 <object class="GtkLabel" id="dmy2">
|
||||
32 <property name="hexpand">TRUE</property>
|
||||
33 </object>
|
||||
34 </child>
|
||||
35 <child>
|
||||
36 <object class="GtkButton" id="btns">
|
||||
37 <property name="label">Save</property>
|
||||
38 <signal name="clicked" handler="save_cb"></signal>
|
||||
39 </object>
|
||||
40 </child>
|
||||
41 <child>
|
||||
42 <object class="GtkButton" id="btnc">
|
||||
43 <property name="label">Close</property>
|
||||
44 <signal name="clicked" handler="close_cb"></signal>
|
||||
45 </object>
|
||||
46 </child>
|
||||
47 <child>
|
||||
48 <object class="GtkLabel" id="dmy3">
|
||||
49 <property name="width-chars">10</property>
|
||||
50 </object>
|
||||
51 </child>
|
||||
52 </object>
|
||||
53 </child>
|
||||
54 <child>
|
||||
55 <object class="GtkBox" id="boxh2">
|
||||
56 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
57 <property name="homogeneous">TRUE</property>
|
||||
58 <child>
|
||||
59 <object class="GtkScrolledWindow" id="scr">
|
||||
60 <property name="hexpand">TRUE</property>
|
||||
61 <property name="vexpand">TRUE</property>
|
||||
62 <child>
|
||||
63 <object class="TfeTextView" id="tv">
|
||||
64 <property name="wrap-mode">GTK_WRAP_WORD_CHAR</property>
|
||||
65 </object>
|
||||
66 </child>
|
||||
67 </object>
|
||||
68 </child>
|
||||
69 <child>
|
||||
70 <object class="GtkDrawingArea" id="da">
|
||||
71 <property name="hexpand">TRUE</property>
|
||||
72 <property name="vexpand">TRUE</property>
|
||||
73 </object>
|
||||
74 </child>
|
||||
75 </object>
|
||||
76 </child>
|
||||
77 </object>
|
||||
78 </child>
|
||||
79 </object>
|
||||
80 </interface>
|
||||
~~~
|
||||
|
||||
- 10-53: This part describes the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
|
||||
This is similar to the toolbar of tfe text editor in [Section 8](sec8.md).
|
||||
There are two differences.
|
||||
`Run` button replaces `New` button.
|
||||
A signal element is added to each button object.
|
||||
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function.
|
||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||
And be careful that the handler must be defined without 'static' class.
|
||||
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
|
||||
TfeTextView is a child of GtkScrolledWindow.
|
||||
|
||||
The xml file for the resource compiler is almost same as before.
|
||||
Just substitute "color" for "tfe".
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/color">
|
||||
4 <file>color.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
## Tfetextview.h, tfetextview.c and color.h
|
||||
|
||||
First two files are the same as before.
|
||||
Color.h just includes tfetextview.h.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "../tfetextview/tfetextview.h"
|
||||
~~~
|
||||
|
||||
## Colorapplication.c
|
||||
|
||||
This is the main file.
|
||||
It deals with:
|
||||
|
||||
- Building widgets by GtkBuilder.
|
||||
- Seting a drawing function of GtkDrawingArea.
|
||||
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||
- Implementing each call back functions.
|
||||
Particularly, `Run` signal handler is the point in this program.
|
||||
|
||||
The following is `colorapplication.c`.
|
||||
|
||||
~~~C
|
||||
1 #include "color.h"
|
||||
2
|
||||
3 static GtkWidget *win;
|
||||
4 static GtkWidget *tv;
|
||||
5 static GtkWidget *da;
|
||||
6
|
||||
7 static cairo_surface_t *surface = NULL;
|
||||
8
|
||||
9 static void
|
||||
10 run (void) {
|
||||
11 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
12 GtkTextIter start_iter;
|
||||
13 GtkTextIter end_iter;
|
||||
14 char *contents;
|
||||
15 cairo_t *cr;
|
||||
16
|
||||
17 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
18 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||
19 if (surface) {
|
||||
20 cr = cairo_create (surface);
|
||||
21 if (g_strcmp0 ("red", contents) == 0)
|
||||
22 cairo_set_source_rgb (cr, 1, 0, 0);
|
||||
23 else if (g_strcmp0 ("green", contents) == 0)
|
||||
24 cairo_set_source_rgb (cr, 0, 1, 0);
|
||||
25 else if (g_strcmp0 ("blue", contents) == 0)
|
||||
26 cairo_set_source_rgb (cr, 0, 0, 1);
|
||||
27 else if (g_strcmp0 ("white", contents) == 0)
|
||||
28 cairo_set_source_rgb (cr, 1, 1, 1);
|
||||
29 else if (g_strcmp0 ("black", contents) == 0)
|
||||
30 cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
31 else if (g_strcmp0 ("light", contents) == 0)
|
||||
32 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
|
||||
33 else if (g_strcmp0 ("dark", contents) == 0)
|
||||
34 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
|
||||
35 else
|
||||
36 cairo_set_source_surface (cr, surface, 0, 0);
|
||||
37 cairo_paint (cr);
|
||||
38 cairo_destroy (cr);
|
||||
39 }
|
||||
40 }
|
||||
41
|
||||
42 void
|
||||
43 run_cb (GtkWidget *btnr) {
|
||||
44 run ();
|
||||
45 gtk_widget_queue_draw (GTK_WIDGET (da));
|
||||
46 }
|
||||
47
|
||||
48 void
|
||||
49 open_cb (GtkWidget *btno) {
|
||||
50 tfe_text_view_open (TFE_TEXT_VIEW (tv), win);
|
||||
51 }
|
||||
52
|
||||
53 void
|
||||
54 save_cb (GtkWidget *btns) {
|
||||
55 tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||
56 }
|
||||
57
|
||||
58 void
|
||||
59 close_cb (GtkWidget *btnc) {
|
||||
60 if (surface)
|
||||
61 cairo_surface_destroy (surface);
|
||||
62 gtk_window_destroy (GTK_WINDOW (win));
|
||||
63 }
|
||||
64
|
||||
65 static void
|
||||
66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
|
||||
67 if (surface)
|
||||
68 cairo_surface_destroy (surface);
|
||||
69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
70 run ();
|
||||
71 }
|
||||
72
|
||||
73 static void
|
||||
74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||
75 if (surface) {
|
||||
76 cairo_set_source_surface (cr, surface, 0, 0);
|
||||
77 cairo_paint (cr);
|
||||
78 }
|
||||
79 }
|
||||
80
|
||||
81 static void
|
||||
82 activate (GApplication *application) {
|
||||
83 gtk_widget_show (win);
|
||||
84 }
|
||||
85
|
||||
86 static void
|
||||
87 startup (GApplication *application) {
|
||||
88 GtkApplication *app = GTK_APPLICATION (application);
|
||||
89 GtkBuilder *build;
|
||||
90
|
||||
91 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
|
||||
92 win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
93 gtk_window_set_application (GTK_WINDOW (win), app);
|
||||
94 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv"));
|
||||
95 da = GTK_WIDGET (gtk_builder_get_object (build, "da"));
|
||||
96 g_object_unref(build);
|
||||
97 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
|
||||
98 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
|
||||
99
|
||||
100 GdkDisplay *display;
|
||||
101
|
||||
102 display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||
103 GtkCssProvider *provider = gtk_css_provider_new ();
|
||||
104 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
|
||||
105 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
106 }
|
||||
107
|
||||
108 int
|
||||
109 main (int argc, char **argv) {
|
||||
110 GtkApplication *app;
|
||||
111 int stat;
|
||||
112
|
||||
113 app = gtk_application_new ("com.github.ToshioCP.color", G_APPLICATION_FLAGS_NONE);
|
||||
114
|
||||
115 g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
|
||||
116 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
|
||||
117
|
||||
118 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
119 g_object_unref (app);
|
||||
120 return stat;
|
||||
121 }
|
||||
122
|
||||
~~~
|
||||
|
||||
- 108-121: The function `main` is almost same as before but there are some differences.
|
||||
The application ID is "com.github.ToshioCP.color".
|
||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||
- 86-106: Startup handler.
|
||||
- 91-96: Builds widgets.
|
||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||
This is because these objects are often used in handlers.
|
||||
They never be rewritten so they're thread safe.
|
||||
- 97: connects "resize" signal and the handler.
|
||||
- 98: sets the drawing function.
|
||||
- 81-84: Activates handler, which just shows the widgets.
|
||||
- 73-79: The drawing function.
|
||||
It just copies `surface` to destination.
|
||||
- 65-71: Resize handler.
|
||||
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||
- 58-63: Closes the handler.
|
||||
It destroys `surface` if it exists.
|
||||
Then it destroys the top window and quits the application.
|
||||
- 48-56: Open and save handler.
|
||||
They just call the corresponding functions of TfeTextView.
|
||||
- 42-46: Run handler.
|
||||
It calls run function to paint the surface.
|
||||
After that `gtk_widget_queue_draw` is called.
|
||||
This fhunction adds the widget (GtkDrawingArea) to the queue to be redrawn.
|
||||
It is important to know that the drawing function is called when it is necessary.
|
||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||
But repaint of `surface` is not automatically notified to gtk.
|
||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||
- 9-40: Run function paints the surface.
|
||||
First, it gets the contents of GtkTextBuffer.
|
||||
Then it compares it to "red", "green" and so on.
|
||||
If it matches the color, then the surface is painted the color.
|
||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||
Alpha channel is used.
|
||||
|
||||
## Meson.build
|
||||
|
||||
This file is almost same as before.
|
||||
An argument "export_dynamic: true" is added to executable function.
|
||||
|
||||
~~~meson
|
||||
1 project('color', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
4
|
||||
5 gnome=import('gnome')
|
||||
6 resources = gnome.compile_resources('resources','color.gresource.xml')
|
||||
7
|
||||
8 sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
|
||||
9
|
||||
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
||||
~~~
|
||||
|
||||
## Compile and execute it
|
||||
|
||||
First you need to export some variables (refer to [Section 2](sec2.md)).
|
||||
|
||||
$ . env.sh
|
||||
|
||||
Then type the following to compile it.
|
||||
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
|
||||
The application is made in `_build` directory.
|
||||
Type the following to execute it.
|
||||
|
||||
$ _build/color
|
||||
|
||||
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
|
||||
Then, click on `Run` button.
|
||||
Make sure the color of GtkDrawingArea changes.
|
||||
|
||||
In this program TfeTextView is used to change the color.
|
||||
You can use buttons or menus instead of textview.
|
||||
Probably it is more appropriate.
|
||||
Using textview is unnatural.
|
||||
It is a good practice to make such application by yourself.
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md)
|
|
@ -1,4 +1,4 @@
|
|||
# Upgrade text file editor
|
||||
# GtkMenuButton, accelerators, font, pango and gsettings
|
||||
|
||||
Traditional menu structure is fine.
|
||||
However, Buttons or menu items we often use are not so many.
|
||||
|
|
357
src/sec20.src.md
357
src/sec20.src.md
|
@ -1,129 +1,280 @@
|
|||
# GtkDrawingArea and Cairo
|
||||
# Template XML
|
||||
|
||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
||||
You can draw or redraw an image in this widget freely.
|
||||
It is called custom drawing.
|
||||
The tfe program in the previous section is not so good because many things are crammed into `tfepplication.c`.
|
||||
Many static variables in `tfepplication.c` shows that.
|
||||
|
||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
||||
In this section, I will explain:
|
||||
~~~C
|
||||
static GtkDialog *pref;
|
||||
static GtkFontButton *fontbtn;
|
||||
static GSettings *settings;
|
||||
static GtkDialog *alert;
|
||||
static GtkLabel *lb_alert;
|
||||
static GtkButton *btn_accept;
|
||||
|
||||
1. Cairo, but briefly.
|
||||
2. GtkDrawingArea with very simple example.
|
||||
static gulong pref_close_request_handler_id = 0;
|
||||
static gulong alert_close_request_handler_id = 0;
|
||||
static gboolean is_quit;
|
||||
~~~
|
||||
|
||||
## Cairo
|
||||
Generally, if there are many global or static variables in the program, it is not a good program.
|
||||
Such programs are difficult to maintain.
|
||||
|
||||
Cairo is a two dimensional graphics library.
|
||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||
The file `tfeapplication.c` should be divided into several files.
|
||||
|
||||
- Surface represents an image.
|
||||
It is like a canvas.
|
||||
We can draw shapes and images with different colors on surfaces.
|
||||
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
||||
- Mask is image mask used in the transference.
|
||||
- Destination is a target surface.
|
||||
- Cairo context manages the transference from source to destination through mask with its functions.
|
||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||
- Transformation is applied before the transfer completes.
|
||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||
In this section, we don't use it.
|
||||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
- `tfeapplication.c` only has codes related to GtkApplication.
|
||||
- A file about GtkApplicationWindow
|
||||
- A file about a preference dialog
|
||||
- A file about an alert dialog
|
||||
|
||||
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
|
||||
The preference dialog is defined by a ui file.
|
||||
And it has GtkBox, GtkLabel and GtkFontButton in it.
|
||||
Such widget is called composite widget.
|
||||
Composite widget is a child object of the parent widget.
|
||||
For example, the preference composite widget is a child object of GtkDialog.
|
||||
Composite widget can be built from template XML.
|
||||
Next subsection shows how to build a preference dialog.
|
||||
|
||||
The instruction is as follows:
|
||||
## Preference dialog
|
||||
|
||||
1. Create a surface.
|
||||
This will be a destination.
|
||||
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
||||
3. Create a source pattern within the context.
|
||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||
6. Save the destination surface to a file if necessary.
|
||||
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
First, write a template XML file.
|
||||
|
||||
@@@include
|
||||
misc/cairo.c
|
||||
tfe7/tfepref.ui
|
||||
@@@
|
||||
|
||||
- 1: Includes the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||
Each data has 8 bit quantity.
|
||||
Modern displays have this type of color depth.
|
||||
Width and height are pixels and given as integers.
|
||||
- 13: Creates cairo context.
|
||||
The surface given as an argument will be the destination of the context.
|
||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||
The second to fourth argument is red, green and blue color depth respectively.
|
||||
Their type is float and the values are between zero and one.
|
||||
(0,0,0) is black and (1,1,1) is white.
|
||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||
The destination is filled with white pixels by this command.
|
||||
- 20: Sets the source color to black.
|
||||
- 21: `cairo_set_line_width` set the width of lines.
|
||||
In this case, the line width is set to two pixels.
|
||||
(It is because the transformation is identity.
|
||||
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||
- 22: Draws a rectangle (square).
|
||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||
- 23: `cairo_stroke` transfer the source to destination through the rectangle in mask.
|
||||
- 26: Outputs the image to a png file `rectangle.png`.
|
||||
- 27: Destroys the context. At the same time the source is destroyed.
|
||||
- 28: Destroys the destination surface.
|
||||
- 3: Template tag specifies a composite widget.
|
||||
The value of a class attribute is the object name of the composite widget.
|
||||
This XML file names the object "TfePref".
|
||||
It is defined in a C source file and it will be shown later.
|
||||
A parent attribute specifies the direct parent object of the composite widget.
|
||||
`TfePref` is a child object of `GtkDialog`.
|
||||
Therefore the value of the attribute is "GtkDialog".
|
||||
A parent attribute is optional but it is recommended to specify.
|
||||
|
||||
To compile this, type the following.
|
||||
|
||||
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
|
||||
|
||||
![rectangle.png](misc/rectangle.png)
|
||||
|
||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
||||
If you aren't familiar with cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
||||
|
||||
## GtkDrawingArea
|
||||
|
||||
The following is a very simple example.
|
||||
Other lines are the same as before.
|
||||
The object `TfePref` is defined in `tfepref.h` and `tfepref.c`.
|
||||
|
||||
@@@include
|
||||
misc/da1.c
|
||||
tfe7/tfepref.h
|
||||
@@@
|
||||
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
- 6-7: When you define a new object, you need to write these two lines.
|
||||
Refer to [Section 7](sec7.src.md).
|
||||
- 9-10: `tfe_pref_new` generates a new TfePref object.
|
||||
It has a parameter which the object use as a transient parent to show the dialog.
|
||||
|
||||
- 16: Generates a GtkDrawingArea object.
|
||||
- 20,21: Sets the width and height of the contents of the GtkDrawingArea widget.
|
||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||
- 22: Sets a drawing function of the widget.
|
||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||
Then, the whole window needs to be redrawn.
|
||||
@@@include
|
||||
tfe7/tfepref.c
|
||||
@@@
|
||||
|
||||
The drawing function has five parameters.
|
||||
- 3-8: The structure of an instance of this object.
|
||||
It has two variables, settings and fontbtn.
|
||||
- 10: G\_DEFINE\_TYPE macro generates lines to register the type.
|
||||
- 12-18: dispose handler.
|
||||
This handler is called when this object is finalizing.
|
||||
The process has two stages, disposing and finalizing.
|
||||
When disposing, the object releases all the objects it has had.
|
||||
TfePref object holds a GSetting object.
|
||||
It is released in line 16.
|
||||
After that parents dispose handler is called in line 17.
|
||||
Refer to [Section 10](sec10.src.md).
|
||||
- 27-34: Class initialization function.
|
||||
This is called in the class generation process.
|
||||
- 31: Set the dispose handler.
|
||||
- 32: `gtk_widget_class_set_template_from_resource` function associates the description in the XML file with the widget.
|
||||
At this moment no object is generated.
|
||||
It just make the class to know the structure of the object.
|
||||
That's why the top level tag is not an object but template in the XML file.
|
||||
- 33: `gtk_widget_class_bind_template_child` function binds a private variable of the object with a child object in the template.
|
||||
This function is a macro.
|
||||
The name of the private variable (in the line 7) and the id (in the line 24) in the XML file must be the same.
|
||||
In the program above, the name is `fontbtn`.
|
||||
The pointer to the object will be assigned to the variable when an instance is generated.
|
||||
- 20-25: Instance initialization function.
|
||||
- 22: Initializes the template of this object.
|
||||
The template has been made during the class initialization process.
|
||||
Now it is implemented to the instance.
|
||||
- 23: Create GSettings object with the id `com.github.ToshioCP.tfe`.
|
||||
- 24: Bind the font key in the GSettings object and the font property in the GtkFontButton.
|
||||
|
||||
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
|
||||
gpointer user_data);
|
||||
- 36-39: The function `tfe_pref_new` creates an instance of TfePref.
|
||||
The parameter `win` is a transient parent.
|
||||
|
||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||
The second parameter is a cairo context given by the widget.
|
||||
The destination surface of the context is connected to the contents of the widget.
|
||||
What you draw to this surface will appear in the widget on the screen.
|
||||
The third and fourth parameters are the size of the destination surface.
|
||||
Now, It is very simple to use this dialog.
|
||||
A caller just creates this object and shows it.
|
||||
|
||||
- 3-11: The drawing function.
|
||||
- 4-5: Sets the source to be white and paint the destination white.
|
||||
- 7: Sets the line width to be 2.
|
||||
- 8: Sets the source to be black.
|
||||
- 9: Adds a rectangle to the mask.
|
||||
- 10: Draws the rectangle with black color to the destination.
|
||||
~~~C
|
||||
TfePref *pref;
|
||||
pref = tfe_pref_new (win) /* win is the top level window */
|
||||
gtk_widget_show (GTK_WINDOW (win));
|
||||
~~~
|
||||
|
||||
Compile and run it, then a window with a black rectangle (square) appears.
|
||||
Try resizing the window.
|
||||
The square always appears at the center of the window because the drawing function is invoked every moment the window is resized.
|
||||
This instance is automatically destroyed when a user clicks on the close button.
|
||||
That's all.
|
||||
If you want to show the dialog again, just create and show it.
|
||||
|
||||
![Square in the window](../image/da1.png)
|
||||
## Alert dialog
|
||||
|
||||
It is almost same as preference dialog.
|
||||
|
||||
Its XML file is:
|
||||
|
||||
@@@include
|
||||
tfe7/tfealert.ui
|
||||
@@@
|
||||
|
||||
The header file is:
|
||||
|
||||
@@@include
|
||||
tfe7/tfealert.h
|
||||
@@@
|
||||
|
||||
There are three public functions.
|
||||
The functions `tfe_alert_set_message` and `tfe_alert_set_button_label` sets the label and button name of the alert dialog.
|
||||
For example, if you want to show an alert that the user tries to close without saving the content, set them like:
|
||||
|
||||
~~~C
|
||||
tfe_alert_set_message (alert, "Are you really close without saving?"); /* alert points to a TfeAlert object */
|
||||
tfe_alert_set_button_label (alert, "Close");
|
||||
~~~
|
||||
|
||||
The function `tfe_alert_new` creates a TfeAlert dialog.
|
||||
|
||||
The C source file is:
|
||||
|
||||
@@@include
|
||||
tfe7/tfealert.c
|
||||
@@@
|
||||
|
||||
The program is almost same as `tfepref.c`.
|
||||
|
||||
The instruction how to use this object is as follows.
|
||||
|
||||
1. Write a "response" signal handler.
|
||||
2. Create a TfeAlert object.
|
||||
3. Connect "response" signal to a handler
|
||||
4. Show the dialog
|
||||
5. In the signal handler do something along the response-id.
|
||||
Then destroy the dialog.
|
||||
|
||||
## Top level window
|
||||
|
||||
In the same way, create a child object of GtkApplicationWindow.
|
||||
The object name is "TfeWindow".
|
||||
|
||||
@@@include
|
||||
tfe7/tfewindow.ui
|
||||
@@@
|
||||
|
||||
This XML file is the same as before except template tag.
|
||||
|
||||
@@@include
|
||||
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`.
|
||||
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.
|
||||
|
||||
@@@include
|
||||
tfe7/tfewindow.c
|
||||
@@@
|
||||
|
||||
- 20-32: `alert_response_cb` is a call back function of the "response" signal of TfeAlert dialog.
|
||||
This is the same as before except `gtk_window_destroy(GTK_WINDOW (win))` is used instead of `tfe_application_quit`.
|
||||
- 34-60: Handlers of Button clicked signal.
|
||||
- 62-123: Handlers of action activated signal.
|
||||
The `user_data` is a pointer to TfeWindow instance.
|
||||
- 125-135: A handler of "changed::font" signal of GSettings object.
|
||||
- 132: Gets the font from GSettings data.
|
||||
- 133: Gets a PangoFontDescription from the font.
|
||||
In the previous version, the program gets the font description from the GtkFontButton.
|
||||
The button data and GSettings data are the same.
|
||||
Therefore, the data got here is the same as the data in the GtkFontButton.
|
||||
In addition, we don't need to worry about the preference dialog is alive or not thanks to the GSettings.
|
||||
- 134: Sets CSS on the display with the font description.
|
||||
- 137-152: Public functions.
|
||||
- 155-161: Dispose handler.
|
||||
The GSettings object needs to be released.
|
||||
- 163-191: Object initialize function.
|
||||
- 168: Generates a composite widget with the template.
|
||||
- 170-173: Insert menu to the menu button.
|
||||
- 175-176: Creates a GSettings object with the id.
|
||||
Connects "changed::font" signal to the handler `changed_font_cb`.
|
||||
This signal emits when the GSettings data is changed.
|
||||
The second part "font" of the signal name "changed::font" is called details.
|
||||
Signals can have details.
|
||||
If a GSettings object has more than one key, "changed" signal emits only if the key which has the same name as the detail changes its value.
|
||||
For example, Suppose a GSettings object has three keys "a", "b" and "c".
|
||||
- "changed::a" is emitted when the value of "a" is changed. It isn't emitted when the value of "b" or "c" is changed.
|
||||
- "changed::b" is emitted when the value of "b" is changed. It isn't emitted when the value of "a" or "c" is changed.
|
||||
- "changed::c" is emitted when the value of "c" is changed. It isn't emitted when the value of "a" or "b" is changed.
|
||||
In this version of tfe, there is only one key ("font").
|
||||
So, even if the signal doesn't have a detail, the result is the same.
|
||||
But in the future version, it will probably need details.
|
||||
- 178-188: Creates actions.
|
||||
- 190: Sets CSS font.
|
||||
- 193-207: Class initialization function.
|
||||
- 197: Sets the dispose handler.
|
||||
- 198: Sets the composite widget template
|
||||
- 199-203: Binds private variable with child objects in the template.
|
||||
- 204-206: Binds signal handlers with signal tags in the template.
|
||||
- 209-212: `tfe_window_new`.
|
||||
This function creates TfeWindow instance.
|
||||
|
||||
## GtkApplication
|
||||
|
||||
The file `tfeapplication.c` is now very simple.
|
||||
|
||||
@@@include
|
||||
tfe7/tfeapplication.c
|
||||
@@@
|
||||
|
||||
- 3-11: Activate signal handler.
|
||||
It uses `tfe_window_notebook_page_new` instead of `notebook_page_new`.
|
||||
- 13-20: Open signal handler.
|
||||
Thanks to `tfe_window_notebook_page_new_with_files`, this handler becomes very simple.
|
||||
- 22-46: Startup signal handler.
|
||||
Most of the task is moved to TfeWindow, the remaining task is creating a window and setting accelerations.
|
||||
- 49-63: A function main.
|
||||
|
||||
## Other files
|
||||
|
||||
Resource XML file.
|
||||
|
||||
@@@include
|
||||
tfe7/tfe.gresource.xml
|
||||
@@@
|
||||
|
||||
GSchema XML file
|
||||
|
||||
@@@include
|
||||
tfe7/com.github.ToshioCP.tfe.gschema.xml
|
||||
@@@
|
||||
|
||||
Meson.build
|
||||
|
||||
@@@include
|
||||
tfe7/meson.build
|
||||
@@@
|
||||
|
||||
## Compiling and installation.
|
||||
|
||||
~~~
|
||||
$ meson --prefix=$HOME/local _build
|
||||
$ ninja -C _build
|
||||
$ ninja -C _build install
|
||||
===
|
||||
|
||||
Source files are in [src/tfe7](tfe7) directory.
|
||||
|
||||
We made a very small text editor.
|
||||
You can add features to this editor.
|
||||
When you add a new feature, care about the structure of the program.
|
||||
Maybe you need to divide a file into several files like this section.
|
||||
It isn't good to put many things into one file.
|
||||
And it is important to think about the relationship between source files and widget structures.
|
||||
It is appropriate that they correspond to each other in many cases.
|
||||
|
|
228
src/sec21.src.md
228
src/sec21.src.md
|
@ -1,153 +1,129 @@
|
|||
# Combine GtkDrawingArea and TfeTextView
|
||||
# GtkDrawingArea and Cairo
|
||||
|
||||
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
|
||||
Its name is "color".
|
||||
If you write a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
|
||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
||||
You can draw or redraw an image in this widget freely.
|
||||
It is called custom drawing.
|
||||
|
||||
![color](../image/color.png){width=7.0cm height=5.13cm}
|
||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
||||
In this section, I will explain:
|
||||
|
||||
The following colors are available.
|
||||
1. Cairo, but briefly.
|
||||
2. GtkDrawingArea with very simple example.
|
||||
|
||||
- white
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
- blue
|
||||
## Cairo
|
||||
|
||||
In addition the following two options are also available.
|
||||
Cairo is a two dimensional graphics library.
|
||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||
|
||||
- light: Make the color of the drawing area lighter.
|
||||
- dark: Make the color of the drawing area darker.
|
||||
- Surface represents an image.
|
||||
It is like a canvas.
|
||||
We can draw shapes and images with different colors on surfaces.
|
||||
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
||||
- Mask is image mask used in the transference.
|
||||
- Destination is a target surface.
|
||||
- Cairo context manages the transference from source to destination through mask with its functions.
|
||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||
- Transformation is applied before the transfer completes.
|
||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||
In this section, we don't use it.
|
||||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
|
||||
This application can only do very simple things.
|
||||
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
|
||||
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
|
||||
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
|
||||
|
||||
In this section, we focus on how to bind the two objects.
|
||||
The instruction is as follows:
|
||||
|
||||
## Color.ui and color.gresource.xml
|
||||
1. Create a surface.
|
||||
This will be a destination.
|
||||
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
||||
3. Create a source pattern within the context.
|
||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||
6. Save the destination surface to a file if necessary.
|
||||
|
||||
First, We need to make the ui file of the widgets.
|
||||
The image in the previous subsection gives us the structure of the widgets.
|
||||
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
|
||||
The ui file is as follows.
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
|
||||
@@@include
|
||||
color/color.ui
|
||||
misc/cairo.c
|
||||
@@@
|
||||
|
||||
- 10-53: This part describes the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
|
||||
This is similar to the toolbar of tfe text editor in [Section 8](sec8.src.md).
|
||||
There are two differences.
|
||||
`Run` button replaces `New` button.
|
||||
A signal element is added to each button object.
|
||||
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function.
|
||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||
And be careful that the handler must be defined without 'static' class.
|
||||
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
|
||||
TfeTextView is a child of GtkScrolledWindow.
|
||||
- 1: Includes the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||
Each data has 8 bit quantity.
|
||||
Modern displays have this type of color depth.
|
||||
Width and height are pixels and given as integers.
|
||||
- 13: Creates cairo context.
|
||||
The surface given as an argument will be the destination of the context.
|
||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||
The second to fourth argument is red, green and blue color depth respectively.
|
||||
Their type is float and the values are between zero and one.
|
||||
(0,0,0) is black and (1,1,1) is white.
|
||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||
The destination is filled with white pixels by this command.
|
||||
- 20: Sets the source color to black.
|
||||
- 21: `cairo_set_line_width` set the width of lines.
|
||||
In this case, the line width is set to two pixels.
|
||||
(It is because the transformation is identity.
|
||||
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||
- 22: Draws a rectangle (square).
|
||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||
- 23: `cairo_stroke` transfer the source to destination through the rectangle in mask.
|
||||
- 26: Outputs the image to a png file `rectangle.png`.
|
||||
- 27: Destroys the context. At the same time the source is destroyed.
|
||||
- 28: Destroys the destination surface.
|
||||
|
||||
The xml file for the resource compiler is almost same as before.
|
||||
Just substitute "color" for "tfe".
|
||||
To compile this, type the following.
|
||||
|
||||
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
|
||||
|
||||
![rectangle.png](misc/rectangle.png)
|
||||
|
||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
||||
If you aren't familiar with cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
||||
|
||||
## GtkDrawingArea
|
||||
|
||||
The following is a very simple example.
|
||||
|
||||
@@@include
|
||||
color/color.gresource.xml
|
||||
misc/da1.c
|
||||
@@@
|
||||
|
||||
## Tfetextview.h, tfetextview.c and color.h
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
|
||||
First two files are the same as before.
|
||||
Color.h just includes tfetextview.h.
|
||||
- 16: Generates a GtkDrawingArea object.
|
||||
- 20,21: Sets the width and height of the contents of the GtkDrawingArea widget.
|
||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||
- 22: Sets a drawing function of the widget.
|
||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||
Then, the whole window needs to be redrawn.
|
||||
|
||||
@@@include
|
||||
color/color.h
|
||||
@@@
|
||||
The drawing function has five parameters.
|
||||
|
||||
## Colorapplication.c
|
||||
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
|
||||
gpointer user_data);
|
||||
|
||||
This is the main file.
|
||||
It deals with:
|
||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||
The second parameter is a cairo context given by the widget.
|
||||
The destination surface of the context is connected to the contents of the widget.
|
||||
What you draw to this surface will appear in the widget on the screen.
|
||||
The third and fourth parameters are the size of the destination surface.
|
||||
|
||||
- Building widgets by GtkBuilder.
|
||||
- Seting a drawing function of GtkDrawingArea.
|
||||
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||
- Implementing each call back functions.
|
||||
Particularly, `Run` signal handler is the point in this program.
|
||||
- 3-11: The drawing function.
|
||||
- 4-5: Sets the source to be white and paint the destination white.
|
||||
- 7: Sets the line width to be 2.
|
||||
- 8: Sets the source to be black.
|
||||
- 9: Adds a rectangle to the mask.
|
||||
- 10: Draws the rectangle with black color to the destination.
|
||||
|
||||
The following is `colorapplication.c`.
|
||||
Compile and run it, then a window with a black rectangle (square) appears.
|
||||
Try resizing the window.
|
||||
The square always appears at the center of the window because the drawing function is invoked every moment the window is resized.
|
||||
|
||||
@@@include
|
||||
color/colorapplication.c
|
||||
@@@
|
||||
![Square in the window](../image/da1.png)
|
||||
|
||||
- 108-121: The function `main` is almost same as before but there are some differences.
|
||||
The application ID is "com.github.ToshioCP.color".
|
||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||
- 86-106: Startup handler.
|
||||
- 91-96: Builds widgets.
|
||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||
This is because these objects are often used in handlers.
|
||||
They never be rewritten so they're thread safe.
|
||||
- 97: connects "resize" signal and the handler.
|
||||
- 98: sets the drawing function.
|
||||
- 81-84: Activates handler, which just shows the widgets.
|
||||
- 73-79: The drawing function.
|
||||
It just copies `surface` to destination.
|
||||
- 65-71: Resize handler.
|
||||
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||
- 58-63: Closes the handler.
|
||||
It destroys `surface` if it exists.
|
||||
Then it destroys the top window and quits the application.
|
||||
- 48-56: Open and save handler.
|
||||
They just call the corresponding functions of TfeTextView.
|
||||
- 42-46: Run handler.
|
||||
It calls run function to paint the surface.
|
||||
After that `gtk_widget_queue_draw` is called.
|
||||
This fhunction adds the widget (GtkDrawingArea) to the queue to be redrawn.
|
||||
It is important to know that the drawing function is called when it is necessary.
|
||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||
But repaint of `surface` is not automatically notified to gtk.
|
||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||
- 9-40: Run function paints the surface.
|
||||
First, it gets the contents of GtkTextBuffer.
|
||||
Then it compares it to "red", "green" and so on.
|
||||
If it matches the color, then the surface is painted the color.
|
||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||
Alpha channel is used.
|
||||
|
||||
## Meson.build
|
||||
|
||||
This file is almost same as before.
|
||||
An argument "export_dynamic: true" is added to executable function.
|
||||
|
||||
@@@include
|
||||
color/meson.build
|
||||
@@@
|
||||
|
||||
## Compile and execute it
|
||||
|
||||
First you need to export some variables (refer to [Section 2](sec2.src.md)).
|
||||
|
||||
$ . env.sh
|
||||
|
||||
Then type the following to compile it.
|
||||
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
|
||||
The application is made in `_build` directory.
|
||||
Type the following to execute it.
|
||||
|
||||
$ _build/color
|
||||
|
||||
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
|
||||
Then, click on `Run` button.
|
||||
Make sure the color of GtkDrawingArea changes.
|
||||
|
||||
In this program TfeTextView is used to change the color.
|
||||
You can use buttons or menus instead of textview.
|
||||
Probably it is more appropriate.
|
||||
Using textview is unnatural.
|
||||
It is a good practice to make such application by yourself.
|
||||
|
|
153
src/sec22.src.md
Normal file
153
src/sec22.src.md
Normal file
|
@ -0,0 +1,153 @@
|
|||
# Combine GtkDrawingArea and TfeTextView
|
||||
|
||||
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
|
||||
Its name is "color".
|
||||
If you write a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
|
||||
|
||||
![color](../image/color.png){width=7.0cm height=5.13cm}
|
||||
|
||||
The following colors are available.
|
||||
|
||||
- white
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
- blue
|
||||
|
||||
In addition the following two options are also available.
|
||||
|
||||
- light: Make the color of the drawing area lighter.
|
||||
- dark: Make the color of the drawing area darker.
|
||||
|
||||
This application can only do very simple things.
|
||||
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
|
||||
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
|
||||
|
||||
In this section, we focus on how to bind the two objects.
|
||||
|
||||
## Color.ui and color.gresource.xml
|
||||
|
||||
First, We need to make the ui file of the widgets.
|
||||
The image in the previous subsection gives us the structure of the widgets.
|
||||
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
|
||||
The ui file is as follows.
|
||||
|
||||
@@@include
|
||||
color/color.ui
|
||||
@@@
|
||||
|
||||
- 10-53: This part describes the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
|
||||
This is similar to the toolbar of tfe text editor in [Section 8](sec8.src.md).
|
||||
There are two differences.
|
||||
`Run` button replaces `New` button.
|
||||
A signal element is added to each button object.
|
||||
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function.
|
||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||
And be careful that the handler must be defined without 'static' class.
|
||||
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
|
||||
TfeTextView is a child of GtkScrolledWindow.
|
||||
|
||||
The xml file for the resource compiler is almost same as before.
|
||||
Just substitute "color" for "tfe".
|
||||
|
||||
@@@include
|
||||
color/color.gresource.xml
|
||||
@@@
|
||||
|
||||
## Tfetextview.h, tfetextview.c and color.h
|
||||
|
||||
First two files are the same as before.
|
||||
Color.h just includes tfetextview.h.
|
||||
|
||||
@@@include
|
||||
color/color.h
|
||||
@@@
|
||||
|
||||
## Colorapplication.c
|
||||
|
||||
This is the main file.
|
||||
It deals with:
|
||||
|
||||
- Building widgets by GtkBuilder.
|
||||
- Seting a drawing function of GtkDrawingArea.
|
||||
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||
- Implementing each call back functions.
|
||||
Particularly, `Run` signal handler is the point in this program.
|
||||
|
||||
The following is `colorapplication.c`.
|
||||
|
||||
@@@include
|
||||
color/colorapplication.c
|
||||
@@@
|
||||
|
||||
- 108-121: The function `main` is almost same as before but there are some differences.
|
||||
The application ID is "com.github.ToshioCP.color".
|
||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||
- 86-106: Startup handler.
|
||||
- 91-96: Builds widgets.
|
||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||
This is because these objects are often used in handlers.
|
||||
They never be rewritten so they're thread safe.
|
||||
- 97: connects "resize" signal and the handler.
|
||||
- 98: sets the drawing function.
|
||||
- 81-84: Activates handler, which just shows the widgets.
|
||||
- 73-79: The drawing function.
|
||||
It just copies `surface` to destination.
|
||||
- 65-71: Resize handler.
|
||||
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||
- 58-63: Closes the handler.
|
||||
It destroys `surface` if it exists.
|
||||
Then it destroys the top window and quits the application.
|
||||
- 48-56: Open and save handler.
|
||||
They just call the corresponding functions of TfeTextView.
|
||||
- 42-46: Run handler.
|
||||
It calls run function to paint the surface.
|
||||
After that `gtk_widget_queue_draw` is called.
|
||||
This fhunction adds the widget (GtkDrawingArea) to the queue to be redrawn.
|
||||
It is important to know that the drawing function is called when it is necessary.
|
||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||
But repaint of `surface` is not automatically notified to gtk.
|
||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||
- 9-40: Run function paints the surface.
|
||||
First, it gets the contents of GtkTextBuffer.
|
||||
Then it compares it to "red", "green" and so on.
|
||||
If it matches the color, then the surface is painted the color.
|
||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||
Alpha channel is used.
|
||||
|
||||
## Meson.build
|
||||
|
||||
This file is almost same as before.
|
||||
An argument "export_dynamic: true" is added to executable function.
|
||||
|
||||
@@@include
|
||||
color/meson.build
|
||||
@@@
|
||||
|
||||
## Compile and execute it
|
||||
|
||||
First you need to export some variables (refer to [Section 2](sec2.src.md)).
|
||||
|
||||
$ . env.sh
|
||||
|
||||
Then type the following to compile it.
|
||||
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
|
||||
The application is made in `_build` directory.
|
||||
Type the following to execute it.
|
||||
|
||||
$ _build/color
|
||||
|
||||
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
|
||||
Then, click on `Run` button.
|
||||
Make sure the color of GtkDrawingArea changes.
|
||||
|
||||
In this program TfeTextView is used to change the color.
|
||||
You can use buttons or menus instead of textview.
|
||||
Probably it is more appropriate.
|
||||
Using textview is unnatural.
|
||||
It is a good practice to make such application by yourself.
|
10
src/tfe7/com.github.ToshioCP.tfe.gschema.xml
Normal file
10
src/tfe7/com.github.ToshioCP.tfe.gschema.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist>
|
||||
<schema path="/com/github/ToshioCP/tfe/" id="com.github.ToshioCP.tfe">
|
||||
<key name="font" type="s">
|
||||
<default>'Monospace 12'</default>
|
||||
<summary>Font</summary>
|
||||
<description>The font to be used for textview.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
92
src/tfe7/css.c
Normal file
92
src/tfe7/css.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* css.h */
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
void
|
||||
set_css_for_display (GtkWindow *win, char *css) {
|
||||
GdkDisplay *display;
|
||||
|
||||
display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||
GtkCssProvider *provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider, css, -1);
|
||||
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
}
|
||||
|
||||
void
|
||||
set_font_for_display (GtkWindow *win, const char *fontfamily, const char *fontstyle, const char *fontweight, int fontsize) {
|
||||
char *textview_css;
|
||||
|
||||
textview_css = g_strdup_printf ("textview {padding: 10px; font-family: \"%s\"; font-style: %s; font-weight: %s; font-size: %dpt;}",
|
||||
fontfamily, fontstyle, fontweight, fontsize);
|
||||
set_css_for_display (win, textview_css);
|
||||
}
|
||||
|
||||
void
|
||||
set_font_for_display_with_pango_font_desc (GtkWindow *win, PangoFontDescription *pango_font_desc) {
|
||||
int pango_style;
|
||||
int pango_weight;
|
||||
const char *family;
|
||||
const char *style;
|
||||
const char *weight;
|
||||
int fontsize;
|
||||
|
||||
family = pango_font_description_get_family (pango_font_desc);
|
||||
pango_style = pango_font_description_get_style (pango_font_desc);
|
||||
switch (pango_style) {
|
||||
case PANGO_STYLE_NORMAL:
|
||||
style = "normal";
|
||||
break;
|
||||
case PANGO_STYLE_ITALIC:
|
||||
style = "italic";
|
||||
break;
|
||||
case PANGO_STYLE_OBLIQUE:
|
||||
style = "oblique";
|
||||
break;
|
||||
default:
|
||||
style = "normal";
|
||||
break;
|
||||
}
|
||||
pango_weight = pango_font_description_get_weight (pango_font_desc);
|
||||
switch (pango_weight) {
|
||||
case PANGO_WEIGHT_THIN:
|
||||
weight = "100";
|
||||
break;
|
||||
case PANGO_WEIGHT_ULTRALIGHT:
|
||||
weight = "200";
|
||||
break;
|
||||
case PANGO_WEIGHT_LIGHT:
|
||||
weight = "300";
|
||||
break;
|
||||
case PANGO_WEIGHT_SEMILIGHT:
|
||||
weight = "350";
|
||||
break;
|
||||
case PANGO_WEIGHT_BOOK:
|
||||
weight = "380";
|
||||
break;
|
||||
case PANGO_WEIGHT_NORMAL:
|
||||
weight = "400"; /* or "normal" */
|
||||
break;
|
||||
case PANGO_WEIGHT_MEDIUM:
|
||||
weight = "500";
|
||||
break;
|
||||
case PANGO_WEIGHT_SEMIBOLD:
|
||||
weight = "600";
|
||||
break;
|
||||
case PANGO_WEIGHT_BOLD:
|
||||
weight = "700"; /* or "bold" */
|
||||
break;
|
||||
case PANGO_WEIGHT_ULTRABOLD:
|
||||
weight = "800";
|
||||
break;
|
||||
case PANGO_WEIGHT_HEAVY:
|
||||
weight = "900";
|
||||
break;
|
||||
case PANGO_WEIGHT_ULTRAHEAVY:
|
||||
weight = "900"; /* In PangoWeight definition, the weight is 1000. But CSS allows the weight below 900. */
|
||||
break;
|
||||
default:
|
||||
weight = "normal";
|
||||
break;
|
||||
}
|
||||
fontsize = pango_font_description_get_size (pango_font_desc) / PANGO_SCALE;
|
||||
set_font_for_display (win, family, style, weight, fontsize);
|
||||
}
|
14
src/tfe7/css.h
Normal file
14
src/tfe7/css.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef __TFE_CSS_H__
|
||||
#define __TFE_CSS_H__
|
||||
|
||||
void
|
||||
set_css (GtkWindow *win, char *css);
|
||||
|
||||
void
|
||||
set_font_for_display (GtkWindow *win, const char *fontfamily, const char *fontstyle, const char *fontweight, int size);
|
||||
|
||||
void
|
||||
set_font_for_display_with_pango_font_desc (GtkWindow *win, PangoFontDescription *pango_font_desc);
|
||||
|
||||
#endif /* __TFE_CSS_H__ */
|
||||
|
27
src/tfe7/menu.ui
Executable file
27
src/tfe7/menu.ui
Executable file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">New</attribute>
|
||||
<attribute name="action">win.new</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Save As…</attribute>
|
||||
<attribute name="action">win.saveas</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Preference</attribute>
|
||||
<attribute name="action">win.pref</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Quit</attribute>
|
||||
<attribute name="action">win.close-all</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
16
src/tfe7/meson.build
Normal file
16
src/tfe7/meson.build
Normal file
|
@ -0,0 +1,16 @@
|
|||
project('tfe', 'c')
|
||||
|
||||
gtkdep = dependency('gtk4')
|
||||
|
||||
gnome=import('gnome')
|
||||
resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
||||
gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')
|
||||
|
||||
sourcefiles=files('tfeapplication.c', 'tfewindow.c', 'tfenotebook.c', 'tfepref.c', 'tfealert.c', 'css.c', '../tfetextview/tfetextview.c')
|
||||
|
||||
executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)
|
||||
|
||||
schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'
|
||||
install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)
|
||||
meson.add_install_script('glib-compile-schemas', schema_dir)
|
||||
|
9
src/tfe7/tfe.gresource.xml
Normal file
9
src/tfe7/tfe.gresource.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/com/github/ToshioCP/tfe">
|
||||
<file>tfewindow.ui</file>
|
||||
<file>tfepref.ui</file>
|
||||
<file>tfealert.ui</file>
|
||||
<file>menu.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
38
src/tfe7/tfealert.c
Normal file
38
src/tfe7/tfealert.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "tfealert.h"
|
||||
|
||||
struct _TfeAlert
|
||||
{
|
||||
GtkDialog parent;
|
||||
GtkLabel *lb_alert;
|
||||
GtkButton *btn_accept;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TfeAlert, tfe_alert, GTK_TYPE_DIALOG);
|
||||
|
||||
void
|
||||
tfe_alert_set_message (TfeAlert *alert, const char *message) {
|
||||
gtk_label_set_text (alert->lb_alert, message);
|
||||
}
|
||||
|
||||
void
|
||||
tfe_alert_set_button_label (TfeAlert *alert, const char *label) {
|
||||
gtk_button_set_label (alert->btn_accept, label);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_alert_init (TfeAlert *alert) {
|
||||
gtk_widget_init_template (GTK_WIDGET (alert));
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_alert_class_init (TfeAlertClass *class) {
|
||||
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_alert);
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_accept);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
tfe_alert_new (GtkWindow *win) {
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_ALERT, "transient-for", win, NULL));
|
||||
}
|
||||
|
19
src/tfe7/tfealert.h
Normal file
19
src/tfe7/tfealert.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef __TFE_ALERT_H__
|
||||
#define __TFE_ALERT_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define TFE_TYPE_ALERT tfe_alert_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (TfeAlert, tfe_alert, TFE, ALERT, GtkDialog)
|
||||
|
||||
void
|
||||
tfe_alert_set_message (TfeAlert *alert, const char *message);
|
||||
|
||||
void
|
||||
tfe_alert_set_button_label (TfeAlert *alert, const char *label);
|
||||
|
||||
GtkWidget *
|
||||
tfe_alert_new (GtkWindow *win);
|
||||
|
||||
#endif /* __TFE_ALERT_H__ */
|
||||
|
47
src/tfe7/tfealert.ui
Normal file
47
src/tfe7/tfealert.ui
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="TfeAlert" parent="GtkDialog">
|
||||
<property name="title">Are you sure?</property>
|
||||
<property name="resizable">FALSE</property>
|
||||
<property name="modal">TRUE</property>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">dialog-warning</property>
|
||||
<property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lb_alert">
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="btn_cancel">
|
||||
<property name="label">Cancel</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="btn_accept">
|
||||
<property name="label">Close</property>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="cancel" default="true">btn_cancel</action-widget>
|
||||
<action-widget response="accept">btn_accept</action-widget>
|
||||
</action-widgets>
|
||||
</template>
|
||||
</interface>
|
||||
|
64
src/tfe7/tfeapplication.c
Normal file
64
src/tfe7/tfeapplication.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include "tfewindow.h"
|
||||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
|
||||
|
||||
tfe_window_notebook_page_new (win);
|
||||
gtk_widget_show (GTK_WIDGET (win));
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_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));
|
||||
|
||||
tfe_window_notebook_page_new_with_files (win, files, n_files);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkBuilder *build;
|
||||
TfeWindow *win;
|
||||
int i;
|
||||
|
||||
win = tfe_window_new (app);
|
||||
|
||||
/* ----- accelerator ----- */
|
||||
struct {
|
||||
const char *action;
|
||||
const char *accels[2];
|
||||
} action_accels[] = {
|
||||
{ "win.open", { "<Control>o", NULL } },
|
||||
{ "win.save", { "<Control>s", NULL } },
|
||||
{ "win.close", { "<Control>w", NULL } },
|
||||
{ "win.new", { "<Control>n", NULL } },
|
||||
{ "win.saveas", { "<Shift><Control>s", NULL } },
|
||||
{ "win.close-all", { "<Control>q", NULL } },
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(action_accels); i++)
|
||||
gtk_application_set_accels_for_action(GTK_APPLICATION(app), action_accels[i].action, action_accels[i].accels);
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
211
src/tfe7/tfenotebook.c
Normal file
211
src/tfe7/tfenotebook.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
#include "tfenotebook.h"
|
||||
#include "../tfetextview/tfetextview.h"
|
||||
|
||||
/* The returned string should be freed with g_free() when no longer needed. */
|
||||
static gchar*
|
||||
get_untitled () {
|
||||
static int c = -1;
|
||||
if (++c == 0)
|
||||
return g_strdup_printf("Untitled");
|
||||
else
|
||||
return g_strdup_printf ("Untitled%u", c);
|
||||
}
|
||||
|
||||
static TfeTextView *
|
||||
get_current_textview (GtkNotebook *nb) {
|
||||
int i;
|
||||
GtkWidget *scr;
|
||||
GtkWidget *tv;
|
||||
|
||||
i = gtk_notebook_get_current_page (nb);
|
||||
scr = gtk_notebook_get_nth_page (nb, i);
|
||||
tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
|
||||
return TFE_TEXT_VIEW (tv);
|
||||
}
|
||||
|
||||
static void
|
||||
file_changed_cb (TfeTextView *tv) {
|
||||
GtkWidget *nb = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);
|
||||
GtkWidget *scr;
|
||||
GtkWidget *label;
|
||||
GFile *file;
|
||||
char *filename;
|
||||
|
||||
if (! GTK_IS_NOTEBOOK (nb)) /* tv not connected to nb yet */
|
||||
return;
|
||||
file = tfe_text_view_get_file (tv);
|
||||
scr = gtk_widget_get_parent (GTK_WIDGET (tv));
|
||||
if (G_IS_FILE (file)) {
|
||||
filename = g_file_get_basename (file);
|
||||
g_object_unref (file);
|
||||
} else
|
||||
filename = get_untitled ();
|
||||
label = gtk_label_new (filename);
|
||||
gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);
|
||||
}
|
||||
|
||||
static void
|
||||
modified_changed_cb (GtkTextBuffer *tb, gpointer user_data) {
|
||||
TfeTextView *tv = TFE_TEXT_VIEW (user_data);
|
||||
GtkWidget *scr = gtk_widget_get_parent (GTK_WIDGET (tv));
|
||||
GtkWidget *nb = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);
|
||||
GtkWidget *label;
|
||||
const char *filename;
|
||||
char *text;
|
||||
|
||||
if (! GTK_IS_NOTEBOOK (nb)) /* tv not connected to nb yet */
|
||||
return;
|
||||
else if (gtk_text_buffer_get_modified (tb)) {
|
||||
filename = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (nb), scr);
|
||||
text = g_strdup_printf ("*%s", filename);
|
||||
label = gtk_label_new (text);
|
||||
gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);
|
||||
} else
|
||||
file_changed_cb (tv);
|
||||
}
|
||||
|
||||
gboolean
|
||||
has_saved (GtkNotebook *nb) {
|
||||
g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);
|
||||
|
||||
TfeTextView *tv;
|
||||
GtkTextBuffer *tb;
|
||||
|
||||
tv = get_current_textview (nb);
|
||||
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
if (gtk_text_buffer_get_modified (tb))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
gboolean
|
||||
has_saved_all (GtkNotebook *nb) {
|
||||
g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);
|
||||
|
||||
int i, n;
|
||||
GtkWidget *scr;
|
||||
GtkWidget *tv;
|
||||
GtkTextBuffer *tb;
|
||||
|
||||
n = gtk_notebook_get_n_pages (nb);
|
||||
for (i = 0; i < n; ++i) {
|
||||
scr = gtk_notebook_get_nth_page (nb, i);
|
||||
tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
|
||||
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
if (gtk_text_buffer_get_modified (tb))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_save (GtkNotebook *nb) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
||||
TfeTextView *tv;
|
||||
|
||||
tv = get_current_textview (nb);
|
||||
tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_saveas (GtkNotebook *nb) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
||||
TfeTextView *tv;
|
||||
|
||||
tv = get_current_textview (nb);
|
||||
tfe_text_view_saveas (TFE_TEXT_VIEW (tv));
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_close (GtkNotebook *nb) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
||||
GtkWidget *win;
|
||||
int i;
|
||||
|
||||
if (gtk_notebook_get_n_pages (nb) == 1) {
|
||||
win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);
|
||||
gtk_window_destroy (GTK_WINDOW (win));
|
||||
} else {
|
||||
i = gtk_notebook_get_current_page (nb);
|
||||
gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) {
|
||||
GtkWidget *scr = gtk_scrolled_window_new ();
|
||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
GtkNotebookPage *nbp;
|
||||
GtkWidget *lab;
|
||||
int i;
|
||||
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
lab = gtk_label_new (filename);
|
||||
i = gtk_notebook_append_page (nb, scr, lab);
|
||||
nbp = gtk_notebook_get_page (nb, scr);
|
||||
g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
gtk_notebook_set_current_page (nb, i);
|
||||
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), NULL);
|
||||
g_signal_connect (tb, "modified-changed", G_CALLBACK (modified_changed_cb), tv);
|
||||
}
|
||||
|
||||
static void
|
||||
open_response (TfeTextView *tv, int response, GtkNotebook *nb) {
|
||||
GFile *file;
|
||||
char *filename;
|
||||
|
||||
if (response != TFE_OPEN_RESPONSE_SUCCESS) {
|
||||
g_object_ref_sink (tv);
|
||||
g_object_unref (tv);
|
||||
}else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) {
|
||||
g_object_ref_sink (tv);
|
||||
g_object_unref (tv);
|
||||
}else {
|
||||
filename = g_file_get_basename (file);
|
||||
g_object_unref (file);
|
||||
notebook_page_build (nb, GTK_WIDGET (tv), filename);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_open (GtkNotebook *nb) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
||||
GtkWidget *tv;
|
||||
|
||||
tv = tfe_text_view_new ();
|
||||
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
|
||||
tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW));
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
g_return_if_fail(G_IS_FILE (file));
|
||||
|
||||
GtkWidget *tv;
|
||||
char *filename;
|
||||
|
||||
if ((tv = tfe_text_view_new_with_file (file)) == NULL)
|
||||
return; /* read error */
|
||||
filename = g_file_get_basename (file);
|
||||
notebook_page_build (nb, tv, filename);
|
||||
}
|
||||
|
||||
void
|
||||
notebook_page_new (GtkNotebook *nb) {
|
||||
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
||||
GtkWidget *tv;
|
||||
char *filename;
|
||||
|
||||
tv = tfe_text_view_new ();
|
||||
filename = get_untitled ();
|
||||
notebook_page_build (nb, tv, filename);
|
||||
}
|
||||
|
31
src/tfe7/tfenotebook.h
Normal file
31
src/tfe7/tfenotebook.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef __TFE_NOTEBOOK_H__
|
||||
#define __TFE_NOTEBOOK_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
gboolean
|
||||
has_saved (GtkNotebook *nb);
|
||||
|
||||
gboolean
|
||||
has_saved_all (GtkNotebook *nb);
|
||||
|
||||
void
|
||||
notebook_page_save(GtkNotebook *nb);
|
||||
|
||||
void
|
||||
notebook_page_saveas(GtkNotebook *nb);
|
||||
|
||||
void
|
||||
notebook_page_close (GtkNotebook *nb);
|
||||
|
||||
void
|
||||
notebook_page_open (GtkNotebook *nb);
|
||||
|
||||
void
|
||||
notebook_page_new_with_file (GtkNotebook *nb, GFile *file);
|
||||
|
||||
void
|
||||
notebook_page_new (GtkNotebook *nb);
|
||||
|
||||
#endif /* __TFE_NOTEBOOK_H__ */
|
||||
|
40
src/tfe7/tfepref.c
Normal file
40
src/tfe7/tfepref.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "tfepref.h"
|
||||
|
||||
struct _TfePref
|
||||
{
|
||||
GtkDialog parent;
|
||||
GSettings *settings;
|
||||
GtkFontButton *fontbtn;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TfePref, tfe_pref, GTK_TYPE_DIALOG);
|
||||
|
||||
static void
|
||||
tfe_pref_dispose (GObject *gobject) {
|
||||
TfePref *pref = TFE_PREF (gobject);
|
||||
|
||||
g_clear_object (&pref->settings);
|
||||
G_OBJECT_CLASS (tfe_pref_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_pref_init (TfePref *pref) {
|
||||
gtk_widget_init_template (GTK_WIDGET (pref));
|
||||
pref->settings = g_settings_new ("com.github.ToshioCP.tfe");
|
||||
g_settings_bind (pref->settings, "font", pref->fontbtn, "font", G_SETTINGS_BIND_DEFAULT);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_pref_class_init (TfePrefClass *class) {
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->dispose = tfe_pref_dispose;
|
||||
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfepref.ui");
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfePref, fontbtn);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
tfe_pref_new (GtkWindow *win) {
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_PREF, "transient-for", win, NULL));
|
||||
}
|
||||
|
13
src/tfe7/tfepref.h
Normal file
13
src/tfe7/tfepref.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef __TFE_PREF_H__
|
||||
#define __TFE_PREF_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define TFE_TYPE_PREF tfe_pref_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (TfePref, tfe_pref, TFE, PREF, GtkDialog)
|
||||
|
||||
GtkWidget *
|
||||
tfe_pref_new (GtkWindow *win);
|
||||
|
||||
#endif /* __TFE_PREF_H__ */
|
||||
|
33
src/tfe7/tfepref.ui
Normal file
33
src/tfe7/tfepref.ui
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="TfePref" parent="GtkDialog">
|
||||
<property name="title">Preferences</property>
|
||||
<property name="resizable">FALSE</property>
|
||||
<property name="modal">TRUE</property>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="content_area">
|
||||
<child>
|
||||
<object class="GtkBox" id="pref_boxh">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="fontlabel">
|
||||
<property name="label">Font:</property>
|
||||
<property name="xalign">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFontButton" id="fontbtn">
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
213
src/tfe7/tfewindow.c
Normal file
213
src/tfe7/tfewindow.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
#include "tfewindow.h"
|
||||
#include "tfenotebook.h"
|
||||
#include "tfepref.h"
|
||||
#include "tfealert.h"
|
||||
#include "css.h"
|
||||
|
||||
struct _TfeWindow {
|
||||
GtkApplicationWindow parent;
|
||||
GtkButton *btno;
|
||||
GtkButton *btns;
|
||||
GtkButton *btnc;
|
||||
GtkMenuButton *btnm;
|
||||
GtkNotebook *nb;
|
||||
GSettings *settings;
|
||||
gboolean is_quit;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TfeWindow, tfe_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||
|
||||
/* alert response signal handler */
|
||||
static void
|
||||
alert_response_cb (GtkDialog *alert, int response_id, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
if (response_id == GTK_RESPONSE_ACCEPT) {
|
||||
if (win->is_quit)
|
||||
gtk_window_destroy(GTK_WINDOW (win));
|
||||
else
|
||||
notebook_page_close (win->nb);
|
||||
}
|
||||
gtk_window_destroy (GTK_WINDOW (alert));
|
||||
}
|
||||
|
||||
/* ----- button handlers ----- */
|
||||
void
|
||||
open_cb (GtkNotebook *nb) {
|
||||
notebook_page_open (nb);
|
||||
}
|
||||
|
||||
void
|
||||
save_cb (GtkNotebook *nb) {
|
||||
notebook_page_save (nb);
|
||||
}
|
||||
|
||||
void
|
||||
close_cb (GtkNotebook *nb) {
|
||||
TfeAlert *alert;
|
||||
TfeWindow *win = TFE_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), TFE_TYPE_WINDOW));
|
||||
|
||||
if (has_saved (nb))
|
||||
notebook_page_close (nb);
|
||||
else {
|
||||
win->is_quit = false;
|
||||
alert = TFE_ALERT (tfe_alert_new (GTK_WINDOW (win)));
|
||||
tfe_alert_set_message (alert, "Contents aren't saved yet.\nAre you sure to close?");
|
||||
tfe_alert_set_button_label (alert, "Close");
|
||||
g_signal_connect (GTK_DIALOG (alert), "response", G_CALLBACK (alert_response_cb), win);
|
||||
gtk_widget_show (GTK_WIDGET (alert));
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- action activated handlers ----- */
|
||||
static void
|
||||
open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
open_cb (GTK_NOTEBOOK (win->nb));
|
||||
}
|
||||
|
||||
static void
|
||||
save_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
save_cb (GTK_NOTEBOOK (win->nb));
|
||||
}
|
||||
|
||||
static void
|
||||
close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
close_cb (GTK_NOTEBOOK (win->nb));
|
||||
}
|
||||
|
||||
static void
|
||||
new_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
notebook_page_new (GTK_NOTEBOOK (win->nb));
|
||||
}
|
||||
|
||||
static void
|
||||
saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
notebook_page_saveas (GTK_NOTEBOOK (win->nb));
|
||||
}
|
||||
|
||||
static void
|
||||
pref_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
GtkWidget *pref;
|
||||
|
||||
pref = tfe_pref_new (GTK_WINDOW (win));
|
||||
gtk_widget_show (pref);
|
||||
}
|
||||
|
||||
static void
|
||||
quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
TfeWindow *win = TFE_WINDOW (user_data);
|
||||
|
||||
TfeAlert *alert;
|
||||
|
||||
if (has_saved_all (GTK_NOTEBOOK (win->nb)))
|
||||
gtk_window_destroy (GTK_WINDOW (win));
|
||||
else {
|
||||
win->is_quit = true;
|
||||
alert = TFE_ALERT (tfe_alert_new (GTK_WINDOW (win)));
|
||||
tfe_alert_set_message (alert, "Contents aren't saved yet.\nAre you sure to quit?");
|
||||
tfe_alert_set_button_label (alert, "Quit");
|
||||
g_signal_connect (GTK_DIALOG (alert), "response", G_CALLBACK (alert_response_cb), win);
|
||||
gtk_widget_show (GTK_WIDGET (alert));
|
||||
}
|
||||
}
|
||||
|
||||
/* gsettings changed::font signal handler */
|
||||
static void
|
||||
changed_font_cb (GSettings *settings, char *key, gpointer user_data) {
|
||||
GtkWindow *win = GTK_WINDOW (user_data);
|
||||
const char *font;
|
||||
PangoFontDescription *pango_font_desc;
|
||||
|
||||
font = g_settings_get_string (settings, "font");
|
||||
pango_font_desc = pango_font_description_from_string (font);
|
||||
set_font_for_display_with_pango_font_desc (win, pango_font_desc);
|
||||
}
|
||||
|
||||
/* --- public functions --- */
|
||||
|
||||
void
|
||||
tfe_window_notebook_page_new (TfeWindow *win) {
|
||||
notebook_page_new (win->nb);
|
||||
}
|
||||
|
||||
void
|
||||
tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, int n_files) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_files; i++)
|
||||
notebook_page_new_with_file (win->nb, files[i]);
|
||||
if (gtk_notebook_get_n_pages (win->nb) == 0)
|
||||
notebook_page_new (win->nb);
|
||||
}
|
||||
|
||||
/* --- TfeWindow object construction/destruction --- */
|
||||
static void
|
||||
tfe_window_dispose (GObject *gobject) {
|
||||
TfeWindow *window = TFE_WINDOW (gobject);
|
||||
|
||||
g_clear_object (&window->settings);
|
||||
G_OBJECT_CLASS (tfe_window_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_window_init (TfeWindow *win) {
|
||||
GtkBuilder *build;
|
||||
GMenuModel *menu;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (win));
|
||||
|
||||
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/menu.ui");
|
||||
menu = G_MENU_MODEL (gtk_builder_get_object (build, "menu"));
|
||||
gtk_menu_button_set_menu_model (win->btnm, menu);
|
||||
g_object_unref(build);
|
||||
|
||||
win->settings = g_settings_new ("com.github.ToshioCP.tfe");
|
||||
g_signal_connect (win->settings, "changed::font", G_CALLBACK (changed_font_cb), win);
|
||||
|
||||
/* ----- action ----- */
|
||||
const GActionEntry win_entries[] = {
|
||||
{ "open", open_activated, NULL, NULL, NULL },
|
||||
{ "save", save_activated, NULL, NULL, NULL },
|
||||
{ "close", close_activated, NULL, NULL, NULL },
|
||||
{ "new", new_activated, NULL, NULL, NULL },
|
||||
{ "saveas", saveas_activated, NULL, NULL, NULL },
|
||||
{ "pref", pref_activated, NULL, NULL, NULL },
|
||||
{ "close-all", quit_activated, NULL, NULL, NULL }
|
||||
};
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
|
||||
|
||||
changed_font_cb(win->settings, "font", win);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_window_class_init (TfeWindowClass *class) {
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->dispose = tfe_window_dispose;
|
||||
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfewindow.ui");
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btno);
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btns);
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btnc);
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btnm);
|
||||
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, nb);
|
||||
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), open_cb);
|
||||
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), save_cb);
|
||||
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), close_cb);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
tfe_window_new (GtkApplication *app) {
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_WINDOW, "application", app, NULL));
|
||||
}
|
||||
|
19
src/tfe7/tfewindow.h
Normal file
19
src/tfe7/tfewindow.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef __TFE_WINDOW_H__
|
||||
#define __TFE_WINDOW_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define TFE_TYPE_WINDOW tfe_window_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (TfeWindow, tfe_window, TFE, WINDOW, GtkApplicationWindow)
|
||||
|
||||
void
|
||||
tfe_window_notebook_page_new (TfeWindow *win);
|
||||
|
||||
void
|
||||
tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, int n_files);
|
||||
|
||||
GtkWidget *
|
||||
tfe_window_new (GtkApplication *app);
|
||||
|
||||
#endif /* __TFE_WINDOW_H__ */
|
||||
|
65
src/tfe7/tfewindow.ui
Normal file
65
src/tfe7/tfewindow.ui
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="TfeWindow" parent="GtkApplicationWindow">
|
||||
<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>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxh">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy1">
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btno">
|
||||
<property name="label">Open</property>
|
||||
<signal name="clicked" handler="open_cb" swapped="TRUE" object="nb"></signal>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btns">
|
||||
<property name="label">Save</property>
|
||||
<signal name="clicked" handler="save_cb" swapped="TRUE" object="nb"></signal>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy2">
|
||||
<property name="hexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnc">
|
||||
<property name="label">Close</property>
|
||||
<signal name="clicked" handler="close_cb" swapped="TRUE" object="nb"></signal>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="btnm">
|
||||
<property name="direction">down</property>
|
||||
<property name="halign">start</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy3">
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="nb">
|
||||
<property name="scrollable">TRUE</property>
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="vexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
Loading…
Reference in a new issue