From 593289e40ba8e2edd7eb885d9e30644ceee4bbdc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 14 Nov 2020 00:09:23 -0500 Subject: gtk-demo: Speed up characters scrolling Use a simple label widget that does not queue a resize when the text is changed. --- demos/gtk-demo/listview_ucd.c | 31 ++--- demos/gtk-demo/meson.build | 1 + demos/gtk-demo/simplelabel.c | 260 ++++++++++++++++++++++++++++++++++++++++++ demos/gtk-demo/simplelabel.h | 15 +++ 4 files changed, 293 insertions(+), 14 deletions(-) create mode 100644 demos/gtk-demo/simplelabel.c create mode 100644 demos/gtk-demo/simplelabel.h diff --git a/demos/gtk-demo/listview_ucd.c b/demos/gtk-demo/listview_ucd.c index 418e7ca418..987346208d 100644 --- a/demos/gtk-demo/listview_ucd.c +++ b/demos/gtk-demo/listview_ucd.c @@ -9,6 +9,7 @@ #include #include "script-names.h" #include "unicode-names.h" +#include "simplelabel.h" #define UCD_TYPE_ITEM (ucd_item_get_type ()) @@ -102,7 +103,9 @@ setup_centered_label (GtkSignalListItemFactory *factory, GObject *listitem) { GtkWidget *label; - label = gtk_label_new (""); + label = simple_label_new (); + simple_label_set_min_chars (SIMPLE_LABEL (label), 3); + simple_label_set_nat_chars (SIMPLE_LABEL (label), 20); gtk_list_item_set_child (GTK_LIST_ITEM (listitem), label); } @@ -111,8 +114,9 @@ setup_label (GtkSignalListItemFactory *factory, GObject *listitem) { GtkWidget *label; - label = gtk_label_new (""); - gtk_label_set_xalign (GTK_LABEL (label), 0); + label = simple_label_new (); + simple_label_set_min_chars (SIMPLE_LABEL (label), 3); + simple_label_set_nat_chars (SIMPLE_LABEL (label), 20); gtk_list_item_set_child (GTK_LIST_ITEM (listitem), label); } @@ -121,10 +125,9 @@ setup_ellipsizing_label (GtkSignalListItemFactory *factory, GObject *listitem) { GtkWidget *label; - label = gtk_label_new (""); - gtk_label_set_xalign (GTK_LABEL (label), 0); - gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); - gtk_label_set_width_chars (GTK_LABEL (label), 20); + label = simple_label_new (); + simple_label_set_min_chars (SIMPLE_LABEL (label), 3); + simple_label_set_nat_chars (SIMPLE_LABEL (label), 20); gtk_list_item_set_child (GTK_LIST_ITEM (listitem), label); } @@ -142,7 +145,7 @@ bind_codepoint (GtkSignalListItemFactory *factory, codepoint = ucd_item_get_codepoint (UCD_ITEM (item)); g_snprintf (buffer, 10, "%#06x", codepoint); - gtk_label_set_label (GTK_LABEL (label), buffer); + simple_label_set_text (SIMPLE_LABEL (label), buffer); } static void @@ -161,7 +164,7 @@ bind_char (GtkSignalListItemFactory *factory, if (g_unichar_isprint (codepoint)) g_unichar_to_utf8 (codepoint, buffer); - gtk_label_set_label (GTK_LABEL (label), buffer); + simple_label_set_text (SIMPLE_LABEL (label), buffer); } static void @@ -176,7 +179,7 @@ bind_name (GtkSignalListItemFactory *factory, item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem)); name = ucd_item_get_name (UCD_ITEM (item)); - gtk_label_set_label (GTK_LABEL (label), name); + simple_label_set_text (SIMPLE_LABEL (label), name); } static void @@ -191,7 +194,7 @@ bind_type (GtkSignalListItemFactory *factory, item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem)); codepoint = ucd_item_get_codepoint (UCD_ITEM (item)); - gtk_label_set_label (GTK_LABEL (label), get_unicode_type_name (g_unichar_type (codepoint))); + simple_label_set_text (SIMPLE_LABEL (label), get_unicode_type_name (g_unichar_type (codepoint))); } static void @@ -206,7 +209,7 @@ bind_break_type (GtkSignalListItemFactory *factory, item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem)); codepoint = ucd_item_get_codepoint (UCD_ITEM (item)); - gtk_label_set_label (GTK_LABEL (label), get_break_type_name (g_unichar_break_type (codepoint))); + simple_label_set_text (SIMPLE_LABEL (label), get_break_type_name (g_unichar_break_type (codepoint))); } static void @@ -221,7 +224,7 @@ bind_combining_class (GtkSignalListItemFactory *factory, item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem)); codepoint = ucd_item_get_codepoint (UCD_ITEM (item)); - gtk_label_set_label (GTK_LABEL (label), get_combining_class_name (g_unichar_combining_class (codepoint))); + simple_label_set_text (SIMPLE_LABEL (label), get_combining_class_name (g_unichar_combining_class (codepoint))); } static void @@ -238,7 +241,7 @@ bind_script (GtkSignalListItemFactory *factory, codepoint = ucd_item_get_codepoint (UCD_ITEM (item)); script = g_unichar_get_script (codepoint); - gtk_label_set_label (GTK_LABEL (label), get_script_name (script)); + simple_label_set_text (SIMPLE_LABEL (label), get_script_name (script)); } static void diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 1347baf2aa..f95297faed 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -130,6 +130,7 @@ extra_demo_sources = files([ 'unicode-names.c', 'suggestionentry.c', 'language-names.c', + 'simplelabel.c', ]) if os_unix diff --git a/demos/gtk-demo/simplelabel.c b/demos/gtk-demo/simplelabel.c new file mode 100644 index 0000000000..ada2848c33 --- /dev/null +++ b/demos/gtk-demo/simplelabel.c @@ -0,0 +1,260 @@ +#include "simplelabel.h" + +struct _SimpleLabel +{ + GtkWidget parent_instance; + + PangoLayout *layout; + int min_chars; + int nat_chars; + + int min_width; + int nat_width; + int height; +}; + +struct _SimpleLabelClass +{ + GtkWidgetClass parent_class; +}; + +enum { + PROP_TEXT = 1, + PROP_MIN_CHARS, + PROP_NAT_CHARS, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (SimpleLabel, simple_label, GTK_TYPE_WIDGET) + +static void +simple_label_init (SimpleLabel *self) +{ + self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), ""); + pango_layout_set_ellipsize (self->layout, PANGO_ELLIPSIZE_NONE); + pango_layout_set_wrap (self->layout, PANGO_WRAP_WORD); + pango_layout_set_width (self->layout, -1); +} + +static void +simple_label_dispose (GObject *object) +{ + SimpleLabel *self = SIMPLE_LABEL (object); + + g_clear_object (&self->layout); + + G_OBJECT_CLASS (simple_label_parent_class)->dispose (object); +} + +static void +simple_label_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SimpleLabel *self = SIMPLE_LABEL (object); + + switch (prop_id) + { + case PROP_TEXT: + simple_label_set_text (self, g_value_get_string (value)); + break; + + case PROP_MIN_CHARS: + simple_label_set_min_chars (self, g_value_get_int (value)); + break; + + case PROP_NAT_CHARS: + simple_label_set_nat_chars (self, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +simple_label_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SimpleLabel *self = SIMPLE_LABEL (object); + + switch (prop_id) + { + case PROP_TEXT: + g_value_set_string (value, pango_layout_get_text (self->layout)); + break; + + case PROP_MIN_CHARS: + g_value_set_int (value, self->min_chars); + break; + + case PROP_NAT_CHARS: + g_value_set_int (value, self->nat_chars); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +simple_label_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + SimpleLabel *self = SIMPLE_LABEL (widget); + + if (orientation == GTK_ORIENTATION_VERTICAL) + { + *minimum = *natural = self->height; + } + else + { + *minimum = self->min_width; + *natural = self->nat_width; + } +} + +static void +simple_label_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + SimpleLabel *self = SIMPLE_LABEL (widget); + + pango_layout_set_width (self->layout, width * PANGO_SCALE); +} + +static void +simple_label_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + SimpleLabel *self = SIMPLE_LABEL (widget); + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + + gtk_snapshot_render_layout (snapshot, context, 0, 0, self->layout); +} + +static void +simple_label_class_init (SimpleLabelClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->dispose = simple_label_dispose; + object_class->set_property = simple_label_set_property; + object_class->get_property = simple_label_get_property; + + widget_class->measure = simple_label_measure; + widget_class->size_allocate = simple_label_size_allocate; + widget_class->snapshot = simple_label_snapshot; + + properties[PROP_TEXT] = + g_param_spec_string ("text", "Text", "Text", + NULL, + G_PARAM_READWRITE); + + properties[PROP_MIN_CHARS] = + g_param_spec_int ("min-chars", "Minimum Characters", "Minimum Characters", + 0, G_MAXINT, 0, + G_PARAM_READWRITE); + + properties[PROP_NAT_CHARS] = + g_param_spec_int ("nat-chars", "Natural Characters", "Natural Characters", + 0, G_MAXINT, 0, + G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); +} + +GtkWidget * +simple_label_new (void) +{ + return g_object_new (SIMPLE_TYPE_LABEL, NULL); +} + +void +simple_label_set_text (SimpleLabel *self, + const char *text) +{ + if (strcmp (text, pango_layout_get_text (self->layout)) == 0) + return; + + pango_layout_set_text (self->layout, text, -1); + + gtk_widget_queue_draw (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]); +} + +static void +recalculate (SimpleLabel *self) +{ + PangoContext *context; + PangoFontMetrics *metrics; + int char_width; + int digit_width; + int width; + int ascent; + int descent; + + context = gtk_widget_get_pango_context (GTK_WIDGET (self)); + metrics = pango_context_get_metrics (context, + pango_context_get_font_description (context), + pango_context_get_language (context)); + char_width = pango_font_metrics_get_approximate_char_width (metrics); + digit_width = pango_font_metrics_get_approximate_digit_width (metrics); + ascent = pango_font_metrics_get_ascent (metrics); + descent = pango_font_metrics_get_descent (metrics); + pango_font_metrics_unref (metrics); + + width = MAX (char_width, digit_width); + + self->min_width = (width * self->min_chars) / PANGO_SCALE; + self->nat_width = (width * self->nat_chars) / PANGO_SCALE; + self->height = (ascent + descent) / PANGO_SCALE; +} + +void +simple_label_set_min_chars (SimpleLabel *self, + int min_chars) +{ + if (self->min_chars == min_chars) + return; + + self->min_chars = min_chars; + + recalculate (self); + gtk_widget_queue_resize (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_CHARS]); +} + +void +simple_label_set_nat_chars (SimpleLabel *self, + int nat_chars) +{ + if (self->nat_chars == nat_chars) + return; + + self->nat_chars = nat_chars; + + recalculate (self); + gtk_widget_queue_resize (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAT_CHARS]); +} diff --git a/demos/gtk-demo/simplelabel.h b/demos/gtk-demo/simplelabel.h new file mode 100644 index 0000000000..04ef3ea8e8 --- /dev/null +++ b/demos/gtk-demo/simplelabel.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define SIMPLE_TYPE_LABEL (simple_label_get_type ()) +G_DECLARE_FINAL_TYPE (SimpleLabel, simple_label, SIMPLE, LABEL, GtkWidget) + +GtkWidget * simple_label_new (void); + +void simple_label_set_text (SimpleLabel *self, + const char *text); +void simple_label_set_min_chars (SimpleLabel *self, + int min_chars); +void simple_label_set_nat_chars (SimpleLabel *self, + int nat_chars); -- cgit v1.2.1