Files
diya-shell/src/widgets/cairo-text.c
2024-04-29 21:41:51 +02:00

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