[gnome-flashback] input-sources: generate svg icon instead of pixbuf



commit cc1f38fe996fbe8b27a3049b7bea0b36b3d9645b
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Wed Nov 23 13:54:34 2016 +0200

    input-sources: generate svg icon instead of pixbuf

 ...gnome.gnome-flashback.input-sources.gschema.xml |   17 +-
 .../libinput-sources/gf-input-sources.c            |  420 +++++++++++++++-----
 2 files changed, 330 insertions(+), 107 deletions(-)
---
diff --git a/data/schemas/org.gnome.gnome-flashback.input-sources.gschema.xml 
b/data/schemas/org.gnome.gnome-flashback.input-sources.gschema.xml
index 37faaeb..48272ca 100644
--- a/data/schemas/org.gnome.gnome-flashback.input-sources.gschema.xml
+++ b/data/schemas/org.gnome.gnome-flashback.input-sources.gschema.xml
@@ -5,17 +5,28 @@
   <schema id="org.gnome.gnome-flashback.input-sources.status-icon" 
path="/org/gnome/gnome-flashback/input-sources/status-icon/">
     <key name="bg-color" type="s">
       <default>'#FFFFFF'</default>
-      <summary>The background color for the status icon.</summary>
+      <summary>The background color</summary>
     </key>
 
     <key name="fg-color" type="s">
       <default>'#000000'</default>
-      <summary>The foreground color for the status icon.</summary>
+      <summary>The foreground color</summary>
     </key>
 
     <key name="font-family" type="s">
       <default>'Cantarell'</default>
-      <summary>Font family</summary>
+      <summary>The font family</summary>
+    </key>
+
+    <key name="font-weight" type="i">
+      <range min="100" max="1000"/>
+      <default>500</default>
+      <summary>The font weight</summary>
+    </key>
+
+    <key name="symbolic" type="b">
+      <default>false</default>
+      <summary>Symbolic</summary>
     </key>
   </schema>
 </schemalist>
diff --git a/gnome-flashback/libinput-sources/gf-input-sources.c 
b/gnome-flashback/libinput-sources/gf-input-sources.c
index 0892274..15546c1 100644
--- a/gnome-flashback/libinput-sources/gf-input-sources.c
+++ b/gnome-flashback/libinput-sources/gf-input-sources.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Alberts Muktupāvels
+ * Copyright (C) 2015-2016 Alberts Muktupāvels
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,10 +20,8 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <libgnome-desktop/gnome-xkb-info.h>
-#include <pango/pangocairo.h>
-
-#define _XOPEN_SOURCE
-#include <math.h>
+#include <locale.h>
+#include <utime.h>
 
 #include "gf-ibus-manager.h"
 #include "gf-input-sources.h"
@@ -41,141 +39,329 @@ struct _GfInputSources
 
   GSettings            *status_icon_settings;
 
+  gchar                *icon_theme_path;
+
   GfInputSource        *current_source;
   GtkStatusIcon        *status_icon;
 };
 
 G_DEFINE_TYPE (GfInputSources, gf_input_sources, G_TYPE_OBJECT)
 
