libgimpbase, libgimpwidgets: add a concept of insensitive choices in GimpChoice.

This is used in the generated GUIs for GimpChoice arguments, but also for
validation of property setting.

New functions:

* gimp_choice_set_sensitive()
* gimp_string_combo_box_set_sensitivity()
This commit is contained in:
Jehan 2023-08-04 01:16:16 +02:00
parent cedc45d8f3
commit d670ff9f82
8 changed files with 266 additions and 57 deletions

View File

@ -27,6 +27,7 @@ EXPORTS
gimp_choice_list_nicks
gimp_choice_new
gimp_choice_new_with_values
gimp_choice_set_sensitive
gimp_clone_type_get_type
gimp_color_tag_get_type
gimp_component_type_get_type

View File

@ -33,8 +33,15 @@ typedef struct _GimpChoiceDesc
gchar *label;
gchar *help;
gint id;
gboolean sensitive;
} GimpChoiceDesc;
enum
{
SENSITIVITY_CHANGED,
LAST_SIGNAL
};
struct _GimpChoice
{
GObject parent_instance;
@ -53,11 +60,22 @@ G_DEFINE_TYPE (GimpChoice, gimp_choice, G_TYPE_OBJECT)
#define parent_class gimp_choice_parent_class
static guint gimp_choice_signals[LAST_SIGNAL] = { 0 };
static void
gimp_choice_class_init (GimpChoiceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
gimp_choice_signals[SENSITIVITY_CHANGED] =
g_signal_new ("sensitivity-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0, /*G_STRUCT_OFFSET (GimpChoiceClass, sensitivity_changed),*/
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_STRING);
object_class->finalize = gimp_choice_finalize;
}
@ -177,6 +195,7 @@ gimp_choice_add (GimpChoice *choice,
desc->id = id;
desc->label = g_strdup (label);
desc->help = help != NULL ? g_strdup (help) : NULL;
desc->sensitive = TRUE;
g_hash_table_insert (choice->choices, g_strdup (nick), desc);
duplicate = g_list_find_custom (choice->keys, nick, (GCompareFunc) g_strcmp0);
@ -205,7 +224,13 @@ gboolean
gimp_choice_is_valid (GimpChoice *choice,
const gchar *nick)
{
return (g_hash_table_lookup (choice->choices, nick) != NULL);
GimpChoiceDesc *desc;
g_return_val_if_fail (GIMP_IS_CHOICE (choice), FALSE);
g_return_val_if_fail (nick != NULL, FALSE);
desc = g_hash_table_lookup (choice->choices, nick);
return (desc != NULL && desc->sensitive);
}
/**
@ -328,6 +353,39 @@ gimp_choice_get_documentation (GimpChoice *choice,
return FALSE;
}
/**
* gimp_choice_set_sensitive:
* @choice: the %GimpChoice.
* @nick: the nick to lookup.
*
* Change the sensitivity of a possible @nick. Technically a non-sensitive @nick
* means it cannot be chosen anymore (so [method@Gimp.Choice.is_valid] will
* return %FALSE; nevertheless [method@Gimp.Choice.list_nicks] and other
* functions to get information about a choice will still function).
*
* Returns: %TRUE if @nick is found, %FALSE otherwise.
*
* Since: 3.0
**/
void
gimp_choice_set_sensitive (GimpChoice *choice,
const gchar *nick,
gboolean sensitive)
{
GimpChoiceDesc *desc;
g_return_if_fail (GIMP_IS_CHOICE (choice));
g_return_if_fail (nick != NULL);
desc = g_hash_table_lookup (choice->choices, nick);
g_return_if_fail (desc != NULL);
if (desc->sensitive != sensitive)
{
desc->sensitive = sensitive;
g_signal_emit (choice, gimp_choice_signals[SENSITIVITY_CHANGED], 0, nick);
}
}
/* Private functions */

View File

@ -63,6 +63,10 @@ gboolean gimp_choice_get_documentation (GimpChoice *choice,
const gchar **label,
const gchar **help);
void gimp_choice_set_sensitive (GimpChoice *choice,
const gchar *nick,
gboolean sensitive);
G_END_DECLS

View File

@ -31,6 +31,8 @@
static void gimp_param_choice_class_init (GParamSpecClass *klass);
static void gimp_param_choice_init (GParamSpec *pspec);
static void gimp_param_choice_value_set_default (GParamSpec *pspec,
GValue *value);
static void gimp_param_choice_finalize (GParamSpec *pspec);
static gboolean gimp_param_choice_validate (GParamSpec *pspec,
GValue *value);
@ -67,6 +69,7 @@ static void
gimp_param_choice_class_init (GParamSpecClass *klass)
{
klass->value_type = G_TYPE_STRING;
klass->value_set_default = gimp_param_choice_value_set_default;
klass->finalize = gimp_param_choice_finalize;
klass->value_validate = gimp_param_choice_validate;
klass->values_cmp = gimp_param_choice_values_cmp;
@ -81,6 +84,15 @@ gimp_param_choice_init (GParamSpec *pspec)
choice->default_value = NULL;
}
static void
gimp_param_choice_value_set_default (GParamSpec *pspec,
GValue *value)
{
GimpParamSpecChoice *cspec = GIMP_PARAM_SPEC_CHOICE (pspec);
g_value_set_string (value, cspec->default_value);
}
static void
gimp_param_choice_finalize (GParamSpec *pspec)
{
@ -99,8 +111,26 @@ gimp_param_choice_validate (GParamSpec *pspec,
const gchar *strval = g_value_get_string (value);
if (! gimp_choice_is_valid (choice, strval))
{
if (gimp_choice_is_valid (choice, spec_choice->default_value))
{
g_value_set_string (value, spec_choice->default_value);
}
else
{
/* This might happen if the default value is set insensitive. Then we
* should just set any valid random nick.
*/
GList *nicks;
nicks = gimp_choice_list_nicks (choice);
for (GList *iter = nicks; iter; iter = iter->next)
if (gimp_choice_is_valid (choice, (gchar *) iter->data))
{
g_value_set_string (value, (gchar *) iter->data);
break;
}
}
return TRUE;
}

View File

@ -2543,6 +2543,11 @@ static void gimp_prop_string_combo_box_notify (GObject *config,
GParamSpec *param_spec,
GtkWidget *widget);
static gboolean
gimp_prop_choice_combo_box_is_sensitive (const gchar *nick,
GimpChoice *choice);
/**
* gimp_prop_string_combo_box_new:
* @config: Object to which property is attached.
@ -2624,7 +2629,6 @@ gimp_prop_choice_combo_box_new (GObject *config,
GimpParamSpecChoice *cspec;
GtkWidget *combo_box;
GtkListStore *store;
gchar *value;
GList *values;
GList *iter;
@ -2655,27 +2659,24 @@ gimp_prop_choice_combo_box_new (GObject *config,
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),
gimp_string_combo_box_set_sensitivity (GIMP_STRING_COMBO_BOX (combo_box),
(GimpStringSensitivityFunc) gimp_prop_choice_combo_box_is_sensitive,
cspec->choice, NULL);
g_signal_connect_swapped (cspec->choice, "sensitivity-changed",
G_CALLBACK (gtk_widget_queue_draw),
combo_box);
g_object_bind_property (config, property_name,
combo_box, "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
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)
@ -2723,6 +2724,13 @@ gimp_prop_string_combo_box_notify (GObject *config,
g_free (value);
}
static gboolean
gimp_prop_choice_combo_box_is_sensitive (const gchar *nick,
GimpChoice *choice)
{
return gimp_choice_is_valid (choice, nick);
}
/*************************/
/* file chooser button */

View File

@ -56,6 +56,10 @@ struct _GimpStringComboBoxPrivate
gint id_column;
gint label_column;
GtkCellRenderer *text_renderer;
GimpStringSensitivityFunc sensitivity_func;
gpointer sensitivity_data;
GDestroyNotify sensitivity_destroy;
};
#define GET_PRIVATE(obj) (((GimpStringComboBox *) (obj))->priv)
@ -71,6 +75,12 @@ static void gimp_string_combo_box_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gimp_string_combo_box_cell_data_func (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
GimpStringComboBoxPrivate *priv);
G_DEFINE_TYPE_WITH_PRIVATE (GimpStringComboBox, gimp_string_combo_box,
GTK_TYPE_COMBO_BOX)
@ -153,7 +163,8 @@ gimp_string_combo_box_class_init (GimpStringComboBoxClass *klass)
"Value",
"Value of active item",
NULL,
GIMP_PARAM_READWRITE));
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY));
}
static void
@ -176,6 +187,16 @@ gimp_string_combo_box_constructed (GObject *object)
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell,
"text", priv->label_column,
NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (object), priv->text_renderer,
(GtkCellLayoutDataFunc) gimp_string_combo_box_cell_data_func,
priv, NULL);
/* The "changed" signal of the GtkComboBox also triggers a "value" property
* notification.
*/
g_signal_connect (object, "changed",
G_CALLBACK (g_object_notify),
"value");
}
static void
@ -345,6 +366,7 @@ gimp_string_combo_box_set_active (GimpStringComboBox *combo_box,
if (gimp_string_model_lookup (model, column, id, &iter))
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
g_object_notify (G_OBJECT (combo_box), "value");
return TRUE;
}
@ -353,6 +375,7 @@ gimp_string_combo_box_set_active (GimpStringComboBox *combo_box,
else
{
gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
g_object_notify (G_OBJECT (combo_box), "value");
return TRUE;
}
@ -392,3 +415,74 @@ gimp_string_combo_box_get_active (GimpStringComboBox *combo_box)
return NULL;
}
/**
* gimp_string_combo_box_set_sensitivity:
* @combo_box: a #GimpStringComboBox
* @func: a function that returns a boolean value, or %NULL to unset
* @data: data to pass to @func
* @destroy: destroy notification for @data
*
* Sets a function that is used to decide about the sensitivity of
* rows in the @combo_box. Use this if you want to set certain rows
* insensitive.
*
* Calling gtk_widget_queue_draw() on the @combo_box will cause the
* sensitivity to be updated.
*
* Since: 3.0
**/
void
gimp_string_combo_box_set_sensitivity (GimpStringComboBox *combo_box,
GimpStringSensitivityFunc func,
gpointer data,
GDestroyNotify destroy)
{
GimpStringComboBoxPrivate *priv;
g_return_if_fail (GIMP_IS_STRING_COMBO_BOX (combo_box));
priv = GET_PRIVATE (combo_box);
if (priv->sensitivity_destroy)
{
GDestroyNotify d = priv->sensitivity_destroy;
priv->sensitivity_destroy = NULL;
d (priv->sensitivity_data);
}
priv->sensitivity_func = func;
priv->sensitivity_data = data;
priv->sensitivity_destroy = destroy;
gtk_widget_queue_draw (GTK_WIDGET (combo_box));
}
/* Private functions. */
static void
gimp_string_combo_box_cell_data_func (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
GimpStringComboBoxPrivate *priv)
{
if (priv->sensitivity_func)
{
gchar *id;
gboolean sensitive;
gtk_tree_model_get (tree_model, iter,
priv->id_column, &id,
-1);
sensitive = priv->sensitivity_func (id, priv->sensitivity_data);
g_object_set (cell,
"sensitive", sensitive,
NULL);
g_free (id);
}
}

