From 2ab018f7ddabcf1d3f0e7c806ceaca4db825495f Mon Sep 17 00:00:00 2001 From: Kolan Sh Date: Sun, 5 Feb 2017 17:32:09 +0300 Subject: [PATCH] Drawing Area, Rotated Text (Gtk3-demo) examples added. --- c/gtk/gtk3-demo/Drawing_Area/build.sh | 4 + c/gtk/gtk3-demo/Drawing_Area/main.c | 294 ++++++++++++++++++++++++++ c/gtk/gtk3-demo/RotadedText/build.sh | 4 + c/gtk/gtk3-demo/RotadedText/main.c | 229 ++++++++++++++++++++ 4 files changed, 531 insertions(+) create mode 100755 c/gtk/gtk3-demo/Drawing_Area/build.sh create mode 100644 c/gtk/gtk3-demo/Drawing_Area/main.c create mode 100755 c/gtk/gtk3-demo/RotadedText/build.sh create mode 100644 c/gtk/gtk3-demo/RotadedText/main.c diff --git a/c/gtk/gtk3-demo/Drawing_Area/build.sh b/c/gtk/gtk3-demo/Drawing_Area/build.sh new file mode 100755 index 0000000..a6e2f57 --- /dev/null +++ b/c/gtk/gtk3-demo/Drawing_Area/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/lib/colorgcc/bin/gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0` || exit -1 +./a.out diff --git a/c/gtk/gtk3-demo/Drawing_Area/main.c b/c/gtk/gtk3-demo/Drawing_Area/main.c new file mode 100644 index 0000000..a8f9bac --- /dev/null +++ b/c/gtk/gtk3-demo/Drawing_Area/main.c @@ -0,0 +1,294 @@ +#include + +static GtkWidget *window = NULL; +/* Pixmap for scribble area, to store current scribbles */ +static cairo_surface_t *surface = NULL; + +/* Create a new surface of the appropriate size to store our scribbles */ +static gboolean +scribble_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer data) +{ + GtkAllocation allocation; + cairo_t *cr; + + if (surface) + cairo_surface_destroy (surface); + + gtk_widget_get_allocation (widget, &allocation); + surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget), + CAIRO_CONTENT_COLOR, + allocation.width, + allocation.height); + + /* Initialize the surface to white */ + cr = cairo_create (surface); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_destroy (cr); + + /* We've handled the configure event, no need for further processing. */ + return TRUE; +} + +/* Redraw the screen from the surface */ +static gboolean +scribble_draw (GtkWidget *widget, + cairo_t *cr, + gpointer data) +{ + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + + return FALSE; +} + +/* Draw a rectangle on the screen */ +static void +draw_brush (GtkWidget *widget, + gdouble x, + gdouble y) +{ + GdkRectangle update_rect; + cairo_t *cr; + + update_rect.x = x - 3; + update_rect.y = y - 3; + update_rect.width = 6; + update_rect.height = 6; + + /* Paint to the surface, where we store our state */ + cr = cairo_create (surface); + + gdk_cairo_rectangle (cr, &update_rect); + cairo_fill (cr); + + cairo_destroy (cr); + + /* Now invalidate the affected region of the drawing area. */ + gdk_window_invalidate_rect (gtk_widget_get_window (widget), + &update_rect, + FALSE); +} + +static gboolean +scribble_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + if (surface == NULL) + return FALSE; /* paranoia check, in case we haven't gotten a configure event */ + + if (event->button == GDK_BUTTON_PRIMARY) + draw_brush (widget, event->x, event->y); + + /* We've handled the event, stop processing */ + return TRUE; +} + +static gboolean +scribble_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + int x, y; + GdkModifierType state; + + if (surface == NULL) + return FALSE; /* paranoia check, in case we haven't gotten a configure event */ + + /* This call is very important; it requests the next motion event. + * If you don't call gdk_window_get_pointer() you'll only get + * a single motion event. The reason is that we specified + * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events(). + * If we hadn't specified that, we could just use event->x, event->y + * as the pointer location. But we'd also get deluged in events. + * By requesting the next event as we handle the current one, + * we avoid getting a huge number of events faster than we + * can cope. + */ + + gdk_window_get_device_position (event->window, event->device, &x, &y, &state); + + if (state & GDK_BUTTON1_MASK) + draw_brush (widget, x, y); + + /* We've handled it, stop processing */ + return TRUE; +} + + +static gboolean +checkerboard_draw (GtkWidget *da, + cairo_t *cr, + gpointer data) +{ + gint i, j, xcount, ycount, width, height; + +#define CHECK_SIZE 10 +#define SPACING 2 + + /* At the start of a draw handler, a clip region has been set on + * the Cairo context, and the contents have been cleared to the + * widget's background color. The docs for + * gdk_window_begin_paint_region() give more details on how this + * works. + */ + + xcount = 0; + width = gtk_widget_get_allocated_width (da); + height = gtk_widget_get_allocated_height (da); + i = SPACING; + while (i < width) + { + j = SPACING; + ycount = xcount % 2; /* start with even/odd depending on row */ + while (j < height) + { + if (ycount % 2) + cairo_set_source_rgb (cr, 0.45777, 0, 0.45777); + else + cairo_set_source_rgb (cr, 1, 1, 1); + + /* If we're outside the clip, this will do nothing. + */ + cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE); + cairo_fill (cr); + + j += CHECK_SIZE + SPACING; + ++ycount; + } + + i += CHECK_SIZE + SPACING; + ++xcount; + } + + /* return TRUE because we've handled this event, so no + * further processing is required. + */ + return TRUE; +} + +static void +close_window (void) +{ + window = NULL; + + if (surface) + cairo_surface_destroy (surface); + surface = NULL; +} + +GtkWidget * +do_drawingarea (GtkWidget *do_widget) +{ + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *da; + GtkWidget *label; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "Drawing Area"); + + g_signal_connect (window, "destroy", + G_CALLBACK (close_window), NULL); + + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (window), vbox); + + /* + * Create the checkerboard area + */ + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "Checkerboard pattern"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + da = gtk_drawing_area_new (); + /* set a minimum size */ + gtk_widget_set_size_request (da, 100, 100); + + gtk_container_add (GTK_CONTAINER (frame), da); + + g_signal_connect (da, "draw", + G_CALLBACK (checkerboard_draw), NULL); + + /* + * Create the scribble area + */ + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "Scribble area"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + da = gtk_drawing_area_new (); + /* set a minimum size */ + gtk_widget_set_size_request (da, 100, 100); + + gtk_container_add (GTK_CONTAINER (frame), da); + + /* Signals used to handle backing surface */ + + g_signal_connect (da, "draw", + G_CALLBACK (scribble_draw), NULL); + g_signal_connect (da,"configure-event", + G_CALLBACK (scribble_configure_event), NULL); + + /* Event signals */ + + g_signal_connect (da, "motion-notify-event", + G_CALLBACK (scribble_motion_notify_event), NULL); + g_signal_connect (da, "button-press-event", + G_CALLBACK (scribble_button_press_event), NULL); + + + /* Ask to receive events the drawing area doesn't normally + * subscribe to + */ + gtk_widget_set_events (da, gtk_widget_get_events (da) + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + return window; +} + +int +main(int argc, char **argv) +{ + GtkWidget *window; + + gtk_init(&argc, &argv); + window = do_drawingarea(NULL); + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + gtk_main(); + return 0; +} diff --git a/c/gtk/gtk3-demo/RotadedText/build.sh b/c/gtk/gtk3-demo/RotadedText/build.sh new file mode 100755 index 0000000..a6e2f57 --- /dev/null +++ b/c/gtk/gtk3-demo/RotadedText/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/lib/colorgcc/bin/gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0` || exit -1 +./a.out diff --git a/c/gtk/gtk3-demo/RotadedText/main.c b/c/gtk/gtk3-demo/RotadedText/main.c new file mode 100644 index 0000000..250faf9 --- /dev/null +++ b/c/gtk/gtk3-demo/RotadedText/main.c @@ -0,0 +1,229 @@ +#include +#include + +#define HEART "♥" +const char text[] = "I ♥ GTK+"; + +static void +fancy_shape_renderer (cairo_t *cr, + PangoAttrShape *attr, + gboolean do_path, + gpointer data) +{ + double x, y; + cairo_get_current_point (cr, &x, &y); + cairo_translate (cr, x, y); + + cairo_scale (cr, + (double) attr->ink_rect.width / PANGO_SCALE, + (double) attr->ink_rect.height / PANGO_SCALE); + + switch (GPOINTER_TO_UINT (attr->data)) + { + case 0x2665: /* U+2665 BLACK HEART SUIT */ + { + cairo_move_to (cr, .5, .0); + cairo_line_to (cr, .9, -.4); + cairo_curve_to (cr, 1.1, -.8, .5, -.9, .5, -.5); + cairo_curve_to (cr, .5, -.9, -.1, -.8, .1, -.4); + cairo_close_path (cr); + } + break; + } + + if (!do_path) { + cairo_set_source_rgb (cr, 1., 0., 0.); + cairo_fill (cr); + } +} + +PangoAttrList * +create_fancy_attr_list_for_layout (PangoLayout *layout) +{ + PangoAttrList *attrs; + PangoFontMetrics *metrics; + int ascent; + PangoRectangle ink_rect, logical_rect; + const char *p; + + /* Get font metrics and prepare fancy shape size */ + metrics = pango_context_get_metrics (pango_layout_get_context (layout), + pango_layout_get_font_description (layout), + NULL); + ascent = pango_font_metrics_get_ascent (metrics); + logical_rect.x = 0; + logical_rect.width = ascent; + logical_rect.y = -ascent; + logical_rect.height = ascent; + ink_rect = logical_rect; + pango_font_metrics_unref (metrics); + + /* Set fancy shape attributes for all hearts */ + attrs = pango_attr_list_new (); + for (p = text; (p = strstr (p, HEART)); p += strlen (HEART)) + { + PangoAttribute *attr; + + attr = pango_attr_shape_new_with_data (&ink_rect, + &logical_rect, + GUINT_TO_POINTER (g_utf8_get_char (p)), + NULL, NULL); + + attr->start_index = p - text; + attr->end_index = attr->start_index + strlen (HEART); + + pango_attr_list_insert (attrs, attr); + } + + return attrs; +} + +static gboolean +rotated_text_draw (GtkWidget *widget, + cairo_t *cr, + gpointer data) +{ +#define RADIUS 150 +#define N_WORDS 5 +#define FONT "Serif 18" + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *desc; + + cairo_pattern_t *pattern; + + PangoAttrList *attrs; + + double device_radius; + int width, height; + int i; + + /* Create a cairo context and set up a transformation matrix so that the user + * space coordinates for the centered square where we draw are [-RADIUS, RADIUS], + * [-RADIUS, RADIUS]. + * We first center, then change the scale. */ + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + device_radius = MIN (width, height) / 2.; + cairo_translate (cr, + device_radius + (width - 2 * device_radius) / 2, + device_radius + (height - 2 * device_radius) / 2); + cairo_scale (cr, device_radius / RADIUS, device_radius / RADIUS); + + /* Create and a subtle gradient source and use it. */ + pattern = cairo_pattern_create_linear (-RADIUS, -RADIUS, RADIUS, RADIUS); + cairo_pattern_add_color_stop_rgb (pattern, 0., .5, .0, .0); + cairo_pattern_add_color_stop_rgb (pattern, 1., .0, .0, .5); + cairo_set_source (cr, pattern); + + /* Create a PangoContext and set up our shape renderer */ + context = gtk_widget_create_pango_context (widget); + pango_cairo_context_set_shape_renderer (context, + fancy_shape_renderer, + NULL, NULL); + + /* Create a PangoLayout, set the text, font, and attributes */ + layout = pango_layout_new (context); + pango_layout_set_text (layout, text, -1); + desc = pango_font_description_from_string (FONT); + pango_layout_set_font_description (layout, desc); + + attrs = create_fancy_attr_list_for_layout (layout); + pango_layout_set_attributes (layout, attrs); + pango_attr_list_unref (attrs); + + /* Draw the layout N_WORDS times in a circle */ + for (i = 0; i < N_WORDS; i++) + { + int width, height; + + /* Inform Pango to re-layout the text with the new transformation matrix */ + pango_cairo_update_layout (cr, layout); + + pango_layout_get_pixel_size (layout, &width, &height); + cairo_move_to (cr, - width / 2, - RADIUS * .9); + pango_cairo_show_layout (cr, layout); + + /* Rotate for the next turn */ + cairo_rotate (cr, G_PI*2 / N_WORDS); + } + + /* free the objects we created */ + pango_font_description_free (desc); + g_object_unref (layout); + g_object_unref (context); + cairo_pattern_destroy (pattern); + + return FALSE; +} + +GtkWidget * +do_rotated_text (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *box; + GtkWidget *drawing_area; + GtkWidget *label; + PangoLayout *layout; + PangoAttrList *attrs; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "Rotated Text"); + gtk_window_set_default_size (GTK_WINDOW (window), 4 * RADIUS, 2 * RADIUS); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous (GTK_BOX (box), TRUE); + gtk_container_add (GTK_CONTAINER (window), box); + + /* Add a drawing area */ + drawing_area = gtk_drawing_area_new (); + gtk_container_add (GTK_CONTAINER (box), drawing_area); + gtk_style_context_add_class (gtk_widget_get_style_context (drawing_area), + GTK_STYLE_CLASS_VIEW); + + g_signal_connect (drawing_area, "draw", + G_CALLBACK (rotated_text_draw), NULL); + + /* And a label */ + label = gtk_label_new (text); + gtk_container_add (GTK_CONTAINER (box), label); + + gtk_label_set_angle (GTK_LABEL (label), 45); + + /* Set up fancy stuff on the label */ + layout = gtk_label_get_layout (GTK_LABEL (label)); + pango_cairo_context_set_shape_renderer (pango_layout_get_context (layout), + fancy_shape_renderer, + NULL, NULL); + attrs = create_fancy_attr_list_for_layout (layout); + gtk_label_set_attributes (GTK_LABEL (label), attrs); + pango_attr_list_unref (attrs); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + return window; +} + +int +main(int argc, char **argv) +{ + GtkWidget *window; + + gtk_init(&argc, &argv); + window = do_rotated_text(NULL); + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + gtk_main(); + return 0; +}