mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
Add section 20.
This commit is contained in:
parent
7f1228cb26
commit
26fc01e64e
22 changed files with 1003 additions and 11 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -15,6 +15,7 @@ src/tfe/resources.c
|
||||||
src/tfe5/_build
|
src/tfe5/_build
|
||||||
src/tfe5/hello.txt
|
src/tfe5/hello.txt
|
||||||
src/menu/a.out
|
src/menu/a.out
|
||||||
|
src/color/_build
|
||||||
|
|
||||||
html/*
|
html/*
|
||||||
latex/*
|
latex/*
|
||||||
|
|
|
@ -32,3 +32,4 @@ You can read it without download.
|
||||||
1. [Stateful action](gfm/sec17.md)
|
1. [Stateful action](gfm/sec17.md)
|
||||||
1. [Ui file for menu and action entries](gfm/sec18.md)
|
1. [Ui file for menu and action entries](gfm/sec18.md)
|
||||||
1. [GtkDrawingArea and Cairo](gfm/sec19.md)
|
1. [GtkDrawingArea and Cairo](gfm/sec19.md)
|
||||||
|
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec20.md)
|
||||||
|
|
|
@ -5,7 +5,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 9](sec9.md), Next: [Section 11](s
|
||||||
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
||||||
It is tfe5.
|
It is tfe5.
|
||||||
It has many changes from the prior version.
|
It has many changes from the prior version.
|
||||||
All the sources are listed after the five sections.
|
All the sources are listed in [Section 15](sec15.md).
|
||||||
|
|
||||||
## Encapsulation
|
## Encapsulation
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ We can draw shapes and images with different colors on surfaces.
|
||||||
- cairo context manages the transference from source to destination through mask with its functions.
|
- 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.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||||
- transformation is applied before the transfer completes.
|
- transformation is applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematicsterminology, and represented by matrix multiplication and vector addition.
|
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.
|
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||||
In this section, we don't use it.
|
In this section, we don't use it.
|
||||||
That means we only use identity transformation.
|
That means we only use identity transformation.
|
||||||
|
|
372
gfm/sec20.md
Normal file
372
gfm/sec20.md
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
Up: [Readme.md](../Readme.md), Prev: [Section 19](sec19.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.
|
||||||
|
|
||||||
|
1 <interface>
|
||||||
|
2 <object class="GtkApplicationWindow" id="win">
|
||||||
|
3 <property name="title">color changer</property>
|
||||||
|
4 <property name="default-width">600</property>
|
||||||
|
5 <property name="default-height">400</property>
|
||||||
|
6 <child>
|
||||||
|
7 <object class="GtkBox" id="boxv">
|
||||||
|
8 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||||
|
9 <child>
|
||||||
|
10 <object class="GtkBox" id="boxh1">
|
||||||
|
11 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||||
|
12 <child>
|
||||||
|
13 <object class="GtkLabel" id="dmy1">
|
||||||
|
14 <property name="width-chars">10</property>
|
||||||
|
15 </object>
|
||||||
|
16 </child>
|
||||||
|
17 <child>
|
||||||
|
18 <object class="GtkButton" id="btnr">
|
||||||
|
19 <property name="label">Run</property>
|
||||||
|
20 <signal name="clicked" handler="run_cb"></signal>
|
||||||
|
21
|
||||||
|
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>
|
||||||
|
81
|
||||||
|
|
||||||
|
- 9-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.
|
||||||
|
Signal element are 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: Put 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".
|
||||||
|
|
||||||
|
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 almost same as before.
|
||||||
|
The only difference is the header file in tfettextview.c.
|
||||||
|
|
||||||
|
$ diff tfe5/tfetextview.c color/tfetextview.c
|
||||||
|
1c1
|
||||||
|
< #include "tfe.h"
|
||||||
|
---
|
||||||
|
> #include "color.h"
|
||||||
|
|
||||||
|
Color.h just includes tfetextview.h.
|
||||||
|
|
||||||
|
1 #include <gtk/gtk.h>
|
||||||
|
2
|
||||||
|
3 #include "tfetextview.h"
|
||||||
|
|
||||||
|
# Colorapplication.c
|
||||||
|
|
||||||
|
This is the main file.
|
||||||
|
It deals with:
|
||||||
|
|
||||||
|
- Build widgets by GtkBuilder.
|
||||||
|
- Set drawing function to GtkDrawingArea.
|
||||||
|
And connect a handler to "resize" signal on GtkDrawingArea.
|
||||||
|
- Implement each call back functions.
|
||||||
|
Particularly, `Run` signal handler is the point in this program.
|
||||||
|
|
||||||
|
The following is `colorapplication.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: Build 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: connect "resize" signal and the handler.
|
||||||
|
- 98: set the drawing function.
|
||||||
|
- 81-84: Activate handler, which just show the widgets.
|
||||||
|
- 73-79: The drawing function.
|
||||||
|
It just copy `surface` to destination.
|
||||||
|
- 65-71: Resize handler.
|
||||||
|
Re-create the surface to fit the width and height of the drawing area and paint by calling the function `run`.
|
||||||
|
- 58-63: Close 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 paint the surface.
|
||||||
|
First, it gets the contents of GtkTextBuffer.
|
||||||
|
Then compare 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.
|
||||||
|
|
||||||
|
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.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 19](sec19.md)
|
|
@ -127,8 +127,8 @@ The function `g_signal_connect` has four arguments.
|
||||||
4. Data to pass to the handler. If no data is necessary, NULL should be given.
|
4. Data to pass to the handler. If no data is necessary, NULL should be given.
|
||||||
|
|
||||||
You can find the description of each signal in API reference.
|
You can find the description of each signal in API reference.
|
||||||
For example, "activate" signal is in GApplication subsection in GIO API reference.
|
For example, "activate" signal is in GApplication section in GIO API reference.
|
||||||
The handler function is described in that subsection.
|
The handler function is described in that section.
|
||||||
|
|
||||||
In addition, `g_signal_connect` is described in GObject API reference.
|
In addition, `g_signal_connect` is described in GObject API reference.
|
||||||
API reference is very important.
|
API reference is very important.
|
||||||
|
|
|
@ -49,7 +49,7 @@ For example, TfeTextView has GtkTextbuffer correspods to GtkTextView inside TfeT
|
||||||
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
||||||
|
|
||||||
However, to understand the general theory about gobjects is very hard especially for beginners.
|
However, to understand the general theory about gobjects is very hard especially for beginners.
|
||||||
So, I will just show you the way how to write the code and avoid the theoretical side in the next section.
|
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
||||||
|
|
||||||
## How to define a child widget of GtkTextView
|
## How to define a child widget of GtkTextView
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 65 KiB |
BIN
image/color.png
Normal file
BIN
image/color.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -142,7 +142,7 @@ def change_rel_link line, src_dir, basedir
|
||||||
p_basedir = Pathname.new basedir
|
p_basedir = Pathname.new basedir
|
||||||
left = ""
|
left = ""
|
||||||
right = line
|
right = line
|
||||||
while right =~ /(!?\[[^\]]*\])\((.*)\)/
|
while right =~ /(!?\[[^\]]*\])\(([^\)]*)\)/
|
||||||
left = $`
|
left = $`
|
||||||
right = $'
|
right = $'
|
||||||
name = $1
|
name = $1
|
||||||
|
|
6
src/color/color.gresource.xml
Normal file
6
src/color/color.gresource.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/com/github/ToshioCP/color">
|
||||||
|
<file>color.ui</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
3
src/color/color.h
Normal file
3
src/color/color.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "tfetextview.h"
|
81
src/color/color.ui
Normal file
81
src/color/color.ui
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<interface>
|
||||||
|
<object class="GtkApplicationWindow" id="win">
|
||||||
|
<property name="title">color changer</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="boxh1">
|
||||||
|
<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="btnr">
|
||||||
|
<property name="label">Run</property>
|
||||||
|
<signal name="clicked" handler="run_cb"></signal>
|
||||||
|
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="btno">
|
||||||
|
<property name="label">Open</property>
|
||||||
|
<signal name="clicked" handler="open_cb"></signal>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="dmy2">
|
||||||
|
<property name="hexpand">TRUE</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="btns">
|
||||||
|
<property name="label">Save</property>
|
||||||
|
<signal name="clicked" handler="save_cb"></signal>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="btnc">
|
||||||
|
<property name="label">Close</property>
|
||||||
|
<signal name="clicked" handler="close_cb"></signal>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="dmy3">
|
||||||
|
<property name="width-chars">10</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="boxh2">
|
||||||
|
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||||
|
<property name="homogeneous">TRUE</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="scr">
|
||||||
|
<property name="hexpand">TRUE</property>
|
||||||
|
<property name="vexpand">TRUE</property>
|
||||||
|
<child>
|
||||||
|
<object class="TfeTextView" id="tv">
|
||||||
|
<property name="wrap-mode">GTK_WRAP_WORD_CHAR</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkDrawingArea" id="da">
|
||||||
|
<property name="hexpand">TRUE</property>
|
||||||
|
<property name="vexpand">TRUE</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
|
|
122
src/color/colorapplication.c
Normal file
122
src/color/colorapplication.c
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
|
static GtkWidget *win;
|
||||||
|
static GtkWidget *tv;
|
||||||
|
static GtkWidget *da;
|
||||||
|
|
||||||
|
static cairo_surface_t *surface = NULL;
|
||||||
|
|
||||||
|
static void
|
||||||
|
run (void) {
|
||||||
|
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
GtkTextIter start_iter;
|
||||||
|
GtkTextIter end_iter;
|
||||||
|
char *contents;
|
||||||
|
cairo_t *cr;
|
||||||
|
|
||||||
|
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||||
|
contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||||
|
if (surface) {
|
||||||
|
cr = cairo_create (surface);
|
||||||
|
if (g_strcmp0 ("red", contents) == 0)
|
||||||
|
cairo_set_source_rgb (cr, 1, 0, 0);
|
||||||
|
else if (g_strcmp0 ("green", contents) == 0)
|
||||||
|
cairo_set_source_rgb (cr, 0, 1, 0);
|
||||||
|
else if (g_strcmp0 ("blue", contents) == 0)
|
||||||
|
cairo_set_source_rgb (cr, 0, 0, 1);
|
||||||
|
else if (g_strcmp0 ("white", contents) == 0)
|
||||||
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
||||||
|
else if (g_strcmp0 ("black", contents) == 0)
|
||||||
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
||||||
|
else if (g_strcmp0 ("light", contents) == 0)
|
||||||
|
cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
|
||||||
|
else if (g_strcmp0 ("dark", contents) == 0)
|
||||||
|
cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
|
||||||
|
else
|
||||||
|
cairo_set_source_surface (cr, surface, 0, 0);
|
||||||
|
cairo_paint (cr);
|
||||||
|
cairo_destroy (cr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_cb (GtkWidget *btnr) {
|
||||||
|
run ();
|
||||||
|
gtk_widget_queue_draw (GTK_WIDGET (da));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
open_cb (GtkWidget *btno) {
|
||||||
|
tfe_text_view_open (TFE_TEXT_VIEW (tv), win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
save_cb (GtkWidget *btns) {
|
||||||
|
tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
close_cb (GtkWidget *btnc) {
|
||||||
|
if (surface)
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
gtk_window_destroy (GTK_WINDOW (win));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
|
||||||
|
if (surface)
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||||
|
run ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||||
|
if (surface) {
|
||||||
|
cairo_set_source_surface (cr, surface, 0, 0);
|
||||||
|
cairo_paint (cr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate (GApplication *application) {
|
||||||
|
gtk_widget_show (win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
startup (GApplication *application) {
|
||||||
|
GtkApplication *app = GTK_APPLICATION (application);
|
||||||
|
GtkBuilder *build;
|
||||||
|
|
||||||
|
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
|
||||||
|
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||||
|
gtk_window_set_application (GTK_WINDOW (win), app);
|
||||||
|
tv = GTK_WIDGET (gtk_builder_get_object (build, "tv"));
|
||||||
|
da = GTK_WIDGET (gtk_builder_get_object (build, "da"));
|
||||||
|
g_object_unref(build);
|
||||||
|
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
|
||||||
|
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
|
||||||
|
|
||||||
|
GdkDisplay *display;
|
||||||
|
|
||||||
|
display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||||
|
GtkCssProvider *provider = gtk_css_provider_new ();
|
||||||
|
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
|
||||||
|
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv) {
|
||||||
|
GtkApplication *app;
|
||||||
|
int stat;
|
||||||
|
|
||||||
|
app = gtk_application_new ("com.github.ToshioCP.color", G_APPLICATION_FLAGS_NONE);
|
||||||
|
|
||||||
|
g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
|
||||||
|
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
|
||||||
|
|
||||||
|
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||||
|
g_object_unref (app);
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
10
src/color/meson.build
Normal file
10
src/color/meson.build
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
project('color', 'c')
|
||||||
|
|
||||||
|
gtkdep = dependency('gtk4')
|
||||||
|
|
||||||
|
gnome=import('gnome')
|
||||||
|
resources = gnome.compile_resources('resources','color.gresource.xml')
|
||||||
|
|
||||||
|
sourcefiles=files('colorapplication.c', 'tfetextview.c')
|
||||||
|
|
||||||
|
executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
218
src/color/tfetextview.c
Normal file
218
src/color/tfetextview.c
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
|
struct _TfeTextView
|
||||||
|
{
|
||||||
|
GtkTextView parent;
|
||||||
|
GFile *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CHANGE_FILE,
|
||||||
|
OPEN_RESPONSE,
|
||||||
|
NUMBER_OF_SIGNALS
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
||||||
|
|
||||||
|
static void
|
||||||
|
tfe_text_view_dispose (GObject *gobject) {
|
||||||
|
TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
||||||
|
|
||||||
|
if (G_IS_FILE (tv->file))
|
||||||
|
g_clear_object (&tv->file);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tfe_text_view_init (TfeTextView *tv) {
|
||||||
|
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
|
||||||
|
tv->file = NULL;
|
||||||
|
gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
|
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tfe_text_view_class_init (TfeTextViewClass *class) {
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||||
|
|
||||||
|
object_class->dispose = tfe_text_view_dispose;
|
||||||
|
tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
|
||||||
|
G_TYPE_FROM_CLASS (class),
|
||||||
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
|
NULL /* closure */,
|
||||||
|
NULL /* accumulator */,
|
||||||
|
NULL /* accumulator data */,
|
||||||
|
NULL /* C marshaller */,
|
||||||
|
G_TYPE_NONE /* return_type */,
|
||||||
|
0 /* n_params */,
|
||||||
|
NULL /* param_types */);
|
||||||
|
GType param_types[] = {G_TYPE_INT};
|
||||||
|
tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
|
||||||
|
G_TYPE_FROM_CLASS (class),
|
||||||
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
|
NULL /* closure */,
|
||||||
|
NULL /* accumulator */,
|
||||||
|
NULL /* accumulator data */,
|
||||||
|
NULL /* C marshaller */,
|
||||||
|
G_TYPE_NONE /* return_type */,
|
||||||
|
1 /* n_params */,
|
||||||
|
param_types);
|
||||||
|
}
|
||||||
|
|
||||||
|
GFile *
|
||||||
|
tfe_text_view_get_file (TfeTextView *tv) {
|
||||||
|
g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
||||||
|
|
||||||
|
return g_file_dup (tv->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||||
|
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
GFile *file;
|
||||||
|
char *contents;
|
||||||
|
gsize length;
|
||||||
|
GtkWidget *message_dialog;
|
||||||
|
GError *err = NULL;
|
||||||
|
|
||||||
|
if (response != GTK_RESPONSE_ACCEPT)
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
|
||||||
|
else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||||
|
else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
|
||||||
|
if (G_IS_FILE (file))
|
||||||
|
g_object_unref (file);
|
||||||
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
||||||
|
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
|
"%s.\n", err->message);
|
||||||
|
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
|
gtk_widget_show (message_dialog);
|
||||||
|
g_error_free (err);
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||||
|
} else {
|
||||||
|
gtk_text_buffer_set_text (tb, contents, length);
|
||||||
|
g_free (contents);
|
||||||
|
if (G_IS_FILE (tv->file))
|
||||||
|
g_object_unref (tv->file);
|
||||||
|
tv->file = file;
|
||||||
|
gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||||
|
}
|
||||||
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
||||||
|
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
|
g_return_if_fail (GTK_IS_WINDOW (win));
|
||||||
|
|
||||||
|
GtkWidget *dialog;
|
||||||
|
|
||||||
|
dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
"Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"Open", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
||||||
|
gtk_widget_show (dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||||
|
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
|
if (G_IS_FILE(file)) {
|
||||||
|
tv->file = file;
|
||||||
|
gtk_text_buffer_set_modified (tb, TRUE);
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
|
tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_save (TfeTextView *tv) {
|
||||||
|
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
|
|
||||||
|
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
GtkTextIter start_iter;
|
||||||
|
GtkTextIter end_iter;
|
||||||
|
gchar *contents;
|
||||||
|
GtkWidget *message_dialog;
|
||||||
|
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
|
GError *err = NULL;
|
||||||
|
|
||||||
|
if (! gtk_text_buffer_get_modified (tb))
|
||||||
|
return; /* no necessary to save it */
|
||||||
|
else if (tv->file == NULL)
|
||||||
|
tfe_text_view_saveas (tv);
|
||||||
|
else {
|
||||||
|
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||||
|
contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||||
|
if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
||||||
|
gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
|
else {
|
||||||
|
/* It is possible that tv->file is broken. */
|
||||||
|
/* It is a good idea to set tv->file to NULL. */
|
||||||
|
if (G_IS_FILE (tv->file))
|
||||||
|
g_object_unref (tv->file);
|
||||||
|
tv->file =NULL;
|
||||||
|
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
|
gtk_text_buffer_set_modified (tb, TRUE);
|
||||||
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
||||||
|
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
|
"%s.\n", err->message);
|
||||||
|
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
|
gtk_widget_show (message_dialog);
|
||||||
|
g_error_free (err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_saveas (TfeTextView *tv) {
|
||||||
|
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
|
|
||||||
|
GtkWidget *dialog;
|
||||||
|
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
|
|
||||||
|
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"_Save", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
||||||
|
gtk_widget_show (dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
tfe_text_view_new_with_file (GFile *file) {
|
||||||
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||||
|
|
||||||
|
GtkWidget *tv;
|
||||||
|
GtkTextBuffer *tb;
|
||||||
|
char *contents;
|
||||||
|
gsize length;
|
||||||
|
|
||||||
|
if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tv = tfe_text_view_new();
|
||||||
|
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
|
gtk_text_buffer_set_text (tb, contents, length);
|
||||||
|
g_free (contents);
|
||||||
|
TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
|
||||||
|
return tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
tfe_text_view_new (void) {
|
||||||
|
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||||
|
}
|
||||||
|
|
29
src/color/tfetextview.h
Normal file
29
src/color/tfetextview.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||||
|
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||||
|
|
||||||
|
/* "open-response" signal response */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TFE_OPEN_RESPONSE_SUCCESS,
|
||||||
|
TFE_OPEN_RESPONSE_CANCEL,
|
||||||
|
TFE_OPEN_RESPONSE_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
GFile *
|
||||||
|
tfe_text_view_get_file (TfeTextView *tv);
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_save (TfeTextView *tv);
|
||||||
|
|
||||||
|
void
|
||||||
|
tfe_text_view_saveas (TfeTextView *tv);
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
tfe_text_view_new_with_file (GFile *file);
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
tfe_text_view_new (void);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
||||||
It is tfe5.
|
It is tfe5.
|
||||||
It has many changes from the prior version.
|
It has many changes from the prior version.
|
||||||
All the sources are listed after the five sections.
|
All the sources are listed in [Section 15](sec15.src.md).
|
||||||
|
|
||||||
## Encapsulation
|
## Encapsulation
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ We can draw shapes and images with different colors on surfaces.
|
||||||
- cairo context manages the transference from source to destination through mask with its functions.
|
- 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.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||||
- transformation is applied before the transfer completes.
|
- transformation is applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematicsterminology, and represented by matrix multiplication and vector addition.
|
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.
|
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||||
In this section, we don't use it.
|
In this section, we don't use it.
|
||||||
That means we only use identity transformation.
|
That means we only use identity transformation.
|
||||||
|
|
149
src/sec20.src.md
Normal file
149
src/sec20.src.md
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
@@@ color/color.ui
|
||||||
|
|
||||||
|
- 9-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.
|
||||||
|
Signal element are 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: Put 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".
|
||||||
|
|
||||||
|
@@@ color/color.gresource.xml
|
||||||
|
|
||||||
|
# Tfetextview.h, tfetextview.c and color.h
|
||||||
|
|
||||||
|
First two files are almost same as before.
|
||||||
|
The only difference is the header file in tfettextview.c.
|
||||||
|
|
||||||
|
$$$
|
||||||
|
diff tfe5/tfetextview.c color/tfetextview.c
|
||||||
|
$$$
|
||||||
|
|
||||||
|
Color.h just includes tfetextview.h.
|
||||||
|
|
||||||
|
@@@ color/color.h
|
||||||
|
|
||||||
|
# Colorapplication.c
|
||||||
|
|
||||||
|
This is the main file.
|
||||||
|
It deals with:
|
||||||
|
|
||||||
|
- Build widgets by GtkBuilder.
|
||||||
|
- Set drawing function to GtkDrawingArea.
|
||||||
|
And connect a handler to "resize" signal on GtkDrawingArea.
|
||||||
|
- Implement each call back functions.
|
||||||
|
Particularly, `Run` signal handler is the point in this program.
|
||||||
|
|
||||||
|
The following is `colorapplication.c`.
|
||||||
|
|
||||||
|
@@@ 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: Build 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: connect "resize" signal and the handler.
|
||||||
|
- 98: set the drawing function.
|
||||||
|
- 81-84: Activate handler, which just show the widgets.
|
||||||
|
- 73-79: The drawing function.
|
||||||
|
It just copy `surface` to destination.
|
||||||
|
- 65-71: Resize handler.
|
||||||
|
Re-create the surface to fit the width and height of the drawing area and paint by calling the function `run`.
|
||||||
|
- 58-63: Close 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 paint the surface.
|
||||||
|
First, it gets the contents of GtkTextBuffer.
|
||||||
|
Then compare 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.
|
||||||
|
|
||||||
|
@@@ 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.
|
|
@ -95,8 +95,8 @@ The function `g_signal_connect` has four arguments.
|
||||||
4. Data to pass to the handler. If no data is necessary, NULL should be given.
|
4. Data to pass to the handler. If no data is necessary, NULL should be given.
|
||||||
|
|
||||||
You can find the description of each signal in API reference.
|
You can find the description of each signal in API reference.
|
||||||
For example, "activate" signal is in GApplication subsection in GIO API reference.
|
For example, "activate" signal is in GApplication section in GIO API reference.
|
||||||
The handler function is described in that subsection.
|
The handler function is described in that section.
|
||||||
|
|
||||||
In addition, `g_signal_connect` is described in GObject API reference.
|
In addition, `g_signal_connect` is described in GObject API reference.
|
||||||
API reference is very important.
|
API reference is very important.
|
||||||
|
|
|
@ -47,7 +47,7 @@ For example, TfeTextView has GtkTextbuffer correspods to GtkTextView inside TfeT
|
||||||
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
||||||
|
|
||||||
However, to understand the general theory about gobjects is very hard especially for beginners.
|
However, to understand the general theory about gobjects is very hard especially for beginners.
|
||||||
So, I will just show you the way how to write the code and avoid the theoretical side in the next section.
|
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
||||||
|
|
||||||
## How to define a child widget of GtkTextView
|
## How to define a child widget of GtkTextView
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue