blob: 46e5cf721e9b3c99902f1cfb75ae9801a7688310 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts
// to support this. GtkCustomMenuItems only render properly when put in a
// GtkCustomMenu; there's a lot of collaboration between these two classes
// necessary to work around how gtk normally does menus.
//
// We can't rely on the normal event infrastructure. While a menu is up, the
// GtkMenu has a grab on all events. Instead of trying to pump events through
// the normal channels, we have the GtkCustomMenu selectively forward mouse
// motion through a back channel. The GtkCustomMenu only listens for button
// press information so it can block the effects of the click if the cursor
// isn't in a button in the menu item.
//
// A GtkCustomMenuItem doesn't try to take these signals and forward them to
// the buttons it owns. The GtkCustomMenu class keeps track of which button is
// selected (due to key events and mouse movement) and otherwise acts like a
// normal GtkItem. The buttons are only for sizing and rendering; they don't
// respond to events. Instead, when the GtkCustomMenuItem is activated by the
// GtkMenu, it uses which button was selected as a signal of what to do.
//
// Users should connect to the "button-pushed" signal to be notified when a
// button was pushed. We don't go through the normal "activate" signal because
// we need to communicate additional information, namely which button was
// activated.
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTK_TYPE_CUSTOM_MENU_ITEM \
(gtk_custom_menu_item_get_type())
#define GTK_CUSTOM_MENU_ITEM(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
GtkCustomMenuItem))
#define GTK_CUSTOM_MENU_ITEM_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM, \
GtkCustomMenuItemClass))
#define GTK_IS_CUSTOM_MENU_ITEM(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM))
#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM))
#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
GtkCustomMenuItemClass))
typedef struct _GtkCustomMenuItem GtkCustomMenuItem;
typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass;
struct _GtkCustomMenuItem {
GtkMenuItem menu_item;
// Container for button widgets.
GtkWidget* hbox;
// Label on left side of menu item.
GtkWidget* label;
// List of all widgets we added. Used to find the leftmost and rightmost
// continuous buttons.
GList* all_widgets;
// Possible button widgets. Used for keyboard navigation.
GList* button_widgets;
// The widget that currently has highlight.
GtkWidget* currently_selected_button;
// The widget that was selected *before* |currently_selected_button|. Why do
// we hang on to this? Because the menu system sends us a deselect signal
// right before activating us. We need to listen to deselect since that's
// what we receive when the mouse cursor leaves us entirely.
GtkWidget* previously_selected_button;
};
struct _GtkCustomMenuItemClass {
GtkMenuItemClass parent_class;
};
GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST;
GtkWidget* gtk_custom_menu_item_new(const char* title);
// Adds a button to our list of items in the |hbox|.
GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
int command_id);
// Adds a button to our list of items in the |hbox|, but that isn't part of
// |button_widgets| to prevent it from being activatable.
GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
int command_id);
// Adds a vertical space in the |hbox|.
void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item);
// Receives a motion event from the GtkCustomMenu that contains us. We can't
// just subscribe to motion-event or the individual widget enter/leave events
// because the top level GtkMenu has an event grab.
void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
gdouble x, gdouble y);
// Notification that the menu got a cursor key event. Used to move up/down
// within the menu buttons. Returns TRUE to stop the default signal handler
// from running.
gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
GtkMenuDirectionType direction);
// Because we only get a generic "selected" signal when we've changed, we need
// to have a way for the GtkCustomMenu to tell us that we were just
// selected.
void gtk_custom_menu_item_select_item_by_direction(
GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction);
// Whether we are currently hovering over a clickable region on the menu
// item. Used by GtkCustomMenu to determine whether it should discard click
// events.
gboolean gtk_custom_menu_item_is_in_clickable_region(
GtkCustomMenuItem* menu_item);
// If the button is released while the |currently_selected_button| isn't
// supposed to dismiss the menu, this signals to our listeners that we want to
// run this command if it doesn't dismiss the menu. Returns TRUE if we acted
// on this button click (and should prevent the normal GtkMenu machinery from
// firing an "activate" signal).
gboolean gtk_custom_menu_item_try_no_dismiss_command(
GtkCustomMenuItem* menu_item);
// Calls |callback| with every button and button-label in the container.
void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
GtkCallback callback,
gpointer callback_data);
G_END_DECLS
#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_