Merge pull request #21 from PaulSchulz/main

Some more edits. Sec 9 and 22.
This commit is contained in:
ToshioCP 2022-03-07 17:57:02 +09:00 committed by GitHub
commit 978e5b1a34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 190 additions and 174 deletions

View file

@ -3,8 +3,8 @@
#### Contents of this Repository #### Contents of this Repository
This tutorial illustrates how to write C programs with the Gtk4 library. This tutorial illustrates how to write C programs with the Gtk4 library.
It focuses on beginners so the contents are limited to basic things. It focuses on beginners so the contents are limited to the basics.
The table of contents is shown at the end of this abstract. The table of contents is at the end of this abstract.
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor). - Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
- Section 22 to 24 describes GtkDrawingArea. - Section 22 to 24 describes GtkDrawingArea.
@ -56,7 +56,7 @@ There is a documentation \("[How to build Gtk4 Tutorial](gfm/Readme_for_develope
1. [String and memory management](gfm/sec6.md) 1. [String and memory management](gfm/sec6.md)
1. [Widgets (3)](gfm/sec7.md) 1. [Widgets (3)](gfm/sec7.md)
1. [Defining a Child object](gfm/sec8.md) 1. [Defining a Child object](gfm/sec8.md)
1. [Ui file and GtkBuilder](gfm/sec9.md) 1. [The User Interface (UI) file and GtkBuilder](gfm/sec9.md)
1. [Build system](gfm/sec10.md) 1. [Build system](gfm/sec10.md)
1. [Initialization and destruction of instances](gfm/sec11.md) 1. [Initialization and destruction of instances](gfm/sec11.md)
1. [Signals](gfm/sec12.md) 1. [Signals](gfm/sec12.md)

View file

@ -2,49 +2,54 @@ Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23]
# GtkDrawingArea and Cairo # GtkDrawingArea and Cairo
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget. If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
You can draw or redraw an image in this widget freely. You can freely draw or redraw an image in this widget.
It is called custom drawing. This is called custom drawing.
GtkDrawingArea provides a cairo context so users can draw images by cairo functions. GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain: In this section, I will explain:
1. Cairo, but briefly. 1. Cairo, but only briefly; and
2. GtkDrawingArea with very simple example. 2. GtkDrawingArea, with a very simple example.
## Cairo ## Cairo
Cairo is a two dimensional graphics library. Cairo is a set of two dimensional graphical drawing functions (or graphics library).
First, you need to know surface, source, mask, destination, cairo context and transformation. There is a lot of documentation on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/).
- Surface represents an image. The following is a gentle introduction on the Cairo library and how to use it.
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image.
It is like a canvas. It is like a canvas.
We can draw shapes and images with different colors on surfaces. 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. - The source pattern, or simply source, is like paint, which will be transferred to destination surface by cairo functions.
- Mask is image mask used in the transference. - The mask describes the area to be used in the copy;
- Destination is a target surface. - The destination is a target surface;
- Cairo context manages the transference from source to destination through mask with its functions. - The cairo context manages the transfer 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. For example, `cairo_stroke` is a function to draw a path to the destination by the transfer.
- Transformation is applied before the transfer completes. - A transformation can be applied before the transfer completes.
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
Scaling, rotation, reflection, shearing and translation are examples of affine transformation. that preserve straight lines.
In this section, we don't use it. Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
That means we only use identity transformation. They are mathematically represented by matrix multiplication and vector addition.
Therefore, the coordinate in source and mask is the same as the coordinate in destination. In this section we don't use it, instead we will only use the identity transformation.
This means that the coordinates in the source and mask are the same as the coordinates in destination.
![Stroke a rectangle](../image/cairo.png) ![Stroke a rectangle](../image/cairo.png)
The instruction is as follows: The instruction is as follows:
1. Create a surface. 1. Create a surface.
This will be a destination. This will be the destination.
2. Create a cairo context with the surface and the surface will be the destination of the context. 2. Create a cairo context with the surface, the surface will be the destination of the context.
3. Create a source pattern within the context. 3. Create a source pattern within the context.
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask. 4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination. 5. Use a 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. 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. Here's a simple example program that draws a small square and saves it as a png file.
~~~C ~~~C
1 #include <cairo.h> 1 #include <cairo.h>
@ -69,40 +74,43 @@ Here's a simple example code that draws a small square and save it as a png file
20 /* Draw a black rectangle */ 20 /* Draw a black rectangle */
21 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); 21 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
22 cairo_set_line_width (cr, 2.0); 22 cairo_set_line_width (cr, 2.0);
23 cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size); 23 cairo_rectangle (cr,
24 cairo_stroke (cr); 24 width/2.0 - square_size/2,
25 25 height/2.0 - square_size/2,
26 /* Write the surface to a png file and clean up cairo and surface. */ 26 square_size,
27 cairo_surface_write_to_png (surface, "rectangle.png"); 27 square_size);
28 cairo_destroy (cr); 28 cairo_stroke (cr);
29 cairo_surface_destroy (surface); 29
30 30 /* Write the surface to a png file and clean up cairo and surface. */
31 return 0; 31 cairo_surface_write_to_png (surface, "rectangle.png");
32 } 32 cairo_destroy (cr);
33 cairo_surface_destroy (surface);
34
35 return 0;
36 }
~~~ ~~~
- 1: Includes the header file of Cairo. - 1: Includes the header file of Cairo.
- 6: `cairo_surface_t` is the type of a surface. - 6: `cairo_surface_t` is the type of a surface.
- 7: `cairo_t` is the type of a cairo context. - 7: `cairo_t` is the type of a cairo context.
- 8-10: `width` and `height` is the size of `surface`. - 8-10: `width` and `height` are the size of `surface`.
`square_size` is the size of a square drawn on the surface. `square_size` is the size of a square to be drawn on the surface.
- 13: `cairo_image_surface_create` creates an image surface. - 13: `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. `CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data,
Each data has 8 bits quantity. and each data point is an 8 bits number (for 24 bits in total).
Therefore, 24 bits (3x8=24) is the size of RGB24.
Modern displays have this type of color depth. Modern displays have this type of color depth.
Width and height are pixels and given as integers. Width and height are in pixels and given as integers.
- 14: Creates cairo context. - 14: Creates cairo context.
The surface given as an argument will be the destination of the context. The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint. - 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth argument is red, green and blue color depth respectively. The second to fourth argument are red, green and blue color values respectively, and they are
Their type is float and the values are between zero and one. of type float. The values are between zero (0.0) and one (1.0), with
(0,0,0) is black and (1,1,1) is white. black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination. - 19: `cairo_paint` copies everywhere in the source to destination.
The destination is filled with white pixels with this command. The destination is filled with white pixels with this command.
- 21: Sets the source color to black. - 21: Sets the source color to black.
- 22: `cairo_set_line_width` set the width of lines. - 22: `cairo_set_line_width` set the width of lines.
In this case, the line width is set to two pixels. In this case, the line width is set to be two pixels and will end up that same size.
(It is because the transformation is identity. (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.) If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
- 23: Draws a rectangle (square) on the mask. - 23: Draws a rectangle (square) on the mask.
@ -118,8 +126,7 @@ To compile this, type the following.
![rectangle.png](../image/rectangle.png) ![rectangle.png](../image/rectangle.png)
There are lots of documentations in [Cairo's website](https://www.cairographics.org/). See the [Cairo's website](https://www.cairographics.org/) for more details.
If you aren't familiar with Cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
## GtkDrawingArea ## GtkDrawingArea
@ -132,46 +139,49 @@ The following is a very simple example.
4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { 4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
5 int square_size = 40.0; 5 int square_size = 40.0;
6 6
7 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */ 7 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
8 cairo_paint (cr); 8 cairo_paint (cr);
9 cairo_set_line_width (cr, 2.0); 9 cairo_set_line_width (cr, 2.0);
10 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ 10 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
11 cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size); 11 cairo_rectangle (cr,
12 cairo_stroke (cr); 12 width/2.0 - square_size/2,
13 } 13 height/2.0 - square_size/2,
14 14 square_size,
15 static void 15 square_size);
16 app_activate (GApplication *app, gpointer user_data) { 16 cairo_stroke (cr);
17 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); 17 }
18 GtkWidget *area = gtk_drawing_area_new (); 18
19 19 static void
20 gtk_window_set_title (GTK_WINDOW (win), "da1"); 20 app_activate (GApplication *app, gpointer user_data) {
21 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL); 21 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
22 gtk_window_set_child (GTK_WINDOW (win), area); 22 GtkWidget *area = gtk_drawing_area_new ();
23 23
24 gtk_widget_show (win); 24 gtk_window_set_title (GTK_WINDOW (win), "da1");
25 } 25 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
26 26 gtk_window_set_child (GTK_WINDOW (win), area);
27 #define APPLICATION_ID "com.github.ToshioCP.da1" 27
28 28 gtk_widget_show (win);
29 int 29 }
30 main (int argc, char **argv) { 30
31 GtkApplication *app; 31 #define APPLICATION_ID "com.github.ToshioCP.da1"
32 int stat; 32
33 33 int
34 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE); 34 main (int argc, char **argv) {
35 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 35 GtkApplication *app;
36 stat =g_application_run (G_APPLICATION (app), argc, argv); 36 int stat;
37 g_object_unref (app); 37
38 return stat; 38 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
39 } 39 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
40 40 stat =g_application_run (G_APPLICATION (app), argc, argv);
41 g_object_unref (app);
42 return stat;
43 }
~~~ ~~~
The function `main` is almost same as before. The function `main` is almost same as before.
The two functions `app_activate` and `draw_function` is important in this example. The two functions `app_activate` and `draw_function` are important in this example.
- 18: Creates a GtkDrawingArea instance. - 18: Creates a GtkDrawingArea instance; and
- 21: Sets a drawing function of the widget. - 21: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary. 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. For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
@ -191,7 +201,7 @@ 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. 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. 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. The third and fourth parameters are the size of the destination surface.
Now, look at the program of the example again. Now, look at the program example again.
- 3-13: The drawing function. - 3-13: The drawing function.
- 7-8: Sets the source to be white and paint the destination white. - 7-8: Sets the source to be white and paint the destination white.
@ -202,9 +212,8 @@ Now, look at the program of the example again.
Compile and run it, then a window with a black rectangle (square) appears. Compile and run it, then a window with a black rectangle (square) appears.
Try resizing the window. 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. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.
![Square in the window](../image/da1.png) ![Square in the window](../image/da1.png)
Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23](sec23.md) Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23](sec23.md)