View File

@ -29,6 +29,15 @@
G_BEGIN_DECLS
/**
* GimpStringSensitivityFunc:
* @id: the string value from the column @id_column as passed to [ctor@StringComboBox.new].
* @data: (closure): the data passed in [method@StringComboBox.set_sensitivity].
*/
typedef gboolean (* GimpStringSensitivityFunc) (const gchar *id,
gpointer data);
#define GIMP_TYPE_STRING_COMBO_BOX (gimp_string_combo_box_get_type ())
#define GIMP_STRING_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_STRING_COMBO_BOX, GimpStringComboBox))
#define GIMP_STRING_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_STRING_COMBO_BOX, GimpStringComboBoxClass))
@ -72,6 +81,10 @@ gboolean gimp_string_combo_box_set_active (GimpStringComboBox *combo_box,
const gchar *id);
gchar * gimp_string_combo_box_get_active (GimpStringComboBox *combo_box);
void gimp_string_combo_box_set_sensitivity (GimpStringComboBox *combo_box,
GimpStringSensitivityFunc func,
gpointer data,
GDestroyNotify destroy);
G_END_DECLS

View File

@ -472,6 +472,7 @@ EXPORTS
gimp_string_combo_box_get_type
gimp_string_combo_box_new
gimp_string_combo_box_set_active
gimp_string_combo_box_set_sensitivity
gimp_toggle_button_update
gimp_uint_adjustment_update
gimp_unit_combo_box_get_active