[gimp] libgimp: improvements to GimpProcedureDialog API.



commit 3fb2ff1b9d1380be35e68861cdb1dc2ac70c5723
Author: Jehan <jehan girinstud io>
Date:   Tue Nov 24 15:00:34 2020 +0100

    libgimp: improvements to GimpProcedureDialog API.
    
    - New gimp_procedure_dialog_fill_box(_list)() functions to create a
      GtkBox in the layout.
    - Generating widgets for parameters of type double (and computing
      appropriate "ok defaults" digits for these, depending on the min-max
      range of the property).

 libgimp/gimpproceduredialog.c | 337 +++++++++++++++++++++++++++++++-----------
 libgimp/gimpproceduredialog.h |   7 +
 libgimp/gimpui.def            |   2 +
 3 files changed, 259 insertions(+), 87 deletions(-)
---
diff --git a/libgimp/gimpproceduredialog.c b/libgimp/gimpproceduredialog.c
index 47e292482c..21202e50ba 100644
--- a/libgimp/gimpproceduredialog.c
+++ b/libgimp/gimpproceduredialog.c
@@ -88,12 +88,18 @@ static void   gimp_procedure_dialog_save_defaults (GtkWidget           *button,
 static void   gimp_procedure_dialog_estimate_increments (gdouble        lower,
                                                          gdouble        upper,
                                                          gdouble       *step,
-                                                         gdouble       *page);
+                                                         gdouble       *page,
+                                                         gint          *digits);
 
 static gboolean gimp_procedure_dialog_check_mnemonic    (GimpProcedureDialog *dialog,
                                                          GtkWidget           *widget,
                                                          const gchar         *id,
                                                          const gchar         *core_id);
+static GtkWidget *
+              gimp_procedure_dialog_fill_container_list (GimpProcedureDialog *dialog,
+                                                         const gchar         *container_id,
+                                                         GtkContainer        *container,
+                                                         GList               *properties);
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpProcedureDialog, gimp_procedure_dialog,
                             GIMP_TYPE_DIALOG)
@@ -427,32 +433,48 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
                                        _(g_param_spec_get_nick (pspec)),
                                        &label, NULL);
     }
-  else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT)
+  else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT ||
+           G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_DOUBLE)
     {
+      gdouble minimum;
+      gdouble maximum;
+      gdouble step   = 0.0;
+      gdouble page   = 0.0;
+      gint    digits = 0;
+
+      if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT)
+        {
+          GParamSpecInt *pspecint = (GParamSpecInt *) pspec;
+
+          minimum = (gdouble) pspecint->minimum;
+          maximum = (gdouble) pspecint->maximum;
+        }
+      else /* G_TYPE_PARAM_DOUBLE */
+        {
+          GParamSpecDouble *pspecdouble = (GParamSpecDouble *) pspec;
+
+          minimum = pspecdouble->minimum;
+          maximum = pspecdouble->maximum;
+        }
+      gimp_procedure_dialog_estimate_increments (minimum, maximum, &step, &page, &digits);
+
       if (widget_type == G_TYPE_NONE || widget_type == GIMP_TYPE_LABEL_SPIN)
         {
           widget = gimp_prop_label_spin_new (G_OBJECT (dialog->priv->config),
-                                             property, 0);
+                                             property, digits);
         }
       else if (widget_type == GIMP_TYPE_SCALE_ENTRY)
         {
           widget = gimp_prop_scale_entry_new (G_OBJECT (dialog->priv->config),
                                               property,
                                               _(g_param_spec_get_nick (pspec)),
-                                              0, FALSE, 0.0, 0.0);
+                                              digits, FALSE, 0.0, 0.0);
         }
       else if (widget_type == GIMP_TYPE_SPIN_BUTTON)
         {
           /* Just some spin button without label. */
-          GParamSpecInt *pspecint = (GParamSpecInt *) pspec;
-          gdouble        step     = 0.0;
-          gdouble        page     = 0.0;
-
-          gimp_procedure_dialog_estimate_increments (pspecint->minimum,
-                                                     pspecint->maximum,
-                                                     &step, &page);
           widget = gimp_prop_spin_button_new (G_OBJECT (dialog->priv->config),
-                                              property, step, page, 0);
+                                              property, step, page, digits);
         }
     }
   else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_STRING)
@@ -755,6 +777,98 @@ gimp_procedure_dialog_fill_list (GimpProcedureDialog *dialog,
     g_list_free (properties);
 }
 
