mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-27 22:20:24 +00:00
d732c869c2
Add the names of the main widgets as keywords to our demos, but also things like "game". This helps finding relevant demos in our growing list. You can now for example type "label", and find the "error states" and "links" demos showing GtkLabel features.
382 lines
10 KiB
C
382 lines
10 KiB
C
/* Drawing Area
|
|
* #Keywords: GtkDrawingArea
|
|
*
|
|
* GtkDrawingArea is a blank area where you can draw custom displays
|
|
* of various kinds.
|
|
*
|
|
* This demo has two drawing areas. The checkerboard area shows
|
|
* how you can just draw something; all you have to do is set a function
|
|
* via gtk_drawing_area_set_draw_func(), as shown here.
|
|
*
|
|
* The "scribble" area is a bit more advanced, and shows how to handle
|
|
* events such as button presses and mouse motion. Click the mouse
|
|
* and drag in the scribble area to draw squiggles. Resize the window
|
|
* to clear the area.
|
|
*/
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
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 void
|
|
create_surface (GtkWidget *widget)
|
|
{
|
|
cairo_t *cr;
|
|
|
|
if (surface)
|
|
cairo_surface_destroy (surface);
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
gtk_widget_get_width (widget),
|
|
gtk_widget_get_height (widget));
|
|
|
|
/* Initialize the surface to white */
|
|
cr = cairo_create (surface);
|
|
|
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
|
cairo_paint (cr);
|
|
|
|
cairo_destroy (cr);
|
|
}
|
|
|
|
static void
|
|
scribble_resize (GtkWidget *widget,
|
|
int width,
|
|
int height)
|
|
{
|
|
create_surface (widget);
|
|
}
|
|
|
|
/* Redraw the screen from the surface */
|
|
static void
|
|
scribble_draw (GtkDrawingArea *da,
|
|
cairo_t *cr,
|
|
int width,
|
|
int height,
|
|
gpointer data)
|
|
{
|
|
cairo_set_source_surface (cr, surface, 0, 0);
|
|
cairo_paint (cr);
|
|
}
|
|
|
|
/* Draw a rectangle on the screen */
|
|
static void
|
|
draw_brush (GtkWidget *widget,
|
|
double x,
|
|
double y)
|
|
{
|
|
GdkRectangle update_rect;
|
|
cairo_t *cr;
|
|
|
|
if (surface == NULL ||
|
|
cairo_image_surface_get_width (surface) != gtk_widget_get_width (widget) ||
|
|
cairo_image_surface_get_height (surface) != gtk_widget_get_height (widget))
|
|
create_surface (widget);
|
|
|
|
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);
|
|
|
|
gtk_widget_queue_draw (widget);
|
|
}
|
|
|
|
static double start_x;
|
|
static double start_y;
|
|
|
|
static void
|
|
drag_begin (GtkGestureDrag *gesture,
|
|
double x,
|
|
double y,
|
|
GtkWidget *area)
|
|
{
|
|
start_x = x;
|
|
start_y = y;
|
|
|
|
draw_brush (area, x, y);
|
|
}
|
|
|
|
static void
|
|
drag_update (GtkGestureDrag *gesture,
|
|
double x,
|
|
double y,
|
|
GtkWidget *area)
|
|
{
|
|
draw_brush (area, start_x + x, start_y + y);
|
|
}
|
|
|
|
static void
|
|
drag_end (GtkGestureDrag *gesture,
|
|
double x,
|
|
double y,
|
|
GtkWidget *area)
|
|
{
|
|
draw_brush (area, start_x + x, start_y + y);
|
|
}
|
|
|
|
static void
|
|
oval_path (cairo_t *cr,
|
|
double xc, double yc,
|
|
double xr, double yr)
|
|
{
|
|
cairo_save (cr);
|
|
|
|
cairo_translate (cr, xc, yc);
|
|
cairo_scale (cr, 1.0, yr / xr);
|
|
cairo_move_to (cr, xr, 0.0);
|
|
cairo_arc (cr,
|
|
0, 0,
|
|
xr,
|
|
0, 2 * G_PI);
|
|
cairo_close_path (cr);
|
|
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
/* Fill the given area with checks in the standard style
|
|
* for showing compositing effects.
|
|
*
|
|
* It would make sense to do this as a repeating surface,
|
|
* but most implementations of RENDER currently have broken
|
|
* implementations of repeat + transform, even when the
|
|
* transform is a translation.
|
|
*/
|
|
static void
|
|
fill_checks (cairo_t *cr,
|
|
int x, int y,
|
|
int width, int height)
|
|
{
|
|
int i, j;
|
|
|
|
#define CHECK_SIZE 16
|
|
|
|
cairo_rectangle (cr, x, y, width, height);
|
|
cairo_set_source_rgb (cr, 0.4, 0.4, 0.4);
|
|
cairo_fill (cr);
|
|
|
|
/* Only works for CHECK_SIZE a power of 2 */
|
|
j = x & (-CHECK_SIZE);
|
|
|
|
for (; j < height; j += CHECK_SIZE)
|
|
{
|
|
i = y & (-CHECK_SIZE);
|
|
for (; i < width; i += CHECK_SIZE)
|
|
if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0)
|
|
cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
|
|
}
|
|
|
|
cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
|
|
cairo_fill (cr);
|
|
|
|
#undef CHECK_SIZE
|
|
}
|
|
|
|
/* Draw a red, green, and blue circle equally spaced inside
|
|
* the larger circle of radius r at (xc, yc)
|
|
*/
|
|
static void
|
|
draw_3circles (cairo_t *cr,
|
|
double xc, double yc,
|
|
double radius,
|
|
double alpha)
|
|
{
|
|
double subradius = radius * (2 / 3. - 0.1);
|
|
|
|
cairo_set_source_rgba (cr, 1., 0., 0., alpha);
|
|
oval_path (cr,
|
|
xc + radius / 3. * cos (G_PI * (0.5)),
|
|
yc - radius / 3. * sin (G_PI * (0.5)),
|
|
subradius, subradius);
|
|
cairo_fill (cr);
|
|
|
|
cairo_set_source_rgba (cr, 0., 1., 0., alpha);
|
|
oval_path (cr,
|
|
xc + radius / 3. * cos (G_PI * (0.5 + 2/.3)),
|
|
yc - radius / 3. * sin (G_PI * (0.5 + 2/.3)),
|
|
subradius, subradius);
|
|
cairo_fill (cr);
|
|
|
|
cairo_set_source_rgba (cr, 0., 0., 1., alpha);
|
|
oval_path (cr,
|
|
xc + radius / 3. * cos (G_PI * (0.5 + 4/.3)),
|
|
yc - radius / 3. * sin (G_PI * (0.5 + 4/.3)),
|
|
subradius, subradius);
|
|
cairo_fill (cr);
|
|
}
|
|
|
|
static void
|
|
groups_draw (GtkDrawingArea *darea,
|
|
cairo_t *cr,
|
|
int width,
|
|
int height,
|
|
gpointer data)
|
|
{
|
|
cairo_surface_t *overlay, *punch, *circles;
|
|
cairo_t *overlay_cr, *punch_cr, *circles_cr;
|
|
|
|
/* Fill the background */
|
|
double radius = 0.5 * (width < height ? width : height) - 10;
|
|
double xc = width / 2.;
|
|
double yc = height / 2.;
|
|
|
|
overlay = cairo_surface_create_similar (cairo_get_target (cr),
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
width, height);
|
|
|
|
punch = cairo_surface_create_similar (cairo_get_target (cr),
|
|
CAIRO_CONTENT_ALPHA,
|
|
width, height);
|
|
|
|
circles = cairo_surface_create_similar (cairo_get_target (cr),
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
width, height);
|
|
|
|
fill_checks (cr, 0, 0, width, height);
|
|
|
|
/* Draw a black circle on the overlay
|
|
*/
|
|
overlay_cr = cairo_create (overlay);
|
|
cairo_set_source_rgb (overlay_cr, 0., 0., 0.);
|
|
oval_path (overlay_cr, xc, yc, radius, radius);
|
|
cairo_fill (overlay_cr);
|
|
|
|
/* Draw 3 circles to the punch surface, then cut
|
|
* that out of the main circle in the overlay
|
|
*/
|
|
punch_cr = cairo_create (punch);
|
|
draw_3circles (punch_cr, xc, yc, radius, 1.0);
|
|
cairo_destroy (punch_cr);
|
|
|
|
cairo_set_operator (overlay_cr, CAIRO_OPERATOR_DEST_OUT);
|
|
cairo_set_source_surface (overlay_cr, punch, 0, 0);
|
|
cairo_paint (overlay_cr);
|
|
|
|
/* Now draw the 3 circles in a subgroup again
|
|
* at half intensity, and use OperatorAdd to join up
|
|
* without seams.
|
|
*/
|
|
circles_cr = cairo_create (circles);
|
|
|
|
cairo_set_operator (circles_cr, CAIRO_OPERATOR_OVER);
|
|
draw_3circles (circles_cr, xc, yc, radius, 0.5);
|
|
cairo_destroy (circles_cr);
|
|
|
|
cairo_set_operator (overlay_cr, CAIRO_OPERATOR_ADD);
|
|
cairo_set_source_surface (overlay_cr, circles, 0, 0);
|
|
cairo_paint (overlay_cr);
|
|
|
|
cairo_destroy (overlay_cr);
|
|
|
|
cairo_set_source_surface (cr, overlay, 0, 0);
|
|
cairo_paint (cr);
|
|
|
|
cairo_surface_destroy (overlay);
|
|
cairo_surface_destroy (punch);
|
|
cairo_surface_destroy (circles);
|
|
}
|
|
|
|
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;
|
|
GtkGesture *drag;
|
|
|
|
if (!window)
|
|
{
|
|
window = gtk_window_new ();
|
|
gtk_window_set_display (GTK_WINDOW (window),
|
|
gtk_widget_get_display (do_widget));
|
|
gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (close_window), NULL);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
|
|
gtk_widget_set_margin_start (vbox, 16);
|
|
gtk_widget_set_margin_end (vbox, 16);
|
|
gtk_widget_set_margin_top (vbox, 16);
|
|
gtk_widget_set_margin_bottom (vbox, 16);
|
|
gtk_window_set_child (GTK_WINDOW (window), vbox);
|
|
|
|
/*
|
|
* Create the groups area
|
|
*/
|
|
label = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (label),
|
|
"<u>Knockout groups</u>");
|
|
gtk_box_append (GTK_BOX (vbox), label);
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_widget_set_vexpand (frame, TRUE);
|
|
gtk_box_append (GTK_BOX (vbox), frame);
|
|
|
|
da = gtk_drawing_area_new ();
|
|
gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (da), 100);
|
|
gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (da), 100);
|
|
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), groups_draw, NULL, NULL);
|
|
gtk_frame_set_child (GTK_FRAME (frame), da);
|
|
|
|
/*
|
|
* Create the scribble area
|
|
*/
|
|
|
|
label = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (label),
|
|
"<u>Scribble area</u>");
|
|
gtk_box_append (GTK_BOX (vbox), label);
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_widget_set_vexpand (frame, TRUE);
|
|
gtk_box_append (GTK_BOX (vbox), frame);
|
|
|
|
da = gtk_drawing_area_new ();
|
|
gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (da), 100);
|
|
gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (da), 100);
|
|
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), scribble_draw, NULL, NULL);
|
|
gtk_frame_set_child (GTK_FRAME (frame), da);
|
|
|
|
g_signal_connect (da, "resize",
|
|
G_CALLBACK (scribble_resize), NULL);
|
|
|
|
drag = gtk_gesture_drag_new ();
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
|
|
gtk_widget_add_controller (da, GTK_EVENT_CONTROLLER (drag));
|
|
|
|
g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), da);
|
|
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
|
|
g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
|
|
|
|
}
|
|
|
|
if (!gtk_widget_get_visible (window))
|
|
gtk_widget_show (window);
|
|
else
|
|
gtk_window_destroy (GTK_WINDOW (window));
|
|
|
|
return window;
|
|
}
|