[libdazzle] util: add various gtk utility API



commit 3ba5225da382bc5c602f271b6fb8296d3c1ef86b
Author: Christian Hergert <chergert redhat com>
Date:   Tue Jun 6 13:56:25 2017 -0700

    util: add various gtk utility API
    
    These are a few helpers we use a bunch in Builder so it would be nice to
    break them out into their own utility API in libdazzle.

 src/dazzle.h       |    1 +
 src/meson.build    |    2 +
 src/util/dzl-gtk.c |  330 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/dzl-gtk.h |   48 ++++++++
 4 files changed, 381 insertions(+), 0 deletions(-)
---
diff --git a/src/dazzle.h b/src/dazzle.h
index 5117fb2..609b75a 100644
--- a/src/dazzle.h
+++ b/src/dazzle.h
@@ -120,6 +120,7 @@ G_BEGIN_DECLS
 #include "util/dzl-dnd.h"
 #include "util/dzl-file-manager.h"
 #include "util/dzl-gdk.h"
+#include "util/dzl-gtk.h"
 #include "util/dzl-heap.h"
 #include "util/dzl-pango.h"
 #include "util/dzl-rgba.h"
diff --git a/src/meson.build b/src/meson.build
index f0264d2..44df2af 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -133,6 +133,7 @@ libdazzle_public_headers = [
   'util/dzl-dnd.h',
   'util/dzl-file-manager.h',
   'util/dzl-gdk.h',
+  'util/dzl-gtk.h',
   'util/dzl-heap.h',
   'util/dzl-pango.h',
   'util/dzl-rgba.h',
@@ -267,6 +268,7 @@ libdazzle_public_sources = [
   'util/dzl-dnd.c',
   'util/dzl-file-manager.c',
   'util/dzl-gdk.c',
+  'util/dzl-gtk.c',
   'util/dzl-heap.c',
   'util/dzl-pango.c',
   'util/dzl-rgba.c',
diff --git a/src/util/dzl-gtk.c b/src/util/dzl-gtk.c
new file mode 100644
index 0000000..e737668
--- /dev/null
+++ b/src/util/dzl-gtk.c
@@ -0,0 +1,330 @@
+/* dzl-gtk.c
+ *
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "dzl-gtk"
+
+#include "animation/dzl-animation.h"
+#include "util/dzl-gtk.h"
+
+gboolean
+dzl_gtk_widget_action (GtkWidget   *widget,
+                       const gchar *prefix,
+                       const gchar *action_name,
+                       GVariant    *parameter)
+{
+  GtkWidget *toplevel;
+  GApplication *app;
+  GActionGroup *group = NULL;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (prefix, FALSE);
+  g_return_val_if_fail (action_name, FALSE);
+
+  app = g_application_get_default ();
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  while ((group == NULL) && (widget != NULL))
+    {
+      group = gtk_widget_get_action_group (widget, prefix);
+
+      if G_UNLIKELY (GTK_IS_POPOVER (widget))
+        {
+          GtkWidget *relative_to;
+
+          relative_to = gtk_popover_get_relative_to (GTK_POPOVER (widget));
+
+          if (relative_to != NULL)
+            widget = relative_to;
+          else
+            widget = gtk_widget_get_parent (widget);
+        }
+      else
+        {
+          widget = gtk_widget_get_parent (widget);
+        }
+    }
+
+  if (!group && g_str_equal (prefix, "win") && G_IS_ACTION_GROUP (toplevel))
+    group = G_ACTION_GROUP (toplevel);
+
+  if (!group && g_str_equal (prefix, "app") && G_IS_ACTION_GROUP (app))
+    group = G_ACTION_GROUP (app);
+
+  if (group && g_action_group_has_action (group, action_name))
+    {
+      g_action_group_activate_action (group, action_name, parameter);
+      return TRUE;
+    }
+
+  if (parameter && g_variant_is_floating (parameter))
+    {
+      parameter = g_variant_ref_sink (parameter);
+      g_variant_unref (parameter);
+    }
+
+  g_warning ("Failed to locate action %s.%s", prefix, action_name);
+
+  return FALSE;
+}
+
+gboolean
+dzl_gtk_widget_action_with_string (GtkWidget   *widget,
+                                   const gchar *group,
+                                   const gchar *name,
+                                   const gchar *param)
+{
+  GVariant *variant = NULL;
+  gboolean ret;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (group != NULL, FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  if (param == NULL)
+    param = "";
+
+  if (*param != 0)
+    {
+      g_autoptr(GError) error = NULL;
+
+      variant = g_variant_parse (NULL, param, NULL, NULL, &error);
+
+      if (variant == NULL)
+        {
+          g_warning ("can't parse keybinding parameters \"%s\": %s",
+                     param, error->message);
+          return FALSE;
+        }
+    }
+
+  ret = dzl_gtk_widget_action (widget, group, name, variant);
+
+  return ret;
+}
+
+static void
+show_callback (gpointer data)
+{
+  g_object_set_data (data, "DZL_FADE_ANIMATION", NULL);
+  g_object_unref (data);
+}
+
+static void
+hide_callback (gpointer data)
+{
+  GtkWidget *widget = data;
+
+  g_object_set_data (data, "DZL_FADE_ANIMATION", NULL);
+  gtk_widget_hide (widget);
+  gtk_widget_set_opacity (widget, 1.0);
+  g_object_unref (widget);
+}
+
+void
+dzl_gtk_widget_hide_with_fade (GtkWidget *widget)
+{
+  GdkFrameClock *frame_clock;
+  DzlAnimation *anim;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  if (gtk_widget_get_visible (widget))
+    {
+      anim = g_object_get_data (G_OBJECT (widget), "DZL_FADE_ANIMATION");
+      if (anim != NULL)
+        dzl_animation_stop (anim);
+
+      frame_clock = gtk_widget_get_frame_clock (widget);
+      anim = dzl_object_animate_full (widget,
+                                      DZL_ANIMATION_LINEAR,
+                                      1000,
+                                      frame_clock,
+                                      hide_callback,
+                                      g_object_ref (widget),
+                                      "opacity", 0.0,
+                                      NULL);
+      g_object_set_data_full (G_OBJECT (widget), "DZL_FADE_ANIMATION",
+                              g_object_ref (anim), g_object_unref);
+    }
+}
+
+void
+dzl_gtk_widget_show_with_fade (GtkWidget *widget)
+{
+  GdkFrameClock *frame_clock;
+  DzlAnimation *anim;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  if (!gtk_widget_get_visible (widget))
+    {
+      anim = g_object_get_data (G_OBJECT (widget), "DZL_FADE_ANIMATION");
+      if (anim != NULL)
+        dzl_animation_stop (anim);
+
+      frame_clock = gtk_widget_get_frame_clock (widget);
+      gtk_widget_set_opacity (widget, 0.0);
+      gtk_widget_show (widget);
+      anim = dzl_object_animate_full (widget,
+                                      DZL_ANIMATION_LINEAR,
+                                      500,
+                                      frame_clock,
+                                      show_callback,
+                                      g_object_ref (widget),
+                                      "opacity", 1.0,
+                                      NULL);
+      g_object_set_data_full (G_OBJECT (widget), "DZL_FADE_ANIMATION",
+                              g_object_ref (anim), g_object_unref);
+    }
+}
+
+static void
+dzl_gtk_widget_find_child_typed_cb (GtkWidget *widget,
+                                    gpointer   user_data)
+{
+  struct {
+    gpointer ret;
+    GType type;
+  } *state = user_data;
+
+  if (state->ret != NULL)
+    return;
+
+  if (g_type_is_a (G_OBJECT_TYPE (widget), state->type))
+    {
+      state->ret = widget;
+    }
+  else if (GTK_IS_CONTAINER (widget))
+    {
+      gtk_container_foreach (GTK_CONTAINER (widget),
+                             dzl_gtk_widget_find_child_typed_cb,
+                             state);
+    }
+}
+
+gpointer
+dzl_gtk_widget_find_child_typed (GtkWidget *widget,
+                                 GType      child_type)
+{
+  struct {
+    gpointer ret;
+    GType type;
+  } state;
+
+  g_return_val_if_fail (GTK_IS_CONTAINER (widget), NULL);
+  g_return_val_if_fail (g_type_is_a (child_type, GTK_TYPE_WIDGET), NULL);
+
+  state.ret = NULL;
+  state.type = child_type;
+
+  gtk_container_foreach (GTK_CONTAINER (widget),
+                         dzl_gtk_widget_find_child_typed_cb,
+                         &state);
+
+  return state.ret;
+}
+
+/**
+ * dzl_gtk_text_buffer_remove_tag:
+ *
+ * Like gtk_text_buffer_remove_tag() but allows specifying that the tags
+ * should be removed one at a time to avoid over-damaging the views
+ * displaying @buffer.
+ */
+void
+dzl_gtk_text_buffer_remove_tag (GtkTextBuffer     *buffer,
+                                GtkTextTag        *tag,
+                                const GtkTextIter *start,
+                                const GtkTextIter *end,
+                                gboolean           minimal_damage)
+{
+  GtkTextIter tag_begin;
+  GtkTextIter tag_end;
+
+  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (GTK_IS_TEXT_TAG (tag));
+  g_return_if_fail (start != NULL);
+  g_return_if_fail (end != NULL);
+
+  if (!minimal_damage)
+    {
+      gtk_text_buffer_remove_tag (buffer, tag, start, end);
+      return;
+    }
+
+  tag_begin = *start;
+
+  if (!gtk_text_iter_starts_tag (&tag_begin, tag))
+    {
+      if (!gtk_text_iter_forward_to_tag_toggle (&tag_begin, tag))
+        return;
+    }
+
+  while (gtk_text_iter_starts_tag (&tag_begin, tag) &&
+         gtk_text_iter_compare (&tag_begin, end) < 0)
+    {
+      gint count = 1;
+
+      tag_end = tag_begin;
+
+      /*
+       * We might have found the start of another tag embedded
+       * inside this tag. So keep scanning forward until we have
+       * reached the right number of end tags.
+       */
+
+      while (gtk_text_iter_forward_to_tag_toggle (&tag_end, tag))
+        {
+          if (gtk_text_iter_starts_tag (&tag_end, tag))
+            count++;
+          else if (gtk_text_iter_ends_tag (&tag_end, tag))
+            count--;
+
+          if (count == 0)
+            break;
+        }
+
+      if (gtk_text_iter_ends_tag (&tag_end, tag))
+        gtk_text_buffer_remove_tag (buffer, tag, &tag_begin, &tag_end);
+
+      tag_begin = tag_end;
+
+      /*
+       * Move to the next start tag. It's possible to have an overlapped
+       * end tag, which would be non-ideal, but possible.
+       */
+      if (!gtk_text_iter_starts_tag (&tag_begin, tag))
+        {
+          while (gtk_text_iter_forward_to_tag_toggle (&tag_begin, tag))
+            {
+              if (gtk_text_iter_starts_tag (&tag_begin, tag))
+                break;
+            }
+        }
+    }
+}
+
+void
+dzl_gtk_widget_add_style_class (GtkWidget   *widget,
+                                const gchar *class_name)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (class_name != NULL);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (widget), class_name);
+}
diff --git a/src/util/dzl-gtk.h b/src/util/dzl-gtk.h
new file mode 100644
index 0000000..697b8f8
--- /dev/null
+++ b/src/util/dzl-gtk.h
@@ -0,0 +1,48 @@
+/* dzl-gtk.h
+ *
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DZL_GTK_H
+#define DZL_GTK_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean      dzl_gtk_widget_action              (GtkWidget               *widget,
+                                                  const gchar             *group,
+                                                  const gchar             *name,
+                                                  GVariant                *param);
+gboolean      dzl_gtk_widget_action_with_string  (GtkWidget               *widget,
+                                                  const gchar             *group,
+                                                  const gchar             *name,
+                                                  const gchar             *param);
+void          dzl_gtk_widget_hide_with_fade      (GtkWidget               *widget);
+void          dzl_gtk_widget_show_with_fade      (GtkWidget               *widget);
+void          dzl_gtk_widget_add_style_class     (GtkWidget               *widget,
+                                                  const gchar             *class_name);
+gpointer      dzl_gtk_widget_find_child_typed    (GtkWidget               *widget,
+                                                  GType                    type);
+void          dzl_gtk_text_buffer_remove_tag     (GtkTextBuffer           *buffer,
+                                                  GtkTextTag              *tag,
+                                                  const GtkTextIter       *start,
+                                                  const GtkTextIter       *end,
+                                                  gboolean                 minimal_damage);
+
+G_END_DECLS
+
+#endif /* DZL_GTK_H */


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