[gimp] libgimpwidgets: new GimpLabeled class and GimpLabelSpin subclass.



commit 4ea2b430beaff6282115eec07639f194a11232fe
Author: Jehan <jehan girinstud io>
Date:   Thu Nov 5 01:13:49 2020 +0100

    libgimpwidgets: new GimpLabeled class and GimpLabelSpin subclass.
    
    There is currently no property widget which just creates a
    GimpSpinButton with a label. Just as the GimpScaleEntry was needed, this
    one is as well.
    I am creating a GimpLabeled abstract class which will represent various
    widgets with a label. While this may seem a bit over-engineered for such
    a basic feature, this will actually bring some consistency and a common
    parent. In particular this can be used to get the GtkLabel with a common
    interface to add them all in a common GtkSizeGroup when generating
    dialogs automatically, hence make dialogs with properly aligned labels
    and edition widgets.

 libgimpwidgets/Makefile.gi        |   4 +
 libgimpwidgets/gimplabeled.c      | 243 ++++++++++++++++++
 libgimpwidgets/gimplabeled.h      |  79 ++++++
 libgimpwidgets/gimplabelspin.c    | 527 ++++++++++++++++++++++++++++++++++++++
 libgimpwidgets/gimplabelspin.h    |  72 ++++++
 libgimpwidgets/gimppropwidgets.c  |  45 ++++
 libgimpwidgets/gimppropwidgets.h  |   5 +
 libgimpwidgets/gimpwidgets.h      |   2 +
 libgimpwidgets/gimpwidgetstypes.h |   2 +
 libgimpwidgets/meson.build        |   4 +
 10 files changed, 983 insertions(+)
---
diff --git a/libgimpwidgets/Makefile.gi b/libgimpwidgets/Makefile.gi
index d00854d1fc..a20897e01f 100644
--- a/libgimpwidgets/Makefile.gi
+++ b/libgimpwidgets/Makefile.gi
@@ -34,6 +34,8 @@ libgimpwidgets_introspectable_headers =       \
        ../libgimpwidgets/gimpicons.h                   \
        ../libgimpwidgets/gimpintcombobox.h                     \
        ../libgimpwidgets/gimpintstore.h                        \
+       ../libgimpwidgets/gimplabeled.h                         \
+       ../libgimpwidgets/gimplabelspin.h                       \
        ../libgimpwidgets/gimpmemsizeentry.h                    \
        ../libgimpwidgets/gimpnumberpairentry.h                 \
        ../libgimpwidgets/gimpoffsetarea.h                      \
@@ -95,6 +97,8 @@ libgimpwidgets_introspectable =       \
        ../libgimpwidgets/gimpicons.c                   \
        ../libgimpwidgets/gimpintcombobox.c                     \
        ../libgimpwidgets/gimpintstore.c                        \
+       ../libgimpwidgets/gimplabeled.c                         \
+       ../libgimpwidgets/gimplabelspin.c                       \
        ../libgimpwidgets/gimpmemsizeentry.c                    \
        ../libgimpwidgets/gimpnumberpairentry.c         \
        ../libgimpwidgets/gimpoffsetarea.c                      \
