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