[gimp] app: add ui_meta keys for controlling some UI attributes dynamically



commit bb2417c29943e68baecd87f6c8c7808ebc3d091e
Author: Ell <ell_se yahoo com>
Date:   Sun Apr 30 16:07:38 2017 -0400

    app: add ui_meta keys for controlling some UI attributes dynamically
    
    Add support for The following GEGL op property keys, which shall
    contain GUM expressions of the specified type, controlling the
    corresponding UI attributes of the property's widget:
    
      - sensitive [boolean]: controls widget sensitivity.
    
      - visible [boolean]: controls widget visibility.
    
      - label [string]: controls widget label (or the label of the
        associated label widget).
    
      - description [string]: controls widget tooltip text.
    
    When any of above keys are present, the values they evaluate to take
    precedence over the static values the corresponding attributes would
    otherwise have.

 app/widgets/gimppropgui-constructors.c |    8 +
 app/widgets/gimppropgui.c              |  237 ++++++++++++++++++++++++++++----
 app/widgets/gimppropgui.h              |    6 +
 3 files changed, 224 insertions(+), 27 deletions(-)
---
diff --git a/app/widgets/gimppropgui-constructors.c b/app/widgets/gimppropgui-constructors.c
index d4e8a7a..10f4fa3 100644
--- a/app/widgets/gimppropgui-constructors.c
+++ b/app/widgets/gimppropgui-constructors.c
@@ -119,6 +119,8 @@ _gimp_prop_gui_new_generic (GObject              *config,
           gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
           gtk_widget_show (hbox);
 
+          gimp_prop_gui_bind_container (widget_x, hbox);
+
           vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
           gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
           gtk_widget_show (vbox);
@@ -200,6 +202,8 @@ _gimp_prop_gui_new_generic (GObject              *config,
               gtk_label_set_xalign (GTK_LABEL (l), 0.0);
               gtk_widget_show (l);
 
+              gimp_prop_gui_bind_label (widget, l);
+
               if (GTK_IS_SCROLLED_WINDOW (widget))
                 {
                   GtkWidget *frame;
@@ -213,6 +217,8 @@ _gimp_prop_gui_new_generic (GObject              *config,
 
                   gtk_container_add (GTK_CONTAINER (frame), widget);
                   gtk_widget_show (widget);
+
+                  gimp_prop_gui_bind_container (widget, frame);
                 }
               else
                 {
@@ -228,6 +234,8 @@ _gimp_prop_gui_new_generic (GObject              *config,
 
                   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
                   gtk_widget_show (widget);
+
+                  gimp_prop_gui_bind_container (widget, hbox);
                 }
             }
           else if (widget)
diff --git a/app/widgets/gimppropgui.c b/app/widgets/gimppropgui.c
index 4ce8168..514b54a 100644
--- a/app/widgets/gimppropgui.c
+++ b/app/widgets/gimppropgui.c
@@ -44,6 +44,7 @@
 #include "gimpspinscale.h"
 #include "gimppropgui.h"
 #include "gimppropgui-constructors.h"
+#include "gimppropgui-eval.h"
 #include "gimppropwidgets.h"
 #include "gimpwidgets-utils.h"
 
@@ -53,14 +54,20 @@
 #define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v)
 
 
-static GtkWidget * gimp_prop_kelvin_presets_new      (GObject       *config,
-                                                      const gchar   *property_name);
-static void        gimp_prop_widget_new_seed_clicked (GtkButton     *button,
-                                                      GtkAdjustment *adj);
-static gboolean    gimp_prop_string_to_boolean       (GBinding      *binding,
-                                                      const GValue  *from_value,
-                                                      GValue        *to_value,
-                                                      gpointer       user_data);
+static GtkWidget   * gimp_prop_kelvin_presets_new      (GObject        *config,
+                                                        const gchar    *property_name);
+static void          gimp_prop_widget_new_seed_clicked (GtkButton      *button,
+                                                        GtkAdjustment  *adj);
+static gboolean      gimp_prop_string_to_boolean       (GBinding       *binding,
+                                                        const GValue   *from_value,
+                                                        GValue         *to_value,
+                                                        gpointer        user_data);
+static void          gimp_prop_config_notify           (GObject        *config,
+                                                        GParamSpec     *pspec,
+                                                        GtkWidget      *widget);
+static void          gimp_prop_widget_show             (GtkWidget      *widget,
+                                                        GObject        *config);
+static void          gimp_prop_free_label_ref          (GWeakRef       *label_ref);
 
 /*  public functions  */
 
@@ -213,6 +220,9 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
           gtk_box_pack_start (GTK_BOX (hbox), dial, FALSE, FALSE, 0);
           gtk_widget_show (dial);
 
+          gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+          gimp_prop_gui_bind_label (hbox, widget);
+
           widget = hbox;
         }
       else if (HAS_KEY (pspec, "unit", "kelvin"))
@@ -229,30 +239,38 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
           gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
           gtk_widget_show (button);
 
+          gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+          gimp_prop_gui_bind_label (hbox, widget);
+
           widget = hbox;
         }
-      else if (area)
+      else
         {
-          if (HAS_KEY (pspec, "unit", "pixel-coordinate") ||
-              HAS_KEY (pspec, "unit", "pixel-distance"))
-            {
-              if (HAS_KEY (pspec, "axis", "x"))
-                {
-                  g_printerr ("XXX setting width %d on %s\n",
-                              area->width, pspec->name);
+          gimp_prop_gui_bind_label (widget, widget);
 
-                  gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
-                                                    area->x,
-                                                    area->x + area->width);
-                }
-              else if (HAS_KEY (pspec, "axis","y"))
+          if (area)
+            {
+              if (HAS_KEY (pspec, "unit", "pixel-coordinate") ||
+                  HAS_KEY (pspec, "unit", "pixel-distance"))
                 {
-                  g_printerr ("XXX setting height %d on %s\n",
-                              area->height, pspec->name);
-
-                  gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
-                                                    area->y,
-                                                    area->y + area->height);
+                  if (HAS_KEY (pspec, "axis", "x"))
+                    {
+                      g_printerr ("XXX setting width %d on %s\n",
+                                  area->width, pspec->name);
+
+                      gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
+                                                        area->x,
+                                                        area->x + area->width);
+                    }
+                  else if (HAS_KEY (pspec, "axis","y"))
+                    {
+                      g_printerr ("XXX setting height %d on %s\n",
+                                  area->height, pspec->name);
+
+                      gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
+                                                        area->y,
+                                                        area->y + area->height);
+                    }
                 }
             }
         }