diff --git a/libgimpwidgets/gimplabeled.c b/libgimpwidgets/gimplabeled.c
new file mode 100644
index 0000000000..f39a8b8739
--- /dev/null
+++ b/libgimpwidgets/gimplabeled.c
@@ -0,0 +1,243 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimplabeled.c
+ * Copyright (C) 2020 Jehan
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgets.h"
+
+
+/**
+ * SECTION: gimplabeled
+ * @title: GimpLabeled
+ * @short_description: Widget containing a label as mnemonic for another
+ *                     widget.
+ *
+ * This widget is a #GtkGrid showing a #GtkLabel used as mnemonic on
+ * another widget.
+ **/
+
+enum
+{
+  PROP_0,
+  PROP_LABEL,
+};
+
+typedef struct _GimpLabeledPrivate
+{
+  GtkWidget     *label;
+  GtkWidget     *mnemonic_widget;
+} GimpLabeledPrivate;
+
+
+static void       gimp_labeled_constructed       (GObject       *object);
+static void       gimp_labeled_set_property      (GObject       *object,
+                                                  guint          property_id,
+                                                  const GValue  *value,
+                                                  GParamSpec    *pspec);
+static void       gimp_labeled_get_property      (GObject       *object,
+                                                  guint          property_id,
+                                                  GValue        *value,
+                                                  GParamSpec    *pspec);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpLabeled, gimp_labeled, GTK_TYPE_GRID)
+
+#define parent_class gimp_labeled_parent_class
+
+
+static void
+gimp_labeled_class_init (GimpLabeledClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed  = gimp_labeled_constructed;
+  object_class->set_property = gimp_labeled_set_property;
+  object_class->get_property = gimp_labeled_get_property;
+
+  /**
+   * GimpLabeled:label:
+   *
+   * Label text with pango markup and mnemonic.
+   *
+   * Since: 3.0
+   **/
+  g_object_class_install_property (object_class, PROP_LABEL,
+                                   g_param_spec_string ("label",
+                                                        "Label text",
+                                                        "The text of the label part of this widget",
+                                                        NULL,
+                                                        GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_labeled_init (GimpLabeled *labeled)
+{
+}
+
+static void
+gimp_labeled_constructed (GObject *object)
+{
+  GimpLabeledClass   *klass;
+  GimpLabeled        *labeled = GIMP_LABELED (object);
+  GimpLabeledPrivate *priv    = gimp_labeled_get_instance_private (labeled);
+  gint                x       = 0;
+  gint                y       = 0;
+  gint                width   = 1;
+  gint                height  = 1;
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  priv->label = gtk_label_new_with_mnemonic (NULL);
+  gtk_label_set_xalign (GTK_LABEL (priv->label), 0.0);
+
+  klass = GIMP_LABELED_GET_CLASS (labeled);
+  g_return_if_fail (klass->populate);
+  priv->mnemonic_widget = klass->populate (labeled, &x, &y, &width, &height);
+
+  if (priv->mnemonic_widget)
+    gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), priv->mnemonic_widget);
+
+  gtk_grid_attach (GTK_GRID (labeled), priv->label, x, y, width, height);
+  gtk_widget_show (priv->label);
+}
+
+static void
+gimp_labeled_set_property (GObject      *object,
+                           guint         property_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  GimpLabeled        *entry = GIMP_LABELED (object);
+  GimpLabeledPrivate *priv  = gimp_labeled_get_instance_private (entry);
+
+  switch (property_id)
+    {
+    case PROP_LABEL:
+        {
+          /* This should not happen since the property is **not** set with
+           * G_PARAM_CONSTRUCT, hence the label should exist when the
+           * property is first set.
+           */
+          g_return_if_fail (priv->label);
+
+          gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label),
+                                              g_value_get_string (value));
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_labeled_get_property (GObject    *object,
+                           guint       property_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GimpLabeled        *entry = GIMP_LABELED (object);
+  GimpLabeledPrivate *priv  = gimp_labeled_get_instance_private (entry);
+
+  switch (property_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+/* Public functions */
+
+/**
+ * gimp_labeled_get_label:
+ * @labeled: The #GimpLabeled.
+ *
+ * This function returns the #GtkLabel packed in @labeled. This can be
+ * useful if you need to customize some aspects of the widget.
+ *
+ * Returns: (transfer none): The #GtkLabel contained in @labeled.
+ **/
+GtkWidget *
+gimp_labeled_get_label (GimpLabeled *labeled)
+{
+  GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
+
+  g_return_val_if_fail (GIMP_IS_LABELED (labeled), NULL);
+
+  return priv->label;
+}
+
+/**
+ * gimp_labeled_get_text:
+ * @labeled: the #GimpLabeled.
+ *
+ * This function will return exactly what you entered with
+ * gimp_labeled_set_text() or through the "label" property because this
+ * class expects labels to have mnemonics (and allows Pango formatting).
+ * To obtain instead the text as displayed with mnemonics and markup
+ * removed, call:
+ * |[<!-- language="C" -->
+ * gtk_label_get_text (GTK_LABEL (gimp_labeled_get_label (@labeled)));
+ * ]|
+ *
+ * Returns: the label text as entered, which includes pango markup and
+ *          mnemonics similarly to gtk_label_get_label().
+ */
+const gchar *
+gimp_labeled_get_text (GimpLabeled *labeled)
+{
+  GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
+
+  g_return_val_if_fail (GIMP_IS_LABELED (labeled), NULL);
+
+  return gtk_label_get_label (GTK_LABEL (priv->label));
+}
+
+/**
+ * gimp_labeled_set_text:
+ * @labeled: the #GimpLabeled.
+ * @text: label text with Pango markup and mnemonic.
+ *
+ * This is the equivalent of running
+ * gtk_label_set_markup_with_mnemonic() on the #GtkLabel as a
+ * #GimpLabeled expects a mnemonic. Pango markup are also allowed.
+ */
+void
+gimp_labeled_set_text (GimpLabeled *labeled,
+                       const gchar *text)
+{
+  GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
+
+  g_return_if_fail (GIMP_IS_LABELED (labeled));
+
+  gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label), text);
+}
diff --git a/libgimpwidgets/gimplabeled.h b/libgimpwidgets/gimplabeled.h
new file mode 100644
index 0000000000..e0a5aa019b
--- /dev/null
+++ b/libgimpwidgets/gimplabeled.h
@@ -0,0 +1,79 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimplabeled.h
+ * Copyright (C) 2020 Jehan
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_LABELED_H__
+#define __GIMP_LABELED_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_LABELED (gimp_labeled_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GimpLabeled, gimp_labeled, GIMP, LABELED, GtkGrid)
+
+struct _GimpLabeledClass
+{
+  GtkGridClass       parent_class;
+
+  /*  Class methods  */
+
+  /**
+   * GimpLabelledClass::populate:
+   *
+   * Fill the #GtkGrid with any necessary widget and sets the
+   * coordinates and dimensions the #GtkLabel should be attached to.
+   * By default, @x, @y, @width and @height will be pre-filled with 0,
+   * 0, 1 and 1 respectively, i.e. the top-left of the grid. There is no
+   * need to edit these output variables unless your subclass wants the
+   * label to be placed elsewhere.
+   *
+   * Returns: (transfer none): the #GtkWidget which the label must be
+   *                           set as mnemonic to.
+   **/
+  GtkWidget     * (* populate) (GimpLabeled *labeled,
+                                gint        *x,
+                                gint        *y,
+                                gint        *width,
+                                gint        *height);
+
+  /* Padding for future expansion */
+  void (* _gimp_reserved1) (void);
+  void (* _gimp_reserved2) (void);
+  void (* _gimp_reserved3) (void);
+  void (* _gimp_reserved4) (void);
+  void (* _gimp_reserved5) (void);
+  void (* _gimp_reserved6) (void);
+  void (* _gimp_reserved7) (void);
+  void (* _gimp_reserved8) (void);
+};
+
+GtkWidget     * gimp_labeled_get_label (GimpLabeled *labeled);
+
+const gchar   * gimp_labeled_get_text  (GimpLabeled *labeled);
+void            gimp_labeled_set_text  (GimpLabeled *labeled,
+                                        const gchar *text);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_LABELED_H__ */
diff --git a/libgimpwidgets/gimplabelspin.c b/libgimpwidgets/gimplabelspin.c
new file mode 100644
index 0000000000..55f37a24db
--- /dev/null
+++ b/libgimpwidgets/gimplabelspin.c
@@ -0,0 +1,527 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimplabelspin.c
+ * Copyright (C) 2020 Jehan
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgets.h"
+
+
+/**
+ * SECTION: gimplabelspin
+ * @title: GimpLabelSpin
+ * @short_description: Widget containing a spin button and a label.
+ *
+ * This widget is a subclass of #GimpLabeled with a #GimpSpinButton.
+ **/
+
+enum
+{
+  VALUE_CHANGED,
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_VALUE,
+  PROP_LOWER,
+  PROP_UPPER,
+  PROP_DIGITS,
+};
+
+typedef struct _GimpLabelSpinPrivate
+{
+  GimpLabeled    parent_instance;
+
+  GtkWidget     *spinbutton;
+  GtkAdjustment *spin_adjustment;
+} GimpLabelSpinPrivate;
+
+static void        gimp_label_spin_constructed       (GObject       *object);
+static void        gimp_label_spin_set_property      (GObject       *object,
+                                                      guint          property_id,
+                                                      const GValue  *value,
+                                                      GParamSpec    *pspec);
+static void        gimp_label_spin_get_property      (GObject       *object,
+                                                      guint          property_id,
+                                                      GValue        *value,
+                                                      GParamSpec    *pspec);
+
+static GtkWidget * gimp_label_spin_populate          (GimpLabeled   *spin,
+                                                      gint          *x,
+                                                      gint          *y,
+                                                      gint          *width,
+                                                      gint          *height);
+
+static void        gimp_label_spin_update_spin_width (GimpLabelSpin *spin);
+static void        gimp_label_spin_update_increments (GimpLabelSpin *spin);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpLabelSpin, gimp_label_spin, GIMP_TYPE_LABELED)
+
+#define parent_class gimp_label_spin_parent_class
+
+static guint gimp_label_spin_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gimp_label_spin_class_init (GimpLabelSpinClass *klass)
+{
+  GObjectClass     *object_class  = G_OBJECT_CLASS (klass);
+  GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
+
+  gimp_label_spin_signals[VALUE_CHANGED] =
+    g_signal_new ("value-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpLabelSpinClass, value_changed),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  object_class->constructed  = gimp_label_spin_constructed;
+  object_class->set_property = gimp_label_spin_set_property;
+  object_class->get_property = gimp_label_spin_get_property;
+
+  labeled_class->populate    = gimp_label_spin_populate;
+
+  /**
+   * GimpLabelSpin:value:
+   *
+   * The currently set value.
+   *
+   * Since: 3.0
+   **/
+  g_object_class_install_property (object_class, PROP_VALUE,
+                                   g_param_spec_double ("value", NULL,
+                                                        "Current value",
+                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
+                                                        GIMP_PARAM_READWRITE));
+
+  /**
+   * GimpLabelSpin:lower:
+   *
+   * The lower bound of the spin button.
+   *
+   * Since: 3.0
+   **/
+  g_object_class_install_property (object_class, PROP_LOWER,
+                                   g_param_spec_double ("lower", NULL,
+                                                        "Minimum value",
+                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  /**
+   * GimpLabelSpin:upper:
+   *
+   * The upper bound of the spin button.
+   *
+   * Since: 3.0
+   **/
+  g_object_class_install_property (object_class, PROP_UPPER,
+                                   g_param_spec_double ("upper", NULL,
+                                                        "Max value",
+                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  /**
+   * GimpLabelSpin:digits:
+   *
+   * The number of decimal places to display.
+   *
+   * Since: 3.0
+   **/
+  g_object_class_install_property (object_class, PROP_DIGITS,
+                                   g_param_spec_uint ("digits", NULL,
+                                                      "The number of decimal places to display",
+                                                      0, G_MAXUINT, 2,
+                                                      GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_label_spin_init (GimpLabelSpin *spin)
+{
+  GimpLabelSpinPrivate *priv  = gimp_label_spin_get_instance_private (spin);
+
+  /* We want the adjustment to exist at init so that construction
+   * properties can apply (default values are bogus but should be
+   * properly overrided with expected values if the object was created
+   * with gimp_label_spin_new().
+   */
+  priv->spin_adjustment = gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 10.0, 0.0);
+
+  gtk_grid_set_row_spacing (GTK_GRID (spin), 6);
+  gtk_grid_set_column_spacing (GTK_GRID (spin), 6);
+}
+
+static void
+gimp_label_spin_constructed (GObject *object)
+{
+  GimpLabelSpin        *spin = GIMP_LABEL_SPIN (object);
+  GimpLabelSpinPrivate *priv  = gimp_label_spin_get_instance_private (spin);
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  /* This is important to make this object into a property widget. It
+   * will allow config object to bind the "value" property of this
+   * widget, and therefore be updated automatically.
+   */
+  g_object_bind_property (G_OBJECT (priv->spin_adjustment), "value",
+                          G_OBJECT (spin),                  "value",
+                          G_BINDING_BIDIRECTIONAL |
+                          G_BINDING_SYNC_CREATE);
+
+  gimp_label_spin_update_spin_width (spin);
+  gimp_label_spin_update_increments (spin);
+}
+
+static void
+gimp_label_spin_set_property (GObject      *object,
+                              guint         property_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GimpLabelSpin        *spin = GIMP_LABEL_SPIN (object);
+  GimpLabelSpinPrivate *priv  = gimp_label_spin_get_instance_private (spin);
+
+  switch (property_id)
+    {
+    case PROP_VALUE:
+      /* Avoid looping forever since we have bound this widget's
+       * "value" property with the spin button "value" property.
+       */
+      if (gtk_adjustment_get_value (priv->spin_adjustment) != g_value_get_double (value))
+        gtk_adjustment_set_value (priv->spin_adjustment, g_value_get_double (value));
+
+      g_signal_emit (object, gimp_label_spin_signals[VALUE_CHANGED], 0);
+      break;
+    case PROP_LOWER:
+      gtk_adjustment_set_lower (priv->spin_adjustment,
+                                g_value_get_double (value));
+      if (priv->spinbutton)
+        {
+          gimp_label_spin_update_spin_width (spin);
+          gimp_label_spin_update_increments (spin);
+        }
+      break;
+    case PROP_UPPER:
+      gtk_adjustment_set_upper (priv->spin_adjustment,
+                                g_value_get_double (value));
+      if (priv->spinbutton)
+        {
+          gimp_label_spin_update_spin_width (spin);
+          gimp_label_spin_update_increments (spin);
+        }
+      break;
+    case PROP_DIGITS:
+      if (priv->spinbutton)
+        {
+          gtk_spin_button_set_digits (GTK_SPIN_BUTTON (priv->spinbutton),
+                                      g_value_get_uint (value));
+
+          gimp_label_spin_update_spin_width (spin);
+          gimp_label_spin_update_increments (spin);
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_label_spin_get_property (GObject    *object,
+                              guint       property_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GimpLabelSpin        *spin       = GIMP_LABEL_SPIN (object);
+  GimpLabelSpinPrivate *priv       = gimp_label_spin_get_instance_private (spin);
+  GtkSpinButton        *spinbutton = GTK_SPIN_BUTTON (priv->spinbutton);
+
+  switch (property_id)
+    {
+    case PROP_VALUE:
+      g_value_set_double (value, gtk_adjustment_get_value (priv->spin_adjustment));
+      break;
+    case PROP_LOWER:
+      g_value_set_double (value, gtk_adjustment_get_lower (priv->spin_adjustment));
+      break;
+    case PROP_UPPER:
+      g_value_set_double (value, gtk_adjustment_get_upper (priv->spin_adjustment));
+      break;
+    case PROP_DIGITS:
+      g_value_set_uint (value, gtk_spin_button_get_digits (spinbutton));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static GtkWidget *
+gimp_label_spin_populate (GimpLabeled *labeled,
+                          gint        *x,
+                          gint        *y,
+                          gint        *width,
+                          gint        *height)
+{
+  GimpLabelSpin        *spin = GIMP_LABEL_SPIN (labeled);
+  GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
+
+  priv->spinbutton = gimp_spin_button_new (priv->spin_adjustment, 2.0, 2.0);
+  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (priv->spinbutton), TRUE);
+
+  gtk_grid_attach (GTK_GRID (spin), priv->spinbutton, 1, 0, 1, 1);
+  gtk_widget_show (priv->spinbutton);
+
+  return priv->spinbutton;
+}
+
+static void
+gimp_label_spin_update_spin_width (GimpLabelSpin *spin)
+{
+  GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
+  gint                  width = 0;
+  gdouble               lower;
+  gdouble               upper;
+  gint                  digits;
+
+  g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
+
+  g_object_get (spin,
+                "lower",  &lower,
+                "upper",  &upper,
+                "digits", &digits,
+                NULL);
+
+  /* Necessary size to display the max/min integer values, with optional
+   * negative sign.
+   */
+  width = (gint) floor (log10 (upper) + 1) + (upper < 0.0 ? 1 : 0);
+  width = MAX (width, (gint) floor (log10 (lower) + 1) + (lower < 0.0 ? 1 : 0));
+
+  /* Adding decimal digits and separator. */
+  width += (digits > 0 ? 1 + digits : 0);
+
+  /* Overlong spin button are useless. */
+  width = MIN (10, width);
+
+  gtk_entry_set_width_chars (GTK_ENTRY (priv->spinbutton), width);
+}
+
+static void
+gimp_label_spin_update_increments (GimpLabelSpin *spin)
+{
+  gdouble lower;
+  gdouble upper;
+  gdouble range;
+
+  g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
+
+  g_object_get (spin,
+                "lower",  &lower,
+                "upper",  &upper,
+                NULL);
+
+  g_return_if_fail (upper >= lower);
+
+  range = upper - lower;
+
+  if (range > 0 && range <= 1.0)
+    {
+      gdouble places = 10.0;
+      gdouble step;
+      gdouble page;
+
+      /* Compute some acceptable step and page increments always in the
+       * format `10**-X` where X is the rounded precision.
+       * So for instance:
+       *  0.8 will have increments 0.01 and 0.1.
+       *  0.3 will have increments 0.001 and 0.01.
+       *  0.06 will also have increments 0.001 and 0.01.
+       */
+      while (range * places < 5.0)
+        places *= 10.0;
+
+
+      step = 0.1 / places;
+      page = 1.0 / places;
+
+      gimp_label_spin_set_increments (spin, step, page);
+    }
+  else if (range <= 2.0)
+    {
+      gimp_label_spin_set_increments (spin, 0.01, 0.1);
+    }
+  else if (range <= 5.0)
+    {
+      gimp_label_spin_set_increments (spin, 0.1, 1.0);
+    }
+  else if (range <= 40.0)
+    {
+      gimp_label_spin_set_increments (spin, 1.0, 2.0);
+    }
+  else
+    {
+      gimp_label_spin_set_increments (spin, 1.0, 10.0);
+    }
+}
+
+
+/* Public Functions */
+
+
+/**
+ * gimp_label_spin_new:
+ * @text:   The text for the #GtkLabel.
+ * @value:  The initial value.
+ * @lower:  The lower boundary.
+ * @upper:  The upper boundary.
+ * @digits: The number of decimal digits.
+ *
+ * Returns: (transfer full): The new #GimpLabelSpin widget.
+ **/
+GtkWidget *
+gimp_label_spin_new (const gchar *text,
+                     gdouble      value,
+                     gdouble      lower,
+                     gdouble      upper,
+                     guint        digits)
+{
+  GtkWidget *labeled;
+
+  labeled = g_object_new (GIMP_TYPE_LABEL_SPIN,
+                          "label",  text,
+                          "value",  value,
+                          "lower",  lower,
+                          "upper",  upper,
+                          "digits", digits,
+                          NULL);
+
+  return labeled;
+}
+
+/**
+ * gimp_label_spin_set_value:
+ * @spin: The #GtkLabelSpin.
+ * @value: A new value.
+ *
+ * This function sets the value shown by @spin.
+ **/
+void
+gimp_label_spin_set_value (GimpLabelSpin *spin,
+                           gdouble         value)
+{
+  g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
+
+  g_object_set (spin,
+                "value", value,
+                NULL);
+}
+
+/**
+ * gimp_label_spin_get_value:
+ * @spin: The #GtkLabelSpin.
+ *
+ * This function returns the value shown by @spin.
+ *
+ * Returns: The value currently set.
+ **/
+gdouble
+gimp_label_spin_get_value (GimpLabelSpin *spin)
+{
+  gdouble value;
+
+  g_return_val_if_fail (GIMP_IS_LABEL_SPIN (spin), 0.0);
+
+  g_object_get (spin,
+                "value", &value,
+                NULL);
+  return value;
+}
+
+/**
+ * gimp_label_spin_set_increments:
+ * @spin: the #GimpLabelSpin.
+ * @step: the step increment.
+ * @page: the page increment.
+ *
+ * Set the step and page increments of the spin button.
+ * By default, these increment values are automatically computed
+ * depending on the range based on common usage. So you will likely not
+ * need to run this for most case. Yet if you want specific increments
+ * (which the widget cannot guess), you can call this function.
+ */
+void
+gimp_label_spin_set_increments (GimpLabelSpin *spin,
+                                gdouble        step,
+                                gdouble        page)
+{
+  GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
+  GtkSpinButton        *spinbutton;
+  gdouble               lower;
+  gdouble               upper;
+
+  g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
+  g_return_if_fail (step < page);
+
+  spinbutton = GTK_SPIN_BUTTON (priv->spinbutton);
+
+  gtk_spin_button_get_range (spinbutton, &lower, &upper);
+  g_return_if_fail (upper >= lower);
+  g_return_if_fail (step < upper - lower && page < upper - lower);
+
+  g_object_freeze_notify (G_OBJECT (spinbutton));
+  gtk_adjustment_set_step_increment (gtk_spin_button_get_adjustment (spinbutton), step);
+  gtk_adjustment_set_page_increment (gtk_spin_button_get_adjustment (spinbutton), page);
+  g_object_thaw_notify (G_OBJECT (spinbutton));
+
+  g_object_set (priv->spinbutton,
+                "climb-rate", step,
+                NULL);
+}
+
+/**
+ * gimp_label_spin_get_spin_button:
+ * @spin: The #GimpLabelSpin
+ *
+ * This function returns the #GimpSpinButton packed in @spin.
+ *
+ * Returns: (transfer none): The #GimpSpinButton contained in @spin.
+ **/
+GtkWidget *
+gimp_label_spin_get_spin_button (GimpLabelSpin *spin)
+{
+  GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
+
+  g_return_val_if_fail (GIMP_IS_LABEL_SPIN (spin), NULL);
+
+  return priv->spinbutton;
+}
diff --git a/libgimpwidgets/gimplabelspin.h b/libgimpwidgets/gimplabelspin.h
new file mode 100644
index 0000000000..63316aa69c
--- /dev/null
+++ b/libgimpwidgets/gimplabelspin.h
@@ -0,0 +1,72 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorscaleentry.h
+ * Copyright (C) 2020 Jehan
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_LABEL_SPIN_H__
+#define __GIMP_LABEL_SPIN_H__
+
+#include <libgimpwidgets/gimplabeled.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_LABEL_SPIN (gimp_label_spin_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GimpLabelSpin, gimp_label_spin, GIMP, LABEL_SPIN, GimpLabeled)
+
+struct _GimpLabelSpinClass
+{
+  GimpLabeledClass   parent_class;
+
+  /*  Signals */
+  void            (* value_changed)    (GimpLabelSpin *spin);
+
+  /* Padding for future expansion */
+  void (* _gimp_reserved1) (void);
+  void (* _gimp_reserved2) (void);
+  void (* _gimp_reserved3) (void);
+  void (* _gimp_reserved4) (void);
+  void (* _gimp_reserved5) (void);
+  void (* _gimp_reserved6) (void);
+  void (* _gimp_reserved7) (void);
+  void (* _gimp_reserved8) (void);
+};
+
+GtkWidget  * gimp_label_spin_new             (const gchar   *text,
+                                              gdouble        value,
+                                              gdouble        lower,
+                                              gdouble        upper,
+                                              guint          digits);
+
+void         gimp_label_spin_set_value       (GimpLabelSpin *spin,
+                                              gdouble        value);
+gdouble      gimp_label_spin_get_value       (GimpLabelSpin *spin);
+
+void         gimp_label_spin_set_increments  (GimpLabelSpin *spin,
+                                              gdouble        step,
+                                              gdouble        page);
+
+GtkWidget  * gimp_label_spin_get_spin_button (GimpLabelSpin *spin);
+
+G_END_DECLS
+
+#endif /* __GIMP_LABEL_SPIN_H__ */
diff --git a/libgimpwidgets/gimppropwidgets.c b/libgimpwidgets/gimppropwidgets.c
index 2290d44677..a9d9104cac 100644
--- a/libgimpwidgets/gimppropwidgets.c
+++ b/libgimpwidgets/gimppropwidgets.c
@@ -1395,6 +1395,51 @@ gimp_prop_spin_button_new (GObject     *config,
   return spinbutton;
 }
 
+/**
+ * gimp_prop_label_spin_new:
+ * @config:            Object to which property is attached.
+ * @digits:            Number of digits after decimal point to display.
+ *
+ * Creates a #GimpLabelSpin to set and display the value of the
+ * specified double property.
+ *
+ * Returns: (transfer full): A new #libgimpwidgets-gimpspinbutton.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_label_spin_new (GObject     *config,
+                          const gchar *property_name,
+                          gint         digits)
+{
+  const gchar   *label;
+  GParamSpec    *param_spec;
+  GtkWidget     *widget;
+  gdouble        value;
+  gdouble        lower;
+  gdouble        upper;
+
+  param_spec = find_param_spec (config, property_name, G_STRFUNC);
+  if (! param_spec)
+    return NULL;
+
+  if (! get_numeric_values (config,
+                            param_spec, &value, &lower, &upper, G_STRFUNC))
+    return NULL;
+
+  if (! G_IS_PARAM_SPEC_DOUBLE (param_spec))
+    digits = 0;
+
+  label = g_param_spec_get_nick (param_spec);
+  widget = gimp_label_spin_new (label, value, lower, upper, digits);
+
+  g_object_bind_property (config, property_name,
+                          widget, "value",
+                          G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+  return widget;
+}
+
 /**
  * gimp_prop_hscale_new:
  * @config:         Object to which property is attached.
diff --git a/libgimpwidgets/gimppropwidgets.h b/libgimpwidgets/gimppropwidgets.h
index 03922c5823..78adefde97 100644
--- a/libgimpwidgets/gimppropwidgets.h
+++ b/libgimpwidgets/gimppropwidgets.h
@@ -114,6 +114,11 @@ GtkWidget     * gimp_prop_spin_button_new         (GObject      *config,
                                                    gdouble       page_increment,
                                                    gint          digits);
 
+GtkWidget     * gimp_prop_label_spin_new          (GObject      *config,
+                                                   const gchar  *property_name,
+                                                   gint          digits);
+
+
 GtkWidget     * gimp_prop_hscale_new              (GObject      *config,
                                                    const gchar  *property_name,
                                                    gdouble       step_increment,
diff --git a/libgimpwidgets/gimpwidgets.h b/libgimpwidgets/gimpwidgets.h
index 7c3c866098..6b13e0ae45 100644
--- a/libgimpwidgets/gimpwidgets.h
+++ b/libgimpwidgets/gimpwidgets.h
@@ -61,6 +61,8 @@
 #include <libgimpwidgets/gimpicons.h>
 #include <libgimpwidgets/gimpintcombobox.h>
 #include <libgimpwidgets/gimpintstore.h>
+#include <libgimpwidgets/gimplabeled.h>
+#include <libgimpwidgets/gimplabelspin.h>
 #include <libgimpwidgets/gimpmemsizeentry.h>
 #include <libgimpwidgets/gimpnumberpairentry.h>
 #include <libgimpwidgets/gimpoffsetarea.h>
diff --git a/libgimpwidgets/gimpwidgetstypes.h b/libgimpwidgets/gimpwidgetstypes.h
index 6f868bcc1d..3177bf39e9 100644
--- a/libgimpwidgets/gimpwidgetstypes.h
+++ b/libgimpwidgets/gimpwidgetstypes.h
@@ -62,6 +62,8 @@ typedef struct _GimpFrame                     GimpFrame;
 typedef struct _GimpHintBox                   GimpHintBox;
 typedef struct _GimpIntComboBox               GimpIntComboBox;
 typedef struct _GimpIntStore                  GimpIntStore;
+typedef struct _GimpLabeled                   GimpLabeled;
+typedef struct _GimpLabelSpin                 GimpLabelSpin;
 typedef struct _GimpMemsizeEntry              GimpMemsizeEntry;
 typedef struct _GimpNumberPairEntry           GimpNumberPairEntry;
 typedef struct _GimpOffsetArea                GimpOffsetArea;
diff --git a/libgimpwidgets/meson.build b/libgimpwidgets/meson.build
index 44c781610e..beec696930 100644
--- a/libgimpwidgets/meson.build
+++ b/libgimpwidgets/meson.build
@@ -54,6 +54,8 @@ libgimpwidgets_sources_introspectable = files(
   'gimpicons.c',
   'gimpintcombobox.c',
   'gimpintstore.c',
+  'gimplabeled.c',
+  'gimplabelspin.c',
   'gimpmemsizeentry.c',
   'gimpnumberpairentry.c',
   'gimpoffsetarea.c',
@@ -126,6 +128,8 @@ libgimpwidgets_headers_introspectable = files(
   'gimphintbox.h',
   'gimpicons.h',
   'gimpintcombobox.h',
+  'gimplabeled.h',
+  'gimplabelspin.h',
   'gimpintstore.h',
   'gimpmemsizeentry.h',
   'gimpnumberpairentry.h',



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