368 lines
12 KiB
C
368 lines
12 KiB
C
#include <assert.h>
|
|
#include <pango/pangocairo.h>
|
|
#include <math.h>
|
|
#include "cairo-text.h"
|
|
|
|
#define FONT "Sans Bold 10"
|
|
|
|
enum
|
|
{
|
|
NO_PROP,
|
|
C_TEXT,
|
|
C_FONT_FAMILY,
|
|
C_FONT_SIZE,
|
|
C_FONT_STYLE,
|
|
C_FONT_WEIGHT,
|
|
C_COLOR,
|
|
C_WRAP,
|
|
N_PROPERTIES
|
|
};
|
|
static GParamSpec *g_prop[N_PROPERTIES] = {0};
|
|
|
|
typedef struct _DiyaCairoTextPrivate
|
|
{
|
|
DiyaCairoWidget parent_object;
|
|
|
|
gchar *text;
|
|
gchar *family;
|
|
int size;
|
|
guint style;
|
|
guint wrap;
|
|
guint weight;
|
|
diya_color_t color;
|
|
} DiyaCairoTextPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(DiyaCairoText, diya_cairo_text, DIYA_TYPE_CAIRO_WIDGET)
|
|
|
|
static void diya_cairo_text_dispose(GObject *object)
|
|
{
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
g_debug("diya_cairo_text_dispose: %s", diya_object_to_string(object));
|
|
if (priv->text)
|
|
{
|
|
g_free(priv->text);
|
|
}
|
|
if (priv->family)
|
|
{
|
|
g_free(priv->family);
|
|
}
|
|
G_OBJECT_CLASS(diya_cairo_text_parent_class)->dispose(object);
|
|
}
|
|
|
|
static void diya_cairo_text_init(DiyaCairoText *self)
|
|
{
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
priv->text = NULL;
|
|
priv->family = NULL;
|
|
priv->size = 10;
|
|
priv->style = DIYA_CAIRO_FONT_STYLE_NORMAL;
|
|
priv->weight = DIYA_CAIRO_FONT_WEIGHT_NORMAL;
|
|
priv->wrap = DIYA_CAIRO_TEXT_WRAP_WORD;
|
|
}
|
|
|
|
static void diya_cairo_text_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
switch (property_id)
|
|
{
|
|
case C_TEXT:
|
|
if (priv->text)
|
|
{
|
|
g_free(priv->text);
|
|
}
|
|
priv->text = g_strdup(g_value_get_pointer(value));
|
|
break;
|
|
case C_FONT_FAMILY:
|
|
if (priv->family)
|
|
{
|
|
g_free(priv->family);
|
|
}
|
|
priv->family = g_strdup(g_value_get_pointer(value));
|
|
break;
|
|
case C_FONT_SIZE:
|
|
priv->size = g_value_get_int(value);
|
|
break;
|
|
case C_FONT_STYLE:
|
|
priv->style = g_value_get_uint(value);
|
|
break;
|
|
case C_WRAP:
|
|
priv->wrap = g_value_get_uint(value);
|
|
break;
|
|
case C_FONT_WEIGHT:
|
|
priv->weight = g_value_get_uint(value);
|
|
break;
|
|
case C_COLOR:
|
|
memcpy(&priv->color,g_value_get_pointer(value), sizeof(priv->color));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void diya_cairo_text_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
switch (property_id)
|
|
{
|
|
case C_TEXT:
|
|
g_value_set_pointer(value, priv->text);
|
|
break;
|
|
case C_FONT_FAMILY:
|
|
g_value_set_pointer(value, priv->family);
|
|
break;
|
|
case C_FONT_SIZE:
|
|
g_value_set_int(value, priv->size);
|
|
break;
|
|
case C_FONT_STYLE:
|
|
g_value_set_uint(value, priv->style);
|
|
break;
|
|
case C_WRAP:
|
|
g_value_set_uint(value, priv->wrap);
|
|
break;
|
|
case C_FONT_WEIGHT:
|
|
g_value_set_uint(value, priv->weight);
|
|
break;
|
|
case C_COLOR:
|
|
g_value_set_pointer(value, &priv->color);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static PangoFontDescription *diya_cairo_text_get_pango_font_desc(DiyaCairoText *self)
|
|
{
|
|
PangoFontDescription *desc = pango_font_description_new();
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
if (priv->family)
|
|
{
|
|
pango_font_description_set_family(desc, priv->family);
|
|
}
|
|
pango_font_description_set_size(desc, priv->size * PANGO_SCALE);
|
|
pango_font_description_set_style(desc, priv->style);
|
|
pango_font_description_set_weight(desc, priv->weight);
|
|
return desc;
|
|
}
|
|
|
|
static gboolean diya_cairo_text_draw(gpointer object, cairo_t *context)
|
|
{
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
if(!diya_cairo_widget_is_damaged(object) || !diya_cairo_widget_is_visible(object))
|
|
{
|
|
return FALSE;
|
|
}
|
|
DIYA_CAIRO_WIDGET_CLASS(diya_cairo_text_parent_class)->draw(object, context);
|
|
diya_rect_t view_port = diya_cairo_widget_get_view_port(object);
|
|
diya_rect_dump("diya_cairo_text_draw: ", &view_port);
|
|
|
|
if(diya_rect_is_empty(&view_port))
|
|
{
|
|
return FALSE;
|
|
}
|
|
cairo_save(context);
|
|
cairo_rectangle(context, view_port.x, view_port.y, view_port.width, view_port.height);
|
|
cairo_clip(context);
|
|
|
|
diya_rect_t extent = diya_cairo_widget_get_extent(object);
|
|
g_warning("diya_cairo_widget_get_extent: width %d, %d", extent.width, extent.height);
|
|
diya_offset_t * padding, *margin;
|
|
diya_stroke_t *stroke;
|
|
diya_color_t *color;
|
|
guint halign, valign;
|
|
g_object_get(object,
|
|
"stroke", &stroke,
|
|
"halign", &halign,
|
|
"valign", &valign,
|
|
"margin", &margin,
|
|
"padding", &padding,
|
|
"color", &color,
|
|
NULL);
|
|
gint stroke_size = ceil(stroke->size);
|
|
diya_point_t gpos = diya_cairo_widget_get_global_position(object);
|
|
|
|
PangoFontDescription *desc = diya_cairo_text_get_pango_font_desc(object);
|
|
cairo_set_source_rgba(context, color->r, color->g, color->b, color->a);
|
|
int display_w = extent.width - padding->left - padding->right - margin->left - margin->right - 2*stroke_size;
|
|
int display_h = extent.height - padding->top - padding->bottom - margin->top - margin->bottom - 2*stroke_size;
|
|
int off_x = padding->left + margin->left + stroke_size;
|
|
int off_y = padding->top + margin->top + stroke_size;
|
|
|
|
PangoLayout *layout = pango_cairo_create_layout(context);
|
|
// take into account my padding setting
|
|
pango_layout_set_text(layout, priv->text, -1);
|
|
pango_layout_set_font_description(layout, desc);
|
|
pango_layout_set_width(layout,display_w*PANGO_SCALE);
|
|
pango_layout_set_height(layout, display_h*PANGO_SCALE);
|
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
|
pango_layout_set_wrap(layout, priv->wrap);
|
|
switch(halign)
|
|
{
|
|
case DIYA_CAIRO_WIDGET_ALIGN_START:
|
|
pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
|
|
break;
|
|
case DIYA_CAIRO_WIDGET_ALIGN_MIDDLE:
|
|
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
|
break;
|
|
case DIYA_CAIRO_WIDGET_ALIGN_END:
|
|
pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
|
|
break;
|
|
}
|
|
PangoRectangle text_rect = {0};
|
|
pango_layout_get_extents(layout, NULL, &text_rect);
|
|
pango_extents_to_pixels(&text_rect, NULL);
|
|
// draw text
|
|
switch (valign)
|
|
{
|
|
case DIYA_CAIRO_WIDGET_ALIGN_MIDDLE:
|
|
off_y += (display_h - text_rect.height)/ 2;
|
|
break;
|
|
case DIYA_CAIRO_WIDGET_ALIGN_END:
|
|
off_y += display_h - text_rect.height;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
cairo_move_to(context, gpos.x + off_x, gpos.y + off_y);
|
|
pango_cairo_update_layout(context, layout);
|
|
pango_cairo_show_layout(context, layout);
|
|
|
|
cairo_restore(context);
|
|
pango_font_description_free(desc);
|
|
g_object_unref(layout);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void diya_cairo_text_content_size(gpointer object, int *width, int *height)
|
|
{
|
|
diya_offset_t* padding, *margin;
|
|
diya_stroke_t * stroke;
|
|
diya_rect_t * def_xtent;
|
|
guint hspace,vspace;
|
|
g_object_get(object,
|
|
"padding", &padding,
|
|
"margin", &margin,
|
|
"stroke", &stroke,
|
|
"hspace", &hspace,
|
|
"vspace", &vspace,
|
|
"default-extent", &def_xtent,
|
|
NULL);
|
|
gint stroke_size = ceil(stroke->size) * 2;
|
|
*width = padding->left + padding->right + margin->left + margin->right + stroke_size;
|
|
*height = padding->top + padding->bottom + margin->top + margin->bottom + stroke_size;
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
// diya_rect_t default_extent = DIYA_CAIRO_WIDGET_GET_CLASS(object)->default_extent(object);
|
|
int text_width = 0;
|
|
int text_height = 0;
|
|
if (priv->text)
|
|
{
|
|
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
|
|
cairo_t *context = cairo_create(surface);
|
|
PangoLayout *layout = pango_cairo_create_layout(context);
|
|
PangoFontDescription *desc = diya_cairo_text_get_pango_font_desc(object);
|
|
pango_layout_set_font_description(layout, desc);
|
|
pango_layout_set_text(layout, priv->text, -1);
|
|
pango_layout_set_width(layout, -1);
|
|
pango_layout_set_height(layout, -1);
|
|
pango_layout_set_single_paragraph_mode(layout, FALSE);
|
|
//pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_MIDDLE);
|
|
PangoRectangle rect = {0};
|
|
pango_layout_get_extents(layout, NULL, &rect);
|
|
pango_extents_to_pixels(&rect, NULL);
|
|
//rect.width += 4;
|
|
text_width = rect.width;
|
|
text_height = rect.height;
|
|
cairo_destroy(context);
|
|
cairo_surface_destroy(surface);
|
|
pango_font_description_free(desc);
|
|
g_object_unref(layout);
|
|
}
|
|
|
|
switch (hspace)
|
|
{
|
|
case DIYA_CAIRO_WIDGET_FILL_NONE:
|
|
*width = def_xtent->width;
|
|
break;
|
|
default:
|
|
*width += text_width;
|
|
break;
|
|
}
|
|
switch (vspace)
|
|
{
|
|
case DIYA_CAIRO_WIDGET_FILL_NONE:
|
|
*height = def_xtent->height;
|
|
break;
|
|
default:
|
|
*height += text_height;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void diya_cairo_text_class_init(DiyaCairoTextClass *class)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
|
|
DiyaCairoWidgetClass *wclass = DIYA_CAIRO_WIDGET_CLASS(class);
|
|
|
|
class->set_text = diya_cairo_text_set_text;
|
|
class->get_text = diya_cairo_text_get_text;
|
|
|
|
wclass->draw = diya_cairo_text_draw;
|
|
wclass->content_size = diya_cairo_text_content_size;
|
|
|
|
DIYA_CAIRO_WIDGET_SET_NAME(class, "DiyaCairoText");
|
|
gobject_class->dispose = diya_cairo_text_dispose;
|
|
gobject_class->set_property = diya_cairo_text_set_property;
|
|
gobject_class->get_property = diya_cairo_text_get_property;
|
|
|
|
g_prop[C_TEXT] = g_param_spec_pointer("text", NULL, "text content", G_PARAM_READWRITE);
|
|
g_prop[C_FONT_FAMILY] = g_param_spec_pointer("font-family", NULL, "text font face", G_PARAM_READWRITE);
|
|
g_prop[C_COLOR] = g_param_spec_pointer("color", NULL, "text color", G_PARAM_READWRITE);
|
|
|
|
g_prop[C_FONT_STYLE] = g_param_spec_uint(
|
|
"font-style", NULL, "text font style",
|
|
DIYA_CAIRO_FONT_STYLE_NORMAL,
|
|
DIYA_CAIRO_FONT_STYLE_ITALIC,
|
|
DIYA_CAIRO_FONT_STYLE_NORMAL,
|
|
G_PARAM_READWRITE);
|
|
g_prop[C_FONT_WEIGHT] = g_param_spec_uint(
|
|
"font-weight", NULL, "text font weight",
|
|
PANGO_WEIGHT_THIN,
|
|
PANGO_WEIGHT_ULTRAHEAVY,
|
|
DIYA_CAIRO_FONT_WEIGHT_NORMAL,
|
|
G_PARAM_READWRITE);
|
|
g_prop[C_FONT_SIZE] = g_param_spec_int(
|
|
"font-size", NULL, "text font size",
|
|
0,
|
|
G_MAXINT,
|
|
10,
|
|
G_PARAM_READWRITE);
|
|
g_prop[C_WRAP] = g_param_spec_uint(
|
|
"wrap", NULL, "text wrap mode",
|
|
DIYA_CAIRO_TEXT_WRAP_WORD,
|
|
DIYA_CAIRO_TEXT_WRAP_WORD_CHAR,
|
|
DIYA_CAIRO_TEXT_WRAP_WORD,
|
|
G_PARAM_READWRITE);
|
|
|
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, g_prop);
|
|
}
|
|
|
|
void diya_cairo_text_set_text(gpointer object, const gchar *text)
|
|
{
|
|
assert(DIYA_IS_CAIRO_TEXT(object));
|
|
g_object_set(object, "text", text, NULL);
|
|
}
|
|
const gchar *diya_cairo_text_get_text(gpointer object)
|
|
{
|
|
assert(DIYA_IS_CAIRO_TEXT(object));
|
|
DiyaCairoText *self = DIYA_CAIRO_TEXT(object);
|
|
DiyaCairoTextPrivate *priv = diya_cairo_text_get_instance_private(self);
|
|
return priv->text;
|
|
}
|