forked from AuroraMiddleware/gtk
7c034cc283
We use "label" just like GtkLabel as the two widgets differ in the way they are measured, but they should be styled the same. If it turns out we change our opinion on this for specific cases, we can add style classes later.
1192 lines
33 KiB
C
1192 lines
33 KiB
C
/*
|
|
* 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 "gtkcssnodeprivate.h"
|
|
#include "gtkcssstylechangeprivate.h"
|
|
#include "gtkpango.h"
|
|
#include "gtksnapshot.h"
|
|
#include "gtktypebuiltins.h"
|
|
#include "gtkwidgetprivate.h"
|
|
|
|
/**
|
|
* GtkInscription:
|
|
*
|
|
* `GtkInscription` is a widget to show 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 use case is usage inside lists
|
|
* such as `GtkColumnView`.
|
|
*
|
|
* While a `GtkLabel` sizes itself according to the text that is displayed,
|
|
* `GtkInscription` is given a size and inscribes the given text into that space
|
|
* as good as it can.
|
|
*
|
|
* As it is a common occurrence that text doesn't fit, users of this widget should
|
|
* plan for that case.
|
|
*/
|
|
|
|
/* 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.5f
|
|
|
|
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_XALIGN,
|
|
PROP_YALIGN,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
G_DEFINE_TYPE (GtkInscription, gtk_inscription, GTK_TYPE_WIDGET)
|
|
|
|
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_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_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
|
|
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 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);
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
gtk_inscription_get_layout_location (GtkInscription *self,
|
|
int *x_out,
|
|
int *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;
|
|
int 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);
|
|
x = floor ((xalign * (widget_width - logical.width)) - logical.x);
|
|
|
|
baseline = gtk_widget_get_allocated_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
|
|
{
|
|
y = floor ((widget_height - logical.height) * self->yalign);
|
|
}
|
|
|
|
*x_out = x;
|
|
*y_out = y;
|
|
}
|
|
|
|
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)
|
|
pango_layout_set_width (self->layout, -1);
|
|
}
|
|
}
|
|
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);
|
|
GtkStyleContext *context;
|
|
int lx, ly;
|
|
|
|
if (!self->text || (*self->text == '\0'))
|
|
return;
|
|
|
|
context = _gtk_widget_get_style_context (widget);
|
|
|
|
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_snapshot_render_layout (snapshot, context, 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->measure = gtk_inscription_measure;
|
|
widget_class->size_allocate = gtk_inscription_allocate;
|
|
widget_class->snapshot = gtk_inscription_snapshot;
|
|
|
|
/**
|
|
* GtkInscription:attributes: (attributes org.gtk.Property.get=gtk_inscription_get_attributes org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:markup: (attributes org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:min-chars: (attributes org.gtk.Property.get=gtk_inscription_get_min_chars org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:min-lines: (attributes org.gtk.Property.get=gtk_inscription_get_min_lines org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:nat-chars: (attributes org.gtk.Property.get=gtk_inscription_get_nat_chars org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:nat-lines: (attributes org.gtk.Property.get=gtk_inscription_get_nat_lines org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:text: (attributes org.gtk.Property.get=gtk_inscription_get_text org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:text-overflow: (attributes org.gtk.Property.get=gtk_inscription_get_text_overflow org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:xalign: (attributes org.gtk.Property.get=gtk_inscription_get_xalign org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
/**
|
|
* GtkInscription:yalign: (attributes org.gtk.Property.get=gtk_inscription_get_yalign org.gtk.Property.set=gtk_inscription_set_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_STRINGS);
|
|
|
|
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);
|
|
}
|
|
|
|
/* 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: (attributes org.gtk.Method.set_property=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_inscription_get_text: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XALIGN]);
|
|
}
|
|
|
|
/**
|
|
* gtk_inscription_get_xalign: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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: (attributes org.gtk.Method.set_property=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: (attributes org.gtk.Method.get_property=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_markup: (attributes org.gtk.Method.set_property=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);
|
|
}
|