-static void
-draw_background (GfInputSources *sources,
-                 cairo_t        *cr,
-                 gint            size)
+static GString *
+cairo_path_to_string (cairo_path_t   *path,
+                      cairo_matrix_t *matrix)
 {
-  gdouble x;
-  gdouble y;
-  gdouble width;
-  gdouble height;
-  gdouble radius;
-  gdouble degrees;
-  gchar *color;
-  GdkRGBA rgba;
-
-  x = size * 0.04;
-  y = size * 0.04;
-  width = size - x * 2;
-  height = size - y * 2;
-
-  radius = height / 10;
-  degrees = M_PI / 180.0;
-
-  cairo_new_sub_path (cr);
-  cairo_arc (cr, x + width - radius, y + radius,
-             radius, -90 * degrees, 0 * degrees);
-  cairo_arc (cr, x + width - radius, y + height - radius,
-             radius, 0 * degrees, 90 * degrees);
-  cairo_arc (cr, x + radius, y + height - radius,
-             radius, 90 * degrees, 180 * degrees);
-  cairo_arc (cr, x + radius, y + radius,
-             radius, 180 * degrees, 270 * degrees);
-  cairo_close_path (cr);
-
-  color = g_settings_get_string (sources->status_icon_settings, "bg-color");
-
-  gdk_rgba_parse (&rgba, color);
-  g_free (color);
-
-  gdk_cairo_set_source_rgba (cr, &rgba);
-  cairo_fill_preserve (cr);
+  gchar *locale;
+  GString *string;
+  gint i;
+
+  locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+  setlocale (LC_NUMERIC, "C");
+
+  string = g_string_new (NULL);
+  for (i = 0; i < path->num_data; i += path->data[i].header.length)
+    {
+      cairo_path_data_t *data;
+      gdouble x1, y1;
+      gdouble x2, y2;
+      gdouble x3, y3;
+
+      data = &path->data[i];
+
+      switch (data->header.type)
+        {
+          case CAIRO_PATH_MOVE_TO:
+            x1 = data[1].point.x;
+            y1 = data[1].point.y;
+
+            cairo_matrix_transform_point (matrix, &x1, &y1);
+
+            g_string_append_printf (string, "M %f,%f ", x1, y1);
+            break;
+
+          case CAIRO_PATH_LINE_TO:
+            x1 = data[1].point.x;
+            y1 = data[1].point.y;
+
+            cairo_matrix_transform_point (matrix, &x1, &y1);
+
+            g_string_append_printf (string, "L %f,%f ", x1, y1);
+            break;
+
+          case CAIRO_PATH_CURVE_TO:
+            x1 = data[1].point.x;
+            y1 = data[1].point.y;
+            x2 = data[2].point.x;
+            y2 = data[2].point.y;
+            x3 = data[3].point.x;
+            y3 = data[3].point.y;
+
+            cairo_matrix_transform_point (matrix, &x1, &y1);
+            cairo_matrix_transform_point (matrix, &x2, &y2);
+            cairo_matrix_transform_point (matrix, &x3, &y3);
+
+            g_string_append_printf (string, "C %f,%f %f,%f %f,%f ",
+                                    x1, y1, x2, y2, x3, y3);
+            break;
+
+          case CAIRO_PATH_CLOSE_PATH:
+            g_string_append (string, "Z ");
+            break;
+
+          default:
+            break;
+        }
+    }
+
+  setlocale (LC_NUMERIC, locale);
+  g_free (locale);
+
+  return string;
 }
 
-static void
-draw_text (GfInputSources *sources,
-           cairo_t        *cr,
-           gint            size)
+static PangoLayout *
+get_pango_layout (const gchar *text,
+                  const gchar *font_family,
+                  gint         font_weight,
+                  gint         font_size)
 {
-  gchar *font_name;
+  GdkScreen *screen;
+  PangoContext *context;
   PangoFontDescription *font_desc;
-  gdouble font_size;
   PangoLayout *layout;
-  const gchar *short_name;
-  gint text_width;
-  gint text_height;
-  gdouble factor;
-  gdouble center;
-  gdouble x;
-  gdouble y;
-  gchar *color;
-  GdkRGBA rgba;
-
-  font_name = g_settings_get_string (sources->status_icon_settings, "font-family");
-  font_desc = pango_font_description_from_string (font_name);
-  g_free (font_name);
 
-  pango_font_description_set_weight (font_desc, PANGO_WEIGHT_MEDIUM);
+  screen = gdk_screen_get_default ();
+  context = gdk_pango_context_get_for_screen (screen);
+  font_desc = pango_font_description_new ();
 
-  font_size = PANGO_SCALE * size * 0.5;
-  pango_font_description_set_absolute_size (font_desc, font_size);
+  pango_font_description_set_family (font_desc, font_family);
+  pango_font_description_set_absolute_size (font_desc, font_size * PANGO_SCALE);
+  pango_font_description_set_weight (font_desc, font_weight);
+  pango_font_description_set_stretch (font_desc, PANGO_STRETCH_NORMAL);
+  pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL);
+  pango_font_description_set_variant (font_desc, PANGO_VARIANT_NORMAL);
 