@@ -316,12 +334,16 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
     {
       widget = gimp_prop_check_button_new (config, pspec->name,
                                            g_param_spec_get_nick (pspec));
+
+      gimp_prop_gui_bind_label (widget, widget);
     }
   else if (G_IS_PARAM_SPEC_ENUM (pspec))
     {
       widget = gimp_prop_enum_combo_box_new (config, pspec->name, 0, 0);
       gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (widget),
                                     g_param_spec_get_nick (pspec));
+
+      gimp_prop_gui_bind_label (widget, widget);
     }
   else if (GIMP_IS_PARAM_SPEC_RGB (pspec))
     {
@@ -338,6 +360,8 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
       gtk_box_pack_start (GTK_BOX (widget), button, TRUE, TRUE, 0);
       gtk_widget_show (button);
 
+      gimp_prop_gui_bind_tooltip (widget, button);
+
       if (create_picker_func)
         {
           button = create_picker_func (picker_creator,
@@ -357,6 +381,36 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
                  g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name);
     }
 
+  /* if we have any keys for dynamic properties, listen to config's notify
+   * signal, and update the properties accordingly.
+   */
+  if (gegl_param_spec_get_property_key (pspec, "sensitive") ||
+      gegl_param_spec_get_property_key (pspec, "visible")   ||
+      gegl_param_spec_get_property_key (pspec, "label")     ||
+      gegl_param_spec_get_property_key (pspec, "description"))
+    {
+      g_object_set_data (G_OBJECT (widget), "gimp-prop-pspec", pspec);
+
+      g_signal_connect_object (config, "notify",
+                               G_CALLBACK (gimp_prop_config_notify),
+                               widget, 0);
+
+      if (gegl_param_spec_get_property_key (pspec, "visible"))
+        {
+          /* a bit of a hack: if we have a dynamic "visible" property key,
+           * connect to the widget's "show" signal, so that we can intercept
+           * our caller's gtk_widget_show() call, and keep the widget hidden if
+           * necessary.
+           */
+          g_signal_connect (widget, "show",
+                            G_CALLBACK (gimp_prop_widget_show),
+                            config);
+        }
+
+      /* update all the properties now */
+      gimp_prop_config_notify (config, NULL, widget);
+    }
+
   return widget;
 }
 
@@ -467,6 +521,62 @@ gimp_prop_gui_new (GObject              *config,
   return gui;
 }
 
