/*
* Copyright © 2022 Benjamin Otte
*
* This library 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 library is distributed in the hope that 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 library . If not , see < http : //www.gnu.org/licenses/>.
*
* Authors : Benjamin Otte < otte @ gnome . org >
*/
#include "config.h"
#include "gtkinscriptionprivate.h"
#include "gtkaccessibletextprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkcssstylechangeprivate.h"
#include "gtkpangoprivate.h"
#include "gtksnapshot.h"
#include "gtkrenderlayoutprivate.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "a11y/gtkatspipangoprivate.h"
/**
* GtkInscription :
*
* Shows text in a predefined area .
*
* You likely want to use ` GtkLabel ` instead as this widget is intended only
* for a small subset of use cases . The main scenario envisaged is inside lists
* such as ` GtkColumnView ` .
*
* While a ` GtkLabel ` sizes itself depending on the text that is displayed ,
* ` GtkInscription ` is given a size and inscribes the given text into that
* space as well as it can .
*
* Users of this widget should take care to plan behaviour for the common case
* where the text doesn ' t fit exactly in the allocated space .
*
* # # CSS nodes
*
* ` GtkInscription ` has a single CSS node with the name label .
*
* Since : 4 . 8
*/
/* 3 chars are enough to display ellipsizing "..." */
#define DEFAULT_MIN_CHARS 3
/* This means we request no natural size and fall back to min size */
#define DEFAULT_NAT_CHARS 0
/* 1 line is what people want in 90% of cases */
#define DEFAULT_MIN_LINES 1
/* This means we request no natural size and fall back to min size */
#define DEFAULT_NAT_LINES 0
/* Unlike GtkLabel, we default to not centering text */
#define DEFAULT_XALIGN 0 .f
/* But just like GtkLabel, we center vertically */
#define DEFAULT_YALIGN 0 .5 f
struct _GtkInscription
{
GtkWidget parent_instance;
char *text;
guint min_chars;
guint nat_chars;
guint min_lines;
guint nat_lines;
float xalign;
float yalign;
PangoAttrList *attrs;
GtkInscriptionOverflow overflow;
PangoLayout *layout;
};
enum
{
PROP_0,
PROP_ATTRIBUTES,
PROP_MARKUP,
PROP_MIN_CHARS,
PROP_MIN_LINES,
PROP_NAT_CHARS,
PROP_NAT_LINES,
PROP_TEXT,
PROP_TEXT_OVERFLOW,
PROP_WRAP_MODE,
PROP_XALIGN,
PROP_YALIGN,
N_PROPS
};
static void gtk_inscription_accessible_text_init (GtkAccessibleTextInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkInscription, gtk_inscription, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE_TEXT,
gtk_inscription_accessible_text_init))
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_inscription_dispose (GObject *object)
{
GtkInscription *self = GTK_INSCRIPTION (object);
g_clear_pointer (&self->text, g_free);
G_OBJECT_CLASS (gtk_inscription_parent_class)->dispose (object);
}
static void
gtk_inscription_finalize (GObject *object)
{
GtkInscription *self = GTK_INSCRIPTION (object);
g_clear_object (&self->layout);
G_OBJECT_CLASS (gtk_inscription_parent_class)->finalize (object);
}
static void
gtk_inscription_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkInscription *self = GTK_INSCRIPTION (object);
switch (property_id)
{
case PROP_ATTRIBUTES:
g_value_set_boxed (value, self->attrs);
break ;
case PROP_MIN_CHARS:
g_value_set_uint (value, self->min_chars);
break ;
case PROP_MIN_LINES:
g_value_set_uint (value, self->min_lines);
break ;
case PROP_NAT_CHARS:
g_value_set_uint (value, self->nat_chars);
break ;
case PROP_NAT_LINES:
g_value_set_uint (value, self->nat_lines);
break ;
case PROP_TEXT:
g_value_set_string (value, self->text);
break ;
case PROP_TEXT_OVERFLOW:
g_value_set_enum (value, self->overflow);
break ;
case PROP_WRAP_MODE:
g_value_set_enum (value, pango_layout_get_wrap (self->layout));
break ;
case PROP_XALIGN:
g_value_set_float (value, self->xalign);
break ;
case PROP_YALIGN:
g_value_set_float (value, self->yalign);
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break ;
}
}
static void
gtk_inscription_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkInscription *self = GTK_INSCRIPTION (object);
switch (property_id)
{
case PROP_ATTRIBUTES:
gtk_inscription_set_attributes (self, g_value_get_boxed (value));
break ;
case PROP_MARKUP:
gtk_inscription_set_markup (self, g_value_get_string (value));
break ;
case PROP_MIN_CHARS:
gtk_inscription_set_min_chars (self, g_value_get_uint (value));
break ;
case PROP_MIN_LINES:
gtk_inscription_set_min_lines (self, g_value_get_uint (value));
break ;
case PROP_NAT_CHARS:
gtk_inscription_set_nat_chars (self, g_value_get_uint (value));
break ;
case PROP_NAT_LINES:
gtk_inscription_set_nat_lines (self, g_value_get_uint (value));
break ;
case PROP_TEXT:
gtk_inscription_set_text (self, g_value_get_string (value));
break ;
case PROP_TEXT_OVERFLOW:
gtk_inscription_set_text_overflow (self, g_value_get_enum (value));
break ;
case PROP_WRAP_MODE:
gtk_inscription_set_wrap_mode (self, g_value_get_enum (value));
break ;
case PROP_XALIGN:
gtk_inscription_set_xalign (self, g_value_get_float (value));
break ;
case PROP_YALIGN:
gtk_inscription_set_yalign (self, g_value_get_float (value));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break ;
}
}
static void
update_pango_alignment (GtkInscription *self)
{
PangoAlignment align;
gboolean ltr;
ltr = _gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
if (self->xalign < 0 .33 )
align = ltr ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
else if (self->xalign < 0 .67 )
align = PANGO_ALIGN_CENTER;
else
align = ltr ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
pango_layout_set_alignment (self->layout, align);
}
static void
gtk_inscription_update_layout_attributes (GtkInscription *self,
PangoAttrList *css_attrs)
{
PangoAttrList *new_attrs;
if (css_attrs == NULL)
css_attrs = gtk_css_style_get_pango_attributes (gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (self))));
new_attrs = css_attrs;
new_attrs = _gtk_pango_attr_list_merge (new_attrs, self->attrs);
pango_layout_set_attributes (self->layout, new_attrs);
pango_attr_list_unref (new_attrs);
}
static void
gtk_inscription_css_changed (GtkWidget *widget,
GtkCssStyleChange *change)
{
GtkInscription *self = GTK_INSCRIPTION (widget);
GTK_WIDGET_CLASS (gtk_inscription_parent_class)->css_changed (widget, change);
if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
{
gtk_inscription_update_layout_attributes (self,
gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change)));
gtk_widget_queue_draw (widget);
}
}
static void
gtk_inscription_direction_changed (GtkWidget *widget,
GtkTextDirection previous_direction)
{
GtkInscription *self = GTK_INSCRIPTION (widget);
GTK_WIDGET_CLASS (gtk_inscription_parent_class)->direction_changed (widget, previous_direction);
update_pango_alignment (self);
}
static PangoFontMetrics *
gtk_inscription_get_font_metrics (GtkInscription *self)
{
PangoContext *context;
context = gtk_widget_get_pango_context (GTK_WIDGET (self));
return pango_context_get_metrics (context, NULL, NULL);
}
static int
get_char_pixels (GtkInscription *self)
{
int char_width, digit_width;
PangoFontMetrics *metrics;
metrics = gtk_inscription_get_font_metrics (self);
char_width = pango_font_metrics_get_approximate_char_width (metrics);
digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
pango_font_metrics_unref (metrics);
return MAX (char_width, digit_width);
}
static void
gtk_inscription_measure_width (GtkInscription *self,
int *minimum,
int *natural)
{
int char_pixels = get_char_pixels (self);
if (self->min_chars == 0 && self->nat_chars == 0 )
return ;
*minimum = self->min_chars * char_pixels;
*natural = MAX (self->min_chars, self->nat_chars) * char_pixels;
}
static int
get_line_pixels (GtkInscription *self,
int *baseline)
{
PangoFontMetrics *metrics;
int ascent, descent;
metrics = gtk_inscription_get_font_metrics (self);
ascent = pango_font_metrics_get_ascent (metrics);
descent = pango_font_metrics_get_descent (metrics);
pango_font_metrics_unref (metrics);
if (baseline)
*baseline = ascent;
return ascent + descent;
}
static void
gtk_inscription_measure_height (GtkInscription *self,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
int line_pixels, baseline;
if (self->min_lines == 0 && self->nat_lines == 0 )
return ;
line_pixels = get_line_pixels (self, &baseline);
*minimum = self->min_lines * line_pixels;
*natural = MAX (self->min_lines, self->nat_lines) * line_pixels;
if (minimum_baseline)
*minimum_baseline = self->min_lines ? baseline : 0 ;
if (natural_baseline)
*natural_baseline = (self->min_lines || self->nat_lines) ? baseline : 0 ;
}
static void
gtk_inscription_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkInscription *self = GTK_INSCRIPTION (widget);
/* We split this into 2 functions explicitly, so nobody gets the idea
* of adding height - for - width to this .
* This is meant to be fast , so that is a big no - no .
*/
if (orientation == GTK_ORIENTATION_HORIZONTAL)
gtk_inscription_measure_width (self, minimum, natural);
else
gtk_inscription_measure_height (self, minimum, natural, minimum_baseline, natural_baseline);
*minimum = PANGO_PIXELS_CEIL (*minimum);
*natural = PANGO_PIXELS_CEIL (*natural);
if (*minimum_baseline > 0 )
*minimum_baseline = PANGO_PIXELS_CEIL (*minimum_baseline);
if (*natural_baseline > 0 )
*natural_baseline = PANGO_PIXELS_CEIL (*natural_baseline);
}
void
gtk_inscription_get_layout_location (GtkInscription *self,
float *x_out,
float *y_out)
{
GtkWidget *widget = GTK_WIDGET (self);
const int widget_width = gtk_widget_get_width (widget);
const int widget_height = gtk_widget_get_height (widget);
PangoRectangle logical;
float xalign;
int baseline;
float x, y;
g_assert (x_out);
g_assert (y_out);
xalign = self->xalign;
if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
xalign = 1 .0 - xalign;
pango_layout_get_pixel_extents (self->layout, NULL, &logical);
if (pango_layout_get_width (self->layout) > 0 )
x = 0 .f;
else
x = floor ((xalign * (widget_width - logical.width)) - logical.x);
baseline = gtk_widget_get_baseline (widget);
if (baseline != -1 )
{
int layout_baseline = pango_layout_get_baseline (self->layout) / PANGO_SCALE;
/* yalign is 0 because we can't support yalign while baseline aligning */
y = baseline - layout_baseline;
}
else if (pango_layout_is_ellipsized (self->layout))
{
y = 0 .f;
}
else
{
y = floor ((widget_height - logical.height) * self->yalign);
if (y < 0 )
y = 0 .f;
}
*x_out = x;
*y_out = y;
}
static gboolean
gtk_inscription_get_layout_index (GtkInscription *self,
int x,
int y,
int *index)
{
int trailing = 0 ;
const char *cluster;
const char *cluster_end;
gboolean inside;
float lx, ly;
*index = 0 ;
gtk_inscription_get_layout_location (self, &lx, &ly);
inside = pango_layout_xy_to_index (self->layout,
(x - lx) * PANGO_SCALE,
(y - ly) * PANGO_SCALE,
index, &trailing);
cluster = self->text + *index;
cluster_end = cluster;
while (trailing)
{
cluster_end = g_utf8_next_char (cluster_end);
--trailing;
}
*index += (cluster_end - cluster);
return inside;
}
static void
gtk_inscription_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkInscription *self = GTK_INSCRIPTION (widget);
pango_layout_set_width (self->layout, width * PANGO_SCALE);
switch (self->overflow)
{
case GTK_INSCRIPTION_OVERFLOW_CLIP:
pango_layout_set_height (self->layout, -1 );
/* figure out if we're single line (clip horizontally)
* or multiline ( clip vertically ) :
* If we can ' t fit 2 rows , we ' re single line .
*/
{
PangoLayoutIter *iter = pango_layout_get_iter (self->layout);
if (pango_layout_iter_next_line (iter))
{
PangoRectangle rect;
pango_layout_iter_get_line_extents (iter, NULL, &rect);
if (rect.y + rect.height > height * PANGO_SCALE)
{
while (!pango_layout_line_is_paragraph_start (pango_layout_iter_get_line_readonly (iter)))
{
if (!pango_layout_iter_next_line (iter))
break ;
}
if (!pango_layout_line_is_paragraph_start (pango_layout_iter_get_line_readonly (iter)))
{
pango_layout_set_width (self->layout, -1 );
}
}
}
pango_layout_iter_free (iter);
}
break ;
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_START:
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_MIDDLE:
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_END:
pango_layout_set_height (self->layout, height * PANGO_SCALE);
break ;
default :
g_assert_not_reached();
break ;
}
}
static void
gtk_inscription_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkInscription *self = GTK_INSCRIPTION (widget);
float lx, ly;
GtkCssBoxes boxes;
if (!self->text || (*self->text == '\0' ))
return ;
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT(0 , 0 , gtk_widget_get_width (widget), gtk_widget_get_height (widget)));
gtk_inscription_get_layout_location (self, &lx, &ly);
gtk_css_boxes_init (&boxes, widget);
gtk_css_style_snapshot_layout (&boxes, snapshot, lx, ly, self->layout);
gtk_snapshot_pop (snapshot);
}
static void
gtk_inscription_class_init (GtkInscriptionClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gobject_class->dispose = gtk_inscription_dispose;
gobject_class->finalize = gtk_inscription_finalize;
gobject_class->get_property = gtk_inscription_get_property;
gobject_class->set_property = gtk_inscription_set_property;
widget_class->css_changed = gtk_inscription_css_changed;
widget_class->direction_changed = gtk_inscription_direction_changed;
widget_class->measure = gtk_inscription_measure;
widget_class->size_allocate = gtk_inscription_allocate;
widget_class->snapshot = gtk_inscription_snapshot;
/**
* GtkInscription : attributes :
*
* A list of style attributes to apply to the text of the inscription .
*
* Since : 4 . 8
*/
properties[PROP_ATTRIBUTES] =
g_param_spec_boxed ("attributes" , NULL, NULL,
PANGO_TYPE_ATTR_LIST,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : markup :
*
* Utility property that sets both the [ property @ Gtk . Inscription : text ] and
* [ property @ Gtk . Inscription : attributes ] properties , mainly intended for use in
* GtkBuilder ui files to ease translation support and bindings .
*
* This function uses [ func @ Pango . parse_markup ] to parse the markup into text and
* attributes . The markup must be valid . If you cannot ensure that , consider using
* [ func @ Pango . parse_markup ] and setting the two properties yourself .
*
* Since : 4 . 8
*/
properties[PROP_MARKUP] =
g_param_spec_string ("markup" , NULL, NULL,
NULL,
G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : min - chars :
*
* The number of characters that should fit into the inscription at minimum .
*
* This influences the requested width , not the width actually given to the widget ,
* which might turn out to be larger .
*
* Note that this is an approximate character width , so some characters might be
* wider and some might be thinner , so do not expect the number of characters to
* exactly match .
*
* If you set this property to 0 , the inscription will not request any width at all
* and its width will be determined entirely by its surroundings .
*
* Since : 4 . 8
*/
properties[PROP_MIN_CHARS] =
g_param_spec_uint ("min-chars" , NULL, NULL,
0 , G_MAXUINT,
DEFAULT_MIN_CHARS,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : min - lines :
*
* The number of lines that should fit into the inscription at minimum .
*
* This influences the requested height , not the height actually given to the widget ,
* which might turn out to be larger .
*
* Note that this is an approximate line height , so if the text uses things like fancy
* Unicode or attribute that influence the height , the text might not fit .
*
* If you set this property to 0 , the inscription will not request any height at all
* and its height will be determined entirely by its surroundings .
*
* Since : 4 . 8
*/
properties[PROP_MIN_LINES] =
g_param_spec_uint ("min-lines" , NULL, NULL,
0 , G_MAXUINT,
DEFAULT_MIN_LINES,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : nat - chars :
*
* The number of characters that should ideally fit into the inscription .
*
* This influences the requested width , not the width actually given to the widget .
* The widget might turn out larger as well as smaller .
*
* If this property is set to a value smaller than [ property @ Gtk . Inscription : min - chars ] ,
* that value will be used . In particular , for the default value of 0 , this will always
* be the case .
*
* Since : 4 . 8
*/
properties[PROP_NAT_CHARS] =
g_param_spec_uint ("nat-chars" , NULL, NULL,
0 , G_MAXUINT,
DEFAULT_NAT_CHARS,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : nat - lines :
*
* The number of lines that should ideally fit into the inscription .
*
* This influences the requested height , not the height actually given to the widget .
* The widget might turn out larger as well as smaller .
*
* If this property is set to a value smaller than [ property @ Gtk . Inscription : min - lines ] ,
* that value will be used . In particular , for the default value of 0 , this will always
* be the case .
*
* Since : 4 . 8
*/
properties[PROP_NAT_LINES] =
g_param_spec_uint ("nat-lines" , NULL, NULL,
0 , G_MAXUINT,
DEFAULT_NAT_LINES,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : text :
*
* The displayed text .
*
* Since : 4 . 8
*/
properties[PROP_TEXT] =
g_param_spec_string ("text" , NULL, NULL,
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : text - overflow :
*
* The overflow method to use for the text .
*
* Since : 4 . 8
*/
properties[PROP_TEXT_OVERFLOW] =
g_param_spec_enum ("text-overflow" , NULL, NULL,
GTK_TYPE_INSCRIPTION_OVERFLOW,
GTK_INSCRIPTION_OVERFLOW_CLIP,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : wrap - mode :
*
* Controls how the line wrapping is done .
*
* Note that unlike ` GtkLabel ` , the default here is % PANGO_WRAP_WORD_CHAR .
*
* Since : 4 . 8
*/
properties[PROP_WRAP_MODE] =
g_param_spec_enum ("wrap-mode" , NULL, NULL,
PANGO_TYPE_WRAP_MODE,
PANGO_WRAP_WORD_CHAR,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : xalign :
*
* The horizontal alignment of the text inside the allocated size .
*
* Compare this to [ property @ Gtk . Widget : halign ] , which determines how the
* inscription ' s size allocation is positioned in the available space .
*
* Since : 4 . 8
*/
properties[PROP_XALIGN] =
g_param_spec_float ("xalign" , NULL, NULL,
0 .0 , 1 .0 ,
DEFAULT_XALIGN,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
/**
* GtkInscription : yalign :
*
* The vertical alignment of the text inside the allocated size .
*
* Compare this to [ property @ Gtk . Widget : valign ] , which determines how the
* inscription ' s size allocation is positioned in the available space .
*
* Since : 4 . 8
*/
properties[PROP_YALIGN] =
g_param_spec_float ("yalign" , NULL, NULL,
0 .0 , 1 .0 ,
DEFAULT_YALIGN,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
gtk_widget_class_set_css_name (widget_class, "label" );
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
}
static void
gtk_inscription_init (GtkInscription *self)
{
self->min_chars = DEFAULT_MIN_CHARS;
self->nat_chars = DEFAULT_NAT_CHARS;
self->min_lines = DEFAULT_MIN_LINES;
self->nat_lines = DEFAULT_NAT_LINES;
self->xalign = DEFAULT_XALIGN;
self->yalign = DEFAULT_YALIGN;
self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), NULL);
pango_layout_set_wrap (self->layout, PANGO_WRAP_WORD_CHAR);
update_pango_alignment (self);
}
/* for a11y */
PangoLayout *
gtk_inscription_get_layout (GtkInscription *self)
{
return self->layout;
}
/**
* gtk_inscription_new :
* @ text : ( nullable ) : The text to display .
*
* Creates a new ` GtkInscription ` with the given text .
*
* Returns : a new ` GtkInscription `
*
* Since : 4 . 8
*/
GtkWidget *
gtk_inscription_new (const char *text)
{
return g_object_new (GTK_TYPE_INSCRIPTION,
"text" , text,
NULL);
}
/**
* gtk_inscription_set_text :
* @ self : a ` GtkInscription `
* @ text : ( nullable ) : The text to display
*
* Sets the text to be displayed .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_text (GtkInscription *self,
const char *text)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (g_strcmp0 (self->text, text) == 0 )
return ;
g_free (self->text);
self->text = g_strdup (text);
pango_layout_set_text (self->layout,
self->text ? self->text : "" ,
-1 );
/* This here not being a gtk_widget_queue_resize() is why this widget exists */
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
GTK_ACCESSIBLE_PROPERTY_LABEL,
text,
-1 );
}
/**
* gtk_inscription_get_text :
* @ self : a ` GtkInscription `
*
* Gets the text that is displayed .
*
* Returns : ( nullable ) ( transfer none ) : The displayed text
*
* Since : 4 . 8
*/
const char *
gtk_inscription_get_text (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), NULL);
return self->text;
}
/**
* gtk_inscription_set_min_chars :
* @ self : a ` GtkInscription `
* @ min_chars : the minimum number of characters that should fit , approximately
*
* Sets the ` min - chars ` of the inscription .
*
* See the [ property @ Gtk . Inscription : min - chars ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_min_chars (GtkInscription *self,
guint min_chars)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->min_chars == min_chars)
return ;
self->min_chars = min_chars;
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_CHARS]);
}
/**
* gtk_inscription_get_min_chars :
* @ self : a ` GtkInscription `
*
* Gets the ` min - chars ` of the inscription .
*
* See the [ property @ Gtk . Inscription : min - chars ] property .
*
* Returns : the min - chars property
*
* Since : 4 . 8
*/
guint
gtk_inscription_get_min_chars (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_MIN_CHARS);
return self->min_chars;
}
/**
* gtk_inscription_set_nat_chars :
* @ self : a ` GtkInscription `
* @ nat_chars : the number of characters that should ideally fit , approximately
*
* Sets the ` nat - chars ` of the inscription .
*
* See the [ property @ Gtk . Inscription : nat - chars ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_nat_chars (GtkInscription *self,
guint nat_chars)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->nat_chars == nat_chars)
return ;
self->nat_chars = nat_chars;
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAT_CHARS]);
}
/**
* gtk_inscription_get_nat_chars :
* @ self : a ` GtkInscription `
*
* Gets the ` nat - chars ` of the inscription .
*
* See the [ property @ Gtk . Inscription : nat - chars ] property .
*
* Returns : the nat - chars property
*
* Since : 4 . 8
*/
guint
gtk_inscription_get_nat_chars (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_NAT_CHARS);
return self->nat_chars;
}
/**
* gtk_inscription_set_min_lines :
* @ self : a ` GtkInscription `
* @ min_lines : the minimum number of lines that should fit , approximately
*
* Sets the ` min - lines ` of the inscription .
*
* See the [ property @ Gtk . Inscription : min - lines ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_min_lines (GtkInscription *self,
guint min_lines)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->min_lines == min_lines)
return ;
self->min_lines = min_lines;
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_LINES]);
}
/**
* gtk_inscription_get_min_lines :
* @ self : a ` GtkInscription `
*
* Gets the ` min - lines ` of the inscription .
*
* See the [ property @ Gtk . Inscription : min - lines ] property .
*
* Returns : the min - lines property
*
* Since : 4 . 8
*/
guint
gtk_inscription_get_min_lines (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_MIN_LINES);
return self->min_lines;
}
/**
* gtk_inscription_set_nat_lines :
* @ self : a ` GtkInscription `
* @ nat_lines : the number of lines that should ideally fit
*
* Sets the ` nat - lines ` of the inscription .
*
* See the [ property @ Gtk . Inscription : nat - lines ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_nat_lines (GtkInscription *self,
guint nat_lines)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->nat_lines == nat_lines)
return ;
self->nat_lines = nat_lines;
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAT_LINES]);
}
/**
* gtk_inscription_get_nat_lines :
* @ self : a ` GtkInscription `
*
* Gets the ` nat - lines ` of the inscription .
*
* See the [ property @ Gtk . Inscription : nat - lines ] property .
*
* Returns : the nat - lines property
*
* Since : 4 . 8
*/
guint
gtk_inscription_get_nat_lines (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_NAT_LINES);
return self->nat_lines;
}
/**
* gtk_inscription_set_xalign :
* @ self : a ` GtkInscription `
* @ xalign : the new xalign value , between 0 and 1
*
* Sets the ` xalign ` of the inscription .
*
* See the [ property @ Gtk . Inscription : xalign ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_xalign (GtkInscription *self,
float xalign)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
xalign = CLAMP (xalign, 0 .0 , 1 .0 );
if (self->xalign == xalign)
return ;
self->xalign = xalign;
update_pango_alignment (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XALIGN]);
}
/**
* gtk_inscription_get_xalign :
* @ self : a ` GtkInscription `
*
* Gets the ` xalign ` of the inscription .
*
* See the [ property @ Gtk . Inscription : xalign ] property .
*
* Returns : the xalign property
*
* Since : 4 . 8
*/
float
gtk_inscription_get_xalign (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_XALIGN);
return self->xalign;
}
/**
* gtk_inscription_set_yalign :
* @ self : a ` GtkInscription `
* @ yalign : the new yalign value , between 0 and 1
*
* Sets the ` yalign ` of the inscription .
*
* See the [ property @ Gtk . Inscription : yalign ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_yalign (GtkInscription *self,
float yalign)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
yalign = CLAMP (yalign, 0 .0 , 1 .0 );
if (self->yalign == yalign)
return ;
self->yalign = yalign;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_YALIGN]);
}
/**
* gtk_inscription_get_yalign :
* @ self : a ` GtkInscription `
*
* Gets the ` yalign ` of the inscription .
*
* See the [ property @ Gtk . Inscription : yalign ] property .
*
* Returns : the yalign property
*
* Since : 4 . 8
*/
float
gtk_inscription_get_yalign (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), DEFAULT_YALIGN);
return self->yalign;
}
/**
* gtk_inscription_set_attributes :
* @ self : a ` GtkInscription `
* @ attrs : ( nullable ) : a [ struct @ Pango . AttrList ]
*
* Apply attributes to the inscription text .
*
* These attributes will not be evaluated for sizing the inscription .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_attributes (GtkInscription *self,
PangoAttrList *attrs)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->attrs == attrs)
return ;
if (attrs)
pango_attr_list_ref (attrs);
if (self->attrs)
pango_attr_list_unref (self->attrs);
self->attrs = attrs;
gtk_inscription_update_layout_attributes (self, NULL);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ATTRIBUTES]);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
/**
* gtk_inscription_get_attributes :
* @ self : a ` GtkInscription `
*
* Gets the inscription ' s attribute list .
*
* Returns : ( nullable ) ( transfer none ) : the attribute list
*
* Since : 4 . 8
*/
PangoAttrList *
gtk_inscription_get_attributes (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), NULL);
return self->attrs;
}
/**
* gtk_inscription_set_text_overflow :
* @ self : a ` GtkInscription `
* @ overflow : the overflow method to use
*
* Sets what to do when the text doesn ' t fit .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_text_overflow (GtkInscription *self,
GtkInscriptionOverflow overflow)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (self->overflow == overflow)
return ;
self->overflow = overflow;
switch (self->overflow)
{
case GTK_INSCRIPTION_OVERFLOW_CLIP:
pango_layout_set_ellipsize (self->layout, PANGO_ELLIPSIZE_NONE);
break ;
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_START:
pango_layout_set_ellipsize (self->layout, PANGO_ELLIPSIZE_START);
break ;
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_MIDDLE:
pango_layout_set_ellipsize (self->layout, PANGO_ELLIPSIZE_MIDDLE);
break ;
case GTK_INSCRIPTION_OVERFLOW_ELLIPSIZE_END:
pango_layout_set_ellipsize (self->layout, PANGO_ELLIPSIZE_END);
break ;
default :
g_assert_not_reached();
break ;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT_OVERFLOW]);
}
/**
* gtk_inscription_get_text_overflow :
* @ self : a ` GtkInscription `
*
* Gets the inscription ' s overflow method .
*
* Returns : the overflow method
*
* Since : 4 . 8
*/
GtkInscriptionOverflow
gtk_inscription_get_text_overflow (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), GTK_INSCRIPTION_OVERFLOW_CLIP);
return self->overflow;
}
/**
* gtk_inscription_set_wrap_mode :
* @ self : a ` GtkInscription `
* @ wrap_mode : the line wrapping mode
*
* Controls how line wrapping is done .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_wrap_mode (GtkInscription *self,
PangoWrapMode wrap_mode)
{
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (pango_layout_get_wrap (self->layout) == wrap_mode)
return ;
pango_layout_set_wrap (self->layout, wrap_mode);
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WRAP_MODE]);
}
/**
* gtk_inscription_get_wrap_mode :
* @ self : a ` GtkInscription `
*
* Returns line wrap mode used by the inscription .
*
* See [ method @ Gtk . Inscription . set_wrap_mode ] .
*
* Returns : the line wrap mode
*
* Since : 4 . 8
*/
PangoWrapMode
gtk_inscription_get_wrap_mode (GtkInscription *self)
{
g_return_val_if_fail (GTK_IS_INSCRIPTION (self), PANGO_WRAP_WORD_CHAR);
return pango_layout_get_wrap (self->layout);
}
/**
* gtk_inscription_set_markup :
* @ self : a ` GtkInscription `
* @ markup : ( nullable ) : The markup to display
*
* Utility function to set the text and attributes to be displayed .
*
* See the [ property @ Gtk . Inscription : markup ] property .
*
* Since : 4 . 8
*/
void
gtk_inscription_set_markup (GtkInscription *self,
const char *markup)
{
PangoAttrList *attrs;
char *text;
GError *error = NULL;
g_return_if_fail (GTK_IS_INSCRIPTION (self));
if (markup == NULL)
{
text = NULL;
attrs = NULL;
}
else if (!pango_parse_markup (markup, -1 ,
0 ,
&attrs, &text,
NULL,
&error))
{
g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s" ,
markup, error->message);
return ;
}
gtk_inscription_set_text (self, text);
gtk_inscription_set_attributes (self, attrs);
g_clear_pointer (&text, g_free);
g_clear_pointer (&attrs, pango_attr_list_unref);
}
/* {{{ GtkAccessibleText implementation */
static GBytes *
gtk_inscription_accessible_text_get_contents (GtkAccessibleText *self,
unsigned int start,
unsigned int end)
{
const char *text;
int len;
char *string;
gsize size;
text = gtk_inscription_get_text (GTK_INSCRIPTION (self));
len = g_utf8_strlen (text, -1 );
start = CLAMP (start, 0 , len);
end = CLAMP (end, 0 , len);
if (end <= start)
{
string = g_strdup ("" );
size = 1 ;
}
else
{
const char *p, *q;
p = g_utf8_offset_to_pointer (text, start);
q = g_utf8_offset_to_pointer (text, end);
size = q - p + 1 ;
string = g_strndup (p, q - p);
}
return g_bytes_new_take (string, size);
}
static GBytes *
gtk_inscription_accessible_text_get_contents_at (GtkAccessibleText *self,
unsigned int offset,
GtkAccessibleTextGranularity granularity,
unsigned int *start,
unsigned int *end)
{
PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (self));
char *string = gtk_pango_get_string_at (layout, offset, granularity, start, end);
return g_bytes_new_take (string, strlen (string));
}
static unsigned int
gtk_inscription_accessible_text_get_caret_position (GtkAccessibleText *self)
{
return 0 ;
}
static gboolean
gtk_inscription_accessible_text_get_selection (GtkAccessibleText *self,
gsize *n_ranges,
GtkAccessibleTextRange **ranges)
{
return FALSE ;
}
static gboolean
gtk_inscription_accessible_text_get_attributes (GtkAccessibleText *self,
unsigned int offset,
gsize *n_ranges,
GtkAccessibleTextRange **ranges,
char ***attribute_names,
char ***attribute_values)
{
PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (self));
unsigned int start, end;
char **names, **values;
gtk_pango_get_run_attributes (layout, offset, &names, &values, &start, &end);
*n_ranges = g_strv_length (names);
*ranges = g_new (GtkAccessibleTextRange, *n_ranges);
for (unsigned i = 0 ; i < *n_ranges; i++)
{
GtkAccessibleTextRange *range = &(*ranges)[i];
range->start = start;
range->length = end - start;
}
*attribute_names = names;
*attribute_values = values;
return TRUE ;
}
static void
gtk_inscription_accessible_text_get_default_attributes (GtkAccessibleText *self,
char ***attribute_names,
char ***attribute_values)
{
PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (self));
char **names, **values;
gtk_pango_get_default_attributes (layout, &names, &values);
*attribute_names = names;
*attribute_values = values;
}
static gboolean
gtk_inscription_accessible_text_get_extents (GtkAccessibleText *self,
unsigned int start,
unsigned int end,
graphene_rect_t *extents)
{
GtkInscription *inscription = GTK_INSCRIPTION (self);
PangoLayout *layout;
const char *text;
float lx, ly;
cairo_region_t *range_clip;
cairo_rectangle_int_t clip_rect;
int range[2 ];
layout = inscription->layout;
text = inscription->text;
gtk_inscription_get_layout_location (inscription, &lx, &ly);
range[0 ] = g_utf8_pointer_to_offset (text, text + start);
range[1 ] = g_utf8_pointer_to_offset (text, text + end);
range_clip = gdk_pango_layout_get_clip_region (layout, lx, ly, range, 1 );
cairo_region_get_extents (range_clip, &clip_rect);
cairo_region_destroy (range_clip);
extents->origin.x = clip_rect.x;
extents->origin.y = clip_rect.y;
extents->size.width = clip_rect.width;
extents->size.height = clip_rect.height;
return TRUE ;
}
static gboolean
gtk_inscription_accessible_text_get_offset (GtkAccessibleText *self,
const graphene_point_t *point,
unsigned int *offset)
{
GtkInscription *inscription = GTK_INSCRIPTION (self);
int index;
if (!gtk_inscription_get_layout_index (inscription, point->x, point->y, &index))
return FALSE ;
*offset = (unsigned int ) g_utf8_pointer_to_offset (inscription->text, inscription->text + index);
return TRUE ;
}
static void
gtk_inscription_accessible_text_init (GtkAccessibleTextInterface *iface)
{
iface->get_contents = gtk_inscription_accessible_text_get_contents;
iface->get_contents_at = gtk_inscription_accessible_text_get_contents_at;
iface->get_caret_position = gtk_inscription_accessible_text_get_caret_position;
iface->get_selection = gtk_inscription_accessible_text_get_selection;
iface->get_attributes = gtk_inscription_accessible_text_get_attributes;
iface->get_default_attributes = gtk_inscription_accessible_text_get_default_attributes;
iface->get_extents = gtk_inscription_accessible_text_get_extents;
iface->get_offset = gtk_inscription_accessible_text_get_offset;
}
/* }}} */
/* vim:set foldmethod=marker: */
Messung V0.5 in Prozent C=97 H=99 G=97
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland