app, libgimp: new GimpChoice procedure argument.

These will replace the int arguments used in place of enums. The problem of int
arguments used as list of choices is that it makes calling PDB functions very
opaque. This is especially bad when a list is long, so you constantly have to
refer to the documentation to understand what a series of numbers mean in
argument lists.

And the second issue is that plug-in developers have to manually maintain a list
of values both in the GUI and in the documentation string. This help text may
get out-of-sync, may end up with missing values or whatnot. Also if it is used
as tooltips, it makes for very weird tooltips in the graphical interface, with
an overlong technical list of int-values mapping which should ideally only be
made visible in the PDB procedure browser listing.
This commit is contained in:
Jehan 2023-08-02 23:55:33 +02:00
parent 20923260cf
commit 4163a29af3
15 changed files with 485 additions and 1 deletions

View File

@ -826,6 +826,56 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
}
break;
case GP_PARAM_DEF_TYPE_CHOICE:
{
GimpChoice *choice;
gint n_choices;
if (! gimp_scanner_parse_string (scanner,
&param_def.meta.m_choice.default_val) ||
! gimp_scanner_parse_int (scanner, &n_choices))
{
token = G_TOKEN_INT;
goto error;
}
choice = gimp_choice_new ();
param_def.meta.m_choice.choice = choice;
for (int i = 0; i < n_choices; i++)
{
gchar *nick = NULL;
gchar *label = NULL;
gchar *help = NULL;
gint id;
if (! gimp_scanner_parse_string (scanner, &nick))
{
token = G_TOKEN_STRING;
goto error;
}
if (! gimp_scanner_parse_int (scanner, &id))
{
token = G_TOKEN_INT;
goto error;
}
if (! gimp_scanner_parse_string (scanner, &label) ||
! gimp_scanner_parse_string (scanner, &help))
{
token = G_TOKEN_STRING;
g_free (nick);
g_free (label);
g_free (help);
goto error;
}
gimp_choice_add (choice, nick, id, label, help);
g_free (nick);
g_free (label);
g_free (help);
}
}
break;
case GP_PARAM_DEF_TYPE_BOOLEAN:
if (! gimp_scanner_parse_int (scanner,
&param_def.meta.m_boolean.default_val))
@ -940,6 +990,11 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
case GP_PARAM_DEF_TYPE_ID:
break;
case GP_PARAM_DEF_TYPE_CHOICE:
g_clear_object (&param_def.meta.m_choice.choice);
g_free (param_def.meta.m_choice.default_val);
break;
case GP_PARAM_DEF_TYPE_ID_ARRAY:
g_free (param_def.meta.m_id_array.type_name);
break;
@ -1034,6 +1089,31 @@ plug_in_rc_write_proc_arg (GimpConfigWriter *writer,
param_def.meta.m_enum.default_val);
break;
case GP_PARAM_DEF_TYPE_CHOICE:
{
GList *choices;
choices = gimp_choice_list_nicks (param_def.meta.m_choice.choice);
gimp_config_writer_string (writer, param_def.meta.m_choice.default_val);
gimp_config_writer_printf (writer, "%d", g_list_length (choices));
for (GList *iter = choices; iter; iter = iter->next)
{
const gchar *nick = iter->data;
const gchar *label;
const gchar *help;
gint id;
gimp_choice_get_documentation (param_def.meta.m_choice.choice,
nick, &label, &help);
id = gimp_choice_get_id (param_def.meta.m_choice.choice, nick);
gimp_config_writer_string (writer, nick);
gimp_config_writer_printf (writer, "%d", id);
gimp_config_writer_string (writer, label);
gimp_config_writer_string (writer, help);
}
}
break;
case GP_PARAM_DEF_TYPE_BOOLEAN:
gimp_config_writer_printf (writer, "%d",
param_def.meta.m_boolean.default_val);

View File

