label: Add gtk_label_set_natural_wrap_mode()

Allows influencing natural size requests so that labels can request more
width than necessary for a given height.

Related: !4245
Related: #4535
This commit is contained in:
Benjamin Otte 2021-12-19 19:22:31 +01:00
parent 92ca52822c
commit 981ed22dff
5 changed files with 152 additions and 15 deletions

View File

@ -285,6 +285,31 @@ typedef enum
GTK_MOVEMENT_HORIZONTAL_PAGES
} GtkMovementStep;
/**
* GtkNaturalWrapMode:
* @GTK_NATURAL_WRAP_INHERIT: Inherit the minimum size request.
* In particular, this should be used with %PANGO_WRAP_CHAR.
* @GTK_NATURAL_WRAP_NONE: Try not to wrap the text. This mode is the
* closest to GTK3's behavior but can lead to a wide label leaving
* lots of empty space below the text.
* @GTK_NATURAL_WRAP_WORD: Attempt to wrap at word boundaries. This
* is useful in particular when using %PANGO_WRAP_WORD_CHAR as the
* wrap mode.
*
* Options for selecting a different wrap mode for natural size
* requests.
*
* See for example the [property@Gtk.Label:natural-wrap-mode] property.
*
* Since: 4.6
*/
typedef enum
{
GTK_NATURAL_WRAP_INHERIT,
GTK_NATURAL_WRAP_NONE,
GTK_NATURAL_WRAP_WORD
} GtkNaturalWrapMode;
/**
* GtkScrollStep:
* @GTK_SCROLL_STEPS: Scroll in steps.

View File

@ -272,6 +272,7 @@ struct _GtkLabel
guint ellipsize : 3;
guint use_markup : 1;
guint wrap_mode : 3;
guint natural_wrap_mode : 3;
guint single_line_mode : 1;
guint in_click : 1;
guint track_links : 1;
@ -380,6 +381,7 @@ enum {
PROP_JUSTIFY,
PROP_WRAP,
PROP_WRAP_MODE,
PROP_NATURAL_WRAP_MODE,
PROP_SELECTABLE,
PROP_MNEMONIC_KEYVAL,
PROP_MNEMONIC_WIDGET,
@ -484,6 +486,9 @@ gtk_label_set_property (GObject *object,
case PROP_WRAP_MODE:
gtk_label_set_wrap_mode (self, g_value_get_enum (value));
break;
case PROP_NATURAL_WRAP_MODE:
gtk_label_set_natural_wrap_mode (self, g_value_get_enum (value));
break;
case PROP_SELECTABLE:
gtk_label_set_selectable (self, g_value_get_boolean (value));
break;
@ -551,6 +556,9 @@ gtk_label_get_property (GObject *object,
case PROP_WRAP_MODE:
g_value_set_enum (value, self->wrap_mode);
break;
case PROP_NATURAL_WRAP_MODE:
g_value_set_enum (value, self->natural_wrap_mode);
break;
case PROP_SELECTABLE:
g_value_set_boolean (value, gtk_label_get_selectable (self));
break;
@ -604,6 +612,7 @@ gtk_label_init (GtkLabel *self)
self->jtype = GTK_JUSTIFY_LEFT;
self->wrap = FALSE;
self->wrap_mode = PANGO_WRAP_WORD;
self->natural_wrap_mode = GTK_NATURAL_WRAP_INHERIT;
self->ellipsize = PANGO_ELLIPSIZE_NONE;
self->use_underline = FALSE;
@ -1218,8 +1227,6 @@ get_width_for_height (GtkLabel *self,
gtk_label_ensure_layout (self);
layout = pango_layout_copy (self->layout);
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
if (self->wrap_mode == PANGO_WRAP_WORD_CHAR)
pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
/* binary search for the smallest width where the height doesn't
* eclipse the given height */
@ -1228,8 +1235,19 @@ get_width_for_height (GtkLabel *self,
pango_layout_set_width (layout, -1);
pango_layout_get_size (layout, &max, NULL);
*natural_width = my_pango_layout_get_width_for_height (layout, height, min, max);
/* first, do natural width */
if (self->natural_wrap_mode == GTK_NATURAL_WRAP_NONE)
{
*natural_width = max;
}
else
{
if (self->natural_wrap_mode == GTK_NATURAL_WRAP_WORD)
pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
*natural_width = my_pango_layout_get_width_for_height (layout, height, min, max);
}
/* then, do minimum width */
if (self->ellipsize != PANGO_ELLIPSIZE_NONE)
{
g_object_unref (layout);
@ -1237,14 +1255,14 @@ get_width_for_height (GtkLabel *self,
pango_layout_get_size (layout, minimum_width, NULL);
*minimum_width = MAX (*minimum_width, minimum_default);
}
else if (self->wrap_mode == PANGO_WRAP_WORD_CHAR)
else if (self->natural_wrap_mode == GTK_NATURAL_WRAP_INHERIT)
{
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
*minimum_width = my_pango_layout_get_width_for_height (layout, height, min, *natural_width);
*minimum_width = *natural_width;
}
else
{
*minimum_width = *natural_width;
pango_layout_set_wrap (layout, self->wrap_mode);
*minimum_width = my_pango_layout_get_width_for_height (layout, height, min, *natural_width);
}
}
@ -2375,6 +2393,9 @@ gtk_label_class_init (GtkLabelClass *class)
* This only affects the formatting if line wrapping is on (see the
* [property@Gtk.Label:wrap] property). The default is %PANGO_WRAP_WORD,
* which means wrap on word boundaries.
*
* For sizing behavior, also consider the [property@Gtk.Label:natural-wrap-mode]
* property.
*/
label_props[PROP_WRAP_MODE] =
g_param_spec_enum ("wrap-mode",
@ -2384,6 +2405,27 @@ gtk_label_class_init (GtkLabelClass *class)
PANGO_WRAP_WORD,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkLabel:natural-wrap-mode: (attributes org.gtk.Property.get=gtk_label_get_natural_wrap_mode org.gtk.Property.set=gtk_label_set_natural_wrap_mode)
*
* Select the line wrapping for the natural size request.
*
* This only affects the natural size requested. For the actual wrapping used,
* see the [property@Gtk.Label:wrap-mode] property.
*
* The default is %GTK_NATURAL_WRAP_INHERIT, which inherits the behavior of the
* [property@Gtk.Label:wrap-mode] property.
*
* Since: 4.6
*/
label_props[PROP_NATURAL_WRAP_MODE] =
g_param_spec_enum ("natural-wrap-mode",
P_("Natrural wrap mode"),
P_("If wrap is set, controls linewrapping for natural size requests"),
GTK_TYPE_NATURAL_WRAP_MODE,
GTK_NATURAL_WRAP_INHERIT,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkLabel:selectable: (attributes org.gtk.Property.get=gtk_label_get_selectable og.gtk.Property.set=gtk_label_set_selectable)
*
@ -3998,6 +4040,9 @@ gtk_label_get_wrap (GtkLabel *self)
* This only affects the label if line wrapping is on. (See
* [method@Gtk.Label.set_wrap]) The default is %PANGO_WRAP_WORD
* which means wrap on word boundaries.
*
* For sizing behavior, also consider the [property@Gtk.Label:natural-wrap-mode]
* property.
*/
void
gtk_label_set_wrap_mode (GtkLabel *self,
@ -4032,6 +4077,53 @@ gtk_label_get_wrap_mode (GtkLabel *self)
return self->wrap_mode;
}
/**
* gtk_label_set_natural_wrap_mode: (attributes org.gtk.Method.set_property=natural-wrap-mode)
* @self: a `GtkLabel`
* @wrap_mode: the line wrapping mode
*
* Select the line wrapping for the natural size request.
*
* This only affects the natural size requested, for the actual wrapping used,
* see the [property@Gtk.Label:wrap-mode] property.
*
* Since: 4.6
*/
void
gtk_label_set_natural_wrap_mode (GtkLabel *self,
GtkNaturalWrapMode wrap_mode)
{
g_return_if_fail (GTK_IS_LABEL (self));
if (self->natural_wrap_mode != wrap_mode)
{
self->natural_wrap_mode = wrap_mode;
g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_NATURAL_WRAP_MODE]);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
}
/**
* gtk_label_get_natural_wrap_mode: (attributes org.gtk.Method.get_property=natural-wrap-mode)
* @self: a `GtkLabel`
*
* Returns line wrap mode used by the label.
*
* See [method@Gtk.Label.set_natural_wrap_mode].
*
* Returns: the natural line wrap mode
*
* Since: 4.6
*/
GtkNaturalWrapMode
gtk_label_get_natural_wrap_mode (GtkLabel *self)
{
g_return_val_if_fail (GTK_IS_LABEL (self), PANGO_WRAP_CHAR);
return self->natural_wrap_mode;
}
static void
gtk_label_clear_layout (GtkLabel *self)
{

View File

@ -122,6 +122,11 @@ void gtk_label_set_wrap_mode (GtkLabel *self,
PangoWrapMode wrap_mode);
GDK_AVAILABLE_IN_ALL
PangoWrapMode gtk_label_get_wrap_mode (GtkLabel *self);
GDK_AVAILABLE_IN_4_6
void gtk_label_set_natural_wrap_mode (GtkLabel *self,
GtkNaturalWrapMode wrap_mode);
GDK_AVAILABLE_IN_4_6
GtkNaturalWrapMode gtk_label_get_natural_wrap_mode(GtkLabel *self);
GDK_AVAILABLE_IN_ALL
void gtk_label_set_selectable (GtkLabel *self,
gboolean setting);

View File

@ -1,21 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow">
<property name="default-width">300</property>
<property name="default-width">600</property>
<property name="default-height">300</property>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkLabel">
<property name="label">two
<property name="label">lots
of
lines</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">unwrapped</property>
<property name="label">unwrappable
words</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">single line of text</property>
</object>
</child>
</object>

View File

@ -1,23 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow">
<property name="default-width">300</property>
<property name="default-width">600</property>
<property name="default-height">300</property>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkLabel">
<property name="label">two
<property name="label">lots
of
lines</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">unwrapped</property>
<property name="label">unwrappable words</property>
<property name="wrap">1</property>
<property name="wrap-mode">word-char</property>
<property name="natural-wrap-mode">word</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">single line of text</property>
<property name="wrap">1</property>
<property name="wrap-mode">word-char</property>
<property name="natural-wrap-mode">none</property>
</object>
</child>
</object>