+/**
+ * gimp_procedure_dialog_fill_box:
+ * @dialog:         the #GimpProcedureDialog.
+ * @container_id:   a container identifier.
+ * @first_property: the first property name.
+ * @...:            a %NULL-terminated list of other property names.
+ *
+ * Creates and populates a new #GtkBox with widgets corresponding to
+ * every listed properties. If the list is empty, the created box will
+ * be filled by the whole list of properties of the associated
+ * #GimpProcedure, in the defined order. This is similar of how
+ * gimp_procedure_dialog_fill() works except that it creates a new
+ * widget which is not inside @dialog itself.
+ *
+ * The @container_id must be a unique ID which is neither the name of a
+ * property of the #GimpProcedureConfig associated to @dialog, nor is it
+ * the ID of any previously created container. This ID can later be used
+ * together with property names to be packed in other containers or
+ * inside @dialog itself.
+ *
+ * Returns: (transfer none): the #GtkBox representing @property. The
+ *                           object belongs to @dialog and must not be
+ *                           freed.
+ */
+GtkWidget *
+gimp_procedure_dialog_fill_box (GimpProcedureDialog *dialog,
+                                const gchar         *container_id,
+                                const gchar         *first_property,
+                                ...)
+{
+  const gchar *prop_name = first_property;
+  GtkWidget   *box;
+  GList       *list      = NULL;
+  va_list      va_args;
+
+  g_return_val_if_fail (GIMP_IS_PROCEDURE_DIALOG (dialog), NULL);
+  g_return_val_if_fail (container_id != NULL, NULL);
+
+  if (first_property)
+    {
+      va_start (va_args, first_property);
+
+      do
+        list = g_list_prepend (list, (gpointer) prop_name);
+      while ((prop_name = va_arg (va_args, const gchar *)));
+
+      va_end (va_args);
+    }
+
+  list = g_list_reverse (list);
+  box = gimp_procedure_dialog_fill_box_list (dialog, container_id, list);
+  if (list)
+    g_list_free (list);
+
+  return box;
+}
+
+/**
+ * gimp_procedure_dialog_fill_box_list: (rename-to gimp_procedure_dialog_fill_box)
+ * @dialog:        the #GimpProcedureDialog.
+ * @container_id:  a container identifier.
+ * @properties: (nullable) (element-type gchar*): the list of property names.
+ *
+ * Creates and populates a new #GtkBox with widgets corresponding to
+ * every listed @properties. If the list is empty, the created box will
+ * be filled by the whole list of properties of the associated
+ * #GimpProcedure, in the defined order. This is similar of how
+ * gimp_procedure_dialog_fill() works except that it creates a new
+ * widget which is not inside @dialog itself.
+ *
+ * The @container_id must be a unique ID which is neither the name of a
+ * property of the #GimpProcedureConfig associated to @dialog, nor is it
+ * the ID of any previously created container. This ID can later be used
+ * together with property names to be packed in other containers or
+ * inside @dialog itself.
+ *
+ * Returns: (transfer none): the #GtkBox representing @property. The
+ *                           object belongs to @dialog and must not be
+ *                           freed.
+ */
+GtkWidget *
+gimp_procedure_dialog_fill_box_list (GimpProcedureDialog *dialog,
+                                     const gchar         *container_id,
+                                     GList               *properties)
+{
+  g_return_val_if_fail (container_id != NULL, NULL);
+
+  return gimp_procedure_dialog_fill_container_list (dialog, container_id,
+                                                    GTK_CONTAINER (gtk_box_new (GTK_ORIENTATION_VERTICAL, 
2)),
+                                                    properties);
+}
+
 /**
  * gimp_procedure_dialog_fill_flowbox:
  * @dialog:         the #GimpProcedureDialog.
@@ -840,80 +954,11 @@ gimp_procedure_dialog_fill_flowbox_list (GimpProcedureDialog *dialog,
                                          const gchar         *container_id,
                                          GList               *properties)
 {
-  GtkWidget *flowbox;
-  GList     *iter;
-  gboolean   free_properties = FALSE;
-
   g_return_val_if_fail (container_id != NULL, NULL);
 
-  if (g_object_class_find_property (G_OBJECT_GET_CLASS (dialog->priv->config),
-                                    container_id))
-    {
-      g_warning ("%s: container identifier '%s' cannot be an existing property name.",
-                 G_STRFUNC, container_id);
-      return NULL;
-    }
-
-  if ((flowbox = g_hash_table_lookup (dialog->priv->widgets, container_id)))
-    {
-      g_warning ("%s: container identifier '%s' was already configured.",
-                 G_STRFUNC, container_id);
-      return flowbox;
-    }
-
-  flowbox = gtk_flow_box_new ();
-
-  if (! properties)
-    {
-      GParamSpec **pspecs;
-      guint        n_pspecs;
-      gint         i;
-
-      pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (dialog->priv->config),
-                                               &n_pspecs);
-
-      for (i = 0; i < n_pspecs; i++)
-        {
-          const gchar *prop_name;
-          GParamSpec  *pspec = pspecs[i];
-
-          /*  skip our own properties  */
-          if (pspec->owner_type == GIMP_TYPE_PROCEDURE_CONFIG)
-            continue;
-
-          prop_name  = g_param_spec_get_name (pspec);
-          properties = g_list_prepend (properties, (gpointer) prop_name);
-        }
-
-      properties = g_list_reverse (properties);
-
-      if (properties)
-        free_properties = TRUE;
-    }
-
-  for (iter = properties; iter; iter = iter->next)
-    {
-      GtkWidget *widget;
-
-      widget = gimp_procedure_dialog_get_widget (dialog, iter->data, G_TYPE_NONE);
-      if (widget)
-        {
-          /* Reference the widget because the hash table will
-           * unreference it anyway when getting destroyed so we don't
-           * want to give the only reference to the parent widget.
-           */
-          g_object_ref (widget);
-          gtk_container_add (GTK_CONTAINER (flowbox), widget);
-          gtk_widget_show (widget);
-        }
-    }
-
-  if (free_properties)
-    g_list_free (properties);
-
-  g_hash_table_insert (dialog->priv->widgets, g_strdup (container_id), flowbox);
-
-  return flowbox;
+  return gimp_procedure_dialog_fill_container_list (dialog, container_id,
+                                                    GTK_CONTAINER (gtk_flow_box_new ()),
+                                                    properties);
 }
 
 
