1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_ 6#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_ 7 8// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts 9// to support this. GtkCustomMenuItems only render properly when put in a 10// GtkCustomMenu; there's a lot of collaboration between these two classes 11// necessary to work around how gtk normally does menus. 12// 13// We can't rely on the normal event infrastructure. While a menu is up, the 14// GtkMenu has a grab on all events. Instead of trying to pump events through 15// the normal channels, we have the GtkCustomMenu selectively forward mouse 16// motion through a back channel. The GtkCustomMenu only listens for button 17// press information so it can block the effects of the click if the cursor 18// isn't in a button in the menu item. 19// 20// A GtkCustomMenuItem doesn't try to take these signals and forward them to 21// the buttons it owns. The GtkCustomMenu class keeps track of which button is 22// selected (due to key events and mouse movement) and otherwise acts like a 23// normal GtkItem. The buttons are only for sizing and rendering; they don't 24// respond to events. Instead, when the GtkCustomMenuItem is activated by the 25// GtkMenu, it uses which button was selected as a signal of what to do. 26// 27// Users should connect to the "button-pushed" signal to be notified when a 28// button was pushed. We don't go through the normal "activate" signal because 29// we need to communicate additional information, namely which button was 30// activated. 31 32#include <gtk/gtk.h> 33 34G_BEGIN_DECLS 35 36#define GTK_TYPE_CUSTOM_MENU_ITEM \ 37 (gtk_custom_menu_item_get_type()) 38#define GTK_CUSTOM_MENU_ITEM(obj) \ 39 (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \ 40 GtkCustomMenuItem)) 41#define GTK_CUSTOM_MENU_ITEM_CLASS(klass) \ 42 (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM, \ 43 GtkCustomMenuItemClass)) 44#define GTK_IS_CUSTOM_MENU_ITEM(obj) \ 45 (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM)) 46#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass) \ 47 (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM)) 48#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj) \ 49 (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \ 50 GtkCustomMenuItemClass)) 51 52typedef struct _GtkCustomMenuItem GtkCustomMenuItem; 53typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass; 54 55struct _GtkCustomMenuItem { 56 GtkMenuItem menu_item; 57 58 // Container for button widgets. 59 GtkWidget* hbox; 60 61 // Label on left side of menu item. 62 GtkWidget* label; 63 64 // List of all widgets we added. Used to find the leftmost and rightmost 65 // continuous buttons. 66 GList* all_widgets; 67 68 // Possible button widgets. Used for keyboard navigation. 69 GList* button_widgets; 70 71 // The widget that currently has highlight. 72 GtkWidget* currently_selected_button; 73 74 // The widget that was selected *before* |currently_selected_button|. Why do 75 // we hang on to this? Because the menu system sends us a deselect signal 76 // right before activating us. We need to listen to deselect since that's 77 // what we receive when the mouse cursor leaves us entirely. 78 GtkWidget* previously_selected_button; 79}; 80 81struct _GtkCustomMenuItemClass { 82 GtkMenuItemClass parent_class; 83}; 84 85GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST; 86GtkWidget* gtk_custom_menu_item_new(const char* title); 87 88// Adds a button to our list of items in the |hbox|. 89GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item, 90 int command_id); 91 92// Adds a button to our list of items in the |hbox|, but that isn't part of 93// |button_widgets| to prevent it from being activatable. 94GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item, 95 int command_id); 96 97// Adds a vertical space in the |hbox|. 98void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item); 99 100// Receives a motion event from the GtkCustomMenu that contains us. We can't 101// just subscribe to motion-event or the individual widget enter/leave events 102// because the top level GtkMenu has an event grab. 103void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item, 104 gdouble x, gdouble y); 105 106// Notification that the menu got a cursor key event. Used to move up/down 107// within the menu buttons. Returns TRUE to stop the default signal handler 108// from running. 109gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item, 110 GtkMenuDirectionType direction); 111 112// Because we only get a generic "selected" signal when we've changed, we need 113// to have a way for the GtkCustomMenu to tell us that we were just 114// selected. 115void gtk_custom_menu_item_select_item_by_direction( 116 GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction); 117 118// Whether we are currently hovering over a clickable region on the menu 119// item. Used by GtkCustomMenu to determine whether it should discard click 120// events. 121gboolean gtk_custom_menu_item_is_in_clickable_region( 122 GtkCustomMenuItem* menu_item); 123 124// If the button is released while the |currently_selected_button| isn't 125// supposed to dismiss the menu, this signals to our listeners that we want to 126// run this command if it doesn't dismiss the menu. Returns TRUE if we acted 127// on this button click (and should prevent the normal GtkMenu machinery from 128// firing an "activate" signal). 129gboolean gtk_custom_menu_item_try_no_dismiss_command( 130 GtkCustomMenuItem* menu_item); 131 132// Calls |callback| with every button and button-label in the container. 133void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item, 134 GtkCallback callback, 135 gpointer callback_data); 136 137G_END_DECLS 138 139#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_ 140