GtkMenuTracker: become a proper GObject

Turn GtkMenuTracker into a GObject with "insert" and "remove" signals
for the sake of accessibility from language bindings.
This commit is contained in:
Ryan Lortie
2013-05-08 10:20:34 -04:00
parent 81845aded9
commit a96fc95d6d
3 changed files with 113 additions and 26 deletions

View File

@@ -533,7 +533,7 @@ gtk_menu_shell_dispose (GObject *object)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
g_clear_pointer (&menu_shell->priv->tracker, gtk_menu_tracker_free);
g_clear_object (&menu_shell->priv->tracker);
gtk_menu_shell_deactivate (menu_shell);
G_OBJECT_CLASS (gtk_menu_shell_parent_class)->dispose (object);
@@ -2225,7 +2225,7 @@ gtk_menu_shell_bind_model (GtkMenuShell *menu_shell,
muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (menu_shell));
g_clear_pointer (&menu_shell->priv->tracker, gtk_menu_tracker_free);
g_clear_object (&menu_shell->priv->tracker);
while (menu_shell->priv->children)
gtk_container_remove (GTK_CONTAINER (menu_shell), menu_shell->priv->children->data);

View File

@@ -27,6 +27,8 @@ typedef struct _GtkMenuTrackerSection GtkMenuTrackerSection;
struct _GtkMenuTracker
{
GObject parent_instance;
GActionObservable *observable;
GtkMenuTrackerInsertFunc insert_func;
GtkMenuTrackerRemoveFunc remove_func;
@@ -35,6 +37,8 @@ struct _GtkMenuTracker
GtkMenuTrackerSection *toplevel;
};
typedef GObjectClass GtkMenuTrackerClass;
struct _GtkMenuTrackerSection
{
GMenuModel *model;
@@ -47,6 +51,8 @@ struct _GtkMenuTrackerSection
gulong handler;
};
G_DEFINE_TYPE (GtkMenuTracker, gtk_menu_tracker, G_TYPE_OBJECT)
static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker,
GMenuModel *model,
gboolean with_separators,
@@ -54,6 +60,38 @@ static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker
const gchar *action_namespace);
static void gtk_menu_tracker_section_free (GtkMenuTrackerSection *section);
static guint insert_signal, remove_signal;
static void
gtk_menu_tracker_emit_insert (GtkMenuTracker *tracker,
GMenuModel *model,
gint model_index,
const gchar *action_namespace,
gboolean is_separator,
gint offset)
{
GtkMenuTrackerItem *item;
item = gtk_menu_tracker_item_new (tracker->observable, model, model_index, action_namespace, is_separator);
if (tracker->insert_func)
(* tracker->insert_func) (item, offset, tracker->user_data);
else
g_signal_emit (tracker, insert_signal, 0, item, offset);
g_object_unref (item);
}
static void
gtk_menu_tracker_emit_remove (GtkMenuTracker *tracker,
gint offset)
{
if (tracker->remove_func)
(* tracker->remove_func) (offset, tracker->user_data);
else
g_signal_emit (tracker, remove_signal, 0, offset);
}
static GtkMenuTrackerSection *
gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section,
GMenuModel *model,
@@ -159,19 +197,13 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section,
if (should_have_separator > section->has_separator)
{
/* Add a separator */
GtkMenuTrackerItem *item;
item = gtk_menu_tracker_item_new (tracker->observable, parent_model, parent_index, NULL, TRUE);
(* tracker->insert_func) (item, offset, tracker->user_data);
g_object_unref (item);
gtk_menu_tracker_emit_insert (tracker, parent_model, parent_index, NULL, TRUE, offset);
section->has_separator = TRUE;
}
else if (should_have_separator < section->has_separator)
{
/* Remove a separator */
(* tracker->remove_func) (offset, tracker->user_data);
gtk_menu_tracker_emit_remove (tracker, offset);
section->has_separator = FALSE;
}
@@ -220,7 +252,7 @@ gtk_menu_tracker_remove_items (GtkMenuTracker *tracker,
gtk_menu_tracker_section_free (subsection);
while (n--)
(* tracker->remove_func) (offset, tracker->user_data);
gtk_menu_tracker_emit_remove (tracker, offset);
}
}
@@ -264,13 +296,7 @@ gtk_menu_tracker_add_items (GtkMenuTracker *tracker,
}
else
{
GtkMenuTrackerItem *item;
item = gtk_menu_tracker_item_new (tracker->observable, model, position + n_items,
section->action_namespace, FALSE);
(* tracker->insert_func) (item, offset, tracker->user_data);
g_object_unref (item);
gtk_menu_tracker_emit_insert (tracker, model, position + n_items, section->action_namespace, FALSE, offset);
*change_point = g_slist_prepend (*change_point, NULL);
}
}
@@ -359,6 +385,33 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker,
return section;
}
static void
gtk_menu_tracker_finalize (GObject *object)
{
GtkMenuTracker *tracker = GTK_MENU_TRACKER (object);
gtk_menu_tracker_section_free (tracker->toplevel);
g_object_unref (tracker->observable);
G_OBJECT_CLASS (gtk_menu_tracker_parent_class)->finalize (object);
}
static void
gtk_menu_tracker_init (GtkMenuTracker *tracker)
{
}
static void
gtk_menu_tracker_class_init (GtkMenuTrackerClass *class)
{
class->finalize = gtk_menu_tracker_finalize;
insert_signal = g_signal_new ("insert", GTK_TYPE_MENU_TRACKER, G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 2, GTK_TYPE_MENU_TRACKER_ITEM, G_TYPE_INT);
remove_signal = g_signal_new ("remove", GTK_TYPE_MENU_TRACKER, G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
}
/*< private >
* gtk_menu_tracker_new:
* @model: the model to flatten
@@ -409,6 +462,10 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker,
* When using #GtkMenuTracker there is no need to hold onto @model or
* monitor it for changes. The model will be unreffed when
* gtk_menu_tracker_free() is called.
*
* As a bindings-friendly alternative to using this function, you can
* create the tracker with g_object_new(), connect to the "insert" and
* "remove" signals and then call gtk_menu_tracker_setup().
*/
GtkMenuTracker *
gtk_menu_tracker_new (GActionObservable *observable,
@@ -421,7 +478,7 @@ gtk_menu_tracker_new (GActionObservable *observable,
{
GtkMenuTracker *tracker;
tracker = g_slice_new (GtkMenuTracker);
tracker = g_object_new (GTK_TYPE_MENU_TRACKER, NULL);
tracker->observable = g_object_ref (observable);
tracker->insert_func = insert_func;
tracker->remove_func = remove_func;
@@ -434,15 +491,32 @@ gtk_menu_tracker_new (GActionObservable *observable,
}
/*< private >
* gtk_menu_tracker_free:
* gtk_menu_tracker_setup:
* @tracker: a #GtkMenuTracker
* @observable: the #GActionObservable to use
* @model: the model to flatten
* @with_separators: if the toplevel should have separators (ie: TRUE
* for menus, FALSE for menubars)
* @action_namespace: the passed-in action namespace
*
* Frees the tracker, ...
* Sets up the tracker.
*
* This will typically cause many 'insert' signals to be emitted.
*
* You can only call this once and you may not call this after using
* gtk_menu_tracker_new().
*/
void
gtk_menu_tracker_free (GtkMenuTracker *tracker)
gtk_menu_tracker_setup (GtkMenuTracker *tracker,
GActionObservable *observable,
GMenuModel *model,
gboolean with_separators,
const gchar *action_namespace)
{
gtk_menu_tracker_section_free (tracker->toplevel);
g_object_unref (tracker->observable);
g_slice_free (GtkMenuTracker, tracker);
g_return_if_fail (GTK_IS_MENU_TRACKER (tracker));
g_return_if_fail (tracker->toplevel == NULL);
tracker->observable = g_object_ref (observable);
tracker->toplevel = gtk_menu_tracker_section_new (tracker, model, with_separators, 0, action_namespace);
gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0);
}

View File

@@ -24,8 +24,17 @@
#include <gtk/gtkmenutrackeritem.h>
#define GTK_TYPE_MENU_TRACKER (gtk_menu_tracker_get_type ())
#define GTK_MENU_TRACKER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
GTK_TYPE_MENU_TRACKER, GtkMenuTracker))
#define GTK_IS_MENU_TRACKER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
GTK_TYPE_MENU_TRACKER))
typedef struct _GtkMenuTracker GtkMenuTracker;
G_GNUC_INTERNAL
GType gtk_menu_tracker_get_type (void) G_GNUC_CONST;
typedef void (* GtkMenuTrackerInsertFunc) (GtkMenuTrackerItem *item,
gint position,
gpointer user_data);
@@ -44,6 +53,10 @@ GtkMenuTracker * gtk_menu_tracker_new (GActionObservable
gpointer user_data);
G_GNUC_INTERNAL
void gtk_menu_tracker_free (GtkMenuTracker *tracker);
void gtk_menu_tracker_setup (GtkMenuTracker *tracker,
GActionObservable *observer,
GMenuModel *model,
gboolean with_separators,
const gchar *action_namespace);
#endif /* __GTK_MENU_TRACKER_H__ */