2009-11-14 21:39:22 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
2010-11-10 22:00:45 +00:00
|
|
|
/*
|
|
|
|
* st-drawing-area.c: A dynamically-sized Cairo drawing area
|
|
|
|
*
|
|
|
|
* Copyright 2009, 2010 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This program 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 program is distributed in the hope 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 program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2009-11-14 21:39:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:st-drawing-area
|
|
|
|
* @short_description: A dynamically-sized Cairo drawing area
|
|
|
|
*
|
2019-11-12 18:43:38 +00:00
|
|
|
* #StDrawingArea allows drawing via Cairo; the primary difference is that
|
|
|
|
* it is dynamically sized. To use, connect to the #StDrawingArea::repaint
|
2009-11-14 21:39:22 +00:00
|
|
|
* signal, and inside the signal handler, call
|
2010-03-03 23:00:05 +00:00
|
|
|
* st_drawing_area_get_context() to get the Cairo context to draw to. The
|
|
|
|
* #StDrawingArea::repaint signal will be emitted by default when the area is
|
2009-11-14 21:39:22 +00:00
|
|
|
* resized or the CSS style changes; you can use the
|
2010-03-03 23:00:05 +00:00
|
|
|
* st_drawing_area_queue_repaint() as well.
|
2009-11-14 21:39:22 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "st-drawing-area.h"
|
|
|
|
|
|
|
|
#include <cairo.h>
|
2017-07-31 12:08:29 +00:00
|
|
|
#include <math.h>
|
2009-11-14 21:39:22 +00:00
|
|
|
|
2015-09-24 14:08:13 +00:00
|
|
|
typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate;
|
2009-11-14 21:39:22 +00:00
|
|
|
struct _StDrawingAreaPrivate {
|
2010-03-03 23:00:05 +00:00
|
|
|
cairo_t *context;
|
|
|
|
guint in_repaint : 1;
|
2009-11-14 21:39:22 +00:00
|
|
|
};
|
|
|
|
|
2015-09-24 16:04:48 +00:00
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (StDrawingArea, st_drawing_area, ST_TYPE_WIDGET);
|
|
|
|
|
2009-11-14 21:39:22 +00:00
|
|
|
/* Signals */
|
|
|
|
enum
|
|
|
|
{
|
2010-03-03 23:00:05 +00:00
|
|
|
REPAINT,
|
2009-11-14 21:39:22 +00:00
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 };
|
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
static gboolean
|
|
|
|
draw_content (ClutterCanvas *canvas,
|
|
|
|
cairo_t *cr,
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
gpointer user_data)
|
2010-03-03 23:00:05 +00:00
|
|
|
{
|
2015-09-26 01:11:12 +00:00
|
|
|
StDrawingArea *area = ST_DRAWING_AREA (user_data);
|
2015-09-24 14:08:13 +00:00
|
|
|
StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
|
2010-03-03 23:00:05 +00:00
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
priv->context = cr;
|
|
|
|
priv->in_repaint = TRUE;
|
|
|
|
|
|
|
|
clutter_cairo_clear (cr);
|
|
|
|
g_signal_emit (area, st_drawing_area_signals[REPAINT], 0);
|
2010-03-10 23:22:06 +00:00
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
priv->context = NULL;
|
|
|
|
priv->in_repaint = FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
2010-03-03 23:00:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-09-26 01:11:12 +00:00
|
|
|
st_drawing_area_allocate (ClutterActor *self,
|
2020-05-09 19:30:26 +00:00
|
|
|
const ClutterActorBox *box)
|
2009-11-14 21:39:22 +00:00
|
|
|
{
|
2010-03-03 23:00:05 +00:00
|
|
|
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
2015-09-26 01:11:12 +00:00
|
|
|
ClutterContent *content = clutter_actor_get_content (self);
|
2010-03-03 23:00:05 +00:00
|
|
|
ClutterActorBox content_box;
|
|
|
|
int width, height;
|
2017-07-31 12:08:29 +00:00
|
|
|
float resource_scale;
|
|
|
|
|
2020-05-28 12:39:11 +00:00
|
|
|
resource_scale = clutter_actor_get_resource_scale (self);
|
2010-03-03 23:00:05 +00:00
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
clutter_actor_set_allocation (self, box);
|
2015-09-26 01:11:12 +00:00
|
|
|
st_theme_node_get_content_box (theme_node, box, &content_box);
|
2009-11-14 21:39:22 +00:00
|
|
|
|
2010-03-03 23:00:05 +00:00
|
|
|
width = (int)(0.5 + content_box.x2 - content_box.x1);
|
|
|
|
height = (int)(0.5 + content_box.y2 - content_box.y1);
|
2017-07-31 12:08:29 +00:00
|
|
|
|
|
|
|
clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
|
2015-09-26 01:11:12 +00:00
|
|
|
clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height);
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_drawing_area_style_changed (StWidget *self)
|
|
|
|
{
|
|
|
|
(ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self);
|
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
st_drawing_area_queue_repaint (ST_DRAWING_AREA (self));
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 12:08:29 +00:00
|
|
|
static void
|
2020-05-28 12:43:26 +00:00
|
|
|
st_drawing_area_resource_scale_changed (ClutterActor *self)
|
2017-07-31 12:08:29 +00:00
|
|
|
{
|
|
|
|
float resource_scale;
|
2020-05-28 12:43:26 +00:00
|
|
|
ClutterContent *content = clutter_actor_get_content (self);
|
2017-07-31 12:08:29 +00:00
|
|
|
|
2020-05-28 12:43:26 +00:00
|
|
|
resource_scale = clutter_actor_get_resource_scale (self);
|
2020-05-28 12:39:11 +00:00
|
|
|
clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
|
2020-05-28 12:43:26 +00:00
|
|
|
|
|
|
|
if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed)
|
|
|
|
CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self);
|
2017-07-31 12:08:29 +00:00
|
|
|
}
|
|
|
|
|
2009-11-14 21:39:22 +00:00
|
|
|
static void
|
|
|
|
st_drawing_area_class_init (StDrawingAreaClass *klass)
|
|
|
|
{
|
|
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
actor_class->allocate = st_drawing_area_allocate;
|
2009-11-14 21:39:22 +00:00
|
|
|
widget_class->style_changed = st_drawing_area_style_changed;
|
2020-05-28 12:43:26 +00:00
|
|
|
actor_class->resource_scale_changed = st_drawing_area_resource_scale_changed;
|
2009-11-14 21:39:22 +00:00
|
|
|
|
2010-03-03 23:00:05 +00:00
|
|
|
st_drawing_area_signals[REPAINT] =
|
|
|
|
g_signal_new ("repaint",
|
2009-11-14 21:39:22 +00:00
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
2010-03-03 23:00:05 +00:00
|
|
|
G_STRUCT_OFFSET (StDrawingAreaClass, repaint),
|
2011-10-18 22:17:49 +00:00
|
|
|
NULL, NULL, NULL,
|
2010-03-03 23:00:05 +00:00
|
|
|
G_TYPE_NONE, 0);
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_drawing_area_init (StDrawingArea *area)
|
|
|
|
{
|
2015-09-26 01:11:12 +00:00
|
|
|
ClutterContent *content = clutter_canvas_new ();
|
|
|
|
g_signal_connect (content, "draw", G_CALLBACK (draw_content), area);
|
|
|
|
clutter_actor_set_content (CLUTTER_ACTOR (area), content);
|
|
|
|
g_object_unref (content);
|
2010-03-03 23:00:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* st_drawing_area_queue_repaint:
|
|
|
|
* @area: the #StDrawingArea
|
|
|
|
*
|
2020-05-20 23:50:09 +00:00
|
|
|
* Will cause the actor to emit a #StDrawingArea::repaint signal before it is
|
|
|
|
* next drawn to the scene. Useful if some parameters for the area being
|
2010-03-03 23:00:05 +00:00
|
|
|
* drawn other than the size or style have changed. Note that
|
|
|
|
* clutter_actor_queue_redraw() will simply result in the same
|
|
|
|
* contents being drawn to the scene again.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
st_drawing_area_queue_repaint (StDrawingArea *area)
|
|
|
|
{
|
|
|
|
g_return_if_fail (ST_IS_DRAWING_AREA (area));
|
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
clutter_content_invalidate (clutter_actor_get_content (CLUTTER_ACTOR (area)));
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-03-03 23:00:05 +00:00
|
|
|
* st_drawing_area_get_context:
|
|
|
|
* @area: the #StDrawingArea
|
2009-11-14 21:39:22 +00:00
|
|
|
*
|
2010-03-03 23:00:05 +00:00
|
|
|
* Gets the Cairo context to paint to. This function must only be called
|
2020-08-19 09:26:11 +00:00
|
|
|
* from a signal handler or virtual function for the #StDrawingArea::repaint
|
2020-05-20 23:50:09 +00:00
|
|
|
* signal.
|
2010-03-03 23:00:05 +00:00
|
|
|
*
|
2020-05-20 23:50:09 +00:00
|
|
|
* JavaScript code must call the special dispose function before returning from
|
|
|
|
* the signal handler or virtual function to avoid leaking memory:
|
|
|
|
*
|
|
|
|
* |[<!-- language="JavaScript" -->
|
|
|
|
* function onRepaint(area) {
|
|
|
|
* let cr = area.get_context();
|
|
|
|
*
|
|
|
|
* // Draw to the context
|
|
|
|
*
|
|
|
|
* cr.$dispose();
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* let area = new St.DrawingArea();
|
|
|
|
* area.connect('repaint', onRepaint);
|
|
|
|
* ]|
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): the Cairo context for the paint operation
|
2009-11-14 21:39:22 +00:00
|
|
|
*/
|
2010-03-03 23:00:05 +00:00
|
|
|
cairo_t *
|
|
|
|
st_drawing_area_get_context (StDrawingArea *area)
|
2009-11-14 21:39:22 +00:00
|
|
|
{
|
2015-09-24 14:08:13 +00:00
|
|
|
StDrawingAreaPrivate *priv;
|
|
|
|
|
2010-03-03 23:00:05 +00:00
|
|
|
g_return_val_if_fail (ST_IS_DRAWING_AREA (area), NULL);
|
|
|
|
|
2015-09-24 14:08:13 +00:00
|
|
|
priv = st_drawing_area_get_instance_private (area);
|
|
|
|
g_return_val_if_fail (priv->in_repaint, NULL);
|
|
|
|
|
|
|
|
return priv->context;
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-03-03 23:00:05 +00:00
|
|
|
* st_drawing_area_get_surface_size:
|
|
|
|
* @area: the #StDrawingArea
|
2020-05-20 23:50:09 +00:00
|
|
|
* @width: (out) (optional): location to store the width of the painted area
|
|
|
|
* @height: (out) (optional): location to store the height of the painted area
|
2009-11-14 21:39:22 +00:00
|
|
|
*
|
2010-03-03 23:00:05 +00:00
|
|
|
* Gets the size of the cairo surface being painted to, which is equal
|
|
|
|
* to the size of the content area of the widget. This function must
|
2020-08-19 09:26:11 +00:00
|
|
|
* only be called from a signal handler for the #StDrawingArea::repaint signal.
|
2009-11-14 21:39:22 +00:00
|
|
|
*/
|
|
|
|
void
|
2010-03-03 23:00:05 +00:00
|
|
|
st_drawing_area_get_surface_size (StDrawingArea *area,
|
|
|
|
guint *width,
|
|
|
|
guint *height)
|
2009-11-14 21:39:22 +00:00
|
|
|
{
|
2010-03-03 23:00:05 +00:00
|
|
|
StDrawingAreaPrivate *priv;
|
2015-09-26 01:11:12 +00:00
|
|
|
ClutterContent *content;
|
2017-07-31 12:08:29 +00:00
|
|
|
float w, h, resource_scale;
|
2010-03-03 23:00:05 +00:00
|
|
|
|
|
|
|
g_return_if_fail (ST_IS_DRAWING_AREA (area));
|
|
|
|
|
2015-09-24 14:08:13 +00:00
|
|
|
priv = st_drawing_area_get_instance_private (area);
|
|
|
|
g_return_if_fail (priv->in_repaint);
|
2010-03-03 23:00:05 +00:00
|
|
|
|
2015-09-26 01:11:12 +00:00
|
|
|
content = clutter_actor_get_content (CLUTTER_ACTOR (area));
|
|
|
|
clutter_content_get_preferred_size (content, &w, &h);
|
|
|
|
|
2020-05-28 12:39:11 +00:00
|
|
|
resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (area));
|
|
|
|
|
|
|
|
w /= resource_scale;
|
|
|
|
h /= resource_scale;
|
2017-07-31 12:08:29 +00:00
|
|
|
|
2010-03-03 23:00:05 +00:00
|
|
|
if (width)
|
2017-07-31 12:08:29 +00:00
|
|
|
*width = ceilf (w);
|
2010-03-03 23:00:05 +00:00
|
|
|
if (height)
|
2017-07-31 12:08:29 +00:00
|
|
|
*height = ceilf (h);
|
2009-11-14 21:39:22 +00:00
|
|
|
}
|