diff options
22 files changed, 437 insertions, 209 deletions
diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc index 5eba994..1eb8d63 100644 --- a/chrome/browser/views/bookmark_bar_view.cc +++ b/chrome/browser/views/bookmark_bar_view.cc @@ -38,6 +38,8 @@ #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/controls/button/menu_button.h" +#include "views/controls/label.h" +#include "views/controls/button/menu_button.h" #include "views/controls/menu/menu_item_view.h" #include "views/drag_utils.h" #include "views/view_constants.h" @@ -797,6 +799,71 @@ views::MenuItemView* BookmarkBarView::GetDropMenu() { return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL; } +const BookmarkNode* BookmarkBarView::GetNodeForButtonAt(const gfx::Point& loc, + int* start_index) { + *start_index = 0; + + if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height()) + return NULL; + + // Check the buttons first. + for (int i = 0; i < GetBookmarkButtonCount(); ++i) { + views::View* child = GetChildViewAt(i); + if (!child->IsVisible()) + break; + if (child->bounds().Contains(loc)) + return model_->GetBookmarkBarNode()->GetChild(i); + } + + // Then the overflow button. + if (overflow_button_->IsVisible() && + overflow_button_->bounds().Contains(loc)) { + *start_index = GetFirstHiddenNodeIndex(); + return model_->GetBookmarkBarNode(); + } + + // And finally the other folder. + if (other_bookmarked_button_->bounds().Contains(loc)) + return model_->other_node(); + + return NULL; +} + +views::MenuButton* BookmarkBarView::GetMenuButtonForNode( + const BookmarkNode* node) { + if (node == model_->other_node()) + return other_bookmarked_button_; + if (node == model_->GetBookmarkBarNode()) + return overflow_button_; + int index = model_->GetBookmarkBarNode()->IndexOfChild(node); + if (index == -1 || !node->is_folder()) + return NULL; + return static_cast<views::MenuButton*>(GetChildViewAt(index)); +} + +void BookmarkBarView::GetAnchorPositionAndStartIndexForButton( + views::MenuButton* button, + MenuItemView::AnchorPosition* anchor, + int* start_index) { + if (button == other_bookmarked_button_ || button == overflow_button_) + *anchor = MenuItemView::TOPRIGHT; + else + *anchor = MenuItemView::TOPLEFT; + + // Invert orientation if right to left. + if (UILayoutIsRightToLeft()) { + if (*anchor == MenuItemView::TOPRIGHT) + *anchor = MenuItemView::TOPLEFT; + else + *anchor = MenuItemView::TOPRIGHT; + } + + if (button == overflow_button_) + *start_index = GetFirstHiddenNodeIndex(); + else + *start_index = 0; +} + void BookmarkBarView::Init() { // Note that at this point we're not in a hierarchy so GetThemeProvider() will // return NULL. When we're inserted into a hierarchy, we'll call @@ -1072,55 +1139,24 @@ int BookmarkBarView::GetDragOperations(View* sender, int x, int y) { void BookmarkBarView::RunMenu(views::View* view, const gfx::Point& pt) { const BookmarkNode* node; - MenuItemView::AnchorPosition anchor_point = MenuItemView::TOPLEFT; - - // When we set the menu's position, we must take into account the mirrored - // position of the View relative to its parent. This can be easily done by - // passing the right flag to View::x(). - int x = view->GetX(APPLY_MIRRORING_TRANSFORMATION); - int bar_height = height() - kMenuOffset; - - if (IsDetached()) - bar_height -= kNewtabVerticalPadding; int start_index = 0; if (view == other_bookmarked_button_) { - UserMetrics::RecordAction(L"BookmarkBar_ShowOtherBookmarks", profile_); - node = model_->other_node(); - if (UILayoutIsRightToLeft()) - anchor_point = MenuItemView::TOPLEFT; - else - anchor_point = MenuItemView::TOPRIGHT; } else if (view == overflow_button_) { node = model_->GetBookmarkBarNode(); start_index = GetFirstHiddenNodeIndex(); - if (UILayoutIsRightToLeft()) - anchor_point = MenuItemView::TOPLEFT; - else - anchor_point = MenuItemView::TOPRIGHT; } else { int button_index = GetChildIndex(view); DCHECK_NE(-1, button_index); node = model_->GetBookmarkBarNode()->GetChild(button_index); - - // When the UI layout is RTL, the bookmarks are laid out from right to left - // and therefore when we display the menu we want it to be aligned with the - // bottom right corner of the bookmark item. - if (UILayoutIsRightToLeft()) - anchor_point = MenuItemView::TOPRIGHT; - else - anchor_point = MenuItemView::TOPLEFT; } - gfx::Point screen_loc(x, 0); - View::ConvertPointToScreen(this, &screen_loc); + bookmark_menu_ = new BookmarkMenuController( browser_, profile_, page_navigator_, GetWindow()->GetNativeWindow(), node, start_index, false); bookmark_menu_->set_observer(this); - bookmark_menu_->RunMenuAt(gfx::Rect(screen_loc.x(), screen_loc.y(), - view->width(), bar_height), - anchor_point, false); + bookmark_menu_->RunMenuAt(this, false); } void BookmarkBarView::ButtonPressed(views::Button* sender, @@ -1298,54 +1334,20 @@ void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) { bookmark_drop_menu_->Cancel(); } + views::MenuButton* menu_button = GetMenuButtonForNode(node); + if (!menu_button) + return; + int start_index = 0; - View* view_to_position_menu_from; - - // Note that both the anchor position and the position of the menu itself - // change depending on the locale. Also note that we must apply the - // mirroring transformation when querying for the child View bounds - // (View::x(), specifically) so that we end up with the correct screen - // coordinates if the View in question is mirrored. - MenuItemView::AnchorPosition anchor = MenuItemView::TOPLEFT; - if (node == model_->other_node()) { - view_to_position_menu_from = other_bookmarked_button_; - if (!UILayoutIsRightToLeft()) - anchor = MenuItemView::TOPRIGHT; - } else if (node == model_->GetBookmarkBarNode()) { - DCHECK(overflow_button_->IsVisible()); - view_to_position_menu_from = overflow_button_; + if (node == model_->GetBookmarkBarNode()) start_index = GetFirstHiddenNodeIndex(); - if (!UILayoutIsRightToLeft()) - anchor = MenuItemView::TOPRIGHT; - } else { - // Make sure node is still valid. - int index = -1; - const BookmarkNode* bb_node = model_->GetBookmarkBarNode(); - for (int i = 0; i < GetBookmarkButtonCount(); ++i) { - if (bb_node->GetChild(i) == node) { - index = i; - break; - } - } - if (index == -1) - return; - view_to_position_menu_from = GetBookmarkButton(index); - if (UILayoutIsRightToLeft()) - anchor = MenuItemView::TOPRIGHT; - } drop_info_->is_menu_showing = true; bookmark_drop_menu_ = new BookmarkMenuController( browser_, profile_, page_navigator_, GetWindow()->GetNativeWindow(), node, start_index, false); bookmark_drop_menu_->set_observer(this); - gfx::Point screen_loc; - View::ConvertPointToScreen(view_to_position_menu_from, &screen_loc); - bookmark_drop_menu_->RunMenuAt( - gfx::Rect(screen_loc.x(), screen_loc.y(), - view_to_position_menu_from->width(), - view_to_position_menu_from->height()), - anchor, true); + bookmark_drop_menu_->RunMenuAt(this, true); } void BookmarkBarView::StopShowFolderDropMenuTimer() { diff --git a/chrome/browser/views/bookmark_bar_view.h b/chrome/browser/views/bookmark_bar_view.h index c7008fb7..0d79cc6 100644 --- a/chrome/browser/views/bookmark_bar_view.h +++ b/chrome/browser/views/bookmark_bar_view.h @@ -13,18 +13,19 @@ #include "chrome/browser/views/bookmark_menu_controller_views.h" #include "chrome/browser/views/detachable_toolbar_view.h" #include "chrome/common/notification_registrar.h" -#include "views/controls/button/menu_button.h" -#include "views/controls/label.h" +#include "views/controls/button/button.h" #include "views/controls/menu/view_menu_delegate.h" -#include "views/view.h" -#include "third_party/skia/include/core/SkRect.h" class Browser; class PageNavigator; class PrefService; namespace views { +class CustomButton; +class Label; +class MenuButton; class MenuItemView; +class TextButton; } // BookmarkBarView renders the BookmarkModel. Each starred entry on the @@ -173,7 +174,7 @@ class BookmarkBarView : public DetachableToolbarView, // Returns the button responsible for showing bookmarks in the other bookmark // folder. - views::TextButton* other_bookmarked_button() const { + views::MenuButton* other_bookmarked_button() const { return other_bookmarked_button_; } @@ -187,7 +188,25 @@ class BookmarkBarView : public DetachableToolbarView, views::MenuItemView* GetContextMenu(); // Returns the button used when not all the items on the bookmark bar fit. - views::TextButton* overflow_button() const { return overflow_button_; } + views::MenuButton* overflow_button() const { return overflow_button_; } + + // If |loc| is over a bookmark button the node is returned corresponding + // to the button and |start_index| is set to 0. If a overflow button is + // showing and |loc| is over the overflow button, the bookmark bar node is + // returned and |start_index| is set to the index of the first node + // contained in the overflow menu. + const BookmarkNode* GetNodeForButtonAt(const gfx::Point& loc, + int* start_index); + + // Returns the MenuButton for node. + views::MenuButton* GetMenuButtonForNode(const BookmarkNode* node); + + // Returns the position to anchor the menu for |button| at, the index of the + // first child of the node to build the menu from. + void GetAnchorPositionAndStartIndexForButton( + views::MenuButton* button, + views::MenuItemView::AnchorPosition* anchor, + int* start_index); // Maximum size of buttons on the bookmark bar. static const int kMaxButtonWidth; @@ -317,12 +336,8 @@ class BookmarkBarView : public DetachableToolbarView, // Returns the drag operations for the specified button. virtual int GetDragOperations(views::View* sender, int x, int y); - // ViewMenuDelegate method. 3 types of menus may be shown: - // . the menu allowing the user to choose when the bookmark bar is visible. - // . most recently bookmarked menu - // . menu for star groups. - // The latter two are handled by a MenuRunner, which builds the appropriate - // menu. + // ViewMenuDelegate method. Ends up creating a BookmarkMenuController to + // show the menu. virtual void RunMenu(views::View* view, const gfx::Point& pt); // Invoked when a star entry corresponding to a URL on the bookmark bar is diff --git a/chrome/browser/views/bookmark_bar_view_test.cc b/chrome/browser/views/bookmark_bar_view_test.cc index 751a1ec..cedfd19 100644 --- a/chrome/browser/views/bookmark_bar_view_test.cc +++ b/chrome/browser/views/bookmark_bar_view_test.cc @@ -14,6 +14,7 @@ #include "chrome/common/pref_service.h" #include "chrome/test/testing_profile.h" #include "chrome/test/interactive_ui/view_event_test_base.h" +#include "views/controls/button/menu_button.h" #include "views/controls/button/text_button.h" #include "views/controls/menu/menu_controller.h" #include "views/controls/menu/menu_item_view.h" @@ -279,7 +280,7 @@ class BookmarkBarViewTest3 : public BookmarkBarViewEventTestBase { virtual void DoTestOnMessageLoop() { // Move the mouse to the first folder on the bookmark bar and press the // mouse. - views::TextButton* button = bb_view_->other_bookmarked_button(); + views::MenuButton* button = bb_view_->other_bookmarked_button(); ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP, CreateEventTask(this, &BookmarkBarViewTest3::Step2)); diff --git a/chrome/browser/views/bookmark_context_menu.cc b/chrome/browser/views/bookmark_context_menu.cc index ddb0d5b..a3f3a15 100644 --- a/chrome/browser/views/bookmark_context_menu.cc +++ b/chrome/browser/views/bookmark_context_menu.cc @@ -37,7 +37,7 @@ void BookmarkContextMenu::RunMenuAt(const gfx::Point& point) { views::MenuItemView::AnchorPosition anchor = (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? views::MenuItemView::TOPRIGHT : views::MenuItemView::TOPLEFT; - menu_->RunMenuAt(parent_window_, gfx::Rect(point.x(), point.y(), 0, 0), + menu_->RunMenuAt(parent_window_, NULL, gfx::Rect(point.x(), point.y(), 0, 0), anchor, true); } diff --git a/chrome/browser/views/bookmark_manager_view.cc b/chrome/browser/views/bookmark_manager_view.cc index fafa228..ce40b28 100644 --- a/chrome/browser/views/bookmark_manager_view.cc +++ b/chrome/browser/views/bookmark_manager_view.cc @@ -795,8 +795,8 @@ void BookmarkManagerView::ShowToolsMenu(int x, int y) { views::MenuItemView::AnchorPosition anchor = UILayoutIsRightToLeft() ? views::MenuItemView::TOPRIGHT : views::MenuItemView::TOPLEFT; - menu.RunMenuAt(GetWidget()->GetNativeView(), gfx::Rect(x, y, 0, 0), anchor, - true); + menu.RunMenuAt(GetWidget()->GetNativeView(), NULL, gfx::Rect(x, y, 0, 0), + anchor, true); } void BookmarkManagerView::ShowImportBookmarksFileChooser() { diff --git a/chrome/browser/views/bookmark_menu_button.cc b/chrome/browser/views/bookmark_menu_button.cc index 4255ffd..988f1f0 100644 --- a/chrome/browser/views/bookmark_menu_button.cc +++ b/chrome/browser/views/bookmark_menu_button.cc @@ -133,15 +133,11 @@ void BookmarkMenuButton::RunMenu(views::View* source, views::MenuItemView::AnchorPosition anchor = views::MenuItemView::TOPRIGHT; if (UILayoutIsRightToLeft()) anchor = views::MenuItemView::TOPLEFT; - gfx::Point button_origin; - ConvertPointToScreen(this, &button_origin); - gfx::Rect menu_bounds(button_origin.x(), button_origin.y(), width(), - height()); if (for_drop) { bookmark_drop_menu_ = menu; bookmark_drop_menu_->set_observer(this); } - menu->RunMenuAt(menu_bounds, views::MenuItemView::TOPRIGHT, for_drop); + menu->RunMenuAt(this, views::MenuItemView::TOPRIGHT, for_drop); } BookmarkModel* BookmarkMenuButton::GetBookmarkModel() { diff --git a/chrome/browser/views/bookmark_menu_controller_views.cc b/chrome/browser/views/bookmark_menu_controller_views.cc index 58ff604..80bf767 100644 --- a/chrome/browser/views/bookmark_menu_controller_views.cc +++ b/chrome/browser/views/bookmark_menu_controller_views.cc @@ -7,16 +7,21 @@ #include "app/l10n_util.h" #include "app/os_exchange_data.h" #include "app/resource_bundle.h" +#include "base/stl_util-inl.h" #include "chrome/browser/bookmarks/bookmark_drag_data.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/page_navigator.h" +#include "chrome/browser/views/bookmark_bar_view.h" #include "chrome/browser/views/event_utils.h" #include "chrome/common/page_transition_types.h" #include "grit/app_resources.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" +#include "views/controls/button/menu_button.h" + +using views::MenuItemView; BookmarkMenuController::BookmarkMenuController(Browser* browser, Profile* profile, @@ -30,28 +35,45 @@ BookmarkMenuController::BookmarkMenuController(Browser* browser, page_navigator_(navigator), parent_(parent), node_(node), + menu_(NULL), observer_(NULL), for_drop_(false), - show_other_folder_(show_other_folder) { - menu_.reset(new views::MenuItemView(this)); - int next_menu_id = 1; - menu_id_to_node_map_[menu_->GetCommand()] = node; - menu_->set_has_icons(true); - BuildMenu(node, start_child_index, menu_.get(), &next_menu_id); - if (show_other_folder) - BuildOtherFolderMenu(&next_menu_id); + show_other_folder_(show_other_folder), + bookmark_bar_(NULL), + next_menu_id_(1) { + menu_ = CreateMenu(node, start_child_index); +} + +void BookmarkMenuController::RunMenuAt(BookmarkBarView* bookmark_bar, + bool for_drop) { + bookmark_bar_ = bookmark_bar; + views::MenuButton* menu_button = bookmark_bar_->GetMenuButtonForNode(node_); + DCHECK(menu_button); + MenuItemView::AnchorPosition anchor; + int start_index; + bookmark_bar_->GetAnchorPositionAndStartIndexForButton( + menu_button, &anchor, &start_index); + RunMenuAt(menu_button, anchor, for_drop); } void BookmarkMenuController::RunMenuAt( - const gfx::Rect& bounds, - views::MenuItemView::AnchorPosition position, + views::MenuButton* button, + MenuItemView::AnchorPosition position, bool for_drop) { + gfx::Point screen_loc; + views::View::ConvertPointToScreen(button, &screen_loc); + // Subtract 1 from the height to make the popup flush with the button border. + gfx::Rect bounds(screen_loc.x(), screen_loc.y(), button->width(), + button->height() - 1); for_drop_ = for_drop; profile_->GetBookmarkModel()->AddObserver(this); + // The constructor creates the initial menu and that is all we should have + // at this point. + DCHECK(node_to_menu_map_.size() == 1); if (for_drop) { menu_->RunMenuForDropAt(parent_, bounds, position); } else { - menu_->RunMenuAt(parent_, bounds, position, false); + menu_->RunMenuAt(parent_, button, bounds, position, false); delete this; } } @@ -74,7 +96,7 @@ void BookmarkMenuController::ExecuteCommand(int id, int mouse_event_flags) { } bool BookmarkMenuController::GetDropFormats( - views::MenuItemView* menu, + MenuItemView* menu, int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) { *formats = OSExchangeData::URL; @@ -82,11 +104,11 @@ bool BookmarkMenuController::GetDropFormats( return true; } -bool BookmarkMenuController::AreDropTypesRequired(views::MenuItemView* menu) { +bool BookmarkMenuController::AreDropTypesRequired(MenuItemView* menu) { return true; } -bool BookmarkMenuController::CanDrop(views::MenuItemView* menu, +bool BookmarkMenuController::CanDrop(MenuItemView* menu, const OSExchangeData& data) { // Only accept drops of 1 node, which is the case for all data dragged from // bookmark bar and menus. @@ -113,7 +135,7 @@ bool BookmarkMenuController::CanDrop(views::MenuItemView* menu, } int BookmarkMenuController::GetDropOperation( - views::MenuItemView* item, + MenuItemView* item, const views::DropTargetEvent& event, DropPosition* position) { // Should only get here if we have drop data. @@ -138,7 +160,7 @@ int BookmarkMenuController::GetDropOperation( profile_, event, drop_data_, drop_parent, index_to_drop_at); } -int BookmarkMenuController::OnPerformDrop(views::MenuItemView* menu, +int BookmarkMenuController::OnPerformDrop(MenuItemView* menu, DropPosition position, const views::DropTargetEvent& event) { const BookmarkNode* drop_node = menu_id_to_node_map_[menu->GetCommand()]; @@ -163,7 +185,7 @@ int BookmarkMenuController::OnPerformDrop(views::MenuItemView* menu, return result; } -bool BookmarkMenuController::ShowContextMenu(views::MenuItemView* source, +bool BookmarkMenuController::ShowContextMenu(MenuItemView* source, int id, int x, int y, @@ -183,17 +205,17 @@ bool BookmarkMenuController::ShowContextMenu(views::MenuItemView* source, return true; } -void BookmarkMenuController::DropMenuClosed(views::MenuItemView* menu) { +void BookmarkMenuController::DropMenuClosed(MenuItemView* menu) { delete this; } -bool BookmarkMenuController::CanDrag(views::MenuItemView* menu) { +bool BookmarkMenuController::CanDrag(MenuItemView* menu) { const BookmarkNode* node = menu_id_to_node_map_[menu->GetCommand()]; // Don't let users drag the other folder. return node->GetParent() != profile_->GetBookmarkModel()->root_node(); } -void BookmarkMenuController::WriteDragData(views::MenuItemView* sender, +void BookmarkMenuController::WriteDragData(MenuItemView* sender, OSExchangeData* data) { DCHECK(sender && data); @@ -203,11 +225,41 @@ void BookmarkMenuController::WriteDragData(views::MenuItemView* sender, drag_data.Write(profile_, data); } -int BookmarkMenuController::GetDragOperations(views::MenuItemView* sender) { +int BookmarkMenuController::GetDragOperations(MenuItemView* sender) { return bookmark_utils::BookmarkDragOperation( menu_id_to_node_map_[sender->GetCommand()]); } +views::MenuItemView* BookmarkMenuController::GetSiblingMenu( + views::MenuItemView* menu, + const gfx::Point& screen_point, + views::MenuItemView::AnchorPosition* anchor, + bool* has_mnemonics, + views::MenuButton** button) { + if (show_other_folder_ || !bookmark_bar_ || for_drop_) + return NULL; + gfx::Point bookmark_bar_loc(screen_point); + views::View::ConvertPointToView(NULL, bookmark_bar_, &bookmark_bar_loc); + int start_index; + const BookmarkNode* node = + bookmark_bar_->GetNodeForButtonAt(bookmark_bar_loc, &start_index); + if (!node || !node->is_folder()) + return NULL; + + MenuItemView* alt_menu = node_to_menu_map_[node]; + if (!alt_menu) + alt_menu = CreateMenu(node, start_index); + + if (alt_menu) + menu_ = alt_menu; + + *button = bookmark_bar_->GetMenuButtonForNode(node); + bookmark_bar_->GetAnchorPositionAndStartIndexForButton( + *button, anchor, &start_index); + *has_mnemonics = false; + return alt_menu; +} + void BookmarkMenuController::BookmarkModelChanged() { menu_->Cancel(); } @@ -218,13 +270,27 @@ void BookmarkMenuController::BookmarkNodeFavIconLoaded( menu_->SetIcon(model->GetFavIcon(node), node_to_menu_id_map_[node]); } -void BookmarkMenuController::BuildOtherFolderMenu(int* next_menu_id) { +MenuItemView* BookmarkMenuController::CreateMenu(const BookmarkNode* parent, + int start_child_index) { + MenuItemView* menu = new MenuItemView(this); + menu->SetCommand(next_menu_id_++); + menu_id_to_node_map_[menu->GetCommand()] = parent; + menu->set_has_icons(true); + BuildMenu(parent, start_child_index, menu, &next_menu_id_); + if (show_other_folder_) + BuildOtherFolderMenu(menu, &next_menu_id_); + node_to_menu_map_[parent] = menu; + return menu; +} + +void BookmarkMenuController::BuildOtherFolderMenu( + MenuItemView* menu, int* next_menu_id) { const BookmarkNode* other_folder = profile_->GetBookmarkModel()->other_node(); int id = *next_menu_id; (*next_menu_id)++; SkBitmap* folder_icon = ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER); - views::MenuItemView* submenu = menu_->AppendSubMenuWithIcon( + MenuItemView* submenu = menu->AppendSubMenuWithIcon( id, l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_BOOKMARKED), *folder_icon); BuildMenu(other_folder, 0, submenu, next_menu_id); menu_id_to_node_map_[id] = other_folder; @@ -232,7 +298,7 @@ void BookmarkMenuController::BuildOtherFolderMenu(int* next_menu_id) { void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, int start_child_index, - views::MenuItemView* menu, + MenuItemView* menu, int* next_menu_id) { DCHECK(!parent->GetChildCount() || start_child_index < parent->GetChildCount()); @@ -252,7 +318,7 @@ void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, } else if (node->is_folder()) { SkBitmap* folder_icon = ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER); - views::MenuItemView* submenu = + MenuItemView* submenu = menu->AppendSubMenuWithIcon(id, node->GetTitle(), *folder_icon); BuildMenu(node, 0, submenu, next_menu_id); } else { @@ -266,4 +332,5 @@ BookmarkMenuController::~BookmarkMenuController() { profile_->GetBookmarkModel()->RemoveObserver(this); if (observer_) observer_->BookmarkMenuDeleted(this); + STLDeleteValues(&node_to_menu_map_); } diff --git a/chrome/browser/views/bookmark_menu_controller_views.h b/chrome/browser/views/bookmark_menu_controller_views.h index fa6462f..f19c3cc 100644 --- a/chrome/browser/views/bookmark_menu_controller_views.h +++ b/chrome/browser/views/bookmark_menu_controller_views.h @@ -16,8 +16,13 @@ namespace gfx { class Rect; -} +} // namespace gfx +namespace views { +class MenuButton; +} // namespace views + +class BookmarkBarView; class BookmarkContextMenu; class BookmarkNode; class Browser; @@ -48,8 +53,10 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, int start_child_index, bool show_other_folder); + void RunMenuAt(BookmarkBarView* bookmark_bar, bool for_drop); + // Shows the menu. - void RunMenuAt(const gfx::Rect& bounds, + void RunMenuAt(views::MenuButton* button, views::MenuItemView::AnchorPosition position, bool for_drop); @@ -60,7 +67,7 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, const BookmarkNode* node() const { return node_; } // Returns the menu. - views::MenuItemView* menu() const { return menu_.get(); } + views::MenuItemView* menu() const { return menu_; } // Returns the context menu, or NULL if the context menu isn't showing. views::MenuItemView* context_menu() const { @@ -93,7 +100,14 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, virtual bool CanDrag(views::MenuItemView* menu); virtual void WriteDragData(views::MenuItemView* sender, OSExchangeData* data); virtual int GetDragOperations(views::MenuItemView* sender); + virtual views::MenuItemView* GetSiblingMenu( + views::MenuItemView* menu, + const gfx::Point& screen_point, + views::MenuItemView::AnchorPosition* anchor, + bool* has_mnemonics, + views::MenuButton** button); + // BookmarkModelObserver methods. virtual void BookmarkModelChanged(); virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, const BookmarkNode* node); @@ -102,9 +116,14 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, // BookmarkMenuController deletes itself as necessary. ~BookmarkMenuController(); + // Creates a menu and adds it to node_to_menu_id_map_. This uses + // BuildMenu to recursively populate the menu. + views::MenuItemView* CreateMenu(const BookmarkNode* parent, + int start_child_index); + // Builds the menu for the other bookmarks folder. This is added as the last // item to menu_. - void BuildOtherFolderMenu(int* next_menu_id); + void BuildOtherFolderMenu(views::MenuItemView* menu, int* next_menu_id); // Creates an entry in menu for each child node of |parent| starting at // |start_child_index|. @@ -132,8 +151,8 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, // URL. std::map<const BookmarkNode*, int> node_to_menu_id_map_; - // The menu. - scoped_ptr<views::MenuItemView> menu_; + // Current menu. + views::MenuItemView* menu_; // Data for the drop. BookmarkDragData drop_data_; @@ -150,6 +169,16 @@ class BookmarkMenuController : public BaseBookmarkModelObserver, // Should the other folder be shown? bool show_other_folder_; + // The bookmark bar. This is only non-null if we're showing a menu item + // for a folder on the bookmark bar and not for drop. + BookmarkBarView* bookmark_bar_; + + typedef std::map<const BookmarkNode*, views::MenuItemView*> NodeToMenuMap; + NodeToMenuMap node_to_menu_map_; + + // ID of the next menu item. + int next_menu_id_; + DISALLOW_COPY_AND_ASSIGN(BookmarkMenuController); }; diff --git a/views/controls/button/menu_button.cc b/views/controls/button/menu_button.cc index 5dc59cb..e457304 100644 --- a/views/controls/button/menu_button.cc +++ b/views/controls/button/menu_button.cc @@ -47,7 +47,6 @@ MenuButton::MenuButton(ButtonListener* listener, bool show_menu_marker) : TextButton(listener, text), menu_visible_(false), - menu_closed_time_(), menu_delegate_(menu_delegate), show_menu_marker_(show_menu_marker) { if (kMenuMarker == NULL) { diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc index 6175e62..4388d91 100644 --- a/views/controls/menu/menu_controller.cc +++ b/views/controls/menu/menu_controller.cc @@ -9,6 +9,7 @@ #include "app/os_exchange_data.h" #include "base/keyboard_codes.h" #include "base/time.h" +#include "views/controls/button/menu_button.h" #include "views/controls/menu/menu_scroll_view_container.h" #include "views/controls/menu/submenu_view.h" #include "views/drag_utils.h" @@ -143,6 +144,7 @@ static int nested_depth = 0; #endif MenuItemView* MenuController::Run(gfx::NativeWindow parent, + MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, MenuItemView::AnchorPosition position, @@ -168,19 +170,9 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, // Reset current state. pending_state_ = State(); state_ = State(); - pending_state_.initial_bounds = bounds; - if (bounds.height() > 1) { - // Inset the bounds slightly, otherwise drag coordinates don't line up - // nicely and menus close prematurely. - pending_state_.initial_bounds.Inset(0, 1); - } - pending_state_.anchor = position; - owner_ = parent; + UpdateInitialLocation(bounds, position); - // Calculate the bounds of the monitor we'll show menus on. Do this once to - // avoid repeated system queries for the info. - pending_state_.monitor_bounds = Screen::GetMonitorAreaNearestPoint( - bounds.origin()); + owner_ = parent; // Set the selection, which opens the initial menu. SetSelection(root, true, true); @@ -190,6 +182,8 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, // notification when the drag has finished. StartCancelAllTimer(); return NULL; + } else if (button) { + menu_button_ = button; } #ifdef DEBUG_MENU @@ -245,6 +239,11 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, exit_all_ = true; } + if (menu_button_) { + menu_button_->SetState(CustomButton::BS_NORMAL); + menu_button_->SchedulePaint(); + } + return result; } @@ -316,8 +315,7 @@ void MenuController::OnMousePressed(SubmenuView* source, if (!blocking_run_) return; - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); if (part.is_scroll()) return; // Ignore presses on scroll buttons. @@ -362,8 +360,7 @@ void MenuController::OnMouseDragged(SubmenuView* source, #ifdef DEBUG_MENU DLOG(INFO) << "OnMouseDragged source=" << source; #endif - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); UpdateScrolling(part); if (!blocking_run_) @@ -409,6 +406,8 @@ void MenuController::OnMouseDragged(SubmenuView* source, if (!part.menu) part.menu = source->GetMenuItem(); SetSelection(part.menu ? part.menu : state_.item, true, false); + } else if (part.type == MenuPart::NONE) { + ShowSiblingMenu(source, event); } } @@ -423,8 +422,7 @@ void MenuController::OnMouseReleased(SubmenuView* source, DCHECK(state_.item); possible_drag_ = false; DCHECK(blocking_run_); - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); if (event.IsRightMouseButton() && (part.type == MenuPart::MENU_ITEM && part.menu)) { // Set the selection immediately, making sure the submenu is only open @@ -460,14 +458,16 @@ void MenuController::OnMouseMoved(SubmenuView* source, if (showing_submenu_) return; - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); UpdateScrolling(part); if (!blocking_run_) return; + if (part.type == MenuPart::NONE && ShowSiblingMenu(source, event)) + return; + if (part.type == MenuPart::MENU_ITEM && part.menu) { SetSelection(part.menu, true, false); } else if (!part.is_scroll() && pending_state_.item && @@ -803,7 +803,8 @@ MenuController::MenuController(bool blocking) owner_(NULL), possible_drag_(false), valid_drop_coordinates_(false), - showing_submenu_(false) { + showing_submenu_(false), + menu_button_(NULL) { #ifdef DEBUG_MENU instance_count++; DLOG(INFO) << "created MC, count=" << instance_count; @@ -820,6 +821,23 @@ MenuController::~MenuController() { #endif } +void MenuController::UpdateInitialLocation( + const gfx::Rect& bounds, + MenuItemView::AnchorPosition position) { + pending_state_.initial_bounds = bounds; + if (bounds.height() > 1) { + // Inset the bounds slightly, otherwise drag coordinates don't line up + // nicely and menus close prematurely. + pending_state_.initial_bounds.Inset(0, 1); + } + pending_state_.anchor = position; + + // Calculate the bounds of the monitor we'll show menus on. Do this once to + // avoid repeated system queries for the info. + pending_state_.monitor_bounds = Screen::GetMonitorAreaNearestPoint( + bounds.origin()); +} + void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { DCHECK(IsBlockingRun()); result_ = item; @@ -827,6 +845,63 @@ void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { result_mouse_event_flags_ = mouse_event_flags; } +bool MenuController::ShowSiblingMenu(SubmenuView* source, const MouseEvent& e) { + if (!menu_stack_.empty() || !menu_button_) + return false; + + View* source_view = source->GetScrollViewContainer(); + if (e.x() >= 0 && e.x() < source_view->width() && e.y() >= 0 && + e.y() < source_view->height()) { + // The mouse is over the menu, no need to continue. + return false; + } + + gfx::NativeWindow window_under_mouse = Screen::GetWindowAtCursorScreenPoint(); + if (window_under_mouse != owner_) + return false; + + // The user moved the mouse outside the menu and over the owning window. See + // if there is a sibling menu we should show. + gfx::Point screen_point(e.location()); + View::ConvertPointToScreen(source_view, &screen_point); + MenuItemView::AnchorPosition anchor; + bool has_mnemonics; + MenuButton* button = NULL; + MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()-> + GetSiblingMenu(source->GetMenuItem()->GetRootMenuItem(), + screen_point, &anchor, &has_mnemonics, &button); + if (!alt_menu || alt_menu == state_.item) + return false; + + if (!button) { + // If the delegate returns a menu, they must also return a button. + NOTREACHED(); + return false; + } + + // There is a sibling menu, update the button state, hide the current menu + // and show the new one. + menu_button_->SetState(CustomButton::BS_NORMAL); + menu_button_->SchedulePaint(); + menu_button_ = button; + menu_button_->SetState(CustomButton::BS_PUSHED); + menu_button_->SchedulePaint(); + + // Need to reset capture when we show the menu again, otherwise we aren't + // going to get any events. + did_capture_ = false; + gfx::Point screen_menu_loc; + View::ConvertPointToScreen(button, &screen_menu_loc); + // Subtract 1 from the height to make the popup flush with the button border. + UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(), + button->width(), button->height() - 1), + anchor); + alt_menu->PrepareForRun(has_mnemonics); + alt_menu->controller_ = this; + SetSelection(alt_menu, true, true); + return true; +} + void MenuController::CloseAllNestedMenus() { for (std::list<State>::iterator i = menu_stack_.begin(); i != menu_stack_.end(); ++i) { diff --git a/views/controls/menu/menu_controller.h b/views/controls/menu/menu_controller.h index bc57036..2ef1bb7 100644 --- a/views/controls/menu/menu_controller.h +++ b/views/controls/menu/menu_controller.h @@ -22,6 +22,7 @@ class OSExchangeData; namespace views { class DropTargetEvent; +class MenuButton; class MenuHostRootView; class MouseEvent; class SubmenuView; @@ -44,6 +45,7 @@ class MenuController : public MessageLoopForUI::Dispatcher { // block, the selected item is returned. If the menu does not block this // returns NULL immediately. MenuItemView* Run(gfx::NativeWindow parent, + MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, MenuItemView::AnchorPosition position, @@ -182,10 +184,15 @@ class MenuController : public MessageLoopForUI::Dispatcher { ~MenuController(); + void UpdateInitialLocation(const gfx::Rect& bounds, + MenuItemView::AnchorPosition position); + // Invoked when the user accepts the selected item. This is only used // when blocking. This schedules the loop to quit. void Accept(MenuItemView* item, int mouse_event_flags); + bool ShowSiblingMenu(SubmenuView* source, const MouseEvent& e); + // Closes all menus, including any menus of nested invocations of Run. void CloseAllNestedMenus(); @@ -330,7 +337,7 @@ class MenuController : public MessageLoopForUI::Dispatcher { MenuItemView* result_; // The mouse event flags when the user clicked on a menu. Is 0 if the - // user did not use the mousee to select the menu. + // user did not use the mouse to select the menu. int result_mouse_event_flags_; // If not empty, it means we're nested. When Run is invoked from within @@ -376,6 +383,8 @@ class MenuController : public MessageLoopForUI::Dispatcher { // underway. scoped_ptr<MenuScrollTask> scroll_task_; + MenuButton* menu_button_; + DISALLOW_COPY_AND_ASSIGN(MenuController); }; diff --git a/views/controls/menu/menu_delegate.h b/views/controls/menu/menu_delegate.h index ec66a4f..2f7cd66 100644 --- a/views/controls/menu/menu_delegate.h +++ b/views/controls/menu/menu_delegate.h @@ -12,12 +12,13 @@ #include "app/os_exchange_data.h" #include "base/logging.h" #include "views/controls/menu/controller.h" +#include "views/controls/menu/menu_item_view.h" #include "views/event.h" namespace views { class DropTargetEvent; -class MenuItemView; +class MenuButton; // MenuDelegate -------------------------------------------------------------- @@ -180,6 +181,19 @@ class MenuDelegate : Controller { // Notification that the user has highlighted the specified item. virtual void SelectionChanged(MenuItemView* menu) { } + + // If the user drags the mouse outside the bounds of the menu the delegate + // is queried for a sibling menu to show. If this returns non-null the + // current menu is hidden, and the menu returned from this method is shown. + // + // The delegate owns the returned menu, not the controller. + virtual MenuItemView* GetSiblingMenu(MenuItemView* menu, + const gfx::Point& screen_point, + MenuItemView::AnchorPosition* anchor, + bool* has_mnemonics, + MenuButton** button) { + return NULL; + } }; } // namespace views diff --git a/views/controls/menu/menu_host_gtk.cc b/views/controls/menu/menu_host_gtk.cc index 11033c4..38945e5 100644 --- a/views/controls/menu/menu_host_gtk.cc +++ b/views/controls/menu/menu_host_gtk.cc @@ -19,14 +19,13 @@ MenuHost::MenuHost(SubmenuView* submenu) closed_(false), submenu_(submenu), did_pointer_grab_(false) { - GdkModifierType current_event_mod; - if (gtk_get_current_event_state(¤t_event_mod)) { - set_mouse_down( - (current_event_mod & GDK_BUTTON1_MASK) || - (current_event_mod & GDK_BUTTON2_MASK) || - (current_event_mod & GDK_BUTTON3_MASK) || - (current_event_mod & GDK_BUTTON4_MASK) || - (current_event_mod & GDK_BUTTON5_MASK)); + GdkEvent* event = gtk_get_current_event(); + if (event) { + if (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS || + event->type == GDK_3BUTTON_PRESS) { + set_mouse_down(true); + } + gdk_event_free(event); } } @@ -36,33 +35,9 @@ void MenuHost::Init(gfx::NativeWindow parent, bool do_capture) { WidgetGtk::Init(GTK_WIDGET(parent), bounds); SetContentsView(contents_view); - // TODO(sky): see if there is some way to show without changing focus. Show(); - if (do_capture) { - // Release the current grab. - GtkWidget* current_grab_window = gtk_grab_get_current(); - if (current_grab_window) - gtk_grab_remove(current_grab_window); - - // Make sure all app mouse events are targetted at us only. - DoGrab(); - - // And do a grab. - // NOTE: we do this to ensure we get mouse events from other apps, a grab - // done with gtk_grab_add doesn't get events from other apps. - GdkGrabStatus grab_status = - gdk_pointer_grab(window_contents()->window, FALSE, - static_cast<GdkEventMask>( - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK), - NULL, NULL, GDK_CURRENT_TIME); - did_pointer_grab_ = (grab_status == GDK_GRAB_SUCCESS); - DCHECK(did_pointer_grab_); - // need keyboard grab. -#ifdef DEBUG_MENU - DLOG(INFO) << "Doing capture"; -#endif - } + if (do_capture) + DoCapture(); } gfx::NativeWindow MenuHost::GetNativeWindow() { @@ -95,6 +70,32 @@ void MenuHost::HideWindow() { WidgetGtk::Hide(); } +void MenuHost::DoCapture() { + // Release the current grab. + GtkWidget* current_grab_window = gtk_grab_get_current(); + if (current_grab_window) + gtk_grab_remove(current_grab_window); + + // Make sure all app mouse events are targetted at us only. + DoGrab(); + + // And do a grab. + // NOTE: we do this to ensure we get mouse events from other apps, a grab + // done with gtk_grab_add doesn't get events from other apps. + GdkGrabStatus grab_status = + gdk_pointer_grab(window_contents()->window, FALSE, + static_cast<GdkEventMask>( + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK), + NULL, NULL, GDK_CURRENT_TIME); + did_pointer_grab_ = (grab_status == GDK_GRAB_SUCCESS); + DCHECK(did_pointer_grab_); + // need keyboard grab. +#ifdef DEBUG_MENU + DLOG(INFO) << "Doing capture"; +#endif +} + void MenuHost::ReleaseCapture() { ReleaseGrab(); } diff --git a/views/controls/menu/menu_host_gtk.h b/views/controls/menu/menu_host_gtk.h index 8e69ce8..4ae1b77 100644 --- a/views/controls/menu/menu_host_gtk.h +++ b/views/controls/menu/menu_host_gtk.h @@ -27,6 +27,7 @@ class MenuHost : public WidgetGtk { void Show(); virtual void Hide(); virtual void HideWindow(); + void DoCapture(); void ReleaseCapture(); protected: diff --git a/views/controls/menu/menu_host_win.cc b/views/controls/menu/menu_host_win.cc index d6fd655..f2c2c2c 100644 --- a/views/controls/menu/menu_host_win.cc +++ b/views/controls/menu/menu_host_win.cc @@ -37,14 +37,8 @@ void MenuHost::Init(HWND parent, WidgetWin::Init(parent, bounds); SetContentsView(contents_view); Show(); - owns_capture_ = do_capture; - if (do_capture) { - SetCapture(); - has_capture_ = true; -#ifdef DEBUG_MENU - DLOG(INFO) << "Doing capture"; -#endif - } + if (do_capture) + DoCapture(); } void MenuHost::Show() { @@ -82,6 +76,15 @@ void MenuHost::OnCaptureChanged(HWND hwnd) { #endif } +void MenuHost::DoCapture() { + owns_capture_ = true; + SetCapture(); + has_capture_ = true; +#ifdef DEBUG_MENU + DLOG(INFO) << "Doing capture"; +#endif +} + void MenuHost::ReleaseCapture() { if (owns_capture_) { #ifdef DEBUG_MENU diff --git a/views/controls/menu/menu_host_win.h b/views/controls/menu/menu_host_win.h index 6e645d2..ec5d060 100644 --- a/views/controls/menu/menu_host_win.h +++ b/views/controls/menu/menu_host_win.h @@ -28,6 +28,7 @@ class MenuHost : public WidgetWin { virtual void Hide(); virtual void HideWindow(); virtual void OnCaptureChanged(HWND hwnd); + void DoCapture(); void ReleaseCapture(); protected: diff --git a/views/controls/menu/menu_item_view.cc b/views/controls/menu/menu_item_view.cc index 4d5b3ae..2734788 100644 --- a/views/controls/menu/menu_item_view.cc +++ b/views/controls/menu/menu_item_view.cc @@ -66,23 +66,15 @@ MenuItemView::MenuItemView(MenuDelegate* delegate) { } MenuItemView::~MenuItemView() { - if (controller_) { - // We're currently showing. - - // We can't delete ourselves while we're blocking. - DCHECK(!controller_->IsBlockingRun()); - - // Invoking Cancel is going to call us back and notify the delegate. - // Notifying the delegate from the destructor can be problematic. To avoid - // this the delegate is set to NULL. - delegate_ = NULL; - - controller_->Cancel(true); - } + // TODO(sky): ownership is bit wrong now. In particular if a nested message + // loop is running deletion can't be done, otherwise the stack gets + // thoroughly screwed. The destructor should be made private, and + // MenuController should be the only place handling deletion of the menu. delete submenu_; } void MenuItemView::RunMenuAt(gfx::NativeWindow parent, + MenuButton* button, const gfx::Rect& bounds, AnchorPosition anchor, bool has_mnemonics) { @@ -115,7 +107,7 @@ void MenuItemView::RunMenuAt(gfx::NativeWindow parent, // Run the loop. MenuItemView* result = - controller->Run(parent, this, bounds, anchor, &mouse_event_flags); + controller->Run(parent, button, this, bounds, anchor, &mouse_event_flags); RemoveEmptyMenus(); @@ -151,7 +143,7 @@ void MenuItemView::RunMenuForDropAt(gfx::NativeWindow parent, // Set the instance, that way it can be canceled by another menu. MenuController::SetActiveInstance(controller_); - controller_->Run(parent, this, bounds, anchor, NULL); + controller_->Run(parent, NULL, this, bounds, anchor, NULL); } void MenuItemView::Cancel() { @@ -337,9 +329,6 @@ void MenuItemView::PrepareForRun(bool has_mnemonics) { // Currently we only support showing the root. DCHECK(!parent_menu_item_); - // Don't invoke run from within run on the same menu. - DCHECK(!controller_); - // Force us to have a submenu. CreateSubmenu(); diff --git a/views/controls/menu/menu_item_view.h b/views/controls/menu/menu_item_view.h index ecb3b78..a85e265 100644 --- a/views/controls/menu/menu_item_view.h +++ b/views/controls/menu/menu_item_view.h @@ -10,6 +10,7 @@ namespace views { +class MenuButton; class MenuController; class MenuDelegate; class SubmenuView; @@ -82,6 +83,7 @@ class MenuItemView : public View { // whether the items have mnemonics. Mnemonics are identified by way of the // character following the '&'. void RunMenuAt(gfx::NativeWindow parent, + MenuButton* button, const gfx::Rect& bounds, AnchorPosition anchor, bool has_mnemonics); diff --git a/views/controls/menu/submenu_view.cc b/views/controls/menu/submenu_view.cc index d08de6f..3b315ef 100644 --- a/views/controls/menu/submenu_view.cc +++ b/views/controls/menu/submenu_view.cc @@ -221,6 +221,8 @@ void SubmenuView::ShowAt(gfx::NativeWindow parent, bool do_capture) { if (host_) { host_->Show(); + if (do_capture) + host_->DoCapture(); return; } diff --git a/views/screen.h b/views/screen.h index f7c023c..f95b368 100644 --- a/views/screen.h +++ b/views/screen.h @@ -27,6 +27,9 @@ class Screen { // Returns the monitor area (not the work area, but the complete bounds) of // the monitor nearest the specified point. static gfx::Rect GetMonitorAreaNearestPoint(const gfx::Point& point); + + // Returns the window under the cursor. + static gfx::NativeWindow GetWindowAtCursorScreenPoint(); }; } // namespace views diff --git a/views/screen_gtk.cc b/views/screen_gtk.cc index 9302853..a0daa19 100644 --- a/views/screen_gtk.cc +++ b/views/screen_gtk.cc @@ -58,5 +58,19 @@ gfx::Rect Screen::GetMonitorAreaNearestPoint(const gfx::Point& point) { return gfx::Rect(bounds); } +gfx::NativeWindow Screen::GetWindowAtCursorScreenPoint() { + GdkWindow* window = gdk_window_at_pointer(NULL, NULL); + if (!window) + return NULL; + + gpointer data = NULL; + gdk_window_get_user_data(window, &data); + GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); + if (!widget) + return NULL; + widget = gtk_widget_get_toplevel(widget); + return GTK_IS_WINDOW(widget) ? GTK_WINDOW(widget) : NULL; +} + } // namespace diff --git a/views/screen_win.cc b/views/screen_win.cc index 82e95e6..4ddb7c1 100644 --- a/views/screen_win.cc +++ b/views/screen_win.cc @@ -46,5 +46,10 @@ gfx::Rect Screen::GetMonitorAreaNearestPoint(const gfx::Point& point) { return gfx::Rect(mi.rcMonitor); } +gfx::NativeWindow Screen::GetWindowAtCursorScreenPoint() { + POINT location; + return GetCursorPos(&location) ? WindowFromPoint(location) : NULL; +} + } // namespace |