From 251f60910f19a71919696d5a36ae8b2938e1b3ab Mon Sep 17 00:00:00 2001 From: Toshio Sekiya Date: Sun, 23 Jul 2023 11:49:22 +0900 Subject: [PATCH] Update section 24 to 27 --- README.md | 2 +- docs/image/cd0.png | Bin 0 -> 8008 bytes docs/image/cd1.png | Bin 0 -> 8372 bytes docs/image/cd2.png | Bin 0 -> 7701 bytes docs/image/rect.png | Bin 0 -> 8765 bytes docs/index.html | 2 +- docs/sec24.html | 65 +-- docs/sec25.html | 4 +- docs/sec26.html | 729 ++++++++++++-------------- docs/sec27.html | 2 +- gfm/sec24.md | 48 +- gfm/sec25.md | 4 +- gfm/sec26.md | 704 +++++++++++-------------- gfm/sec27.md | 2 +- image/cd0.png | Bin 0 -> 8008 bytes image/cd1.png | Bin 0 -> 8372 bytes image/cd2.png | Bin 0 -> 7701 bytes image/rect.png | Bin 0 -> 8765 bytes src/custom_drawing/meson.build | 8 + src/custom_drawing/rect.c | 137 +++++ src/custom_drawing/rect.gresource.xml | 6 + src/custom_drawing/rect.ui | 16 + src/dnd/dnd.c | 86 +++ src/dnd/dnd.gresource.xml | 6 + src/dnd/dnd.ui | 54 ++ src/dnd/meson.build | 8 + src/sec24.src.md | 48 +- src/sec26.src.md | 314 ++++++----- src/sec27.src.md | 2 +- src/tfc/tfc.c | 2 +- src/tfc/tfc.gresource.c | 251 +++++++++ 31 files changed, 1489 insertions(+), 1011 deletions(-) create mode 100644 docs/image/cd0.png create mode 100644 docs/image/cd1.png create mode 100644 docs/image/cd2.png create mode 100644 docs/image/rect.png create mode 100644 image/cd0.png create mode 100644 image/cd1.png create mode 100644 image/cd2.png create mode 100644 image/rect.png create mode 100644 src/custom_drawing/meson.build create mode 100644 src/custom_drawing/rect.c create mode 100644 src/custom_drawing/rect.gresource.xml create mode 100644 src/custom_drawing/rect.ui create mode 100644 src/dnd/dnd.c create mode 100644 src/dnd/dnd.gresource.xml create mode 100644 src/dnd/dnd.ui create mode 100644 src/dnd/meson.build create mode 100644 src/tfc/tfc.gresource.c diff --git a/README.md b/README.md index 248347a..8993dcb 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ In short, 1. [Pango, CSS and Application](gfm/sec23.md) 1. [GtkDrawingArea and Cairo](gfm/sec24.md) 1. [Periodic Events](gfm/sec25.md) -1. [Combine GtkDrawingArea and TfeTextView](gfm/sec26.md) +1. [Custom drawing](gfm/sec26.md) 1. [Tiny turtle graphics interpreter](gfm/sec27.md) 1. [GtkListView](gfm/sec28.md) 1. [GtkGridView and activate signal](gfm/sec29.md) diff --git a/docs/image/cd0.png b/docs/image/cd0.png new file mode 100644 index 0000000000000000000000000000000000000000..fae25d1b85547ae93510544786770b0f6ae6cba9 GIT binary patch literal 8008 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 Xo{N-FnnJ)AF~n-8z4`SWUWfk+-FNOS literal 0 HcmV?d00001 diff --git a/docs/image/cd1.png b/docs/image/cd1.png new file mode 100644 index 0000000000000000000000000000000000000000..840cda81e81797ed16cee12e7ee0469d6e96d126 GIT binary patch literal 8372 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 literal 0 HcmV?d00001 diff --git a/docs/index.html b/docs/index.html index 6e92f02..e110f71 100644 --- a/docs/index.html +++ b/docs/index.html @@ -190,7 +190,7 @@ destruction
  • Pango, CSS and Application
  • GtkDrawingArea and Cairo
  • Periodic Events
  • -
  • Combine GtkDrawingArea and TfeTextView
  • +
  • Custom drawing
  • Tiny turtle graphics interpreter
  • GtkListView
  • GtkGridView and activate signal
  • diff --git a/docs/sec24.html b/docs/sec24.html index 72141b4..1e27e49 100644 --- a/docs/sec24.html +++ b/docs/sec24.html @@ -112,28 +112,22 @@

    GtkDrawingArea and Cairo

    -

    This section and following sections are not updated yet and -the programs were checked on the older GTK version than 4.10. They will -be updated in the near future.

    -

    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 freely draw or redraw an image in this widget. This is -called custom drawing.

    -

    GtkDrawingArea provides a cairo drawing context so users can draw -images by using cairo functions. In this section, I will explain:

    +

    If you want to draw shapes or paint images dynamically on the screen, +use the GtkDrawingArea widget.

    +

    GtkDrawingArea provides a cairo drawing context. You can draw images +with cairo library functions. This section describes:

      -
    1. Cairo, but only briefly
    2. +
    3. Cairo, but briefly
    4. GtkDrawingArea, with a very simple example.

    Cairo

    -

    Cairo is a set of two dimensional graphical drawing functions (or -graphics library). There are a lot of documents on Cairo’s website. If you aren’t -familiar with Cairo, it is worth reading the tutorial.

    -

    The following is an introduction to the Cairo library and how to use -it. First, you need to know about surfaces, sources, masks, -destinations, cairo context and transformations.

    +

    Cairo is a drawing library for two dimensional graphics. There are a +lot of documents on Cairo’s +website. If you aren’t familiar with Cairo, it is worth reading the +tutorial.

    +

    The following is an introduction to the Cairo library. First, you +need to know surfaces, sources, masks, destinations, cairo context and +transformations.

    • A surface represents an image. It is like a canvas. We can draw shapes and images with different colors on surfaces.
    • @@ -147,8 +141,8 @@ is a function to draw a path to the destination by the transfer.
    • A transformation can be applied before the transfer completes. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations that preserve straight lines. -Scaling, rotating, reflecting, shearing and translating are all examples -of affine transformations. They are mathematically represented by matrix +Scaling, rotating, reflecting, shearing and translating are examples of +affine transformations. They are mathematically represented by matrix multiplication and vector addition. 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 @@ -225,35 +219,32 @@ color depth. Width and height are in pixels and given as integers.
    • 14: Creates cairo context. The surface given as an argument will be the destination of the context.
    • 18: cairo_set_source_rgb creates a source pattern, -which in this case is a solid white paint. The second to fourth -arguments are red, green and blue color values respectively, and they -are of type float. The values are between zero (0.0) and one (1.0), with -black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
    • +which is a solid white paint. The second to fourth arguments are red, +green and blue color values respectively, and they are of type float. +The values are between zero (0.0) and one (1.0). Black is (0.0,0.0,0.0) +and white is (1.0,1.0,1.0).
    • 19: cairo_paint copies everywhere in the source to destination. The destination is filled with white pixels with this command.
    • 21: Sets the source color to black.
    • -
    • 22: cairo_set_line_width set the width of lines. In +
    • 22: cairo_set_line_width sets the width of lines. 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. 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. The square is located at the center.
    • -
    • 24: cairo_stroke transfer the source to destination +
    • 24: cairo_stroke transfers the source to destination through the rectangle in the mask.
    • -
    • 27: Outputs the image to a png file rectangle.png.
    • -
    • 28: Destroys the context. At the same time the source is +
    • 31: Outputs the image to a png file rectangle.png.
    • +
    • 32: Destroys the context. At the same time the source is destroyed.
    • -
    • 29: Destroys the surface.
    • +
    • 33: Destroys the surface.

    To compile this, change your current directory to src/misc and type the following.

    $ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
    -
    -rectangle.png - -
    +

    s rectangle.png

    See the Cairo’s website for further information.

    GtkDrawingArea

    @@ -308,10 +299,10 @@ important in this example.

    • 22: Creates a GtkDrawingArea instance.
    • 25: 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. For the information of +uses the function draw_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. For the information of gtk_drawing_area_set_draw_func, see Gtk API Reference – gtk_drawing_area_set_draw_func.
    • diff --git a/docs/sec25.html b/docs/sec25.html index f4d7d58..ded2431 100644 --- a/docs/sec25.html +++ b/docs/sec25.html @@ -279,7 +279,7 @@ class="sourceCode numberSource C numberLines"> gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL); g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock); - gtk_widget_show(win); + gtk_window_present(GTK_WINDOW (win)); }

      Our time_handler() function is very simple, as it just @@ -448,7 +448,7 @@ class="sourceCode numberSource C numberLines"> gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL); g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock); - gtk_widget_show(win); + gtk_window_present(GTK_WINDOW (win)); } diff --git a/docs/sec26.html b/docs/sec26.html index 18ab413..36a923c 100644 --- a/docs/sec26.html +++ b/docs/sec26.html @@ -111,423 +111,354 @@ -

      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 name of a color -in TfeTextView and click on the run button, then the color -of GtkDrawingArea changes to the color given by you.

      +

      Custom drawing

      +

      Custom drawing is to draw shapes dynamically. This section shows an +example of custom drawing. You can draw rectangles by dragging the +mouse.

      +

      Down the button.

      -color - +down the button +
      -

      The following colors are available. (without new line charactor)

      +

      Move the mouse

      +
      +Move the mouse + +
      +

      Up the button.

      +
      +Up the button + +
      +

      The programs are at src/custom_drawing directory. +Download the repository and see +the directory. There are four files.

        -
      • white
      • -
      • black
      • -
      • red
      • -
      • green
      • -
      • blue
      • +
      • meson.build
      • +
      • rect.c
      • +
      • rect.gresource.xml
      • +
      • rect.ui
      -

      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. Title bar, four -buttons in the tool bar, textview and drawing area. The ui file is as -follows.

      +

      rect.gresource.xml

      +

      This file describes a ui file to compile. The compiler +glib-compile-resources uses it.

      <?xml version="1.0" encoding="UTF-8"?>
      -<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>
      -
        -
      • 10-53: The horizontal box boxh1 makes a tool bar which -has four buttons, Run, Open, Save -and Close. This is similar to the tfe text -editor in Section 9. 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. Options “-WI, –export-dynamic” CFLAG is necessary when you -compile the application. You can achieve this by adding “export_dynamic: -true” argument to the executable function in meson.build. -And be careful that the handler must be defined without ‘static’ -class.
      • -
      • 54-76: The horizontal box boxh2 includes -GtkScrolledWindow and GtkDrawingArea. 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”.

      +<gresources> + <gresource prefix="/com/github/ToshioCP/rect"> + <file>rect.ui</file> + </gresource> +</gresources>
      +

      The prefix is /com/github/ToshioCP/rect and the file is +rect.ui. Therefore, GtkBuilder reads the resource from +/com/github/ToshioCP/rect/rect.ui.

      +

      rect.ui

      +

      The following is the ui file that defines the widgets. There are two +widgets which are GtkApplicationWindow and GtkDrawingArea. The ids are +win and da respectively.

      <?xml version="1.0" encoding="UTF-8"?>
      -<gresources>
      -  <gresource prefix="/com/github/ToshioCP/color">
      -    <file>color.ui</file>
      -  </gresource>
      -</gresources>
      -

      Drawing function and surface

      -

      The main point of this program is a drawing function.

      -
      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);
      -  }
      -}
      -

      The surface variable in line 3 is a static variable.

      -
      static cairo_surface_t *surface = NULL;
      -

      The drawing function just copies the surface to its own -surface with the cairo_paint function. The surface (pointed -by the static variable surface) is built by the -run function.

      +<interface> + <object class="GtkApplicationWindow" id="win"> + <property name="default-width">800</property> + <property name="default-height">600</property> + <property name="resizable">FALSE</property> + <property name="title">Custom drawing</property> + <child> + <object class="GtkDrawingArea" id="da"> + <property name="hexpand">TRUE</property> + <property name="vexpand">TRUE</property> + </object> + </child> + </object> +</interface> +

      rect.c

      +

      GtkApplication

      +

      This program uses GtkApplication. The application ID is +com.github.ToshioCP.rect.

      +
      #define APPLICATION_ID "com.github.ToshioCP.rect"
      +

      See GNOME +Developer Documentation for further information.

      +

      The function main is called at the beginning of the +application.

      +
      int
      +main (int argc, char **argv) {
      +  GtkApplication *app;
      +  int stat;
      +
      +  app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
      +  g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
      +  g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
      +  g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
      +  stat =g_application_run (G_APPLICATION (app), argc, argv);
      +  g_object_unref (app);
      +  return stat;
      +}
      +

      It connects three signals and handlers.

      +
        +
      • startup: It is emitted after the application is registered to the +system.
      • +
      • activate: It is emitted when the application is activated.
      • +
      • shutdown: It is emitted just before the application quits.
      • +
      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;
      +app_startup (GApplication *application) {
      +  GtkApplication *app = GTK_APPLICATION (application);
      +  GtkBuilder *build;
      +  GtkWindow *win;
      +  GtkDrawingArea *da;
      +  GtkGesture *drag;
       
      -  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);
      -  }
      -  g_free (contents);
      -}
      + build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui"); + win = GTK_WINDOW (gtk_builder_get_object (build, "win")); + da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da")); + gtk_window_set_application (win, app); + g_object_unref (build); + + gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL); + g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL); + + drag = gtk_gesture_drag_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); + gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag)); + g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL); + g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da); + g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da); +} +

      The startup handler does three things.

        -
      • 9-10: Gets the string in the GtkTextBuffer and inserts it to -contents.
      • -
      • 11: If the variable surface points a surface instance, -it is painted as follows.
      • -
      • 12- 30: The source is set based on the string contents -and copied to the surface with cairo_paint.
      • -
      • 24,26: Alpha channel is used in “light” and “dark” procedure.
      • -
      -

      The drawing area just reflects the surface. But one -problem is resizing. If a user resizes the main window, the drawing area -is also resized. It makes size difference between the surface and the -drawing area. So, the surface needs to be resized to fit the drawing -area.

      -

      It is accomplished by connecting the “resize” signal on the drawing -area to a handler.

      -
      g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
      -

      The handler is as follows.

      -
      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 ();
      -}
      -

      If the variable surface sets a surface instance, it is -destroyed. A new surface is created and its size fits the drawing area. -The surface is assigned to the variable surface. The -function run is called and the surface is colored.

      -

      The signal is emitted when:

      +
    • Builds the widgets.
    • +
    • Initializes the GtkDrawingArea instance.
        -
      • The drawing area is realized (it appears on the display).
      • -
      • It is changed (resized) while realized
      • +
      • Sets the drawing function
      • +
      • Connects the “resize” signal and the handler.
      • +
    • +
    • Creates the GtkGestureDrag instance and initializes it. Gesture will +be explained in this section later.
    -

    So, the first surface is created when it is realized.

    -

    Colorapplication.c

    -

    This is the main file.

    +
    static void
    +app_activate (GApplication *application) {
    +  GtkApplication *app = GTK_APPLICATION (application);
    +  GtkWindow *win;
    +
    +  win = gtk_application_get_active_window (app);
    +  gtk_window_present (win);
    +}
    +

    The activate handler just shows the window.

    +

    GtkDrawingArea

    +

    The program has two cairo surfaces and they are pointed by the global +variables.

    +
    static cairo_surface_t *surface = NULL;
    +static cairo_surface_t *surface_save = NULL;
    +

    The drawing process is as follows.

      -
    • Builds widgets by GtkBuilder.
    • -
    • Sets a drawing function for GtkDrawingArea. And connects a handler -to the “resize” signal on the GtkDrawingArea instance.
    • -
    • Implements each call back function. Particularly, Run -signal handler is the point in this program.
    • +
    • Creates an image on surface.
    • +
    • Copies surface to the cairo surface of the +GtkDrawingArea.
    • +
    • Calls gtk_widget_queue_draw (da) to draw it if +necessary.
    -

    The following is colorapplication.c.

    +

    They are created in the “resize” signal handler.

    #include <gtk/gtk.h>
    -#include "../tfetextview/tfetextview.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);
    -  }
    -  g_free (contents);
    -}
    -
    -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), GTK_WINDOW (win));
    -}
    -
    -void
    -save_cb (GtkWidget *btns) {
    -  tfe_text_view_save (TFE_TEXT_VIEW (tv));
    -}
    -
    -void
    -close_cb (GtkWidget *btnc) {
    -  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
    -app_activate (GApplication *application) {
    -  gtk_window_present (GTK_WINDOW (win));
    -}
    -
    -static void
    -app_startup (GApplication *application) {
    -  GtkApplication *app = GTK_APPLICATION (application);
    -  GtkBuilder *build;
    -  GdkDisplay *display;
    -
    -  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);
    -
    -  display = gdk_display_get_default ();
    -  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);
    -}
    -
    -static void
    -app_shutdown (GApplication *application) {
    -  if (surface)
    -    cairo_surface_destroy (surface);
    -}
    -
    -#define APPLICATION_ID "com.github.ToshioCP.color"
    -
    -int
    -main (int argc, char **argv) {
    -  GtkApplication *app;
    -  int stat;
    -
    -  app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
    -
    -  g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
    -  g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
    -  g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
    -
    -  stat =g_application_run (G_APPLICATION (app), argc, argv);
    -  g_object_unref (app);
    -  return stat;
    -}
    -
      -
    • 4-8: Win, tv, da and surface are defined as static variables.
    • -
    • 10-42: Run function.
    • -
    • 44-63: Handlers for button signals.
    • -
    • 65-71: Resize handler.
    • -
    • 73-79: Drawing function.
    • -
    • 81-84: Application activate handler. It just shows the main -window.
    • -
    • 86-105: Application startup handler.
    • -
    • 92- 97: It builds widgets according to the ui resource. The static -variables win, tv and da are assigned instances.
    • -
    • 98: Connects “resize” signal and a handler.
    • -
    • 99: Drawing function is set.
    • -
    • 101-104: CSS for textview padding is set.
    • -
    • 107-111: Application shutdown handler. If there exists a surface -instance, it will be destroyed.
    • -
    • 116-129: A function main. It creates a new application -instance. And connects three signals startup, shutdown and activate to -their handlers. It runs the application. It releases the reference to -the application and returns with stat value.
    • -
    -

    Meson.build

    -

    This file is almost same as before. An argument “export_dynamic: -true” is added to executable function.

    +class="sourceCode numberSource C numberLines">static void +resize_cb (GtkWidget *widget, int width, int height, gpointer user_data) { + cairo_t *cr; + + if (surface) + cairo_surface_destroy (surface); + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + if (surface_save) + cairo_surface_destroy (surface_save); + surface_save = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + /* Paint the surface white. It is the background color. */ + cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_paint (cr); + cairo_destroy (cr); +} +

    This callback is called when the GtkDrawingArea is shown. It is the +only call because the window is not resizable.

    +

    It creates image surfaces for surface and +surface_save. The surface surface is painted +white, which is the background color.

    +

    The drawing function copies surface to the +GtkDrawingArea surface.

    project('color', 'c')
    -
    -gtkdep = dependency('gtk4')
    -
    -gnome=import('gnome')
    -resources = gnome.compile_resources('resources','color.gresource.xml')
    -
    -sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
    -
    -executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
    -

    Build and try

    -

    Type the following to compile the program.

    -
    $ 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. No new line charactor is needed. Then, click on the -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.

    +class="sourceCode numberSource C numberLines">static void +draw_cb (GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer user_data) { + if (surface) { + cairo_set_source_surface (cr, surface, 0.0, 0.0); + cairo_paint (cr); + } +} +

    This function is called by the system when it needs to redraw the +drawing area.

    +

    Two surfaces surface and surface_save are +destroyed before the application quits.

    +
    static void
    +app_shutdown (GApplication *application) {
    +  if (surface)
    +    cairo_surface_destroy (surface);
    +  if (surface_save)
    +    cairo_surface_destroy (surface_save);
    +}
    +

    GtkGestureDrag

    +

    Gesture class is used to recognize human gestures such as click, +drag, pan, swipe and so on. It is a subclass of GtkEventController. +GtkGesture class is abstract and there are several implementations.

    +
      +
    • GtkGestureClick
    • +
    • GtkGestureDrag
    • +
    • GtkGesturePan
    • +
    • GtkGestureSwipe
    • +
    • other implementations
    • +
    +

    The program rect.c uses GtkGestureDrag. It is the +implementation for drags. The parent-child relationship is as +follows.

    +
    GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag
    +

    GtkGestureSingle is a subclass of GtkGesture and optimized for +singe-touch and mouse gestures.

    +

    A GtkGestureDrag instance is created and initialized in the startup +signal handler in rect.c. See line 18 to 23 in the +following.

    +
    static void
    +app_startup (GApplication *application) {
    +  GtkApplication *app = GTK_APPLICATION (application);
    +  GtkBuilder *build;
    +  GtkWindow *win;
    +  GtkDrawingArea *da;
    +  GtkGesture *drag;
    +
    +  build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui");
    +  win = GTK_WINDOW (gtk_builder_get_object (build, "win"));
    +  da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da"));
    +  gtk_window_set_application (win, app);
    +  g_object_unref (build);
    +
    +  gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL);
    +  g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL);
    +
    +  drag = gtk_gesture_drag_new ();
    +  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
    +  gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag));
    +  g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL);
    +  g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
    +  g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
    +}
    +
      +
    • The function gtk_gesture_drag_new creates a new +GtkGestureDrag instance.
    • +
    • The function gtk_gesture_single_set_button sets the +button number to listen to. The constant GDK_BUTTON_PRIMARY +is the left button of a mouse.
    • +
    • The function gtk_widget_add_controller adds an event +controller, gestures are descendants of the event controller, to a +widget.
    • +
    • Three signals and handlers are connected. +
        +
      • drag-begin: Emitted when dragging starts.
      • +
      • drag-update: Emitted when the dragging point moves.
      • +
      • drag-end: Emitted when the dragging ends.
      • +
    • +
    +

    The process during the drag is as follows.

    +
      +
    • start: save the surface and start points
    • +
    • update: restore the surface and draw a thin rectangle between the +start point and the current point of the mouse
    • +
    • end: restore the surface and draw a thick rectangle between the +start and end points.
    • +
    +

    We need two global variables for the start point.

    +
    static double start_x;
    +static double start_y;
    +

    The following is the handler for the “drag-begin” signal.

    +
    static void
    +copy_surface (cairo_surface_t *src, cairo_surface_t *dst) {
    +  if (!src || !dst)
    +    return;
    +  cairo_t *cr = cairo_create (dst);
    +  cairo_set_source_surface (cr, src, 0.0, 0.0);
    +  cairo_paint (cr);
    +  cairo_destroy (cr);
    +}
    +
    +static void
    +drag_begin (GtkGestureDrag *gesture, double x, double y, gpointer user_data) {
    +  // save the surface and record (x, y)
    +  copy_surface (surface, surface_save);
    +  start_x = x;
    +  start_y = y;
    +}
    +
      +
    • Copies surface to surface_save, which is +an image just before the dragging.
    • +
    • Stores the points to start_x and +start_y.
    • +
    +
    static void
    +drag_update  (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
    +  GtkWidget *da = GTK_WIDGET (user_data);
    +  cairo_t *cr;
    +  
    +  copy_surface (surface_save, surface);
    +  cr = cairo_create (surface);
    +  cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
    +  cairo_set_line_width (cr, 1.0);
    +  cairo_stroke (cr);
    +  cairo_destroy (cr);
    +  gtk_widget_queue_draw (da);
    +}
    +
      +
    • Restores surface from surface_save.
    • +
    • Draws a rectangle with thin lines.
    • +
    • Calls gtk_widget_queue_draw to add the GtkDrawingArea +to the queue to redraw.
    • +
    +
    static void
    +drag_end  (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
    +  GtkWidget *da = GTK_WIDGET (user_data);
    +  cairo_t *cr;
    +  
    +  copy_surface (surface_save, surface);
    +  cr = cairo_create (surface);
    +  cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
    +  cairo_set_line_width (cr, 6.0);
    +  cairo_stroke (cr);
    +  cairo_destroy (cr);
    +  gtk_widget_queue_draw (da);
    +}
    +
      +
    • Restores surface from surface_save.
    • +
    • Draws a rectangle with thick lines.
    • +
    • Calls gtk_widget_queue_draw to add the GtkDrawingArea +to the queue to redraw.
    • +
    +

    Build and run

    +

    Download the repository. Change +your current directory to src/custom_drawing. Run meson and +ninja to build the program. Type _build/rect to run the +program. Try to draw rectangles.

    +
    $ cd src/custom_drawing
    +$ meson setup _build
    +$ ninja -C _build
    +$ _build/rect
    +
    +The screen of rect program + +
    diff --git a/docs/sec27.html b/docs/sec27.html index 6b8c81d..b9fb4a7 100644 --- a/docs/sec27.html +++ b/docs/sec27.html @@ -116,7 +116,7 @@ interpreter

    A program turtle is an example with the combination of TfeTextView and GtkDrawingArea objects. It is a very small interpreter but you can draw fractal curves with it. The following diagram is a Koch -curve, which is one of famous fractal curves.

    +curve, which is one of the famous fractal curves.

    Koch curve diff --git a/gfm/sec24.md b/gfm/sec24.md index 6723c94..e2ff44d 100644 --- a/gfm/sec24.md +++ b/gfm/sec24.md @@ -2,27 +2,23 @@ Up: [README.md](../README.md), Prev: [Section 23](sec23.md), Next: [Section 25] # GtkDrawingArea and Cairo -This section and following sections are *not* updated yet and the programs were checked on the older GTK version than 4.10. -They will be updated in the near future. +If you want to draw shapes or paint images dynamically on the screen, use the GtkDrawingArea 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 freely draw or redraw an image in this widget. -This is called custom drawing. +GtkDrawingArea provides a cairo drawing context. +You can draw images with cairo library functions. +This section describes: -GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. -In this section, I will explain: - -1. Cairo, but only briefly +1. Cairo, but briefly 2. GtkDrawingArea, with a very simple example. ## Cairo -Cairo is a set of two dimensional graphical drawing functions (or graphics library). +Cairo is a drawing library for two dimensional graphics. There are a lot of documents on [Cairo's website](https://www.cairographics.org/). If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/). -The following is an introduction to the Cairo library and how to use it. -First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations. +The following is an introduction to the Cairo library. +First, you need to know surfaces, sources, masks, destinations, cairo context and transformations. - A surface represents an image. It is like a canvas. @@ -35,7 +31,7 @@ For example, `cairo_stroke` is a function to draw a path to the destination by t - A transformation can be applied before the transfer completes. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations that preserve straight lines. -Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations. +Scaling, rotating, reflecting, shearing and translating are examples of affine transformations. They are mathematically represented by matrix multiplication and vector addition. 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. @@ -106,28 +102,30 @@ Modern displays have this type of color depth. Width and height are in pixels and given as integers. - 14: Creates cairo context. The surface given as an argument will be the destination of the context. -- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint. -The second to fourth arguments are red, green and blue color values respectively, and they are -of type float. The values are between zero (0.0) and one (1.0), with -black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0). +- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint. +The second to fourth arguments are red, green and blue color values respectively, and they are of type float. +The values are between zero (0.0) and one (1.0). +Black is (0.0,0.0,0.0) and white is (1.0,1.0,1.0). - 19: `cairo_paint` copies everywhere in the source to destination. The destination is filled with white pixels with this command. - 21: Sets the source color to black. -- 22: `cairo_set_line_width` set the width of lines. +- 22: `cairo_set_line_width` sets the width of lines. 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. 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. The square is located at the center. -- 24: `cairo_stroke` transfer the source to destination through the rectangle in the mask. -- 27: Outputs the image to a png file `rectangle.png`. -- 28: Destroys the context. At the same time the source is destroyed. -- 29: Destroys the surface. +- 24: `cairo_stroke` transfers the source to destination through the rectangle in the mask. +- 31: Outputs the image to a png file `rectangle.png`. +- 32: Destroys the context. At the same time the source is destroyed. +- 33: Destroys the surface. To compile this, change your current directory to `src/misc` and type the following. - $ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` - +``` +$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` +``` +s ![rectangle.png](../image/rectangle.png) See the [Cairo's website](https://www.cairographics.org/) for further information. @@ -187,7 +185,7 @@ The two functions `app_activate` and `draw_function` are important in this examp - 22: Creates a GtkDrawingArea instance. - 25: 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 `draw_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. For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html). diff --git a/gfm/sec25.md b/gfm/sec25.md index 5f507f6..aea7b01 100644 --- a/gfm/sec25.md +++ b/gfm/sec25.md @@ -173,7 +173,7 @@ every second (or 1000ms). 13 14 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL); 15 g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock); -16 gtk_widget_show(win); +16 gtk_window_present(GTK_WINDOW (win)); 17 18 } ~~~ @@ -349,7 +349,7 @@ You can find the source files in the `tfc` directory. it can be compiled with `. 143 144 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL); 145 g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock); -146 gtk_widget_show(win); +146 gtk_window_present(GTK_WINDOW (win)); 147 148 } 149 diff --git a/gfm/sec26.md b/gfm/sec26.md index 4ca2e3d..e82b9f8 100644 --- a/gfm/sec26.md +++ b/gfm/sec26.md @@ -1,452 +1,382 @@ Up: [README.md](../README.md), Prev: [Section 25](sec25.md), Next: [Section 27](sec27.md) -# Combine GtkDrawingArea and TfeTextView +# Custom drawing -Now, we will make a new application which has GtkDrawingArea and TfeTextView in it. -Its name is "color". -If you write a name of a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you. +Custom drawing is to draw shapes dynamically. +This section shows an example of custom drawing. +You can draw rectangles by dragging the mouse. -![color](../image/color.png) +Down the button. -The following colors are available. -(without new line charactor) +![down the button](../image/cd0.png) -- white -- black -- red -- green -- blue +Move the mouse -In addition the following two options are also available. +![Move the mouse](../image/cd1.png) -- light: Make the color of the drawing area lighter. -- dark: Make the color of the drawing area darker. +Up the button. -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. +![Up the button](../image/cd2.png) -In this section, we focus on how to bind the two objects. +The programs are at `src/custom_drawing` directory. +Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial) and see the directory. +There are four files. -## Color.ui and color.gresource.xml +- meson.build +- rect.c +- rect.gresource.xml +- rect.ui -First, We need to make the ui file of the widgets. -Title bar, four buttons in the tool bar, textview and drawing area. -The ui file is as follows. +## rect.gresource.xml + +This file describes a ui file to compile. +The compiler glib-compile-resources uses it. + +~~~xml +1 +2 +3 +4 rect.ui +5 +6 +~~~ + +The prefix is `/com/github/ToshioCP/rect` and the file is `rect.ui`. +Therefore, GtkBuilder reads the resource from `/com/github/ToshioCP/rect/rect.ui`. + +## rect.ui + +The following is the ui file that defines the widgets. +There are two widgets which are GtkApplicationWindow and GtkDrawingArea. +The ids are `win` and `da` respectively. ~~~xml 1 2 3 - 4 color changer - 5 600 - 6 400 - 7 - 8 - 9 GTK_ORIENTATION_VERTICAL -10 -11 -12 GTK_ORIENTATION_HORIZONTAL -13 -14 -15 10 -16 -17 -18 -19 -20 Run -21 -22 -23 -24 -25 -26 Open -27 -28 -29 -30 -31 -32 TRUE -33 -34 -35 -36 -37 Save -38 -39 -40 -41 -42 -43 Close -44 -45 -46 -47 -48 -49 10 -50 -51 -52 -53 -54 -55 -56 GTK_ORIENTATION_HORIZONTAL -57 TRUE -58 -59 -60 TRUE -61 TRUE -62 -63 -64 GTK_WRAP_WORD_CHAR -65 -66 -67 -68 -69 -70 -71 TRUE -72 TRUE -73 -74 -75 -76 -77 -78 -79 -80 + 4 800 + 5 600 + 6 FALSE + 7 Custom drawing + 8 + 9 +10 TRUE +11 TRUE +12 +13 +14 +15 +16 ~~~ -- 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`. -This is similar to the `tfe` text editor in [Section 9](sec9.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. -Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application. -You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`. -And be careful that the handler must be defined without 'static' class. -- 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea. -GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box. -TfeTextView is a child of GtkScrolledWindow. +## rect.c -The xml file for the resource compiler is almost same as before. -Just substitute "color" for "tfe". +### GtkApplication -~~~xml -1 -2 -3 -4 color.ui -5 -6 +This program uses GtkApplication. +The application ID is `com.github.ToshioCP.rect`. + +```c +#define APPLICATION_ID "com.github.ToshioCP.rect" +``` + +See [GNOME Developer Documentation](https://developer.gnome.org/documentation/tutorials/application-id.html) for further information. + +The function `main` is called at the beginning of the application. + +~~~C + 1 int + 2 main (int argc, char **argv) { + 3 GtkApplication *app; + 4 int stat; + 5 + 6 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN); + 7 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); + 8 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); + 9 g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL); +10 stat =g_application_run (G_APPLICATION (app), argc, argv); +11 g_object_unref (app); +12 return stat; +13 } ~~~ -## Drawing function and surface +It connects three signals and handlers. -The main point of this program is a drawing function. +- startup: It is emitted after the application is registered to the system. +- activate: It is emitted when the application is activated. +- shutdown: It is emitted just before the application quits. + +~~~C + 1 static void + 2 app_startup (GApplication *application) { + 3 GtkApplication *app = GTK_APPLICATION (application); + 4 GtkBuilder *build; + 5 GtkWindow *win; + 6 GtkDrawingArea *da; + 7 GtkGesture *drag; + 8 + 9 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui"); +10 win = GTK_WINDOW (gtk_builder_get_object (build, "win")); +11 da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da")); +12 gtk_window_set_application (win, app); +13 g_object_unref (build); +14 +15 gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL); +16 g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL); +17 +18 drag = gtk_gesture_drag_new (); +19 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); +20 gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag)); +21 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL); +22 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da); +23 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da); +24 } +~~~ + +The startup handler does three things. + +- Builds the widgets. +- Initializes the GtkDrawingArea instance. + - Sets the drawing function + - Connects the "resize" signal and the handler. +- Creates the GtkGestureDrag instance and initializes it. +Gesture will be explained in this section later. ~~~C 1 static void -2 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) { +2 app_activate (GApplication *application) { +3 GtkApplication *app = GTK_APPLICATION (application); +4 GtkWindow *win; +5 +6 win = gtk_application_get_active_window (app); +7 gtk_window_present (win); +8 } +~~~ + +The activate handler just shows the window. + +### GtkDrawingArea + +The program has two cairo surfaces and they are pointed by the global variables. + +```C +static cairo_surface_t *surface = NULL; +static cairo_surface_t *surface_save = NULL; +``` + +The drawing process is as follows. + +- Creates an image on `surface`. +- Copies `surface` to the cairo surface of the GtkDrawingArea. +- Calls ` gtk_widget_queue_draw (da)` to draw it if necessary. + +They are created in the "resize" signal handler. + +~~~C + 1 static void + 2 resize_cb (GtkWidget *widget, int width, int height, gpointer user_data) { + 3 cairo_t *cr; + 4 + 5 if (surface) + 6 cairo_surface_destroy (surface); + 7 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + 8 if (surface_save) + 9 cairo_surface_destroy (surface_save); +10 surface_save = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); +11 /* Paint the surface white. It is the background color. */ +12 cr = cairo_create (surface); +13 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); +14 cairo_paint (cr); +15 cairo_destroy (cr); +16 } +~~~ + +This callback is called when the GtkDrawingArea is shown. +It is the only call because the window is not resizable. + +It creates image surfaces for `surface` and `surface_save`. +The `surface` surface is painted white, which is the background color. + +The drawing function copies `surface` to the GtkDrawingArea surface. + +~~~C +1 static void +2 draw_cb (GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer user_data) { 3 if (surface) { -4 cairo_set_source_surface (cr, surface, 0, 0); +4 cairo_set_source_surface (cr, surface, 0.0, 0.0); 5 cairo_paint (cr); 6 } 7 } ~~~ -The `surface` variable in line 3 is a static variable. +This function is called by the system when it needs to redraw the drawing area. -~~~C -static cairo_surface_t *surface = NULL; -~~~ - -The drawing function just copies the `surface` to its own surface with the `cairo_paint` function. -The surface (pointed by the static variable `surface`) is built by the `run` function. - -~~~C - 1 static void - 2 run (void) { - 3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 4 GtkTextIter start_iter; - 5 GtkTextIter end_iter; - 6 char *contents; - 7 cairo_t *cr; - 8 - 9 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); -10 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); -11 if (surface) { -12 cr = cairo_create (surface); -13 if (g_strcmp0 ("red", contents) == 0) -14 cairo_set_source_rgb (cr, 1, 0, 0); -15 else if (g_strcmp0 ("green", contents) == 0) -16 cairo_set_source_rgb (cr, 0, 1, 0); -17 else if (g_strcmp0 ("blue", contents) == 0) -18 cairo_set_source_rgb (cr, 0, 0, 1); -19 else if (g_strcmp0 ("white", contents) == 0) -20 cairo_set_source_rgb (cr, 1, 1, 1); -21 else if (g_strcmp0 ("black", contents) == 0) -22 cairo_set_source_rgb (cr, 0, 0, 0); -23 else if (g_strcmp0 ("light", contents) == 0) -24 cairo_set_source_rgba (cr, 1, 1, 1, 0.5); -25 else if (g_strcmp0 ("dark", contents) == 0) -26 cairo_set_source_rgba (cr, 0, 0, 0, 0.5); -27 else -28 cairo_set_source_surface (cr, surface, 0, 0); -29 cairo_paint (cr); -30 cairo_destroy (cr); -31 } -32 g_free (contents); -33 } -~~~ - -- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`. -- 11: If the variable `surface` points a surface instance, it is painted as follows. -- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`. -- 24,26: Alpha channel is used in "light" and "dark" procedure. - -The drawing area just reflects the `surface`. -But one problem is resizing. -If a user resizes the main window, the drawing area is also resized. -It makes size difference between the surface and the drawing area. -So, the surface needs to be resized to fit the drawing area. - -It is accomplished by connecting the "resize" signal on the drawing area to a handler. - -~~~C -g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); -~~~ - -The handler is as follows. +Two surfaces `surface` and `surface_save` are destroyed before the application quits. ~~~C 1 static void -2 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { +2 app_shutdown (GApplication *application) { 3 if (surface) 4 cairo_surface_destroy (surface); -5 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); -6 run (); +5 if (surface_save) +6 cairo_surface_destroy (surface_save); 7 } ~~~ -If the variable `surface` sets a surface instance, it is destroyed. -A new surface is created and its size fits the drawing area. -The surface is assigned to the variable `surface`. -The function `run` is called and the surface is colored. +### GtkGestureDrag -The signal is emitted when: +Gesture class is used to recognize human gestures such as click, drag, pan, swipe and so on. +It is a subclass of GtkEventController. +GtkGesture class is abstract and there are several implementations. -- The drawing area is realized (it appears on the display). -- It is changed (resized) while realized +- GtkGestureClick +- GtkGestureDrag +- GtkGesturePan +- GtkGestureSwipe +- other implementations -So, the first surface is created when it is realized. +The program `rect.c` uses GtkGestureDrag. +It is the implementation for drags. +The parent-child relationship is as follows. -## Colorapplication.c +``` +GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag +``` -This is the main file. +GtkGestureSingle is a subclass of GtkGesture and optimized for singe-touch and mouse gestures. -- Builds widgets by GtkBuilder. -- Sets a drawing function for GtkDrawingArea. -And connects a handler to the "resize" signal on the GtkDrawingArea instance. -- Implements each call back function. -Particularly, `Run` signal handler is the point in this program. - -The following is `colorapplication.c`. +A GtkGestureDrag instance is created and initialized in the startup signal handler in `rect.c`. +See line 18 to 23 in the following. ~~~C - 1 #include - 2 #include "../tfetextview/tfetextview.h" - 3 - 4 static GtkWidget *win; - 5 static GtkWidget *tv; - 6 static GtkWidget *da; - 7 - 8 static cairo_surface_t *surface = NULL; - 9 - 10 static void - 11 run (void) { - 12 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 13 GtkTextIter start_iter; - 14 GtkTextIter end_iter; - 15 char *contents; - 16 cairo_t *cr; - 17 - 18 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 19 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 20 if (surface) { - 21 cr = cairo_create (surface); - 22 if (g_strcmp0 ("red", contents) == 0) - 23 cairo_set_source_rgb (cr, 1, 0, 0); - 24 else if (g_strcmp0 ("green", contents) == 0) - 25 cairo_set_source_rgb (cr, 0, 1, 0); - 26 else if (g_strcmp0 ("blue", contents) == 0) - 27 cairo_set_source_rgb (cr, 0, 0, 1); - 28 else if (g_strcmp0 ("white", contents) == 0) - 29 cairo_set_source_rgb (cr, 1, 1, 1); - 30 else if (g_strcmp0 ("black", contents) == 0) - 31 cairo_set_source_rgb (cr, 0, 0, 0); - 32 else if (g_strcmp0 ("light", contents) == 0) - 33 cairo_set_source_rgba (cr, 1, 1, 1, 0.5); - 34 else if (g_strcmp0 ("dark", contents) == 0) - 35 cairo_set_source_rgba (cr, 0, 0, 0, 0.5); - 36 else - 37 cairo_set_source_surface (cr, surface, 0, 0); - 38 cairo_paint (cr); - 39 cairo_destroy (cr); - 40 } - 41 g_free (contents); - 42 } - 43 - 44 void - 45 run_cb (GtkWidget *btnr) { - 46 run (); - 47 gtk_widget_queue_draw (GTK_WIDGET (da)); - 48 } - 49 - 50 void - 51 open_cb (GtkWidget *btno) { - 52 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win)); - 53 } - 54 - 55 void - 56 save_cb (GtkWidget *btns) { - 57 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 58 } - 59 - 60 void - 61 close_cb (GtkWidget *btnc) { - 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 app_activate (GApplication *application) { - 83 gtk_window_present (GTK_WINDOW (win)); - 84 } - 85 - 86 static void - 87 app_startup (GApplication *application) { - 88 GtkApplication *app = GTK_APPLICATION (application); - 89 GtkBuilder *build; - 90 GdkDisplay *display; - 91 - 92 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui"); - 93 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); - 94 gtk_window_set_application (GTK_WINDOW (win), app); - 95 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv")); - 96 da = GTK_WIDGET (gtk_builder_get_object (build, "da")); - 97 g_object_unref(build); - 98 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); - 99 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); -100 -101 display = gdk_display_get_default (); -102 GtkCssProvider *provider = gtk_css_provider_new (); -103 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); -104 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); -105 } -106 -107 static void -108 app_shutdown (GApplication *application) { -109 if (surface) -110 cairo_surface_destroy (surface); -111 } -112 -113 #define APPLICATION_ID "com.github.ToshioCP.color" -114 -115 int -116 main (int argc, char **argv) { -117 GtkApplication *app; -118 int stat; -119 -120 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS); -121 -122 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); -123 g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL); -124 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); -125 -126 stat =g_application_run (G_APPLICATION (app), argc, argv); -127 g_object_unref (app); -128 return stat; -129 } -130 + 1 static void + 2 app_startup (GApplication *application) { + 3 GtkApplication *app = GTK_APPLICATION (application); + 4 GtkBuilder *build; + 5 GtkWindow *win; + 6 GtkDrawingArea *da; + 7 GtkGesture *drag; + 8 + 9 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui"); +10 win = GTK_WINDOW (gtk_builder_get_object (build, "win")); +11 da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da")); +12 gtk_window_set_application (win, app); +13 g_object_unref (build); +14 +15 gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL); +16 g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL); +17 +18 drag = gtk_gesture_drag_new (); +19 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); +20 gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag)); +21 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL); +22 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da); +23 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da); +24 } ~~~ -- 4-8: Win, tv, da and surface are defined as static variables. -- 10-42: Run function. -- 44-63: Handlers for button signals. -- 65-71: Resize handler. -- 73-79: Drawing function. -- 81-84: Application activate handler. -It just shows the main window. -- 86-105: Application startup handler. -- 92- 97: It builds widgets according to the ui resource. -The static variables win, tv and da are assigned instances. -- 98: Connects "resize" signal and a handler. -- 99: Drawing function is set. -- 101-104: CSS for textview padding is set. -- 107-111: Application shutdown handler. -If there exists a surface instance, it will be destroyed. -- 116-129: A function `main`. -It creates a new application instance. -And connects three signals startup, shutdown and activate to their handlers. -It runs the application. -It releases the reference to the application and returns with `stat` value. +- The function `gtk_gesture_drag_new` creates a new GtkGestureDrag instance. +- The function `gtk_gesture_single_set_button` sets the button number to listen to. +The constant `GDK_BUTTON_PRIMARY` is the left button of a mouse. +- The function `gtk_widget_add_controller` adds an event controller, gestures are descendants of the event controller, to a widget. +- Three signals and handlers are connected. + - drag-begin: Emitted when dragging starts. + - drag-update: Emitted when the dragging point moves. + - drag-end: Emitted when the dragging ends. -## Meson.build +The process during the drag is as follows. -This file is almost same as before. -An argument "export_dynamic: true" is added to executable function. +- start: save the surface and start points +- update: restore the surface and draw a thin rectangle between the start point and the current point of the mouse +- end: restore the surface and draw a thick rectangle between the start and end points. -~~~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) +We need two global variables for the start point. + +```C +static double start_x; +static double start_y; +``` + +The following is the handler for the "drag-begin" signal. + +~~~C + 1 static void + 2 copy_surface (cairo_surface_t *src, cairo_surface_t *dst) { + 3 if (!src || !dst) + 4 return; + 5 cairo_t *cr = cairo_create (dst); + 6 cairo_set_source_surface (cr, src, 0.0, 0.0); + 7 cairo_paint (cr); + 8 cairo_destroy (cr); + 9 } +10 +11 static void +12 drag_begin (GtkGestureDrag *gesture, double x, double y, gpointer user_data) { +13 // save the surface and record (x, y) +14 copy_surface (surface, surface_save); +15 start_x = x; +16 start_y = y; +17 } ~~~ -## Build and try +- Copies `surface` to `surface_save`, which is an image just before the dragging. +- Stores the points to `start_x` and `start_y`. -Type the following to compile the program. +~~~C + 1 static void + 2 drag_update (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) { + 3 GtkWidget *da = GTK_WIDGET (user_data); + 4 cairo_t *cr; + 5 + 6 copy_surface (surface_save, surface); + 7 cr = cairo_create (surface); + 8 cairo_rectangle (cr, start_x, start_y, offset_x, offset_y); + 9 cairo_set_line_width (cr, 1.0); +10 cairo_stroke (cr); +11 cairo_destroy (cr); +12 gtk_widget_queue_draw (da); +13 } +~~~ - $ meson _build - $ ninja -C _build +- Restores `surface` from `surface_save`. +- Draws a rectangle with thin lines. +- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw. -The application is made in `_build` directory. -Type the following to execute it. +~~~C + 1 static void + 2 drag_end (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) { + 3 GtkWidget *da = GTK_WIDGET (user_data); + 4 cairo_t *cr; + 5 + 6 copy_surface (surface_save, surface); + 7 cr = cairo_create (surface); + 8 cairo_rectangle (cr, start_x, start_y, offset_x, offset_y); + 9 cairo_set_line_width (cr, 6.0); +10 cairo_stroke (cr); +11 cairo_destroy (cr); +12 gtk_widget_queue_draw (da); +13 } +~~~ - $ _build/color +- Restores `surface` from `surface_save`. +- Draws a rectangle with thick lines. +- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw. -Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView. -No new line charactor is needed. -Then, click on the `Run` button. -Make sure the color of GtkDrawingArea changes. +## Build and run -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. +Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial). +Change your current directory to `src/custom_drawing`. +Run meson and ninja to build the program. +Type `_build/rect` to run the program. +Try to draw rectangles. + +``` +$ cd src/custom_drawing +$ meson setup _build +$ ninja -C _build +$ _build/rect +``` + +![The screen of rect program](../image/rect.png) Up: [README.md](../README.md), Prev: [Section 25](sec25.md), Next: [Section 27](sec27.md) diff --git a/gfm/sec27.md b/gfm/sec27.md index 1a617a6..575c048 100644 --- a/gfm/sec27.md +++ b/gfm/sec27.md @@ -4,7 +4,7 @@ Up: [README.md](../README.md), Prev: [Section 26](sec26.md), Next: [Section 28] A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. It is a very small interpreter but you can draw fractal curves with it. -The following diagram is a Koch curve, which is one of famous fractal curves. +The following diagram is a Koch curve, which is one of the famous fractal curves. ![Koch curve](../src/turtle/image/turtle_koch.png) diff --git a/image/cd0.png b/image/cd0.png new file mode 100644 index 0000000000000000000000000000000000000000..fae25d1b85547ae93510544786770b0f6ae6cba9 GIT binary patch literal 8008 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 Xo{N-FnnJ)AF~n-8z4`SWUWfk+-FNOS literal 0 HcmV?d00001 diff --git a/image/cd1.png b/image/cd1.png new file mode 100644 index 0000000000000000000000000000000000000000..840cda81e81797ed16cee12e7ee0469d6e96d126 GIT binary patch literal 8372 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 literal 0 HcmV?d00001 diff --git a/src/custom_drawing/meson.build b/src/custom_drawing/meson.build new file mode 100644 index 0000000..e34f99b --- /dev/null +++ b/src/custom_drawing/meson.build @@ -0,0 +1,8 @@ +project('rect', 'c') + +gtkdep = dependency('gtk4') + +gnome = import('gnome') +resources = gnome.compile_resources('resources','rect.gresource.xml') + +executable(meson.project_name(), 'rect.c', resources, dependencies: gtkdep, export_dynamic: true, install: false) diff --git a/src/custom_drawing/rect.c b/src/custom_drawing/rect.c new file mode 100644 index 0000000..d394e9b --- /dev/null +++ b/src/custom_drawing/rect.c @@ -0,0 +1,137 @@ +#include + +static cairo_surface_t *surface = NULL; +static cairo_surface_t *surface_save = NULL; +static double start_x; +static double start_y; + +static void +copy_surface (cairo_surface_t *src, cairo_surface_t *dst) { + if (!src || !dst) + return; + cairo_t *cr = cairo_create (dst); + cairo_set_source_surface (cr, src, 0.0, 0.0); + cairo_paint (cr); + cairo_destroy (cr); +} + +/* This callback is called when the GtkDrawingArea is shown. */ +/* It is the only call because the window is not resizable. */ +static void +resize_cb (GtkWidget *widget, int width, int height, gpointer user_data) { + cairo_t *cr; + + if (surface) + cairo_surface_destroy (surface); + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + if (surface_save) + cairo_surface_destroy (surface_save); + surface_save = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + /* Paint the surface white. It is the background color. */ + cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_paint (cr); + cairo_destroy (cr); +} + +static void +draw_cb (GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer user_data) { + if (surface) { + cairo_set_source_surface (cr, surface, 0.0, 0.0); + cairo_paint (cr); + } +} + +static void +drag_begin (GtkGestureDrag *gesture, double x, double y, gpointer user_data) { + // save the surface and record (x, y) + copy_surface (surface, surface_save); + start_x = x; + start_y = y; +} + +static void +drag_update (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) { + GtkWidget *da = GTK_WIDGET (user_data); + cairo_t *cr; + + copy_surface (surface_save, surface); + cr = cairo_create (surface); + cairo_rectangle (cr, start_x, start_y, offset_x, offset_y); + cairo_set_line_width (cr, 1.0); + cairo_stroke (cr); + cairo_destroy (cr); + gtk_widget_queue_draw (da); +} + +static void +drag_end (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) { + GtkWidget *da = GTK_WIDGET (user_data); + cairo_t *cr; + + copy_surface (surface_save, surface); + cr = cairo_create (surface); + cairo_rectangle (cr, start_x, start_y, offset_x, offset_y); + cairo_set_line_width (cr, 6.0); + cairo_stroke (cr); + cairo_destroy (cr); + gtk_widget_queue_draw (da); +} + +static void +app_activate (GApplication *application) { + GtkApplication *app = GTK_APPLICATION (application); + GtkWindow *win; + + win = gtk_application_get_active_window (app); + gtk_window_present (win); +} + +static void +app_shutdown (GApplication *application) { + if (surface) + cairo_surface_destroy (surface); + if (surface_save) + cairo_surface_destroy (surface_save); +} + +static void +app_startup (GApplication *application) { + GtkApplication *app = GTK_APPLICATION (application); + GtkBuilder *build; + GtkWindow *win; + GtkDrawingArea *da; + GtkGesture *drag; + + build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui"); + win = GTK_WINDOW (gtk_builder_get_object (build, "win")); + da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da")); + gtk_window_set_application (win, app); + g_object_unref (build); + + gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL); + g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL); + + drag = gtk_gesture_drag_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); + gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag)); + g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL); + g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da); + g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da); +} + +#define APPLICATION_ID "com.github.ToshioCP.rect" + +int +main (int argc, char **argv) { + GtkApplication *app; + int stat; + + app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN); + g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); + g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); + g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL); + stat =g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + return stat; +} diff --git a/src/custom_drawing/rect.gresource.xml b/src/custom_drawing/rect.gresource.xml new file mode 100644 index 0000000..7467b75 --- /dev/null +++ b/src/custom_drawing/rect.gresource.xml @@ -0,0 +1,6 @@ + + + + rect.ui + + diff --git a/src/custom_drawing/rect.ui b/src/custom_drawing/rect.ui new file mode 100644 index 0000000..f60bee3 --- /dev/null +++ b/src/custom_drawing/rect.ui @@ -0,0 +1,16 @@ + + + + 800 + 600 + FALSE + Custom drawing + + + TRUE + TRUE + + + + + diff --git a/src/dnd/dnd.c b/src/dnd/dnd.c new file mode 100644 index 0000000..551d054 --- /dev/null +++ b/src/dnd/dnd.c @@ -0,0 +1,86 @@ +#include + +static GtkCssProvider *provider = NULL; +static const char *format = "label {padding: 20px;} label#red {background: red;} " + "label#green {background: green;} label#blue {background: blue;} " + "label#canvas {color: %s; font-weight: bold; font-size: 72pt;}"; + +static gboolean +drop_cb (GtkDropTarget* self, const GValue* value, gdouble x, gdouble y, gpointer user_data) { + char *s; + + s = g_strdup_printf (format, g_value_get_string (value)); + gtk_css_provider_load_from_data (provider, s, -1); + g_free (s); + return TRUE; +} + +static void +app_activate (GApplication *application) { + GtkApplication *app = GTK_APPLICATION (application); + GtkWindow *win; + + win = gtk_application_get_active_window (app); + gtk_window_present (win); +} + +static void +app_startup (GApplication *application) { + GtkApplication *app = GTK_APPLICATION (application); + GtkBuilder *build; + GtkWindow *win; + GtkLabel *src_labels[3]; + int i; + GtkLabel *canvas; + GtkDragSource *src; + GdkContentProvider* content; + GtkDropTarget *tgt; + GdkDisplay *display; + char *s; + + build = gtk_builder_new_from_resource ("/com/github/ToshioCP/dnd/dnd.ui"); + win = GTK_WINDOW (gtk_builder_get_object (build, "win")); + src_labels[0] = GTK_LABEL (gtk_builder_get_object (build, "red")); + src_labels[1] = GTK_LABEL (gtk_builder_get_object (build, "green")); + src_labels[2] = GTK_LABEL (gtk_builder_get_object (build, "blue")); + canvas = GTK_LABEL (gtk_builder_get_object (build, "canvas")); + gtk_window_set_application (win, app); + g_object_unref (build); + + for (i=0; i<3; ++i) { + src = gtk_drag_source_new (); + content = gdk_content_provider_new_typed (G_TYPE_STRING, gtk_widget_get_name (GTK_WIDGET (src_labels[i]))); + gtk_drag_source_set_content (src, content); + g_object_unref (content); + gtk_widget_add_controller (GTK_WIDGET (src_labels[i]), GTK_EVENT_CONTROLLER (src)); // The ownership of src is taken by the instance. + } + + tgt = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY); + g_signal_connect (tgt, "drop", G_CALLBACK (drop_cb), NULL); + gtk_widget_add_controller (GTK_WIDGET (canvas), GTK_EVENT_CONTROLLER (tgt)); // The ownership of tgt is taken by the instance. + + provider = gtk_css_provider_new (); + s = g_strdup_printf (format, "black"); + gtk_css_provider_load_from_data (provider, s, -1); + g_free (s); + display = gdk_display_get_default (); + gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); // The provider is still alive because the display owns it. +} + +#define APPLICATION_ID "com.github.ToshioCP.dnd" + +int +main (int argc, char **argv) { + GtkApplication *app; + int stat; + + app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN); + g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); + g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); + stat =g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + return stat; +} + diff --git a/src/dnd/dnd.gresource.xml b/src/dnd/dnd.gresource.xml new file mode 100644 index 0000000..b9dade5 --- /dev/null +++ b/src/dnd/dnd.gresource.xml @@ -0,0 +1,6 @@ + + + + dnd.ui + + diff --git a/src/dnd/dnd.ui b/src/dnd/dnd.ui new file mode 100644 index 0000000..fb66148 --- /dev/null +++ b/src/dnd/dnd.ui @@ -0,0 +1,54 @@ + + + + 800 + 600 + FALSE + Drag and Drop + + + TRUE + TRUE + GTK_ORIENTATION_VERTICAL + 5 + + + GTK_ORIENTATION_HORIZONTAL + TRUE + + + RED + GTK_JUSTIFY_CENTER + red + + + + + GREEN + GTK_JUSTIFY_CENTER + green + + + + + BLUE + GTK_JUSTIFY_CENTER + blue + + + + + + + CANVAS + GTK_JUSTIFY_CENTER + canvas + TRUE + TRUE + + + + + + + diff --git a/src/dnd/meson.build b/src/dnd/meson.build new file mode 100644 index 0000000..68ba77f --- /dev/null +++ b/src/dnd/meson.build @@ -0,0 +1,8 @@ +project('dnd', 'c') + +gtkdep = dependency('gtk4') + +gnome = import('gnome') +resources = gnome.compile_resources('resources','dnd.gresource.xml') + +executable(meson.project_name(), 'dnd.c', resources, dependencies: gtkdep, export_dynamic: true, install: false) diff --git a/src/sec24.src.md b/src/sec24.src.md index f5f7791..bcbe75b 100644 --- a/src/sec24.src.md +++ b/src/sec24.src.md @@ -1,26 +1,22 @@ # GtkDrawingArea and Cairo -This section and following sections are *not* updated yet and the programs were checked on the older GTK version than 4.10. -They will be updated in the near future. +If you want to draw shapes or paint images dynamically on the screen, use the GtkDrawingArea 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 freely draw or redraw an image in this widget. -This is called custom drawing. +GtkDrawingArea provides a cairo drawing context. +You can draw images with cairo library functions. +This section describes: -GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. -In this section, I will explain: - -1. Cairo, but only briefly +1. Cairo, but briefly 2. GtkDrawingArea, with a very simple example. ## Cairo -Cairo is a set of two dimensional graphical drawing functions (or graphics library). +Cairo is a drawing library for two dimensional graphics. There are a lot of documents on [Cairo's website](https://www.cairographics.org/). If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/). -The following is an introduction to the Cairo library and how to use it. -First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations. +The following is an introduction to the Cairo library. +First, you need to know surfaces, sources, masks, destinations, cairo context and transformations. - A surface represents an image. It is like a canvas. @@ -33,7 +29,7 @@ For example, `cairo_stroke` is a function to draw a path to the destination by t - A transformation can be applied before the transfer completes. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations that preserve straight lines. -Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations. +Scaling, rotating, reflecting, shearing and translating are examples of affine transformations. They are mathematically represented by matrix multiplication and vector addition. 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. @@ -69,28 +65,30 @@ Modern displays have this type of color depth. Width and height are in pixels and given as integers. - 14: Creates cairo context. The surface given as an argument will be the destination of the context. -- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint. -The second to fourth arguments are red, green and blue color values respectively, and they are -of type float. The values are between zero (0.0) and one (1.0), with -black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0). +- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint. +The second to fourth arguments are red, green and blue color values respectively, and they are of type float. +The values are between zero (0.0) and one (1.0). +Black is (0.0,0.0,0.0) and white is (1.0,1.0,1.0). - 19: `cairo_paint` copies everywhere in the source to destination. The destination is filled with white pixels with this command. - 21: Sets the source color to black. -- 22: `cairo_set_line_width` set the width of lines. +- 22: `cairo_set_line_width` sets the width of lines. 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. 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. The square is located at the center. -- 24: `cairo_stroke` transfer the source to destination through the rectangle in the mask. -- 27: Outputs the image to a png file `rectangle.png`. -- 28: Destroys the context. At the same time the source is destroyed. -- 29: Destroys the surface. +- 24: `cairo_stroke` transfers the source to destination through the rectangle in the mask. +- 31: Outputs the image to a png file `rectangle.png`. +- 32: Destroys the context. At the same time the source is destroyed. +- 33: Destroys the surface. To compile this, change your current directory to `src/misc` and type the following. - $ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` - +``` +$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` +``` +s ![rectangle.png](../image/rectangle.png) See the [Cairo's website](https://www.cairographics.org/) for further information. @@ -108,7 +106,7 @@ The two functions `app_activate` and `draw_function` are important in this examp - 22: Creates a GtkDrawingArea instance. - 25: 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 `draw_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. For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html). diff --git a/src/sec26.src.md b/src/sec26.src.md index c2c69fc..c5571c9 100644 --- a/src/sec26.src.md +++ b/src/sec26.src.md @@ -1,182 +1,240 @@ -# Combine GtkDrawingArea and TfeTextView +# Custom drawing -Now, we will make a new application which has GtkDrawingArea and TfeTextView in it. -Its name is "color". -If you write a name of a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you. +Custom drawing is to draw shapes dynamically. +This section shows an example of custom drawing. +You can draw rectangles by dragging the mouse. -![color](../image/color.png){width=7.0cm height=5.13cm} +Down the button. -The following colors are available. -(without new line charactor) +![down the button](../image/cd0.png){width=6cm height=4.83cm} -- white -- black -- red -- green -- blue +Move the mouse -In addition the following two options are also available. +![Move the mouse](../image/cd1.png){width=6cm height=4.83cm} -- light: Make the color of the drawing area lighter. -- dark: Make the color of the drawing area darker. +Up the button. -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. +![Up the button](../image/cd2.png){width=6cm height=4.83cm} -In this section, we focus on how to bind the two objects. +The programs are at `src/custom_drawing` directory. +Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial) and see the directory. +There are four files. -## Color.ui and color.gresource.xml +- meson.build +- rect.c +- rect.gresource.xml +- rect.ui -First, We need to make the ui file of the widgets. -Title bar, four buttons in the tool bar, textview and drawing area. -The ui file is as follows. +## rect.gresource.xml + +This file describes a ui file to compile. +The compiler glib-compile-resources uses it. @@@include -color/color.ui +custom_drawing/rect.gresource.xml @@@ -- 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`. -This is similar to the `tfe` text editor in [Section 9](sec9.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. -Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application. -You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`. -And be careful that the handler must be defined without 'static' class. -- 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea. -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 prefix is `/com/github/ToshioCP/rect` and the file is `rect.ui`. +Therefore, GtkBuilder reads the resource from `/com/github/ToshioCP/rect/rect.ui`. -The xml file for the resource compiler is almost same as before. -Just substitute "color" for "tfe". +## rect.ui + +The following is the ui file that defines the widgets. +There are two widgets which are GtkApplicationWindow and GtkDrawingArea. +The ids are `win` and `da` respectively. @@@include -color/color.gresource.xml +custom_drawing/rect.ui @@@ -## Drawing function and surface +## rect.c -The main point of this program is a drawing function. +### GtkApplication + +This program uses GtkApplication. +The application ID is `com.github.ToshioCP.rect`. + +```c +#define APPLICATION_ID "com.github.ToshioCP.rect" +``` + +See [GNOME Developer Documentation](https://developer.gnome.org/documentation/tutorials/application-id.html) for further information. + +The function `main` is called at the beginning of the application. @@@include -color/colorapplication.c draw_func +custom_drawing/rect.c main @@@ -The `surface` variable in line 3 is a static variable. +It connects three signals and handlers. -~~~C +- startup: It is emitted after the application is registered to the system. +- activate: It is emitted when the application is activated. +- shutdown: It is emitted just before the application quits. + +@@@include +custom_drawing/rect.c app_startup +@@@ + +The startup handler does three things. + +- Builds the widgets. +- Initializes the GtkDrawingArea instance. + - Sets the drawing function + - Connects the "resize" signal and the handler. +- Creates the GtkGestureDrag instance and initializes it. +Gesture will be explained in this section later. + +@@@include +custom_drawing/rect.c app_activate +@@@ + +The activate handler just shows the window. + +### GtkDrawingArea + +The program has two cairo surfaces and they are pointed by the global variables. + +@@@if gfm +```C static cairo_surface_t *surface = NULL; -~~~ +static cairo_surface_t *surface_save = NULL; +``` +@@@else +```{.C} +static cairo_surface_t *surface = NULL; +static cairo_surface_t *surface_save = NULL; +``` +@@@end -The drawing function just copies the `surface` to its own surface with the `cairo_paint` function. -The surface (pointed by the static variable `surface`) is built by the `run` function. +The drawing process is as follows. + +- Creates an image on `surface`. +- Copies `surface` to the cairo surface of the GtkDrawingArea. +- Calls ` gtk_widget_queue_draw (da)` to draw it if necessary. + +They are created in the "resize" signal handler. @@@include -color/colorapplication.c run +custom_drawing/rect.c resize_cb @@@ -- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`. -- 11: If the variable `surface` points a surface instance, it is painted as follows. -- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`. -- 24,26: Alpha channel is used in "light" and "dark" procedure. +This callback is called when the GtkDrawingArea is shown. +It is the only call because the window is not resizable. -The drawing area just reflects the `surface`. -But one problem is resizing. -If a user resizes the main window, the drawing area is also resized. -It makes size difference between the surface and the drawing area. -So, the surface needs to be resized to fit the drawing area. +It creates image surfaces for `surface` and `surface_save`. +The `surface` surface is painted white, which is the background color. -It is accomplished by connecting the "resize" signal on the drawing area to a handler. - -~~~C -g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); -~~~ - -The handler is as follows. +The drawing function copies `surface` to the GtkDrawingArea surface. @@@include -color/colorapplication.c resize_cb +custom_drawing/rect.c draw_cb @@@ -If the variable `surface` sets a surface instance, it is destroyed. -A new surface is created and its size fits the drawing area. -The surface is assigned to the variable `surface`. -The function `run` is called and the surface is colored. +This function is called by the system when it needs to redraw the drawing area. -The signal is emitted when: - -- The drawing area is realized (it appears on the display). -- It is changed (resized) while realized - -So, the first surface is created when it is realized. - -## Colorapplication.c - -This is the main file. - -- Builds widgets by GtkBuilder. -- Sets a drawing function for GtkDrawingArea. -And connects a handler to the "resize" signal on the GtkDrawingArea instance. -- Implements each call back function. -Particularly, `Run` signal handler is the point in this program. - -The following is `colorapplication.c`. +Two surfaces `surface` and `surface_save` are destroyed before the application quits. @@@include -color/colorapplication.c +custom_drawing/rect.c app_shutdown @@@ -- 4-8: Win, tv, da and surface are defined as static variables. -- 10-42: Run function. -- 44-63: Handlers for button signals. -- 65-71: Resize handler. -- 73-79: Drawing function. -- 81-84: Application activate handler. -It just shows the main window. -- 86-105: Application startup handler. -- 92- 97: It builds widgets according to the ui resource. -The static variables win, tv and da are assigned instances. -- 98: Connects "resize" signal and a handler. -- 99: Drawing function is set. -- 101-104: CSS for textview padding is set. -- 107-111: Application shutdown handler. -If there exists a surface instance, it will be destroyed. -- 116-129: A function `main`. -It creates a new application instance. -And connects three signals startup, shutdown and activate to their handlers. -It runs the application. -It releases the reference to the application and returns with `stat` value. +### GtkGestureDrag -## Meson.build +Gesture class is used to recognize human gestures such as click, drag, pan, swipe and so on. +It is a subclass of GtkEventController. +GtkGesture class is abstract and there are several implementations. -This file is almost same as before. -An argument "export_dynamic: true" is added to executable function. +- GtkGestureClick +- GtkGestureDrag +- GtkGesturePan +- GtkGestureSwipe +- other implementations + +The program `rect.c` uses GtkGestureDrag. +It is the implementation for drags. +The parent-child relationship is as follows. + +``` +GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag +``` + +GtkGestureSingle is a subclass of GtkGesture and optimized for singe-touch and mouse gestures. + +A GtkGestureDrag instance is created and initialized in the startup signal handler in `rect.c`. +See line 18 to 23 in the following. @@@include -color/meson.build +custom_drawing/rect.c app_startup @@@ -## Build and try +- The function `gtk_gesture_drag_new` creates a new GtkGestureDrag instance. +- The function `gtk_gesture_single_set_button` sets the button number to listen to. +The constant `GDK_BUTTON_PRIMARY` is the left button of a mouse. +- The function `gtk_widget_add_controller` adds an event controller, gestures are descendants of the event controller, to a widget. +- Three signals and handlers are connected. + - drag-begin: Emitted when dragging starts. + - drag-update: Emitted when the dragging point moves. + - drag-end: Emitted when the dragging ends. -Type the following to compile the program. +The process during the drag is as follows. - $ meson _build - $ ninja -C _build +- start: save the surface and start points +- update: restore the surface and draw a thin rectangle between the start point and the current point of the mouse +- end: restore the surface and draw a thick rectangle between the start and end points. -The application is made in `_build` directory. -Type the following to execute it. +We need two global variables for the start point. - $ _build/color +@@@if gfm +```C +static double start_x; +static double start_y; +``` +@@@else +```{.C} +static double start_x; +static double start_y; +``` +@@@end -Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView. -No new line charactor is needed. -Then, click on the `Run` button. -Make sure the color of GtkDrawingArea changes. +The following is the handler for the "drag-begin" signal. -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. +@@@include +custom_drawing/rect.c copy_surface drag_begin +@@@ + +- Copies `surface` to `surface_save`, which is an image just before the dragging. +- Stores the points to `start_x` and `start_y`. + +@@@include +custom_drawing/rect.c drag_update +@@@ + +- Restores `surface` from `surface_save`. +- Draws a rectangle with thin lines. +- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw. + +@@@include +custom_drawing/rect.c drag_end +@@@ + +- Restores `surface` from `surface_save`. +- Draws a rectangle with thick lines. +- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw. + +## Build and run + +Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial). +Change your current directory to `src/custom_drawing`. +Run meson and ninja to build the program. +Type `_build/rect` to run the program. +Try to draw rectangles. + +``` +$ cd src/custom_drawing +$ meson setup _build +$ ninja -C _build +$ _build/rect +``` + +![The screen of rect program](../image/rect.png){width=12.4cm height=10cm} diff --git a/src/sec27.src.md b/src/sec27.src.md index a860c2b..768bd43 100644 --- a/src/sec27.src.md +++ b/src/sec27.src.md @@ -2,7 +2,7 @@ A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. It is a very small interpreter but you can draw fractal curves with it. -The following diagram is a Koch curve, which is one of famous fractal curves. +The following diagram is a Koch curve, which is one of the famous fractal curves. ![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm} diff --git a/src/tfc/tfc.c b/src/tfc/tfc.c index a47588a..6643219 100644 --- a/src/tfc/tfc.c +++ b/src/tfc/tfc.c @@ -143,7 +143,7 @@ app_activate (GApplication *app, gpointer user_data) { gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL); g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock); - gtk_widget_show(win); + gtk_window_present(GTK_WINDOW (win)); } diff --git a/src/tfc/tfc.gresource.c b/src/tfc/tfc.gresource.c new file mode 100644 index 0000000..6bc2c8c --- /dev/null +++ b/src/tfc/tfc.gresource.c @@ -0,0 +1,251 @@ +#include + +#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)) +# define SECTION __attribute__ ((section (".gresource.tfc"), aligned (8))) +#else +# define SECTION +#endif + +static const SECTION union { const guint8 data[739]; const double alignment; void * const ptr;} tfc_resource_data = { + "\107\126\141\162\151\141\156\164\000\000\000\000\000\000\000\000" + "\030\000\000\000\310\000\000\000\000\000\000\050\006\000\000\000" + "\000\000\000\000\002\000\000\000\002\000\000\000\003\000\000\000" + "\003\000\000\000\005\000\000\000\130\345\125\000\004\000\000\000" + "\310\000\000\000\004\000\114\000\314\000\000\000\320\000\000\000" + "\224\135\334\227\003\000\000\000\320\000\000\000\007\000\114\000" + "\330\000\000\000\334\000\000\000\324\265\002\000\377\377\377\377" + "\334\000\000\000\001\000\114\000\340\000\000\000\344\000\000\000" + "\302\257\211\013\002\000\000\000\344\000\000\000\004\000\114\000" + "\350\000\000\000\354\000\000\000\214\044\075\224\001\000\000\000" + "\354\000\000\000\011\000\114\000\370\000\000\000\374\000\000\000" + "\141\320\165\220\000\000\000\000\374\000\000\000\006\000\166\000" + "\010\001\000\000\342\002\000\000\164\146\143\057\005\000\000\000" + "\147\151\164\150\165\142\057\000\004\000\000\000\057\000\000\000" + "\003\000\000\000\143\157\155\057\001\000\000\000\124\157\163\150" + "\151\157\103\120\057\000\000\000\000\000\000\000\164\146\143\056" + "\165\151\000\000\000\000\000\000\312\001\000\000\000\000\000\000" + "\074\077\170\155\154\040\166\145\162\163\151\157\156\075\042\061" + "\056\060\042\040\145\156\143\157\144\151\156\147\075\042\125\124" + "\106\055\070\042\077\076\012\074\151\156\164\145\162\146\141\143" + "\145\076\012\040\040\074\157\142\152\145\143\164\040\143\154\141" + "\163\163\075\042\107\164\153\101\160\160\154\151\143\141\164\151" + "\157\156\127\151\156\144\157\167\042\040\151\144\075\042\167\151" + "\156\042\076\012\040\040\040\040\074\160\162\157\160\145\162\164" + "\171\040\156\141\155\145\075\042\164\151\164\154\145\042\076\103" + "\154\157\143\153\074\057\160\162\157\160\145\162\164\171\076\012" + "\040\040\040\040\074\160\162\157\160\145\162\164\171\040\156\141" + "\155\145\075\042\144\145\146\141\165\154\164\055\167\151\144\164" + "\150\042\076\062\060\060\074\057\160\162\157\160\145\162\164\171" + "\076\012\040\040\040\040\074\160\162\157\160\145\162\164\171\040" + "\156\141\155\145\075\042\144\145\146\141\165\154\164\055\150\145" + "\151\147\150\164\042\076\062\060\060\074\057\160\162\157\160\145" + "\162\164\171\076\012\040\040\040\040\074\143\150\151\154\144\076" + "\012\040\040\040\040\040\040\074\157\142\152\145\143\164\040\143" + "\154\141\163\163\075\042\107\164\153\104\162\141\167\151\156\147" + "\101\162\145\141\042\040\151\144\075\042\143\154\157\143\153\042" + "\076\012\040\040\040\040\040\040\040\040\074\160\162\157\160\145" + "\162\164\171\040\156\141\155\145\075\042\150\145\170\160\141\156" + "\144\042\076\124\122\125\105\074\057\160\162\157\160\145\162\164" + "\171\076\012\040\040\040\040\040\040\040\040\074\160\162\157\160" + "\145\162\164\171\040\156\141\155\145\075\042\166\145\170\160\141" + "\156\144\042\076\124\122\125\105\074\057\160\162\157\160\145\162" + "\164\171\076\012\040\040\040\040\040\040\074\057\157\142\152\145" + "\143\164\076\012\040\040\040\040\074\057\143\150\151\154\144\076" + "\012\040\040\074\057\157\142\152\145\143\164\076\012\074\057\151" + "\156\164\145\162\146\141\143\145\076\012\000\000\050\165\165\141" + "\171\051" }; + +static GStaticResource static_resource = { tfc_resource_data.data, sizeof (tfc_resource_data.data) - 1 /* nul terminator */, NULL, NULL, NULL }; + +G_MODULE_EXPORT +GResource *tfc_get_resource (void); +GResource *tfc_get_resource (void) +{ + return g_static_resource_get_resource (&static_resource); +} +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_CONSTRUCTOR_H__ +#define __G_CONSTRUCTOR_H__ + +/* + If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and + destructors, in a usable way, including e.g. on library unload. If not you're on + your own. + + Some compilers need #pragma to handle this, which does not work with macros, + so the way you need to use this is (for constructors): + + #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA + #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) + #endif + G_DEFINE_CONSTRUCTOR(my_constructor) + static void my_constructor(void) { + ... + } + +*/ + +#ifndef __GTK_DOC_IGNORE__ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); +#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); + +#elif defined (_MSC_VER) && (_MSC_VER >= 1500) +/* Visual studio 2008 and later has _Pragma */ + +/* + * Only try to include gslist.h if not already included via glib.h, + * so that items using gconstructor.h outside of GLib (such as + * GResources) continue to build properly. + */ +#ifndef __G_LIB_H__ +#include "gslist.h" +#endif + +#include + +#define G_HAS_CONSTRUCTORS 1 + +/* We do some weird things to avoid the constructors being optimized + * away on VS2015 if WholeProgramOptimization is enabled. First we + * make a reference to the array from the wrapper to make sure its + * references. Then we use a pragma to make sure the wrapper function + * symbol is always included at the link stage. Also, the symbols + * need to be extern (but not dllexport), even though they are not + * really used from another object file. + */ + +/* We need to account for differences between the mangling of symbols + * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed + * with an underscore but symbols on x64/ARM/ARM64 are not. + */ +#ifdef _M_IX86 +#define G_MSVC_SYMBOL_PREFIX "_" +#else +#define G_MSVC_SYMBOL_PREFIX "" +#endif + +#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX) +#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX) + +#define G_MSVC_CTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; + +#define G_MSVC_DTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined (_MSC_VER) + +#define G_HAS_CONSTRUCTORS 1 + +/* Pre Visual studio 2008 must use #pragma section */ +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined(__SUNPRO_C) + +/* This is not tested, but i believe it should work, based on: + * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c + */ + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + init(_func) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + fini(_func) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); + +#else + +/* constructors not supported for this compiler */ + +#endif + +#endif /* __GTK_DOC_IGNORE__ */ +#endif /* __G_CONSTRUCTOR_H__ */ + +#ifdef G_HAS_CONSTRUCTORS + +#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(tfcresource_constructor) +#endif +G_DEFINE_CONSTRUCTOR(tfcresource_constructor) +#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(tfcresource_destructor) +#endif +G_DEFINE_DESTRUCTOR(tfcresource_destructor) + +#else +#warning "Constructor not supported on this compiler, linking in resources will not work" +#endif + +static void tfcresource_constructor (void) +{ + g_static_resource_init (&static_resource); +} + +static void tfcresource_destructor (void) +{ + g_static_resource_fini (&static_resource); +}