-  layout = pango_cairo_create_layout (cr);
+  layout = pango_layout_new (context);
+  g_object_unref (context);
 
+  pango_layout_set_text (layout, text, -1);
   pango_layout_set_font_description (layout, font_desc);
   pango_font_description_free (font_desc);
 
-  short_name = gf_input_source_get_short_name (sources->current_source);
-  pango_layout_set_text (layout, short_name, -1);
+  return layout;
+}
+
+static cairo_path_t *
+get_cairo_path (const gchar    *text,
+                const gchar    *font_family,
+                gint            font_weight,
+                gint            font_size,
+                cairo_matrix_t *matrix)
+{
+  PangoLayout *layout;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  gint width;
+  gint height;
+  gdouble scale;
+  cairo_path_t *path;
 
-  pango_layout_get_pixel_size (layout, &text_width, &text_height);
+  layout = get_pango_layout (text, font_family, font_weight, font_size);
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 16, 16);
+  cr = cairo_create (surface);
 
-  factor = MIN ((size - (size * 0.1) * 2) / text_width, 1.0);
-  cairo_scale (cr, factor, factor);
+  pango_layout_get_pixel_size (layout, &width, &height);
 
-  center = size / 2.0;
-  x = center - text_width * factor / 2.0;
-  y = center - text_height * factor / 2.0;
-  cairo_move_to (cr, x, y);
+  scale = MIN (1.0, MIN (14.0 / width, 14.0 / height));
+  cairo_scale (cr, scale, scale);
 
-  color = g_settings_get_string (sources->status_icon_settings, "fg-color");
-  gdk_rgba_parse (&rgba, color);
-  g_free (color);
+  cairo_move_to (cr, (16 - width * scale) / 2.0, (16 - height * scale) / 2.0);
 
-  gdk_cairo_set_source_rgba (cr, &rgba);
+  pango_cairo_layout_path (cr, layout);
+  path = cairo_copy_path (cr);
+  cairo_get_matrix (cr, matrix);
 
-  pango_cairo_show_layout (cr, layout);
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
   g_object_unref (layout);
+
+  return path;
+}
+
+static gchar *
+generate_path_description (const gchar *text,
+                           const gchar *font_family,
+                           gint         font_weight,
+                           gint         font_size)
+{
+  cairo_path_t *path;
+  cairo_matrix_t matrix;
+  GString *string;
+
+  path = get_cairo_path (text, font_family, font_weight, font_size, &matrix);
+  string = cairo_path_to_string (path, &matrix);
+  cairo_path_destroy (path);
+
+  return g_string_free (string, FALSE);
+}
+
+static GString *
+generate_svg (const gchar *text,
+              const gchar *font_family,
+              gint         font_weight,
+              gint         font_size,
+              const gchar *bg_color,
+              const gchar *fg_color,
+              gboolean     symbolic)
+{
+  gchar *path_d;
+  GString *svg;
+
+  path_d = generate_path_description (text, font_family, font_weight, font_size);
+  svg = g_string_new ("<?xml version='1.0' encoding='utf-8' standalone='no'?>");
+
+  g_string_append (svg,
+                   "<svg xmlns='http://www.w3.org/2000/svg' "
+                   "width='16' height='16' viewBox='0 0 16 16'>");
+
+  if (symbolic)
+    {
+      g_string_append (svg, "<defs><mask id='m'>");
+      g_string_append (svg,
+                       "<rect width='16' height='16' "
+                       "style='fill:#ffffff!important'/>");
+
+      g_string_append_printf (svg,
+                              "<path d='%s' style='fill:#000000!important'/>",
+                              path_d);
+
+      g_string_append (svg, "</mask></defs>");
+    }
+
+  g_string_append_printf (svg,
+                          "<rect x='0' y='0' width='16' height='16' "
+                          "rx='2.0' ry='2.0' mask='%s' style='fill:%s;'/>",
+                          symbolic ? "url(#m)" : "none",
+                          symbolic ? "#bebebe" : bg_color);
+
+  if (!symbolic)
+    {
+      g_string_append_printf (svg, "<path d='%s' style='fill:%s'/>",
+                              path_d, fg_color);
+    }
+
+  g_free (path_d);
+
+  return g_string_append (svg, "</svg>");
 }
 
 static void
