[gtk/pango2] fontexplorer: Add a glyphs view



commit 14e1df03707fa59ded49a5f59a0e8775b2092b4d
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Jul 7 12:46:36 2022 -0400

    fontexplorer: Add a glyphs view

 demos/font-explorer/fontexplorer.css |   7 ++
 demos/font-explorer/fontview.c       | 103 ++++++++++++++++++++++-
 demos/font-explorer/fontview.ui      |  30 +++++++
 demos/font-explorer/glyphitem.c      |  56 +++++++++++++
 demos/font-explorer/glyphitem.h      |  17 ++++
 demos/font-explorer/glyphmodel.c     |  99 ++++++++++++++++++++++
 demos/font-explorer/glyphmodel.h     |  14 ++++
 demos/font-explorer/glyphview.c      | 155 +++++++++++++++++++++++++++++++++++
 demos/font-explorer/glyphview.h      |  19 +++++
 demos/font-explorer/meson.build      |   3 +
 10 files changed, 501 insertions(+), 2 deletions(-)
---
diff --git a/demos/font-explorer/fontexplorer.css b/demos/font-explorer/fontexplorer.css
index bae9d7e768..032543c2e6 100644
--- a/demos/font-explorer/fontexplorer.css
+++ b/demos/font-explorer/fontexplorer.css
@@ -18,3 +18,10 @@ fontview {
   padding: 10px;
   border-spacing: 10px;
 }
+gridview > child {
+  padding: 0;
+}
+glyphview {
+  padding: 0;
+  border: 0.6px solid gray;
+}
diff --git a/demos/font-explorer/fontview.c b/demos/font-explorer/fontview.c
index 678de65220..32aee30ac5 100644
--- a/demos/font-explorer/fontview.c
+++ b/demos/font-explorer/fontview.c
@@ -1,4 +1,7 @@
 #include "fontview.h"
+#include "glyphitem.h"
+#include "glyphmodel.h"
+#include "glyphview.h"
 #include <gtk/gtk.h>
 
 enum {
@@ -26,6 +29,8 @@ struct _FontView
   GtkTextView *edit;
   GtkLabel *content;
   GtkScrolledWindow *swin;
+  GtkGridView *glyphs;
+  GtkToggleButton *glyphs_toggle;
 
   Pango2FontDescription *font_desc;
   float size;
@@ -188,22 +193,107 @@ toggle_edit (GtkToggleButton *button,
 
       update_view (self);
 
-      gtk_stack_set_visible_child_name (self->stack, "content");
+      if (gtk_toggle_button_get_active (self->glyphs_toggle))
+        gtk_stack_set_visible_child_name (self->stack, "glyphs");
+      else
+        gtk_stack_set_visible_child_name (self->stack, "content");
 
       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SAMPLE_TEXT]);
     }
 }
 
+static void
+plain_changed (GtkToggleButton *button,
+               GParamSpec      *pspec,
+               FontView        *self)
+{
+  if (gtk_toggle_button_get_active (button))
+    {
+      gtk_stack_set_visible_child_name (self->stack, "content");
+      self->do_waterfall = FALSE;
+    }
+
+  update_view (self);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_SIZE]);
+}
+
 static void
 waterfall_changed (GtkToggleButton *button,
                    GParamSpec      *pspec,
                    FontView        *self)
 {
-  self->do_waterfall = gtk_toggle_button_get_active (button);
+  if (gtk_toggle_button_get_active (button))
+    {
+      gtk_stack_set_visible_child_name (self->stack, "content");
+      self->do_waterfall = TRUE;
+    }
+
   update_view (self);
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_SIZE]);
 }
 
