mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-09 18:30:08 +00:00
app-chooser-button: change the API approach for custom items
Introduce a 'custom-item-activated' on the widget, which behaves similairly to GtkEntryCompletion::action-activated, i.e. is emitted when a custom item is chosen from the dropdown list. Clients can use the name provided when adding the item as a detail for the signal, to get notified when that specific item is activated, or use the signal without details to get notifications for all custom items.
This commit is contained in:
parent
8ae7993397
commit
232a5c7b3b
@ -34,59 +34,36 @@
|
||||
#include "gtkcombobox.h"
|
||||
#include "gtkdialog.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkmarshalers.h"
|
||||
|
||||
enum {
|
||||
PROP_CONTENT_TYPE = 1,
|
||||
PROP_SHOW_DIALOG_ITEM,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_CUSTOM_ITEM_ACTIVATED,
|
||||
NUM_SIGNALS
|
||||
};
|
||||
|
||||
enum {
|
||||
COLUMN_APP_INFO,
|
||||
COLUMN_NAME,
|
||||
COLUMN_LABEL,
|
||||
COLUMN_ICON,
|
||||
COLUMN_CUSTOM,
|
||||
COLUMN_SEPARATOR,
|
||||
COLUMN_CALLBACK,
|
||||
NUM_COLUMNS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
GtkAppChooserButtonItemFunc func;
|
||||
gpointer user_data;
|
||||
} CustomAppComboData;
|
||||
|
||||
static gpointer
|
||||
custom_app_data_copy (gpointer boxed)
|
||||
{
|
||||
CustomAppComboData *retval, *original;
|
||||
|
||||
original = boxed;
|
||||
|
||||
retval = g_slice_new0 (CustomAppComboData);
|
||||
retval->func = original->func;
|
||||
retval->user_data = original->user_data;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
custom_app_data_free (gpointer boxed)
|
||||
{
|
||||
g_slice_free (CustomAppComboData, boxed);
|
||||
}
|
||||
|
||||
#define CUSTOM_COMBO_DATA_TYPE custom_app_combo_data_get_type()
|
||||
G_DEFINE_BOXED_TYPE (CustomAppComboData, custom_app_combo_data,
|
||||
custom_app_data_copy,
|
||||
custom_app_data_free);
|
||||
#define CUSTOM_ITEM_OTHER_APP "gtk-internal-item-other-app"
|
||||
|
||||
static void app_chooser_iface_init (GtkAppChooserIface *iface);
|
||||
|
||||
static void real_insert_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *name,
|
||||
const gchar *label,
|
||||
GIcon *icon,
|
||||
GtkAppChooserButtonItemFunc func,
|
||||
gpointer user_data,
|
||||
gboolean custom,
|
||||
GtkTreeIter *iter);
|
||||
|
||||
@ -94,6 +71,8 @@ static void real_insert_separator (GtkAppChooserButton *self,
|
||||
gboolean custom,
|
||||
GtkTreeIter *iter);
|
||||
|
||||
static guint signals[NUM_SIGNALS] = { 0, };
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkAppChooserButton, gtk_app_chooser_button, GTK_TYPE_COMBO_BOX,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER,
|
||||
app_chooser_iface_init));
|
||||
@ -103,6 +82,8 @@ struct _GtkAppChooserButtonPrivate {
|
||||
|
||||
gchar *content_type;
|
||||
gboolean show_dialog_item;
|
||||
|
||||
GHashTable *custom_item_names;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
@ -226,8 +207,7 @@ other_application_dialog_response_cb (GtkDialog *dialog,
|
||||
}
|
||||
|
||||
static void
|
||||
other_application_item_activated_cb (GtkAppChooserButton *self,
|
||||
gpointer _user_data)
|
||||
other_application_item_activated_cb (GtkAppChooserButton *self)
|
||||
{
|
||||
GtkWidget *dialog, *widget;
|
||||
GtkWindow *toplevel;
|
||||
@ -263,10 +243,9 @@ gtk_app_chooser_button_ensure_dialog_item (GtkAppChooserButton *self,
|
||||
*prev_iter = iter;
|
||||
|
||||
gtk_list_store_insert_after (self->priv->store, &iter, prev_iter);
|
||||
real_insert_custom_item (self,
|
||||
real_insert_custom_item (self, CUSTOM_ITEM_OTHER_APP,
|
||||
_("Other application..."), icon,
|
||||
other_application_item_activated_cb,
|
||||
NULL, FALSE, &iter);
|
||||
FALSE, &iter);
|
||||
|
||||
g_object_unref (icon);
|
||||
}
|
||||
@ -307,7 +286,7 @@ gtk_app_chooser_button_populate (GtkAppChooserButton *self)
|
||||
|
||||
gtk_list_store_set (self->priv->store, &iter,
|
||||
COLUMN_APP_INFO, app,
|
||||
COLUMN_NAME, g_app_info_get_display_name (app),
|
||||
COLUMN_LABEL, g_app_info_get_display_name (app),
|
||||
COLUMN_ICON, icon,
|
||||
COLUMN_CUSTOM, FALSE,
|
||||
-1);
|
||||
@ -326,11 +305,11 @@ gtk_app_chooser_button_build_ui (GtkAppChooserButton *self)
|
||||
|
||||
self->priv->store = gtk_list_store_new (NUM_COLUMNS,
|
||||
G_TYPE_APP_INFO,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING, /* name */
|
||||
G_TYPE_STRING, /* label */
|
||||
G_TYPE_ICON,
|
||||
G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN,
|
||||
CUSTOM_COMBO_DATA_TYPE);
|
||||
G_TYPE_BOOLEAN, /* separator */
|
||||
G_TYPE_BOOLEAN); /* custom */
|
||||
|
||||
gtk_combo_box_set_model (GTK_COMBO_BOX (self),
|
||||
GTK_TREE_MODEL (self->priv->store));
|
||||
@ -347,9 +326,11 @@ gtk_app_chooser_button_build_ui (GtkAppChooserButton *self)
|
||||
cell = gtk_cell_renderer_text_new ();
|
||||
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, TRUE);
|
||||
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), cell,
|
||||
"text", COLUMN_NAME,
|
||||
"xpad", 6,
|
||||
"text", COLUMN_LABEL,
|
||||
NULL);
|
||||
g_object_set (cell,
|
||||
"xpad", 6,
|
||||
NULL);
|
||||
|
||||
gtk_app_chooser_button_populate (self);
|
||||
}
|
||||
@ -382,17 +363,33 @@ gtk_app_chooser_button_changed (GtkComboBox *object)
|
||||
{
|
||||
GtkAppChooserButton *self = GTK_APP_CHOOSER_BUTTON (object);
|
||||
GtkTreeIter iter;
|
||||
CustomAppComboData *custom_data = NULL;
|
||||
gchar *name = NULL;
|
||||
gboolean custom;
|
||||
GQuark name_quark;
|
||||
|
||||
if (!gtk_combo_box_get_active_iter (object, &iter))
|
||||
return;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (self->priv->store), &iter,
|
||||
COLUMN_CALLBACK, &custom_data,
|
||||
COLUMN_NAME, &name,
|
||||
COLUMN_CUSTOM, &custom,
|
||||
-1);
|
||||
|
||||
if (custom_data != NULL && custom_data->func != NULL)
|
||||
custom_data->func (self, custom_data->user_data);
|
||||
if (name != NULL)
|
||||
{
|
||||
if (custom)
|
||||
{
|
||||
name_quark = g_quark_from_string (name);
|
||||
g_signal_emit (self, signals[SIGNAL_CUSTOM_ITEM_ACTIVATED], name_quark, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* trigger the dialog internally */
|
||||
other_application_item_activated_cb (self);
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -483,6 +480,7 @@ gtk_app_chooser_button_finalize (GObject *obj)
|
||||
{
|
||||
GtkAppChooserButton *self = GTK_APP_CHOOSER_BUTTON (obj);
|
||||
|
||||
g_hash_table_destroy (self->priv->custom_item_names);
|
||||
g_free (self->priv->content_type);
|
||||
|
||||
G_OBJECT_CLASS (gtk_app_chooser_button_parent_class)->finalize (obj);
|
||||
@ -524,6 +522,16 @@ gtk_app_chooser_button_class_init (GtkAppChooserButtonClass *klass)
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
|
||||
g_object_class_install_property (oclass, PROP_SHOW_DIALOG_ITEM, pspec);
|
||||
|
||||
signals[SIGNAL_CUSTOM_ITEM_ACTIVATED] =
|
||||
g_signal_new ("custom-item-activated",
|
||||
GTK_TYPE_APP_CHOOSER_BUTTON,
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
|
||||
G_STRUCT_OFFSET (GtkAppChooserButtonClass, custom_item_activated),
|
||||
NULL, NULL,
|
||||
_gtk_marshal_VOID__STRING,
|
||||
G_TYPE_NONE,
|
||||
1, G_TYPE_STRING);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GtkAppChooserButtonPrivate));
|
||||
}
|
||||
|
||||
@ -532,27 +540,37 @@ gtk_app_chooser_button_init (GtkAppChooserButton *self)
|
||||
{
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_APP_CHOOSER_BUTTON,
|
||||
GtkAppChooserButtonPrivate);
|
||||
self->priv->custom_item_names =
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
real_insert_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *name,
|
||||
const gchar *label,
|
||||
GIcon *icon,
|
||||
GtkAppChooserButtonItemFunc func,
|
||||
gpointer user_data,
|
||||
gboolean custom,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
CustomAppComboData *data;
|
||||
if (custom)
|
||||
{
|
||||
if (g_hash_table_lookup (self->priv->custom_item_names,
|
||||
name) != NULL)
|
||||
{
|
||||
g_warning ("Attempting to add custom item %s to GtkAppChooserButton, "
|
||||
"when there's already an item with the same name", name);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_slice_new0 (CustomAppComboData);
|
||||
data->func = func;
|
||||
data->user_data = user_data;
|
||||
g_hash_table_insert (self->priv->custom_item_names,
|
||||
g_strdup (name), GINT_TO_POINTER (1));
|
||||
}
|
||||
|
||||
gtk_list_store_set (self->priv->store, iter,
|
||||
COLUMN_NAME, label,
|
||||
COLUMN_NAME, name,
|
||||
COLUMN_LABEL, label,
|
||||
COLUMN_ICON, icon,
|
||||
COLUMN_CALLBACK, data,
|
||||
COLUMN_CUSTOM, custom,
|
||||
COLUMN_SEPARATOR, FALSE,
|
||||
-1);
|
||||
@ -613,30 +631,32 @@ gtk_app_chooser_button_append_separator (GtkAppChooserButton *self)
|
||||
/**
|
||||
* gtk_app_chooser_button_append_custom_item:
|
||||
* @self: a #GtkAppChooserButton
|
||||
* @name: the name of the custom item
|
||||
* @label: the label for the custom item
|
||||
* @icon: the icon for the custom item
|
||||
* @func: callback to call if the item is activated
|
||||
* @user_data: user data for @func
|
||||
*
|
||||
* Appends a custom item to the list of applications that is shown
|
||||
* in the popup. See also gtk_app_chooser_button_append_separator().
|
||||
* in the popup; the item name must be unique per-widget.
|
||||
* Clients can use the provided name as a detail for the ::custom-item-activated
|
||||
* signal, to add a callback for the activation of a particular
|
||||
* custom item in the list.
|
||||
* See also gtk_app_chooser_button_append_separator().
|
||||
*
|
||||
* Since: 3.0
|
||||
*/
|
||||
void
|
||||
gtk_app_chooser_button_append_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *label,
|
||||
GIcon *icon,
|
||||
GtkAppChooserButtonItemFunc func,
|
||||
gpointer user_data)
|
||||
gtk_app_chooser_button_append_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *name,
|
||||
const gchar *label,
|
||||
GIcon *icon)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_return_if_fail (GTK_IS_APP_CHOOSER_BUTTON (self));
|
||||
g_return_if_fail (name != NULL);
|
||||
|
||||
gtk_list_store_append (self->priv->store, &iter);
|
||||
real_insert_custom_item (self, label, icon,
|
||||
func, user_data, TRUE, &iter);
|
||||
real_insert_custom_item (self, name, label, icon, TRUE, &iter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,23 +55,25 @@ struct _GtkAppChooserButton {
|
||||
struct _GtkAppChooserButtonClass {
|
||||
GtkComboBoxClass parent_class;
|
||||
|
||||
void (* custom_item_activated) (GtkAppChooserButton *self,
|
||||
const gchar *item_name);
|
||||
|
||||
/* padding for future class expansion */
|
||||
gpointer padding[16];
|
||||
};
|
||||
|
||||
GType gtk_app_chooser_button_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_app_chooser_button_new (const gchar *content_type);
|
||||
GtkWidget * gtk_app_chooser_button_new (const gchar *content_type);
|
||||
|
||||
void gtk_app_chooser_button_append_separator (GtkAppChooserButton *self);
|
||||
void gtk_app_chooser_button_append_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *label,
|
||||
GIcon *icon,
|
||||
GtkAppChooserButtonItemFunc func,
|
||||
gpointer user_data);
|
||||
void gtk_app_chooser_button_append_separator (GtkAppChooserButton *self);
|
||||
void gtk_app_chooser_button_append_custom_item (GtkAppChooserButton *self,
|
||||
const gchar *name,
|
||||
const gchar *label,
|
||||
GIcon *icon);
|
||||
|
||||
void gtk_app_chooser_button_set_show_dialog_item (GtkAppChooserButton *self,
|
||||
gboolean setting);
|
||||
gboolean gtk_app_chooser_button_get_show_dialog_item (GtkAppChooserButton *self);
|
||||
void gtk_app_chooser_button_set_show_dialog_item (GtkAppChooserButton *self,
|
||||
gboolean setting);
|
||||
gboolean gtk_app_chooser_button_get_show_dialog_item (GtkAppChooserButton *self);
|
||||
|
||||
#endif /* __GTK_APP_CHOOSER_BUTTON_H__ */
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define CUSTOM_ITEM "custom-item"
|
||||
|
||||
static GtkWidget *toplevel, *combobox, *box;
|
||||
static GtkWidget *sel_image, *sel_name;
|
||||
|
||||
@ -45,6 +47,7 @@ combo_changed_cb (GtkComboBox *cb,
|
||||
|
||||
static void
|
||||
special_item_activated_cb (GtkAppChooserButton *b,
|
||||
const gchar *item_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (sel_image), g_themed_icon_new ("face-smile"),
|
||||
@ -52,6 +55,14 @@ special_item_activated_cb (GtkAppChooserButton *b,
|
||||
gtk_label_set_text (GTK_LABEL (sel_name), "Special Item");
|
||||
}
|
||||
|
||||
static void
|
||||
action_cb (GtkAppChooserButton *b,
|
||||
const gchar *item_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_print ("Activated custom item %s\n", item_name);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
@ -87,14 +98,27 @@ main (int argc,
|
||||
|
||||
gtk_app_chooser_button_append_separator (GTK_APP_CHOOSER_BUTTON (combobox));
|
||||
gtk_app_chooser_button_append_custom_item (GTK_APP_CHOOSER_BUTTON (combobox),
|
||||
CUSTOM_ITEM,
|
||||
"Hey, I'm special!",
|
||||
g_themed_icon_new ("face-smile"),
|
||||
special_item_activated_cb,
|
||||
NULL);
|
||||
g_themed_icon_new ("face-smile"));
|
||||
|
||||
/* this one will trigger a warning, and will not be added */
|
||||
gtk_app_chooser_button_append_custom_item (GTK_APP_CHOOSER_BUTTON (combobox),
|
||||
CUSTOM_ITEM,
|
||||
"Hey, I'm fake!",
|
||||
g_themed_icon_new ("face-evil"));
|
||||
|
||||
gtk_app_chooser_button_set_show_dialog_item (GTK_APP_CHOOSER_BUTTON (combobox),
|
||||
TRUE);
|
||||
|
||||
/* connect to the detailed signal */
|
||||
g_signal_connect (combobox, "custom-item-activated::" CUSTOM_ITEM,
|
||||
G_CALLBACK (special_item_activated_cb), NULL);
|
||||
|
||||
/* connect to the generic signal too */
|
||||
g_signal_connect (combobox, "custom-item-activated",
|
||||
G_CALLBACK (action_cb), NULL);
|
||||
|
||||
/* test refresh on a combo */
|
||||
gtk_app_chooser_refresh (GTK_APP_CHOOSER (combobox));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user