From b6a8080b2fc8b8a5b16dd7d260fd87e39edcb28c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 19 May 2022 06:44:23 +0200 Subject: [PATCH 01/15] Add GtkInscription A label alternative that renders itself into a given rectangle. The main use case is cells in column view. Related: #3334 --- gtk/gtk.h | 1 + gtk/gtkinscription.c | 908 +++++++++++++++++++++++++++++++++++++++++++ gtk/gtkinscription.h | 79 ++++ gtk/meson.build | 2 + 4 files changed, 990 insertions(+) create mode 100644 gtk/gtkinscription.c create mode 100644 gtk/gtkinscription.h diff --git a/gtk/gtk.h b/gtk/gtk.h index 0d4521e4ac..bac384a2e0 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -155,6 +155,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkinscription.c b/gtk/gtkinscription.c new file mode 100644 index 0000000000..ca18a200a3 --- /dev/null +++ b/gtk/gtkinscription.c @@ -0,0 +1,908 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkinscription.h" + +#include "gtkcssstylechangeprivate.h" +#include "gtksnapshot.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; + + PangoLayout *layout; +}; + +enum +{ + PROP_0, + PROP_MIN_CHARS, + PROP_MIN_LINES, + PROP_NAT_CHARS, + PROP_NAT_LINES, + PROP_TEXT, + 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_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_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_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_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_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)) + { + PangoAttrList *new_attrs; + + new_attrs = gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change)); + pango_layout_set_attributes (self->layout, new_attrs); + pango_attr_list_unref (new_attrs); + + 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); +} + +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: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: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); +} + +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); +} + +/** + * 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; +} + diff --git a/gtk/gtkinscription.h b/gtk/gtkinscription.h new file mode 100644 index 0000000000..83e7eeb81e --- /dev/null +++ b/gtk/gtkinscription.h @@ -0,0 +1,79 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_INSCRIPTION_H__ +#define __GTK_INSCRIPTION_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_INSCRIPTION (gtk_inscription_get_type ()) + +GDK_AVAILABLE_IN_4_8 +G_DECLARE_FINAL_TYPE (GtkInscription, gtk_inscription, GTK, INSCRIPTION, GtkWidget) + +GDK_AVAILABLE_IN_4_8 +GtkWidget * gtk_inscription_new (const char *text); + +GDK_AVAILABLE_IN_4_8 +const char * gtk_inscription_get_text (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_text (GtkInscription *self, + const char *text); + +GDK_AVAILABLE_IN_4_8 +guint gtk_inscription_get_min_chars (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_min_chars (GtkInscription *self, + guint min_chars); +GDK_AVAILABLE_IN_4_8 +guint gtk_inscription_get_nat_chars (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_nat_chars (GtkInscription *self, + guint nat_chars); +GDK_AVAILABLE_IN_4_8 +guint gtk_inscription_get_min_lines (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_min_lines (GtkInscription *self, + guint min_lines); +GDK_AVAILABLE_IN_4_8 +guint gtk_inscription_get_nat_lines (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_nat_lines (GtkInscription *self, + guint nat_lines); + +GDK_AVAILABLE_IN_4_8 +float gtk_inscription_get_xalign (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_xalign (GtkInscription *self, + float xalign); +GDK_AVAILABLE_IN_4_8 +float gtk_inscription_get_yalign (GtkInscription *self); +GDK_AVAILABLE_IN_4_8 +void gtk_inscription_set_yalign (GtkInscription *self, + float yalign); + +G_END_DECLS + +#endif /* __GTK_INSCRIPTION_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index 322badc1ad..2e404f2ac4 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -283,6 +283,7 @@ gtk_public_sources = files([ 'gtkimmodule.c', 'gtkimmulticontext.c', 'gtkinfobar.c', + 'gtkinscription.c', 'gtklabel.c', 'gtklayoutchild.c', 'gtklayoutmanager.c', @@ -570,6 +571,7 @@ gtk_public_headers = files([ 'gtkimmodule.h', 'gtkimmulticontext.h', 'gtkinfobar.h', + 'gtkinscription.h', 'gtklabel.h', 'gtklayoutchild.h', 'gtklayoutmanager.h', From b56b5ed81bb80f4443756362a6ff78863530ee9d Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 19 May 2022 06:45:05 +0200 Subject: [PATCH 02/15] gtk-demo: Use GtkInscription in the main list --- demos/gtk-demo/main-listitem.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demos/gtk-demo/main-listitem.ui b/demos/gtk-demo/main-listitem.ui index a8d9c47430..aab22e3e20 100644 --- a/demos/gtk-demo/main-listitem.ui +++ b/demos/gtk-demo/main-listitem.ui @@ -7,9 +7,9 @@ GtkListItem - - start - + + 1 + expander From 61bc38cd6188ce978b08cd6846ff32af9d78713e Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 7 Jun 2022 06:09:37 +0200 Subject: [PATCH 03/15] gtk-demo: Use GtkInscription in the wordlist demo --- demos/gtk-demo/listview_words.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/demos/gtk-demo/listview_words.c b/demos/gtk-demo/listview_words.c index bee23dd562..bc4c9b8f66 100644 --- a/demos/gtk-demo/listview_words.c +++ b/demos/gtk-demo/listview_words.c @@ -17,10 +17,9 @@ const char *factory_text = "\n" "