+static void
+glyphs_changed (GtkToggleButton *button,
+                GParamSpec      *pspec,
+                FontView        *self)
+{
+  if (gtk_toggle_button_get_active (button))
+    {
+      gtk_stack_set_visible_child_name (self->stack, "glyphs");
+      self->do_waterfall = FALSE;
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_SIZE]);
+}
+
+static Pango2Font *
+get_font (FontView *self)
+{
+  Pango2Context *context;
+
+  context = gtk_widget_get_pango_context (GTK_WIDGET (self));
+  return pango2_context_load_font (context, self->font_desc);
+}
+
+static void
+update_glyph_model (FontView *self)
+{
+  Pango2Font *font = get_font (self);
+  GlyphModel *gm;
+  GtkSelectionModel *model;
+
+  gm = glyph_model_new (pango2_font_get_face (font));
+  model = GTK_SELECTION_MODEL (gtk_no_selection_new (G_LIST_MODEL (gm)));
+  gtk_grid_view_set_model (self->glyphs, model);
+  g_object_unref (model);
+}
+
+static void
+setup_glyph (GtkSignalListItemFactory *factory,
+             GObject                  *listitem)
+{
+  gtk_list_item_set_child (GTK_LIST_ITEM (listitem), GTK_WIDGET (glyph_view_new ()));
+}
+
+static void
+bind_glyph (GtkSignalListItemFactory *factory,
+            GObject                  *listitem)
+{
+  GlyphView *view;
+  GObject *item;
+
+  view = GLYPH_VIEW (gtk_list_item_get_child (GTK_LIST_ITEM (listitem)));
+  item = gtk_list_item_get_item (GTK_LIST_ITEM (listitem));
+  glyph_view_set_font (view, glyph_item_get_font (GLYPH_ITEM (item)));
+  glyph_view_set_glyph (view, glyph_item_get_glyph (GLYPH_ITEM (item)));
+}
+
+static void
+unbind_glyph (GtkSignalListItemFactory *factory,
+              GObject                  *listitem)
+{
+}
+
 static void
 font_view_set_property (GObject      *object,
                         guint         prop_id,
@@ -217,6 +307,7 @@ font_view_set_property (GObject      *object,
     case PROP_FONT_DESC:
       pango2_font_description_free (self->font_desc);
       self->font_desc = pango2_font_description_copy (g_value_get_boxed (value));
+      update_glyph_model (self);
       break;
 
     case PROP_SIZE:
@@ -400,8 +491,16 @@ font_view_class_init (FontViewClass *class)
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontView, content);
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontView, stack);
   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontView, edit);
+  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontView, glyphs);
+  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontView, glyphs_toggle);
+
   gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), toggle_edit);
+  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), plain_changed);
   gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), waterfall_changed);
+  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), glyphs_changed);
+  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), setup_glyph);
+  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), bind_glyph);
+  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), unbind_glyph);
 
   gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "fontview");
 }
diff --git a/demos/font-explorer/fontview.ui b/demos/font-explorer/fontview.ui
index 74a4b3fa23..1d1d457b70 100644
--- a/demos/font-explorer/fontview.ui
+++ b/demos/font-explorer/fontview.ui
@@ -35,6 +35,28 @@
             </property>
           </object>
         </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">glyphs</property>
+            <property name="child">
+              <object class="GtkScrolledWindow">
+                <property name="hscrollbar-policy">never</property>
+                <property name="vscrollbar-policy">automatic</property>
+                <child>
+                  <object class="GtkGridView" id="glyphs">
+                    <property name="factory">
+                      <object class="GtkSignalListItemFactory">
+                        <signal name="setup" handler="setup_glyph"/>
+                        <signal name="bind" handler="bind_glyph"/>
+                        <signal name="unbind" handler="unbind_glyph"/>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
         <child>
           <object class="GtkStackPage">
             <property name="name">edit</property>
@@ -64,6 +86,7 @@
               <object class="GtkToggleButton" id="plain_toggle">
                 <property name="label" translatable="yes">Plain</property>
                 <property name="active">1</property>
+                <signal name="notify::active" handler="plain_changed"/>
               </object>
             </child>
             <child>
@@ -73,6 +96,13 @@
                 <signal name="notify::active" handler="waterfall_changed"/>
               </object>
             </child>
+            <child>
+              <object class="GtkToggleButton" id="glyphs_toggle">
+                <property name="label" translatable="yes">Glyphs</property>
+                <property name="group">plain_toggle</property>
+                <signal name="notify::active" handler="glyphs_changed"/>
+              </object>
+            </child>
           </object>
         </child>
         <child>