View file

@ -1,13 +1,13 @@
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md) Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
# Ui file and GtkBuilder # The User Interface (UI) file and GtkBuilder
## New, open and save button ## New, Open and Save button
We made the simplest editor in the previous section. In the last section we made the almost simplest editor possible.
It reads the files in `app_open` function at start-up and writes them when closing the window. It reads files in the `app_open` function at start-up and writes them out when closing the window.
It works but is not good. It works but is not very good.
It is better to make "New", "Open", "Save" and "Close" buttons. It would be better if we had "New", "Open", "Save" and "Close" buttons.
This section describes how to put those buttons into the window. This section describes how to put those buttons into the window.
Signals and handlers will be explained later. Signals and handlers will be explained later.
@ -105,7 +105,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
86 } 86 }
~~~ ~~~
The point is how to build the window. The aim is to build the widgets of the main application window.
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size. - 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
- 29-30: Creates a GtkBox instance `boxv`. - 29-30: Creates a GtkBox instance `boxv`.
@ -124,18 +124,18 @@ This makes the label expands horizontally as long as possible.
This makes it expand horizontally and vertically as big as possible. This makes it expand horizontally and vertically as big as possible.
It is appended to `boxv` as the second child. It is appended to `boxv` as the second child.
The number of lines is 33(=57-25+1) to build the widgets. The number of lines to build the widgets is 33(=57-25+1).
And we needed many variables (`boxv`, `boxh`, `dmy1`, ...). We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
Most of them aren't necessary except building the widgets. most of which weren't necessary, except for building the widgets.
Are there any good solution to reduce these work? Are there any good solution to reduce these work?
Gtk provides GtkBuilder. Gtk provides GtkBuilder.
It reads ui data and builds a window. It reads user interface (UI) data and builds a window.
It reduces the cumbersome work. It reduces this cumbersome work.
## Ui file ## The UI File
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets. First, let's look at the UI file `tfe3.ui` that is used to define the widget structure.
~~~xml ~~~xml
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
@ -200,11 +200,11 @@ First, let's look at the ui file `tfe3.ui` that defines a structure of the widge
~~~ ~~~
The structure of this file is XML. The structure of this file is XML.
Constructs beginning with `<` and ending with `>` are called tags. Constructs that begin with `<` and end with `>` are called tags.
And there are two types of tags, start tag and end tag. There are two types of tags, the start tag and the end tag.
For example, `<interface>` is a start tag and `</interface>` is an end tag. For example, `<interface>` is a start tag and `</interface>` is an end tag.
Ui file begins and ends with interface tags. The UI file begins and ends with interface tags.
Some tags, for example, object tags can have a class and id attributes in the start tag. Some tags, for example object tags, can have a class and id attributes in their start tag.
- 1: The first line is XML declaration. - 1: The first line is XML declaration.
It specifies that the version of XML is 1.0 and the encoding is UTF-8. It specifies that the version of XML is 1.0 and the encoding is UTF-8.
@ -431,7 +431,7 @@ It describes resource files.
However, this xml has only one gresource. However, this xml has only one gresource.
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`. - 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
- 4: The gresource has `tfe3.ui`. - 4: The gresource has `tfe3.ui`.
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix. And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
If you want to add more files, then insert them between line 4 and 5. If you want to add more files, then insert them between line 4 and 5.
Save this xml text to `tfe3.gresource.xml`. Save this xml text to `tfe3.gresource.xml`.
@ -485,5 +485,4 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
Then, compile and run it. Then, compile and run it.
The window appears and it is the same as the screenshot at the beginning of this page. The window appears and it is the same as the screenshot at the beginning of this page.
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md) Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)

View file

@ -1,8 +1,8 @@
#### Contents of this Repository #### Contents of this Repository
This tutorial illustrates how to write C programs with the Gtk4 library. This tutorial illustrates how to write C programs with the Gtk4 library.
It focuses on beginners so the contents are limited to basic things. It focuses on beginners so the contents are limited to the basics.
The table of contents is shown at the end of this abstract. The table of contents is at the end of this abstract.
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor). - Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
- Section 22 to 24 describes GtkDrawingArea. - Section 22 to 24 describes GtkDrawingArea.

View file

@ -20,7 +20,11 @@ main (int argc, char **argv)
/* Draw a black rectangle */ /* Draw a black rectangle */
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_set_line_width (cr, 2.0); cairo_set_line_width (cr, 2.0);
cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size); cairo_rectangle (cr,
width/2.0 - square_size/2,
height/2.0 - square_size/2,
square_size,
square_size);
cairo_stroke (cr); cairo_stroke (cr);
/* Write the surface to a png file and clean up cairo and surface. */ /* Write the surface to a png file and clean up cairo and surface. */

View file

@ -4,11 +4,15 @@ static void
draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
int square_size = 40.0; int square_size = 40.0;
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
cairo_paint (cr); cairo_paint (cr);
cairo_set_line_width (cr, 2.0); cairo_set_line_width (cr, 2.0);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size); cairo_rectangle (cr,
width/2.0 - square_size/2,
height/2.0 - square_size/2,
square_size,
square_size);
cairo_stroke (cr); cairo_stroke (cr);
} }
@ -37,4 +41,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }

View file

@ -1,48 +1,53 @@
# GtkDrawingArea and Cairo # GtkDrawingArea and Cairo
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget. If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
You can draw or redraw an image in this widget freely. You can freely draw or redraw an image in this widget.
It is called custom drawing. This is called custom drawing.
GtkDrawingArea provides a cairo context so users can draw images by cairo functions. GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain: In this section, I will explain:
1. Cairo, but briefly. 1. Cairo, but only briefly; and
2. GtkDrawingArea with very simple example. 2. GtkDrawingArea, with a very simple example.
## Cairo ## Cairo
Cairo is a two dimensional graphics library. Cairo is a set of two dimensional graphical drawing functions (or graphics library).
First, you need to know surface, source, mask, destination, cairo context and transformation. There is a lot of documentation on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/).
- Surface represents an image. The following is a gentle introduction to the Cairo library and how to use it.
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image.
It is like a canvas. It is like a canvas.
We can draw shapes and images with different colors on surfaces. 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. - The source pattern, or simply source, is like paint, which will be transferred to destination surface by cairo functions.
- Mask is image mask used in the transference. - The mask describes the area to be used in the copy;
- Destination is a target surface. - The destination is a target surface;
- Cairo context manages the transference from source to destination through mask with its functions. - The cairo context manages the transfer 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. For example, `cairo_stroke` is a function to draw a path to the destination by the transfer.
- Transformation is applied before the transfer completes. - A transformation can be applied before the transfer completes.
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
Scaling, rotation, reflection, shearing and translation are examples of affine transformation. that preserve straight lines.
In this section, we don't use it. Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
That means we only use identity transformation. They are mathematically represented by matrix multiplication and vector addition.
Therefore, the coordinate in source and mask is the same as the coordinate in destination. In this section we don't use it, instead we will only use the identity transformation.
This means that the coordinates in the source and mask are the same as the coordinates in destination.
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm} ![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
The instruction is as follows: The instruction is as follows:
1. Create a surface. 1. Create a surface.
This will be a destination. This will be the destination.
2. Create a cairo context with the surface and the surface will be the destination of the context. 2. Create a cairo context with the surface, the surface will be the destination of the context.
3. Create a source pattern within the context. 3. Create a source pattern within the context.
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask. 4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination. 5. Use a 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. 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. Here's a simple example program that draws a small square and saves it as a png file.
@@@include @@@include
misc/cairo.c misc/cairo.c
@ -51,25 +56,24 @@ misc/cairo.c
- 1: Includes the header file of Cairo. - 1: Includes the header file of Cairo.
- 6: `cairo_surface_t` is the type of a surface. - 6: `cairo_surface_t` is the type of a surface.
- 7: `cairo_t` is the type of a cairo context. - 7: `cairo_t` is the type of a cairo context.
- 8-10: `width` and `height` is the size of `surface`. - 8-10: `width` and `height` are the size of `surface`.
`square_size` is the size of a square drawn on the surface. `square_size` is the size of a square to be drawn on the surface.
- 13: `cairo_image_surface_create` creates an image surface. - 13: `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. `CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data,
Each data has 8 bits quantity. and each data point is an 8 bits number (for 24 bits in total).
Therefore, 24 bits (3x8=24) is the size of RGB24.
Modern displays have this type of color depth. Modern displays have this type of color depth.
Width and height are pixels and given as integers. Width and height are in pixels and given as integers.
- 14: Creates cairo context. - 14: Creates cairo context.
The surface given as an argument will be the destination of the context. The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint. - 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth argument is red, green and blue color depth respectively. The second to fourth argument are red, green and blue color values respectively, and they are
Their type is float and the values are between zero and one. of type float. The values are between zero (0.0) and one (1.0), with
(0,0,0) is black and (1,1,1) is white. black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination. - 19: `cairo_paint` copies everywhere in the source to destination.
The destination is filled with white pixels with this command. The destination is filled with white pixels with this command.
- 21: Sets the source color to black. - 21: Sets the source color to black.
- 22: `cairo_set_line_width` set the width of lines. - 22: `cairo_set_line_width` set the width of lines.
In this case, the line width is set to two pixels. In this case, the line width is set to be two pixels and will end up that same size.
(It is because the transformation is identity. (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.) If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
- 23: Draws a rectangle (square) on the mask. - 23: Draws a rectangle (square) on the mask.
@ -85,8 +89,7 @@ To compile this, type the following.
![rectangle.png](../image/rectangle.png) ![rectangle.png](../image/rectangle.png)
There are lots of documentations in [Cairo's website](https://www.cairographics.org/). See the [Cairo's website](https://www.cairographics.org/) for more details.
If you aren't familiar with Cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
## GtkDrawingArea ## GtkDrawingArea
@ -97,9 +100,9 @@ misc/da1.c
@@@ @@@
The function `main` is almost same as before. The function `main` is almost same as before.
The two functions `app_activate` and `draw_function` is important in this example. The two functions `app_activate` and `draw_function` are important in this example.
- 18: Creates a GtkDrawingArea instance. - 18: Creates a GtkDrawingArea instance; and
- 21: Sets a drawing function of the widget. - 21: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary. 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. For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
@ -119,7 +122,7 @@ 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. 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. 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. The third and fourth parameters are the size of the destination surface.
Now, look at the program of the example again. Now, look at the program example again.
- 3-13: The drawing function. - 3-13: The drawing function.
- 7-8: Sets the source to be white and paint the destination white. - 7-8: Sets the source to be white and paint the destination white.
@ -130,7 +133,6 @@ Now, look at the program of the example again.
Compile and run it, then a window with a black rectangle (square) appears. Compile and run it, then a window with a black rectangle (square) appears.
Try resizing the window. 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. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.
![Square in the window](../image/da1.png){width=8cm height=3.4cm} ![Square in the window](../image/da1.png){width=8cm height=3.4cm}

View file

@ -20,7 +20,7 @@ So, readers can skip that part of this sections.
The documentation of turtle is [here](turtle/turtle_doc.src.md). The documentation of turtle is [here](turtle/turtle_doc.src.md).
@@@elif html @@@elif html
The documentation of turtle is [here](../html/turtle_doc.html). The documentation of turtle is [here](../html/turtle_doc.html).
@@@if latex @@@elif latex
The documentation of turtle is in the appendix. The documentation of turtle is in the appendix.
@@@end @@@end
I'll show you a simple example. I'll show you a simple example.

View file

@ -1,11 +1,11 @@
# Ui file and GtkBuilder # The User Interface (UI) file and GtkBuilder
## New, open and save button ## New, Open and Save button
We made the simplest editor in the previous section. In the last section we made the almost simplest editor possible.
It reads the files in `app_open` function at start-up and writes them when closing the window. It reads files in the `app_open` function at start-up and writes them out when closing the window.
It works but is not good. It works but is not very good.
It is better to make "New", "Open", "Save" and "Close" buttons. It would be better if we had "New", "Open", "Save" and "Close" buttons.
This section describes how to put those buttons into the window. This section describes how to put those buttons into the window.
Signals and handlers will be explained later. Signals and handlers will be explained later.
@ -18,7 +18,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
tfe/tfe2.c app_open tfe/tfe2.c app_open
@@@ @@@
The point is how to build the window. The aim is to build the widgets of the main application window.
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size. - 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
- 29-30: Creates a GtkBox instance `boxv`. - 29-30: Creates a GtkBox instance `boxv`.
@ -37,29 +37,29 @@ This makes the label expands horizontally as long as possible.
This makes it expand horizontally and vertically as big as possible. This makes it expand horizontally and vertically as big as possible.
It is appended to `boxv` as the second child. It is appended to `boxv` as the second child.
The number of lines is 33(=57-25+1) to build the widgets. The number of lines to build the widgets is 33(=57-25+1).
And we needed many variables (`boxv`, `boxh`, `dmy1`, ...). We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
Most of them aren't necessary except building the widgets. most of which weren't necessary, except for building the widgets.
Are there any good solution to reduce these work? Are there any good solution to reduce these work?
Gtk provides GtkBuilder. Gtk provides GtkBuilder.
It reads ui data and builds a window. It reads user interface (UI) data and builds a window.
It reduces the cumbersome work. It reduces this cumbersome work.
## Ui file ## The UI File
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets. First, let's look at the UI file `tfe3.ui` that is used to define the widget structure.
@@@include @@@include
tfe/tfe3.ui tfe/tfe3.ui
@@@ @@@
The structure of this file is XML. The structure of this file is XML.
Constructs beginning with `<` and ending with `>` are called tags. Constructs that begin with `<` and end with `>` are called tags.
And there are two types of tags, start tag and end tag. There are two types of tags, the start tag and the end tag.
For example, `<interface>` is a start tag and `</interface>` is an end tag. For example, `<interface>` is a start tag and `</interface>` is an end tag.
Ui file begins and ends with interface tags. The UI file begins and ends with interface tags.
Some tags, for example, object tags can have a class and id attributes in the start tag. Some tags, for example object tags, can have a class and id attributes in their start tag.
- 1: The first line is XML declaration. - 1: The first line is XML declaration.
It specifies that the version of XML is 1.0 and the encoding is UTF-8. It specifies that the version of XML is 1.0 and the encoding is UTF-8.
@ -177,7 +177,7 @@ tfe/tfe3.gresource.xml
However, this xml has only one gresource. However, this xml has only one gresource.
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`. - 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
- 4: The gresource has `tfe3.ui`. - 4: The gresource has `tfe3.ui`.
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix. And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
If you want to add more files, then insert them between line 4 and 5. If you want to add more files, then insert them between line 4 and 5.
Save this xml text to `tfe3.gresource.xml`. Save this xml text to `tfe3.gresource.xml`.
@ -205,4 +205,3 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
Then, compile and run it. Then, compile and run it.
The window appears and it is the same as the screenshot at the beginning of this page. The window appears and it is the same as the screenshot at the beginning of this page.