-update_status_icon_pixbuf (GfInputSources *sources)
+ensure_file_exists (const gchar *icon_theme_path,
+                    const gchar *icon_name,
+                    const gchar *text,
+                    const gchar *font_family,
+                    gint         font_weight,
+                    gint         font_size,
+                    const gchar *bg_color,
+                    const gchar *fg_color,
+                    gboolean     symbolic)
 {
-  gint size;
-  cairo_surface_t *surface;
-  cairo_t *cr;
-  GdkPixbuf *pixbuf;
+  gchar *filename;
+  gchar *path;
 
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-  size = gtk_status_icon_get_size (sources->status_icon);
-  G_GNUC_END_IGNORE_DEPRECATIONS
+  filename = g_strdup_printf ("%s.svg", icon_name);
+  path = g_build_filename (icon_theme_path, "hicolor", "scalable",
+                           "status", filename, NULL);
 
-  if (size <= 0)
-    return;
+  if (!g_file_test (path, G_FILE_TEST_EXISTS))
+    {
+      GFile *file;
+      GFile *parent;
+      GString *svg;
 
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
-  cr = cairo_create (surface);
+      file = g_file_new_for_path (path);
+      parent = g_file_get_parent (file);
+      svg = generate_svg (text, font_family, font_weight, font_size,
+                          bg_color, fg_color, symbolic);
 
-  draw_background (sources, cr, size);
-  draw_text (sources, cr, size);
+      g_file_make_directory_with_parents (parent, NULL, NULL);
+      g_file_replace_contents (file, svg->str, svg->len, NULL, FALSE,
+                               G_FILE_CREATE_NONE, NULL, NULL, NULL);
 
-  cairo_destroy (cr);
+      utime (icon_theme_path, NULL);
+      gtk_icon_theme_rescan_if_needed (gtk_icon_theme_get_default ());
 
-  pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, size, size);
-  cairo_surface_destroy (surface);
+      g_string_free (svg, TRUE);
+      g_object_unref (parent);
+      g_object_unref (file);
+    }
 
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-  gtk_status_icon_set_from_pixbuf (sources->status_icon, pixbuf);
-  G_GNUC_END_IGNORE_DEPRECATIONS
+  g_free (filename);
+  g_free (path);
+}
 