diff --git a/demos/font-explorer/glyphitem.c b/demos/font-explorer/glyphitem.c
new file mode 100644
index 0000000000..f84695aa6b
--- /dev/null
+++ b/demos/font-explorer/glyphitem.c
@@ -0,0 +1,56 @@
+#include "glyphitem.h"
+
+struct _GlyphItem {
+  GObject parent;
+  Pango2Font *font;
+  hb_codepoint_t glyph;
+};
+
+struct _GlyphItemClass {
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (GlyphItem, glyph_item, G_TYPE_OBJECT)
+
+static void
+glyph_item_init (GlyphItem *self)
+{
+}
+
+static void
+glyph_item_finalize (GObject *object)
+{
+  GlyphItem *item = GLYPH_ITEM (object);
+
+  g_object_unref (item->font);
+
+  G_OBJECT_CLASS (glyph_item_parent_class)->finalize (object);
+}
+
+static void
+glyph_item_class_init (GlyphItemClass *class)
+{
+  G_OBJECT_CLASS (class)->finalize = glyph_item_finalize;
+}
+
+GlyphItem *
+glyph_item_new (Pango2Font *font,
+                hb_codepoint_t  glyph)
+{
+  GlyphItem *item = g_object_new (GLYPH_ITEM_TYPE, NULL);
+  item->font = g_object_ref (font);
+  item->glyph = glyph;
+  return item;
+}
+
+Pango2Font *
+glyph_item_get_font (GlyphItem *item)
+{
+  return item->font;
+}
+
+hb_codepoint_t
+glyph_item_get_glyph (GlyphItem *item)
+{
+  return item->glyph;
+}
diff --git a/demos/font-explorer/glyphitem.h b/demos/font-explorer/glyphitem.h
new file mode 100644
index 0000000000..776048873c
--- /dev/null
+++ b/demos/font-explorer/glyphitem.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define GLYPH_ITEM_TYPE (glyph_item_get_type ())
+#define GLYPH_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLYPH_ITEM_TYPE, GlyphItem))
+
+
+typedef struct _GlyphItem         GlyphItem;
+typedef struct _GlyphItemClass    GlyphItemClass;
+
+
+GType       glyph_item_get_type (void);
+GlyphItem * glyph_item_new      (Pango2Font *font,
+                                 hb_codepoint_t  glyph);
+hb_codepoint_t glyph_item_get_glyph (GlyphItem *item);
+Pango2Font * glyph_item_get_font (GlyphItem *item);
diff --git a/demos/font-explorer/glyphmodel.c b/demos/font-explorer/glyphmodel.c
new file mode 100644
index 0000000000..cfb53b412b
--- /dev/null
+++ b/demos/font-explorer/glyphmodel.c
@@ -0,0 +1,99 @@
+#include "glyphmodel.h"
+#include "glyphitem.h"
+
+struct _GlyphModel {
+  GObject parent;
+  Pango2Font *font;
+  unsigned int num_glyphs;
+  GlyphItem **glyphs;
+};
+
+struct _GlyphModelClass {
+  GObjectClass parent_class;
+};
+
+static GType
+glyph_model_get_item_type (GListModel *model)
+{
+  return GLYPH_ITEM_TYPE;
+}
+
+static guint
+glyph_model_get_n_items (GListModel *model)
+{
+  GlyphModel *self = GLYPH_MODEL (model);
+
+  return self->num_glyphs;
+}
+
+static gpointer
+glyph_model_get_item (GListModel *model,
+                      guint       position)
+{
+  GlyphModel *self = GLYPH_MODEL (model);
+
+  if (position >= self->num_glyphs)
+    return NULL;
+
+  if (self->glyphs[position] == NULL)
+    self->glyphs[position] = glyph_item_new (self->font, position);
+
+  return g_object_ref (self->glyphs[position]);
+}
+
+static void
+glyph_model_list_model_init (GListModelInterface *iface)
+{
+  iface->get_item_type = glyph_model_get_item_type;
+  iface->get_n_items = glyph_model_get_n_items;
+  iface->get_item = glyph_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GlyphModel, glyph_model, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, glyph_model_list_model_init))
+
+static void
+glyph_model_init (GlyphModel *self)
+{
+}
+
+static void
+glyph_model_finalize (GObject *object)
+{
+  GlyphModel *self = GLYPH_MODEL (object);
+
+  g_object_unref (self->font);
+  for (int i = 0; i < self->num_glyphs; i++)
+    {
+      if (self->glyphs[i])
+        g_object_unref (self->glyphs[i]);
+    }
+  g_free (self->glyphs);
+
+  G_OBJECT_CLASS (glyph_model_parent_class)->finalize (object);
+}
+
+static void
+glyph_model_class_init (GlyphModelClass *class)
+{
+  G_OBJECT_CLASS (class)->finalize = glyph_model_finalize;
+}
+
+GlyphModel *
+glyph_model_new (Pango2FontFace *face)
+{
+  GlyphModel *self;
+  Pango2FontDescription *desc;
+  hb_face_t *hb_face;
+
+  self = g_object_new (GLYPH_MODEL_TYPE, NULL);
+
+  desc = pango2_font_description_new ();
+  pango2_font_description_set_size (desc, 60 * PANGO2_SCALE);
+  self->font = PANGO2_FONT (pango2_hb_font_new_for_description (PANGO2_HB_FACE (face), desc, 96., NULL));
+  hb_face = pango2_hb_face_get_hb_face (PANGO2_HB_FACE (face));
+  self->num_glyphs = hb_face_get_glyph_count (hb_face);
+  self->glyphs = g_new0 (GlyphItem *, self->num_glyphs);
+
+  return self;
+}
diff --git a/demos/font-explorer/glyphmodel.h b/demos/font-explorer/glyphmodel.h
new file mode 100644
index 0000000000..c6cd63b465
--- /dev/null
+++ b/demos/font-explorer/glyphmodel.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define GLYPH_MODEL_TYPE (glyph_model_get_type ())
+#define GLYPH_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLYPH_MODEL_TYPE, GlyphModel))
+
+
+typedef struct _GlyphModel         GlyphModel;
+typedef struct _GlyphModelClass    GlyphModelClass;
+
+
+GType        glyph_model_get_type (void);
+GlyphModel * glyph_model_new      (Pango2FontFace *face);
diff --git a/demos/font-explorer/glyphview.c b/demos/font-explorer/glyphview.c
new file mode 100644
index 0000000000..5462403bed
--- /dev/null
+++ b/demos/font-explorer/glyphview.c
@@ -0,0 +1,155 @@
+#include "glyphview.h"
+#include <gtk/gtk.h>
+#include <hb-ot.h>
+
+struct _GlyphView
+{
+  GtkWidget parent;
+
+  Pango2Font *font;
+  hb_codepoint_t glyph;
+};
+
+struct _GlyphViewClass
+{
+  GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE(GlyphView, glyph_view, GTK_TYPE_WIDGET);
+
+static void
+glyph_view_init (GlyphView *self)
+{
+}
+
+static void
+glyph_view_dispose (GObject *object)
+{
+  GlyphView *self = GLYPH_VIEW (object);
+
+  g_clear_object (&self->font);
+
+  G_OBJECT_CLASS (glyph_view_parent_class)->dispose (object);
+}
+
+static void
+glyph_view_snapshot (GtkWidget   *widget,
+                     GtkSnapshot *snapshot)
+{
+  GlyphView *self = GLYPH_VIEW (widget);
+  Pango2GlyphString *glyphs;
+  Pango2Rectangle ink, logical;
+  cairo_t *cr;
+  int width, height;
+  char name[80];
+
+  pango2_font_get_glyph_extents (self->font, self->glyph, &ink, &logical);
+
+  glyphs = pango2_glyph_string_new ();
+  pango2_glyph_string_set_size (glyphs, 1);
+  glyphs->glyphs[0].glyph = self->glyph;
+  glyphs->glyphs[0].geometry.width = ink.width;
+  glyphs->glyphs[0].geometry.x_offset = ink.x;
+  glyphs->glyphs[0].geometry.y_offset = -ink.y;
+
+  width = gtk_widget_get_width (widget);
+  height = gtk_widget_get_height (widget);
+
+  cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height));
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_move_to (cr, (width - logical.width/1024.)/2, (height - logical.height/1024.)/2);
+  pango2_cairo_show_glyph_string (cr, self->font, glyphs);
+
+  if (hb_font_get_glyph_name (pango2_font_get_hb_font (self->font), self->glyph, name, sizeof (name)))
+    {
+      Pango2Layout *layout;
+      Pango2FontDescription *desc;
+      int w, h;
+      char gid[20];
+
+      g_snprintf (gid, sizeof (gid), "%d", self->glyph);
+      layout = gtk_widget_create_pango_layout (widget, gid);
+      desc = pango2_font_description_from_string ("Cantarell 8");
+      pango2_layout_set_font_description (layout, desc);
+      pango2_font_description_free (desc);
+
+      gtk_snapshot_save (snapshot);
+      gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (5, 5));
+      gtk_snapshot_append_layout (snapshot, layout, &(GdkRGBA){ 0.,0.,0.,0.7});
+      gtk_snapshot_restore (snapshot);
+      g_object_unref (layout);
+
+      layout = gtk_widget_create_pango_layout (widget, name);
+      desc = pango2_font_description_from_string ("Cantarell 8");
+      pango2_layout_set_font_description (layout, desc);
+      pango2_font_description_free (desc);
+
+      pango2_lines_get_size (pango2_layout_get_lines (layout), &w, &h);
+
+      gtk_snapshot_save (snapshot);
+      gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (5, height - h/1024. - 5));
+      gtk_snapshot_append_layout (snapshot, layout, &(GdkRGBA){ 0.,0.,0.,1.});
+      gtk_snapshot_restore (snapshot);
+      g_object_unref (layout);
+    }
+}
+
+static void
+glyph_view_measure (GtkWidget      *widget,
+                    GtkOrientation  orientation,
+                    int             for_size,
+                    int            *minimum,
+                    int            *natural,
+                    int            *minimum_baseline,
+                    int            *natural_baseline)
+{
+  GlyphView *self = GLYPH_VIEW (widget);
+  Pango2Rectangle ink, logical;
+
+  pango2_font_get_glyph_extents (self->font, self->glyph, &ink, &logical);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    *minimum = *natural = 2 * logical.width / PANGO2_SCALE;
+  else
+    *minimum = *natural = 2 * logical.height / PANGO2_SCALE;
+}
+
+static void
+glyph_view_class_init (GlyphViewClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+  object_class->dispose = glyph_view_dispose;
+  widget_class->snapshot = glyph_view_snapshot;
+  widget_class->measure = glyph_view_measure;
+
+  gtk_widget_class_set_css_name (widget_class, "glyphview");
+}
+
+GlyphView *
+glyph_view_new (void)
+{
+  return g_object_new (GLYPH_VIEW_TYPE, NULL);
+}
+
+void
+glyph_view_set_font (GlyphView *self,
+                     Pango2Font *font)
+{
+  if (g_set_object (&self->font, font))
+    gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+void
+glyph_view_set_glyph (GlyphView      *self,
+                      hb_codepoint_t  glyph)
+{
+  if (self->glyph == glyph)
+    return;
+
+  self->glyph = glyph;
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+}
diff --git a/demos/font-explorer/glyphview.h b/demos/font-explorer/glyphview.h
new file mode 100644
index 0000000000..11c2fa1bd8
--- /dev/null
+++ b/demos/font-explorer/glyphview.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+
+#define GLYPH_VIEW_TYPE (glyph_view_get_type ())
+#define GLYPH_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLYPH_VIEW_TYPE, GlyphView))
+
+
+typedef struct _GlyphView         GlyphView;
+typedef struct _GlyphViewClass    GlyphViewClass;
+
+
+GType       glyph_view_get_type (void);
+GlyphView * glyph_view_new      (void);
+void        glyph_view_set_font (GlyphView *view,
+                                 Pango2Font *font);
+void        glyph_view_set_glyph (GlyphView *view,
+                                  hb_codepoint_t  glyph);
diff --git a/demos/font-explorer/meson.build b/demos/font-explorer/meson.build
index 9dd3bdfa5f..d552853990 100644
--- a/demos/font-explorer/meson.build
+++ b/demos/font-explorer/meson.build
@@ -10,6 +10,9 @@ fontexplorer_sources = [
   'fontview.c',
   'rangeedit.c',
   'language-names.c',
+  'glyphitem.c',
+  'glyphmodel.c',
+  'glyphview.c',
 ]
 
 fontexplorer_resources = gnome.compile_resources('fontexplorer_resources',


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