+void
+gimp_prop_gui_bind_container (GtkWidget *source,
+                              GtkWidget *target)
+{
+  g_object_bind_property (source, "sensitive",
+                          target, "sensitive",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (source, "visible",
+                          target, "visible",
+                          G_BINDING_SYNC_CREATE);
+}
+
+void
+gimp_prop_gui_bind_label (GtkWidget *source,
+                          GtkWidget *target)
+{
+  GWeakRef    *label_ref;
+  const gchar *label;
+
+  /* we want to update "target"'s "label" property whenever the label
+   * expression associated with "source" is reevaluated, however, "source"
+   * might not itself have a "label" property we can bind to.  just keep around
+   * a reference to "target", and update its label manually.
+   */
+  g_return_if_fail (g_object_get_data (G_OBJECT (source),
+                                       "gimp-prop-label-ref") == NULL);
+
+  label_ref = g_slice_new (GWeakRef);
+
+  g_weak_ref_init (label_ref, target);
+
+  g_object_set_data_full (G_OBJECT (source),
+                          "gimp-prop-label-ref", label_ref,
+                          (GDestroyNotify) gimp_prop_free_label_ref);
+
+  label = g_object_get_data (G_OBJECT (source), "gimp-prop-label");
+
+  if (label)
+    g_object_set (target, "label", label, NULL);
+
+  /* note that "source" might be its own label widget, in which case there's no
+   * need to bind the rest of the properties.
+   */
+  if (source != target)
+    gimp_prop_gui_bind_tooltip (source, target);
+}
+
+void
+gimp_prop_gui_bind_tooltip (GtkWidget *source,
+                            GtkWidget *target)
+{
+  g_object_bind_property (source, "tooltip-text",
+                          target, "tooltip-text",
+                          G_BINDING_SYNC_CREATE);
+}
+
 
 /*  private functions  */
 
@@ -608,3 +718,76 @@ gimp_prop_string_to_boolean (GBinding     *binding,
 
   return TRUE;
 }
+
+static void
+gimp_prop_config_notify (GObject    *config,
+                         GParamSpec *pspec,
+                         GtkWidget  *widget)
+{
+  GParamSpec *widget_pspec;
+  GWeakRef   *label_ref;
+  GtkWidget  *label_widget;
+  gboolean    sensitive;
+  gboolean    visible;
+  gchar      *label;
+  gchar      *description;
+
+  widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+  label_ref    = g_object_get_data (G_OBJECT (widget), "gimp-prop-label-ref");
+
+  if (label_ref)
+    label_widget = g_weak_ref_get (label_ref);
+  else
+    label_widget = NULL;
+
+  sensitive   = gimp_prop_eval_boolean (config, widget_pspec, "sensitive", TRUE);
+  visible     = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+  label       = gimp_prop_eval_string (config, widget_pspec, "label",
+                                       g_param_spec_get_nick (widget_pspec));
+  description = gimp_prop_eval_string (config, widget_pspec, "description",
+                                       g_param_spec_get_blurb (widget_pspec));
+
+  /* we store the label in (and pass ownership over it to) the widget's
+   * "gimp-prop-label" key, so that we can use it to initialize the label
+   * widget's label in gimp_prop_gui_bind_label() upon binding.
+   */
+  g_object_set_data_full (G_OBJECT (widget), "gimp-prop-label", label, g_free);
+
+  g_signal_handlers_block_by_func (widget,
+                                   gimp_prop_widget_show, config);
+
+  gtk_widget_set_sensitive (widget, sensitive);
+  gtk_widget_set_visible (widget, visible);
+  if (label_widget) g_object_set (label_widget, "label", label, NULL);
+  gimp_help_set_help_data (widget, description, NULL);
+
+  g_signal_handlers_unblock_by_func (widget,
+                                     gimp_prop_widget_show, config);
+
+  g_free (description);
+
+  if (label_widget)
+    g_object_unref (label_widget);
+}
+
+static void
+gimp_prop_widget_show (GtkWidget *widget,
+                       GObject   *config)
+{
+  GParamSpec *widget_pspec;
+  gboolean    visible;
+
+  widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+
+  visible = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+
+  gtk_widget_set_visible (widget, visible);
+}
+
+static void
+gimp_prop_free_label_ref (GWeakRef *label_ref)
+{
+  g_weak_ref_clear (label_ref);
+
+  g_slice_free (GWeakRef, label_ref);
+}
diff --git a/app/widgets/gimppropgui.h b/app/widgets/gimppropgui.h
index 167708c..9a1649a 100644
--- a/app/widgets/gimppropgui.h
+++ b/app/widgets/gimppropgui.h
@@ -52,6 +52,12 @@ GtkWidget * gimp_prop_gui_new               (GObject              *config,
                                              GimpCreatePickerFunc  create_picker,
                                              gpointer              picker_creator);
 
+void        gimp_prop_gui_bind_container    (GtkWidget            *source,
+                                             GtkWidget            *target);
+void        gimp_prop_gui_bind_label        (GtkWidget            *source,
+                                             GtkWidget            *target);
+void        gimp_prop_gui_bind_tooltip      (GtkWidget            *source,
+                                             GtkWidget            *target);
 
 
 #endif /* __GIMP_PROP_GUI_H__ */


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