-  g_object_unref (pixbuf);
+static gchar *
+generate_icon_name (const gchar *text,
+                    const gchar *font_family,
+                    gint         font_weight,
+                    gint         font_size,
+                    const gchar *bg_color,
+                    const gchar *fg_color,
+                    gboolean     symbolic)
+{
+  gchar *str;
+  gchar *hash;
+  GString *icon_name;
+
+  str = g_strdup_printf ("%s-%s-%d-%d-%s-%s", text,
+                         font_family, font_weight, font_size,
+                         bg_color, fg_color);
+
+  hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, str, -1);
+  g_free (str);
+
+  icon_name = g_string_new (hash);
+  g_free (hash);
+
+  if (symbolic)
+    g_string_append (icon_name, "-symbolic");
+
+  return g_string_free (icon_name, FALSE);
+}
+
+static gchar *
+get_icon_name (GfInputSources *sources)
+{
+  const gchar *text;
+  gint font_size;
+  gchar *font_family;
+  gint font_weight;
+  gchar *bg_color;
+  gchar *fg_color;
+  gboolean symbolic;
+  gchar *icon_name;
+
+  text = gf_input_source_get_short_name (sources->current_source);
+  font_size = 8;
+
+  font_family = g_settings_get_string (sources->status_icon_settings, "font-family");
+  font_weight = g_settings_get_int (sources->status_icon_settings, "font-weight");
+  bg_color = g_settings_get_string (sources->status_icon_settings, "bg-color");
+  fg_color = g_settings_get_string (sources->status_icon_settings, "fg-color");
+  symbolic = g_settings_get_boolean (sources->status_icon_settings, "symbolic");
+
+  icon_name = generate_icon_name (text, font_family, font_weight, font_size,
+                                  bg_color, fg_color, symbolic);
+
+  ensure_file_exists (sources->icon_theme_path, icon_name, text,
+                      font_family, font_weight, font_size,
+                      bg_color, fg_color, symbolic);
+
+  g_free (font_family);
+  g_free (bg_color);
+  g_free (fg_color);
+
+  return icon_name;
 }
 
 static void
@@ -374,6 +560,7 @@ update_status_icon (GfInputSources *sources)
   GList *input_sources;
   IBusPropList *prop_list;
   const gchar *display_name;
+  gchar *icon_name;
 
   manager = sources->input_source_manager;
 
@@ -414,12 +601,15 @@ update_status_icon (GfInputSources *sources)
     }
 
   display_name = gf_input_source_get_display_name (source);
+  icon_name = get_icon_name (sources);
+
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
   gtk_status_icon_set_title (sources->status_icon, _("Keyboard"));
   gtk_status_icon_set_tooltip_text (sources->status_icon, display_name);
+  gtk_status_icon_set_from_icon_name (sources->status_icon, icon_name);
   G_GNUC_END_IGNORE_DEPRECATIONS
 
-  update_status_icon_pixbuf (sources);
+  g_free (icon_name);
 }
 
 static void
@@ -464,6 +654,18 @@ gf_input_sources_dispose (GObject *object)
 }
 
 static void
+gf_input_sources_finalize (GObject *object)
+{
+  GfInputSources *sources;
+
+  sources = GF_INPUT_SOURCES (object);
+
+  g_clear_pointer (&sources->icon_theme_path, g_free);
+
+  G_OBJECT_CLASS (gf_input_sources_parent_class)->finalize (object);
+}
+
+static void
 gf_input_sources_class_init (GfInputSourcesClass *sources_class)
 {
   GObjectClass *object_class;
@@ -471,16 +673,27 @@ gf_input_sources_class_init (GfInputSourcesClass *sources_class)
   object_class = G_OBJECT_CLASS (sources_class);
 
   object_class->dispose = gf_input_sources_dispose;
+  object_class->finalize = gf_input_sources_finalize;
 }
 
 static void
 gf_input_sources_init (GfInputSources *sources)
 {
+  const gchar *cache_dir;
+
   sources->ibus_manager = gf_ibus_manager_new ();
   sources->input_source_manager = gf_input_source_manager_new (sources->ibus_manager);
 
   sources->status_icon_settings = g_settings_new (STATUS_ICON_SCHEMA);
 
+  cache_dir = g_get_user_cache_dir ();
+  sources->icon_theme_path = g_build_filename (cache_dir, "gnome-flashback",
+                                               "input-sources", "icons",
+                                               NULL);
+
+  gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
+                                     sources->icon_theme_path);
+
   g_signal_connect (sources->input_source_manager, "sources-changed",
                     G_CALLBACK (sources_changed_cb), sources);
 
@@ -491,7 +704,6 @@ gf_input_sources_init (GfInputSources *sources)
                     G_CALLBACK (status_icon_settings_changed_cb), sources);
 
   gf_input_source_manager_reload (sources->input_source_manager);
-  update_status_icon (sources);
 }
 
 GfInputSources *


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