@ -74,6 +74,17 @@ _gimp_gp_param_def_to_param_spec (const GPParamDef *param_def)
break;
case GP_PARAM_DEF_TYPE_CHOICE:
if (! strcmp (param_def->type_name, "GimpParamChoice"))
{
return gimp_param_spec_choice (name, nick, blurb,
g_object_ref (param_def->meta.m_choice.choice),
param_def->meta.m_choice.default_val,
flags);
}
break;
case GP_PARAM_DEF_TYPE_INT:
if (! strcmp (param_def->type_name, "GParamInt"))
return g_param_spec_int (name, nick, blurb,
@ -302,6 +313,15 @@ _gimp_param_spec_to_gp_param_def (GParamSpec *pspec,
param_def->meta.m_enum.default_val = espec->default_value;
}
else if (pspec_type == GIMP_TYPE_PARAM_CHOICE)
{
GimpParamSpecChoice *cspec = GIMP_PARAM_SPEC_CHOICE (pspec);
param_def->param_def_type = GP_PARAM_DEF_TYPE_CHOICE;
param_def->meta.m_choice.default_val = cspec->default_value;
param_def->meta.m_choice.choice = cspec->choice;
}
else if (pspec_type == G_TYPE_PARAM_BOOLEAN)
{
GParamSpecBoolean *bspec = G_PARAM_SPEC_BOOLEAN (pspec);

View File

@ -224,6 +224,33 @@ G_BEGIN_DECLS
g_value_set_enum (gimp_value_array_index (args, n), value)
/* choice */
#define GIMP_PROC_ARG_CHOICE(procedure, name, nick, blurb, choice, default, flags) \
gimp_procedure_add_argument (procedure,\
gimp_param_spec_choice (name, nick, blurb,\
choice, default,\
flags))
#define GIMP_PROC_AUX_ARG_CHOICE(procedure, name, nick, blurb, choice, default, flags) \
gimp_procedure_add_aux_argument (procedure,\
gimp_param_spec_choice (name, nick, blurb,\
choice, default,\
flags))
#define GIMP_PROC_VAL_CHOICE(procedure, name, nick, blurb, enum_type, default, flags) \
gimp_procedure_add_return_value (procedure,\
gimp_param_spec_choice (name, nick, blurb,\
choice, default,\
flags))
#define GIMP_VALUES_GET_CHOICE(args, n) \
g_value_get_int (gimp_value_array_index (args, n))
#define GIMP_VALUES_SET_CHOICE(args, n, value) \
g_value_set_int (gimp_value_array_index (args, n), value)
/* string */
#define GIMP_PROC_ARG_STRING(procedure, name, nick, blurb, default, flags) \

View File

@ -765,6 +765,13 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
property, NULL,
GTK_FILE_CHOOSER_ACTION_OPEN);
}
else if (G_PARAM_SPEC_TYPE (pspec) == GIMP_TYPE_PARAM_CHOICE)
{
widget = gimp_prop_choice_combo_box_new (G_OBJECT (dialog->priv->config), property);
gtk_widget_set_vexpand (widget, FALSE);
gtk_widget_set_hexpand (widget, TRUE);
widget = gimp_label_string_widget_new (g_param_spec_get_nick (pspec), widget);
}
/* GimpResource subclasses */
/* FUTURE: title the chooser more specifically, with a prefix that is the nick of the property. */
else if (G_IS_PARAM_SPEC_OBJECT (pspec) && pspec->value_type == GIMP_TYPE_BRUSH)

View File

@ -117,6 +117,7 @@ EXPORTS
gimp_orientation_type_get_type
gimp_paint_application_mode_get_type
gimp_param_array_get_type
gimp_param_choice_get_type
gimp_param_float_array_get_type
gimp_param_int32_array_get_type
gimp_param_memsize_get_type
@ -124,6 +125,7 @@ EXPORTS
gimp_param_parasite_get_type
gimp_param_rgb_array_get_type
gimp_param_spec_array
gimp_param_spec_choice
gimp_param_spec_float_array
gimp_param_spec_int32_array
gimp_param_spec_memsize

View File

@ -24,6 +24,7 @@
#include <libgimpbase/gimpbasetypes.h>
#include <libgimpbase/gimpchecks.h>
#include <libgimpbase/gimpchoice.h>
#include <libgimpbase/gimpcpuaccel.h>
#include <libgimpbase/gimpenv.h>
#include <libgimpbase/gimplimits.h>

View File

@ -44,6 +44,7 @@ G_BEGIN_DECLS
#endif
typedef struct _GimpChoice GimpChoice;
typedef struct _GimpParasite GimpParasite;
typedef struct _GimpEnumDesc GimpEnumDesc;
typedef struct _GimpFlagsDesc GimpFlagsDesc;

View File