@@ -1157,12 +1202,13 @@ static void
 gimp_procedure_dialog_estimate_increments (gdouble  lower,
                                            gdouble  upper,
                                            gdouble *step,
-                                           gdouble *page)
+                                           gdouble *page,
+                                           gint    *digits)
 {
   gdouble range;
 
   g_return_if_fail (upper >= lower);
-  g_return_if_fail (step || page);
+  g_return_if_fail (step || page || digits);
 
   range = upper - lower;
 
@@ -1170,6 +1216,9 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
     {
       gdouble places = 10.0;
 
+      if (digits)
+        *digits = 3;
+
       /* Compute some acceptable step and page increments always in the
        * format `10**-X` where X is the rounded precision.
        * So for instance:
@@ -1178,7 +1227,11 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
        *  0.06 will also have increments 0.001 and 0.01.
        */
       while (range * places < 5.0)
-        places *= 10.0;
+        {
+          places *= 10.0;
+          if (digits)
+            (*digits)++;
+        }
 
 
       if (step)
@@ -1192,6 +1245,9 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
         *step = 0.01;
       if (page)
         *page = 0.1;
+
+      if (digits)
+        *digits = 3;
     }
   else if (range <= 5.0)
     {
@@ -1199,6 +1255,8 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
         *step = 0.1;
       if (page)
         *page = 1.0;
+      if (digits)
+        *digits = 2;
     }
   else if (range <= 40.0)
     {
@@ -1206,6 +1264,8 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
         *step = 1.0;
       if (page)
         *page = 2.0;
+      if (digits)
+        *digits = 2;
     }
   else
     {
@@ -1213,6 +1273,8 @@ gimp_procedure_dialog_estimate_increments (gdouble  lower,
         *step = 1.0;
       if (page)
         *page = 10.0;
+      if (digits)
+        *digits = 1;
     }
 }
 
@@ -1285,3 +1347,104 @@ gimp_procedure_dialog_check_mnemonic (GimpProcedureDialog *dialog,
 
   return success;
 }
+
+/**
+ * gimp_procedure_dialog_fill_container_list:
+ * @dialog:
+ * @container_id:
+ * @container: (transfer full):
+ * @properties:
+ *
+ * A generic function to be used by various publich functions
+ * gimp_procedure_dialog_fill_*_list(). Note in particular that
+ * @container is taken over by this function which may return it or not.
+ * @container is assumed to be a floating GtkContainer (i.e. newly
+ * created widget without a parent yet).
+ * If the object returns a different object (because @container_id
+ * already represents another widget) or %NULL, the function takes care
+ * of freeing @container. Calling code must therefore not reuse the
+ * pointer anymore.
+ */
+static GtkWidget *
+gimp_procedure_dialog_fill_container_list (GimpProcedureDialog *dialog,
+                                           const gchar         *container_id,
+                                           GtkContainer        *container,
+                                           GList               *properties)
+{
+  GList    *iter;
+  gboolean  free_properties = FALSE;
+
+  g_return_val_if_fail (container_id != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
+  g_return_val_if_fail (g_object_is_floating (G_OBJECT (container)), NULL);
+
+  g_object_ref_sink (container);
+  if (g_object_class_find_property (G_OBJECT_GET_CLASS (dialog->priv->config),
+                                    container_id))
+    {
+      g_warning ("%s: container identifier '%s' cannot be an existing property name.",
+                 G_STRFUNC, container_id);
+      g_object_unref (container);
+      return NULL;
+    }
+
+  if (g_hash_table_lookup (dialog->priv->widgets, container_id))
+    {
+      g_warning ("%s: container identifier '%s' was already configured.",
+                 G_STRFUNC, container_id);
+      g_object_unref (container);
+      return g_hash_table_lookup (dialog->priv->widgets, container_id);
+    }
+
+  if (! properties)
+    {
+      GParamSpec **pspecs;
+      guint        n_pspecs;
+      gint         i;
+
+      pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (dialog->priv->config),
+                                               &n_pspecs);
+
+      for (i = 0; i < n_pspecs; i++)
+        {
+          const gchar *prop_name;
+          GParamSpec  *pspec = pspecs[i];
+
+          /*  skip our own properties  */
+          if (pspec->owner_type == GIMP_TYPE_PROCEDURE_CONFIG)
+            continue;
+
+          prop_name  = g_param_spec_get_name (pspec);
+          properties = g_list_prepend (properties, (gpointer) prop_name);
+        }
+
+      properties = g_list_reverse (properties);
+
+      if (properties)
+        free_properties = TRUE;
+    }
+
+  for (iter = properties; iter; iter = iter->next)
+    {
+      GtkWidget *widget;
+
+      widget = gimp_procedure_dialog_get_widget (dialog, iter->data, G_TYPE_NONE);
+      if (widget)
+        {
+          /* Reference the widget because the hash table will
+           * unreference it anyway when getting destroyed so we don't
+           * want to give the only reference to the parent widget.
+           */
+          g_object_ref (widget);
+          gtk_container_add (container, widget);
+          gtk_widget_show (widget);
+        }
+    }
+
+  if (free_properties)
+    g_list_free (properties);
+
+  g_hash_table_insert (dialog->priv->widgets, g_strdup (container_id), container);
+
+  return GTK_WIDGET (container);
+}
diff --git a/libgimp/gimpproceduredialog.h b/libgimp/gimpproceduredialog.h
index 8825f93d7c..d8d321f357 100644
--- a/libgimp/gimpproceduredialog.h
+++ b/libgimp/gimpproceduredialog.h
@@ -85,6 +85,13 @@ GtkWidget * gimp_procedure_dialog_get_label         (GimpProcedureDialog *dialog
                                                      const gchar         *label_id,
                                                      const gchar         *text);
 
+GtkWidget * gimp_procedure_dialog_fill_box          (GimpProcedureDialog *dialog,
+                                                     const gchar         *container_id,
+                                                     const gchar         *first_property,
+                                                     ...);
+GtkWidget * gimp_procedure_dialog_fill_box_list     (GimpProcedureDialog *dialog,
+                                                     const gchar         *container_id,
+                                                     GList               *properties);
 GtkWidget * gimp_procedure_dialog_fill_flowbox      (GimpProcedureDialog *dialog,
                                                      const gchar         *container_id,
                                                      const gchar         *first_property,
diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def
index e61530fc74..2477e8e26b 100644
--- a/libgimp/gimpui.def
+++ b/libgimp/gimpui.def
@@ -40,6 +40,8 @@ EXPORTS
        gimp_proc_browser_dialog_new
        gimp_proc_view_new
        gimp_procedure_dialog_fill
+       gimp_procedure_dialog_fill_box
+       gimp_procedure_dialog_fill_box_list
        gimp_procedure_dialog_fill_flowbox
        gimp_procedure_dialog_fill_flowbox_list
        gimp_procedure_dialog_fill_frame


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