[libadwaita/wip/exalm/tabs: 13/19] Add AdwFadingLabel




commit caaaaaa47b789529bab59a5d4aea015f21336cae
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Tue Jan 26 21:00:05 2021 +0500

    Add AdwFadingLabel
    
    Add a helper widget for showing fading ellipsized titles in tabs.

 doc/meson.build                |   1 +
 src/adw-fading-label-private.h |  31 ++++
 src/adw-fading-label.c         | 328 +++++++++++++++++++++++++++++++++++++++++
 src/adwaita.gresources.xml     |   1 +
 src/glsl/fade.glsl             |  25 ++++
 src/meson.build                |   1 +
 6 files changed, 387 insertions(+)
---
diff --git a/doc/meson.build b/doc/meson.build
index 5ca0077..ea422f4 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -12,6 +12,7 @@ private_headers = [
     'adw-enums.h',
     'adw-enums-private.h',
     'adw-enum-value-object-private.h',
+    'adw-fading-label-private.h',
     'adw-focus-private.h',
     'adw-gizmo-private.h',
     'adw-main-private.h',
diff --git a/src/adw-fading-label-private.h b/src/adw-fading-label-private.h
new file mode 100644
index 0000000..630d291
--- /dev/null
+++ b/src/adw-fading-label-private.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_FADING_LABEL (adw_fading_label_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwFadingLabel, adw_fading_label, ADW, FADING_LABEL, GtkWidget)
+
+const char *adw_fading_label_get_label (AdwFadingLabel *self);
+void        adw_fading_label_set_label (AdwFadingLabel *self,
+                                        const char     *label);
+
+float       adw_fading_label_get_align (AdwFadingLabel *self);
+void        adw_fading_label_set_align (AdwFadingLabel *self,
+                                        float           align);
+
+G_END_DECLS
diff --git a/src/adw-fading-label.c b/src/adw-fading-label.c
new file mode 100644
index 0000000..d9ad95a
--- /dev/null
+++ b/src/adw-fading-label.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+#include "adw-fading-label-private.h"
+
+#include <glib/gi18n-lib.h>
+#include "adw-bidi-private.h"
+
+/**
+ * PRIVATE:adw-fading-label
+ * @short_description: A helper object for #AdwTab
+ * @title: AdwFadingLabel
+ * @stability: Private
+ *
+ * The AdwFadingLabel widget allows to ellipsize a label with a fading effect.
+ *
+ * Since: 1.0
+ */
+
+#define FADE_WIDTH 18
+
+struct _AdwFadingLabel
+{
+  GtkWidget parent_instance;
+
+  GtkWidget *label;
+  float align;
+
+  GskGLShader *shader;
+  gboolean shader_compiled;
+};
+
+G_DEFINE_TYPE (AdwFadingLabel, adw_fading_label, GTK_TYPE_WIDGET)
+
+enum {
+  PROP_0,
+  PROP_LABEL,
+  PROP_ALIGN,
+  LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP];
+
+static gboolean
+is_rtl (AdwFadingLabel *self)
+{
+  PangoDirection pango_direction = PANGO_DIRECTION_NEUTRAL;
+  const char *label = adw_fading_label_get_label (self);
+
+  if (label)
+    pango_direction = adw_find_base_dir (label, -1);
+
+  if (pango_direction == PANGO_DIRECTION_RTL)
+    return TRUE;
+
+  if (pango_direction == PANGO_DIRECTION_LTR)
+    return FALSE;
+
+  return gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
+}
+
+static void
+ensure_shader (AdwFadingLabel *self)
+{
+  GtkNative *native;
+  GskRenderer *renderer;
+  g_autoptr (GError) error = NULL;
+
+  if (self->shader)
+    return;
+
+  self->shader = gsk_gl_shader_new_from_resource ("/org/gnome/Adwaita/glsl/fade.glsl");
+
+  native = gtk_widget_get_native (GTK_WIDGET (self));
+  renderer = gtk_native_get_renderer (native);
+
+  self->shader_compiled = gsk_gl_shader_compile (self->shader, renderer, &error);
+
+  if (error) {
+    /* If shaders aren't supported, the error doesn't matter and we just
+     * silently fall back */
+    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+      g_critical ("Couldn't compile shader: %s\n", error->message);
+
+    self->shader_compiled = FALSE;
+  }
+}
+
+static void
+adw_fading_label_measure (GtkWidget      *widget,
+                          GtkOrientation  orientation,
+                          int             for_size,
+                          int             *min,
+                          int             *nat,
+                          int             *min_baseline,
+                          int             *nat_baseline)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (widget);
+
+  gtk_widget_measure (self->label, orientation, for_size,
+                      min, nat, min_baseline, nat_baseline);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL && min)
+    *min = 0;
+}
+
+static void
+adw_fading_label_size_allocate (GtkWidget *widget,
+                                int        width,
+                                int        height,
+                                int        baseline)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (widget);
+  float align = is_rtl (self) ? 1 - self->align : self->align;
+  int child_width;
+  float offset;
+  GskTransform *transform;
+
+  gtk_widget_measure (self->label, GTK_ORIENTATION_HORIZONTAL, height,
+                      NULL, &child_width, NULL, NULL);
+
+  offset = (width - child_width) * align;
+  transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (offset, 0));
+
+  gtk_widget_allocate (self->label, child_width, height, baseline, transform);
+}
+
+static void
+adw_fading_label_snapshot (GtkWidget   *widget,
+                           GtkSnapshot *snapshot)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (widget);
+  float align = is_rtl (self) ? 1 - self->align : self->align;
+  int width = gtk_widget_get_width (widget);
+  int clipped_size;
+  GtkSnapshot *child_snapshot;
+  g_autoptr (GskRenderNode) node = NULL;
+  graphene_rect_t bounds;
+
+  if (width <= 0)
+    return;
+
+  clipped_size = gtk_widget_get_allocated_width (self->label) - width;
+
+  if (clipped_size <= 0) {
+    gtk_widget_snapshot_child (widget, self->label, snapshot);
+
+    return;
+  }
+
+  child_snapshot = gtk_snapshot_new ();
+  gtk_widget_snapshot_child (widget, self->label, child_snapshot);
+  node = gtk_snapshot_free_to_node (child_snapshot);
+
+  gsk_render_node_get_bounds (node, &bounds);
+  bounds.origin.x = 0;
+  bounds.size.width = width;
+
+  ensure_shader (self);
+
+  if (self->shader_compiled) {
+    gtk_snapshot_push_gl_shader (snapshot, self->shader, &bounds,
+                                 gsk_gl_shader_format_args (self->shader,
+                                                            "offsetLeft", 0.0f,
+                                                            "offsetRight", 0.0f,
+                                                            "strengthLeft", align > 0 ? 1.0f : 0.0f,
+                                                            "strengthRight", align < 1 ? 1.0f : 0.0f,
+                                                            NULL));
+  } else {
+    gtk_snapshot_push_clip (snapshot, &bounds);
+  }
+
+  gtk_snapshot_append_node (snapshot, node);
+
+  if (self->shader_compiled)
+    gtk_snapshot_gl_shader_pop_texture (snapshot);
+
+  gtk_snapshot_pop (snapshot);
+}
+
+static void
+adw_fading_label_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (object);
+
+  switch (prop_id) {
+  case PROP_LABEL:
+    g_value_set_string (value, adw_fading_label_get_label (self));
+    break;
+
+  case PROP_ALIGN:
+    g_value_set_float (value, adw_fading_label_get_align (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_fading_label_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (object);
+
+  switch (prop_id) {
+  case PROP_LABEL:
+    adw_fading_label_set_label (self, g_value_get_string (value));
+    break;
+
+  case PROP_ALIGN:
+    adw_fading_label_set_align (self, g_value_get_float (value));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_fading_label_dispose (GObject *object)
+{
+  AdwFadingLabel *self = ADW_FADING_LABEL (object);
+
+  g_clear_object (&self->shader);
+  g_clear_pointer (&self->label, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (adw_fading_label_parent_class)->dispose (object);
+}
+
+static void
+adw_fading_label_class_init (AdwFadingLabelClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->get_property = adw_fading_label_get_property;
+  object_class->set_property = adw_fading_label_set_property;
+  object_class->dispose = adw_fading_label_dispose;
+
+  widget_class->measure = adw_fading_label_measure;
+  widget_class->size_allocate = adw_fading_label_size_allocate;
+  widget_class->snapshot = adw_fading_label_snapshot;
+
+  props[PROP_LABEL] =
+    g_param_spec_string ("label",
+                         "Label",
+                         "Label",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  props[PROP_ALIGN] =
+    g_param_spec_float ("align",
+                        "Align",
+                        "Align",
+                        0.0, 1.0, 0.0,
+                        G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+}
+
+static void
+adw_fading_label_init (AdwFadingLabel *self)
+{
+  self->label = gtk_label_new (NULL);
+  gtk_label_set_single_line_mode (GTK_LABEL (self->label), TRUE);
+
+  gtk_widget_set_parent (self->label, GTK_WIDGET (self));
+}
+
+const char *
+adw_fading_label_get_label (AdwFadingLabel *self)
+{
+  g_return_val_if_fail (ADW_IS_FADING_LABEL (self), NULL);
+
+  return gtk_label_get_label (GTK_LABEL (self->label));
+}
+
+void
+adw_fading_label_set_label (AdwFadingLabel *self,
+                            const char     *label)
+{
+  g_return_if_fail (ADW_IS_FADING_LABEL (self));
+
+  if (!g_strcmp0 (label, adw_fading_label_get_label (self)))
+    return;
+
+  gtk_label_set_label (GTK_LABEL (self->label), label);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_LABEL]);
+}
+
+float
+adw_fading_label_get_align (AdwFadingLabel *self)
+{
+  g_return_val_if_fail (ADW_IS_FADING_LABEL (self), 0.0f);
+
+  return self->align;
+}
+
+void
+adw_fading_label_set_align (AdwFadingLabel *self,
+                            float           align)
+{
+  g_return_if_fail (ADW_IS_FADING_LABEL (self));
+
+  align = CLAMP (align, 0.0, 1.0);
+
+  if (self->align == align)
+    return;
+
+  self->align = align;
+
+  gtk_widget_queue_allocate (GTK_WIDGET (self));
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ALIGN]);
+}
diff --git a/src/adwaita.gresources.xml b/src/adwaita.gresources.xml
index 7575012..9b350d0 100644
--- a/src/adwaita.gresources.xml
+++ b/src/adwaita.gresources.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/Adwaita">
+    <file>glsl/fade.glsl</file>
     <file preprocess="xml-stripblanks">icons/scalable/actions/adw-expander-arrow-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/avatar-default-symbolic.svg</file>
   </gresource>
diff --git a/src/glsl/fade.glsl b/src/glsl/fade.glsl
new file mode 100644
index 0000000..64dc111
--- /dev/null
+++ b/src/glsl/fade.glsl
@@ -0,0 +1,25 @@
+uniform float offsetLeft;
+uniform float offsetRight;
+uniform float strengthLeft;
+uniform float strengthRight;
+
+uniform sampler2D u_texture1;
+
+#define FADE_WIDTH 18
+
+void mainImage(out vec4 fragColor,
+               in vec2 fragCoord,
+               in vec2 resolution,
+               in vec2 uv) {
+    float progress;
+
+    fragColor = GskTexture(u_texture1, uv);
+
+    progress = fragCoord.x - offsetLeft;
+    progress = min(max(progress / FADE_WIDTH, 0), 1);
+    fragColor *= (1 + strengthLeft * (progress - 1));
+
+    progress = resolution.x - offsetRight - fragCoord.x;
+    progress = min(max(progress / FADE_WIDTH, 0), 1);
+    fragColor *= (1 + strengthRight * (progress - 1));
+}
diff --git a/src/meson.build b/src/meson.build
index a0f5488..ae3d4e4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -132,6 +132,7 @@ src_sources = [
   'adw-enum-list-model.c',
   'adw-enum-value-object.c',
   'adw-expander-row.c',
+  'adw-fading-label.c',
   'adw-flap.c',
   'adw-focus.c',
   'adw-gizmo.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]