summaryrefslogtreecommitdiffstats
path: root/views/controls/menu/menu_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/controls/menu/menu_controller.cc')
-rw-r--r--views/controls/menu/menu_controller.cc117
1 files changed, 96 insertions, 21 deletions
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) {