Index: views/controls/menu/menu_controller.cc |
=================================================================== |
--- views/controls/menu/menu_controller.cc (revision 29776) |
+++ views/controls/menu/menu_controller.cc (working copy) |
@@ -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 @@ |
#endif |
MenuItemView* MenuController::Run(gfx::NativeWindow parent, |
+ MenuButton* button, |
MenuItemView* root, |
const gfx::Rect& bounds, |
MenuItemView::AnchorPosition position, |
@@ -168,20 +170,10 @@ |
// 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; |
+ UpdateInitialLocation(bounds, position); |
+ |
owner_ = parent; |
- // 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()); |
- |
// Set the selection, which opens the initial menu. |
SetSelection(root, true, true); |
@@ -190,6 +182,8 @@ |
// notification when the drag has finished. |
StartCancelAllTimer(); |
return NULL; |
+ } else if (button) { |
+ menu_button_ = button; |
} |
#ifdef DEBUG_MENU |
@@ -245,6 +239,11 @@ |
exit_all_ = true; |
} |
+ if (menu_button_) { |
+ menu_button_->SetState(CustomButton::BS_NORMAL); |
+ menu_button_->SchedulePaint(); |
+ } |
+ |
return result; |
} |
@@ -316,8 +315,7 @@ |
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 @@ |
#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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
#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 @@ |
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) { |