Home | History | Annotate | Download | only in gtk
      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