@ -25,6 +25,139 @@
#include "gimpbase.h"
/*
* GIMP_TYPE_PARAM_CHOICE
*/
static void gimp_param_choice_class_init (GParamSpecClass *klass);
static void gimp_param_choice_init (GParamSpec *pspec);
static void gimp_param_choice_finalize (GParamSpec *pspec);
static gboolean gimp_param_choice_validate (GParamSpec *pspec,
GValue *value);
static gint gimp_param_choice_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2);
GType
gimp_param_choice_get_type (void)
{
static GType type = 0;
if (! type)
{
const GTypeInfo info =
{
sizeof (GParamSpecClass),
NULL, NULL,
(GClassInitFunc) gimp_param_choice_class_init,
NULL, NULL,
sizeof (GimpParamSpecChoice),
0,
(GInstanceInitFunc) gimp_param_choice_init
};
type = g_type_register_static (G_TYPE_PARAM_BOXED,
"GimpParamChoice", &info, 0);
}
return type;
}
static void
gimp_param_choice_class_init (GParamSpecClass *klass)
{
klass->value_type = G_TYPE_STRING;
klass->finalize = gimp_param_choice_finalize;
klass->value_validate = gimp_param_choice_validate;
klass->values_cmp = gimp_param_choice_values_cmp;
}
static void
gimp_param_choice_init (GParamSpec *pspec)
{
GimpParamSpecChoice *choice = GIMP_PARAM_SPEC_CHOICE (pspec);
choice->choice = NULL;
choice->default_value = NULL;
}
static void
gimp_param_choice_finalize (GParamSpec *pspec)
{
GimpParamSpecChoice *spec_choice = GIMP_PARAM_SPEC_CHOICE (pspec);
g_free (spec_choice->default_value);
g_object_unref (spec_choice->choice);
}
static gboolean
gimp_param_choice_validate (GParamSpec *pspec,
GValue *value)
{
GimpParamSpecChoice *spec_choice = GIMP_PARAM_SPEC_CHOICE (pspec);
GimpChoice *choice = spec_choice->choice;
const gchar *strval = g_value_get_string (value);
if (! gimp_choice_is_valid (choice, strval))
{
g_value_set_string (value, spec_choice->default_value);
return TRUE;
}
return FALSE;
}
static gint
gimp_param_choice_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2)
{
const gchar *choice1 = g_value_get_string (value1);
const gchar *choice2 = g_value_get_string (value2);
return g_strcmp0 (choice1, choice2);
}
/**
* gimp_param_spec_choice:
* @name: Canonical name of the property specified.
* @nick: Nick name of the property specified.
* @blurb: Description of the property specified.
* @choice: (transfer full): the %GimpChoice describing allowed choices.
* @flags: Flags for the property specified.
*
* Creates a new #GimpParamSpecChoice specifying a
* #G_TYPE_STRING property.
* This %GimpParamSpecChoice takes ownership of the reference on @choice.
*
* See g_param_spec_internal() for details on property names.
*
* Returns: (transfer full): The newly created #GimpParamSpecChoice.
*
* Since: 3.0
**/
GParamSpec *
gimp_param_spec_choice (const gchar *name,
const gchar *nick,
const gchar *blurb,
GimpChoice *choice,
const gchar *default_value,
GParamFlags flags)
{
GimpParamSpecChoice *choice_spec;
choice_spec = g_param_spec_internal (GIMP_TYPE_PARAM_CHOICE,
name, nick, blurb, flags);
g_return_val_if_fail (choice_spec, NULL);
choice_spec->choice = choice;
choice_spec->default_value = g_strdup (default_value);
return G_PARAM_SPEC (choice_spec);
}
/*
* GIMP_TYPE_ARRAY
*/

View File

@ -74,6 +74,34 @@ G_BEGIN_DECLS
GIMP_PARAM_STATIC_STRINGS)
/*
* GIMP_TYPE_PARAM_CHOICE
*/
#define GIMP_TYPE_PARAM_CHOICE (gimp_param_choice_get_type ())
#define GIMP_PARAM_SPEC_CHOICE(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CHOICE, GimpParamSpecChoice))
#define GIMP_IS_PARAM_SPEC_CHOICE(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_CHOICE))
typedef struct _GimpParamSpecChoice GimpParamSpecChoice;
struct _GimpParamSpecChoice
{
GParamSpecBoxed parent_instance;
gchar *default_value;
GimpChoice *choice;
};
GType gimp_param_choice_get_type (void) G_GNUC_CONST;
GParamSpec * gimp_param_spec_choice (const gchar *name,
const gchar *nick,
const gchar *blurb,
GimpChoice *choice,
const gchar *default_value,
GParamFlags flags);
/*
* GIMP_TYPE_ARRAY
*/

View File

@ -22,6 +22,7 @@
#include "gimpbasetypes.h"
#include "gimpchoice.h"
#include "gimpparamspecs.h"
#include "gimpparasite.h"
#include "gimpprotocol.h"
@ -1100,6 +1101,47 @@ _gp_param_def_read (GIOChannel *channel,
return FALSE;
break;
case GP_PARAM_DEF_TYPE_CHOICE:
{
GimpChoice *choice;
guint32 size;
gchar *nick;
if (! _gimp_wire_read_string (channel, &nick, (int) 1, user_data))
return FALSE;
param_def->meta.m_choice.default_val = g_strdup (nick);
if (! _gimp_wire_read_int32 (channel, &size, 1, user_data))
return FALSE;
choice = gimp_choice_new ();
for (gint i = 0; i < size; i++)
{
gchar *label;
gchar *help;
gint id;
if (! _gimp_wire_read_string (channel, &nick,
(int) 1, user_data) ||
! _gimp_wire_read_int32 (channel, (guint32 *) &id,
1, user_data) ||
! _gimp_wire_read_string (channel, &label,
(int) 1, user_data) ||
! _gimp_wire_read_string (channel, &help,
(int) 1, user_data))
{
g_object_unref (choice);
return FALSE;
}
gimp_choice_add (choice, nick, id, label, help);
}
param_def->meta.m_choice.choice = choice;
}
break;
case GP_PARAM_DEF_TYPE_UNIT:
if (! _gimp_wire_read_int32 (channel,
(guint32 *) &param_def->meta.m_unit.allow_pixels, 1,
@ -1196,6 +1238,11 @@ _gp_param_def_destroy (GPParamDef *param_def)
case GP_PARAM_DEF_TYPE_FLOAT:
break;
case GP_PARAM_DEF_TYPE_CHOICE:
g_free (param_def->meta.m_choice.default_val);
g_object_unref (param_def->meta.m_choice.choice);
break;
case GP_PARAM_DEF_TYPE_STRING:
g_free (param_def->meta.m_string.default_val);
break;
@ -1342,6 +1389,51 @@ _gp_param_def_write (GIOChannel *channel,
return FALSE;
break;
case GP_PARAM_DEF_TYPE_CHOICE:
{
GList *values;
gint size;
if (! _gimp_wire_write_string (channel,
&param_def->meta.m_choice.default_val,
1, user_data))
return FALSE;
values = gimp_choice_list_nicks (param_def->meta.m_choice.choice);
size = g_list_length (values);
if (! _gimp_wire_write_int32 (channel,
(guint32 *) &size, 1,
user_data))
return FALSE;
for (GList *iter = values; iter; iter = iter->next)
{
const gchar *label;
const gchar *help;
gint id;
gimp_choice_get_documentation (param_def->meta.m_choice.choice,
iter->data, &label, &help);
id = gimp_choice_get_id (param_def->meta.m_choice.choice,
iter->data);
if (! _gimp_wire_write_string (channel,
(gchar **) &iter->data,
1, user_data) ||
! _gimp_wire_write_int32 (channel,
(guint32 *) &id, 1,
user_data) ||
! _gimp_wire_write_string (channel,
(gchar **) &label,
1, user_data) ||
! _gimp_wire_write_string (channel,
(gchar **) &help,
1, user_data))
return FALSE;
}
}
break;
case GP_PARAM_DEF_TYPE_UNIT:
if (! _gimp_wire_write_int32 (channel,
(guint32 *) &param_def->meta.m_unit.allow_pixels, 1,

View File

@ -26,7 +26,7 @@ G_BEGIN_DECLS
/* Increment every time the protocol changes
*/
#define GIMP_PROTOCOL_VERSION 0x010F
#define GIMP_PROTOCOL_VERSION 0x0110
enum
@ -52,6 +52,7 @@ typedef enum
GP_PARAM_DEF_TYPE_INT,
GP_PARAM_DEF_TYPE_UNIT,
GP_PARAM_DEF_TYPE_ENUM,
GP_PARAM_DEF_TYPE_CHOICE,
GP_PARAM_DEF_TYPE_BOOLEAN,
GP_PARAM_DEF_TYPE_FLOAT,
GP_PARAM_DEF_TYPE_STRING,
@ -87,6 +88,7 @@ typedef struct _GPParamDefEnum GPParamDefEnum;
typedef struct _GPParamDefBoolean GPParamDefBoolean;
typedef struct _GPParamDefFloat GPParamDefFloat;
typedef struct _GPParamDefString GPParamDefString;
typedef struct _GPParamDefChoice GPParamDefChoice;
typedef struct _GPParamStrv GPParamStrv;
typedef struct _GPParamDefColor GPParamDefColor;
typedef struct _GPParamDefID GPParamDefID;
@ -203,6 +205,12 @@ struct _GPParamDefIDArray
gchar *type_name;
};
struct _GPParamDefChoice
{
GimpChoice *choice;
gchar *default_val;
};
struct _GPParamDef
{
GPParamDefType param_def_type;
@ -224,6 +232,7 @@ struct _GPParamDef
GPParamDefColor m_color;
GPParamDefID m_id;
GPParamDefIDArray m_id_array;
GPParamDefChoice m_choice;
} meta;
};

View File

@ -240,6 +240,15 @@ gimp_config_param_spec_duplicate (GParamSpec *pspec)
flags);
}
}
else if (GIMP_IS_PARAM_SPEC_CHOICE (pspec))
{
GimpParamSpecChoice *spec = GIMP_PARAM_SPEC_CHOICE (pspec);
copy = gimp_param_spec_choice (name, nick, blurb,
g_object_ref (spec->choice),
spec->default_value,
flags);
}
else if (GIMP_IS_PARAM_SPEC_RGB (pspec))
{
GimpRGB color;

View File

@ -2604,6 +2604,78 @@ gimp_prop_string_combo_box_new (GObject *config,
return combo_box;
}
/**
* gimp_prop_choice_combo_box_new:
* @config: Object to which property is attached.
* @property_name: Name of %GimpChoice property controlled by combo box.
*
* Creates a #GimpStringComboBox widget to display and set the
* specified property.
*
* Returns: (transfer full): The newly created #GimpStringComboBox widget.
*
* Since: 2.4
*/
GtkWidget *
gimp_prop_choice_combo_box_new (GObject *config,
const gchar *property_name)
{
GParamSpec *param_spec;
GimpParamSpecChoice *cspec;
GtkWidget *combo_box;
GtkListStore *store;
gchar *value;
GList *values;
GList *iter;
g_return_val_if_fail (G_IS_OBJECT (config), NULL);
g_return_val_if_fail (property_name != NULL, NULL);
param_spec = check_param_spec_w (config, property_name,
GIMP_TYPE_PARAM_CHOICE, G_STRFUNC);
if (! param_spec)
return NULL;
cspec = GIMP_PARAM_SPEC_CHOICE (param_spec);
values = gimp_choice_list_nicks (cspec->choice);
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
for (iter = values; iter; iter = iter->next)
{
const gchar *nick = iter->data;
const gchar *label = gimp_choice_get_label (cspec->choice, nick);
gtk_list_store_insert_with_values (store, NULL, -1,
0, nick,
1, label,
-1);
}
combo_box = gimp_string_combo_box_new (GTK_TREE_MODEL (store), 0, 1);
g_object_unref (store);
g_object_get (config,
property_name, &value,
NULL);
gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (combo_box), value);
g_signal_connect (combo_box, "changed",
G_CALLBACK (gimp_prop_string_combo_box_callback),
config);
set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
connect_notify (config, property_name,
G_CALLBACK (gimp_prop_string_combo_box_notify),
combo_box);
gimp_widget_set_bound_property (combo_box, config, property_name);
gtk_widget_show (combo_box);
return combo_box;
}
static void
gimp_prop_string_combo_box_callback (GtkWidget *widget,
GObject *config)

View File

@ -166,6 +166,8 @@ GtkWidget * gimp_prop_string_combo_box_new (GObject *config,
GtkTreeModel *model,
gint id_column,
gint label_column);
GtkWidget * gimp_prop_choice_combo_box_new (GObject *config,
const gchar *property_name);
/* GimpParamPath */

View File

@ -363,6 +363,7 @@ EXPORTS
gimp_prop_boolean_combo_box_new
gimp_prop_boolean_radio_frame_new
gimp_prop_check_button_new
gimp_prop_choice_combo_box_new
gimp_prop_color_area_new
gimp_prop_color_select_new
gimp_prop_coordinates_connect