summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorrdevlin.cronin <rdevlin.cronin@chromium.org>2015-10-21 16:50:47 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-21 23:51:23 +0000
commitb6efb0e400582c7cf709b1fbb0339b15c83264bc (patch)
treedc40519205e242f158b254bd7a7cd62118ce7aec /ui
parent103d80f736cc4004a55c64a3ac0a07ab5a0ec98f (diff)
downloadchromium_src-b6efb0e400582c7cf709b1fbb0339b15c83264bc.zip
chromium_src-b6efb0e400582c7cf709b1fbb0339b15c83264bc.tar.gz
chromium_src-b6efb0e400582c7cf709b1fbb0339b15c83264bc.tar.bz2
[Resubmit] [Views] Forward mouse events in menus to the proper root view
This was reverted because it was thought to have caused interactive ui test flakes. It turns out those same tests continued to flake even once this was reverted, indicating this (most likely) wasn't the culprit. Additionally, those flakes seem to have been fixed. Original Description: Mouse events in menus get sent to the main menu's root view, even if there's a nested menu running. Add logic to forward these events to the proper root view. BUG=538414 BUG=541021 Review URL: https://codereview.chromium.org/1414393002 Cr-Commit-Position: refs/heads/master@{#355439}
Diffstat (limited to 'ui')
-rw-r--r--ui/views/controls/menu/menu_controller.cc124
-rw-r--r--ui/views/controls/menu/menu_controller.h27
-rw-r--r--ui/views/controls/menu/menu_host_root_view.cc71
-rw-r--r--ui/views/controls/menu/menu_host_root_view.h15
4 files changed, 195 insertions, 42 deletions
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 0391d54..b3a6ada 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -482,23 +482,65 @@ void MenuController::Cancel(ExitType type) {
}
}
-void MenuController::OnMousePressed(SubmenuView* source,
+bool MenuController::OnMousePressed(SubmenuView* source,
const ui::MouseEvent& event) {
+ // We should either have no current_mouse_event_target_, or should have a
+ // pressed state stored.
+ DCHECK(!current_mouse_event_target_ || current_mouse_pressed_state_);
+
+ // Find the root view to check. If any buttons were previously pressed, this
+ // is the same root view we've been forwarding to. Otherwise, it's the root
+ // view of the target.
+ MenuHostRootView* forward_to_root =
+ current_mouse_pressed_state_ ? current_mouse_event_target_
+ : GetRootView(source, event.location());
+
+ current_mouse_pressed_state_ |= event.changed_button_flags();
+
+ if (forward_to_root) {
+ ui::MouseEvent event_for_root(event);
+ ConvertLocatedEventForRootView(source, forward_to_root, &event_for_root);
+ View* view =
+ forward_to_root->GetEventHandlerForPoint(event_for_root.location());
+ // Empty menu items are always handled by the menu controller.
+ if (!view || view->id() != MenuItemView::kEmptyMenuItemViewID) {
+ bool processed = forward_to_root->ProcessMousePressed(event_for_root);
+ // If the event was processed, the root view becomes our current mouse
+ // handler...
+ if (processed && !current_mouse_event_target_) {
+ current_mouse_event_target_ = forward_to_root;
+ }
+
+ // ...and we always return the result of the current handler.
+ if (current_mouse_event_target_)
+ return processed;
+ }
+ }
+
+ // Otherwise, the menu handles this click directly.
SetSelectionOnPointerDown(source, event);
+ return true;
}
-void MenuController::OnMouseDragged(SubmenuView* source,
+bool MenuController::OnMouseDragged(SubmenuView* source,
const ui::MouseEvent& event) {
+ if (current_mouse_event_target_) {
+ ui::MouseEvent event_for_root(event);
+ ConvertLocatedEventForRootView(source, current_mouse_event_target_,
+ &event_for_root);
+ return current_mouse_event_target_->ProcessMouseDragged(event_for_root);
+ }
+
MenuPart part = GetMenuPart(source, event.location());
UpdateScrolling(part);
if (!blocking_run_)
- return;
+ return false;
if (possible_drag_) {
if (View::ExceededDragThreshold(event.location() - press_pt_))
StartDrag(source, press_pt_);
- return;
+ return true;
}
MenuItemView* mouse_menu = NULL;
if (part.type == MenuPart::MENU_ITEM) {
@@ -511,10 +553,29 @@ void MenuController::OnMouseDragged(SubmenuView* source,
ShowSiblingMenu(source, event.location());
}
UpdateActiveMouseView(source, event, mouse_menu);
+
+ return true;
}
void MenuController::OnMouseReleased(SubmenuView* source,
const ui::MouseEvent& event) {
+ current_mouse_pressed_state_ &= ~event.changed_button_flags();
+
+ if (current_mouse_event_target_) {
+ // If this was the final mouse button, then remove the forwarding target.
+ // We need to do this *before* dispatching the event to the root view
+ // because there's a chance that the event will open a nested (and blocking)
+ // menu, and we need to not have a forwarded root view.
+ MenuHostRootView* cached_event_target = current_mouse_event_target_;
+ if (!current_mouse_pressed_state_)
+ current_mouse_event_target_ = nullptr;
+ ui::MouseEvent event_for_root(event);
+ ConvertLocatedEventForRootView(source, cached_event_target,
+ &event_for_root);
+ cached_event_target->ProcessMouseReleased(event_for_root);
+ return;
+ }
+
if (!blocking_run_)
return;
@@ -589,6 +650,17 @@ void MenuController::OnMouseReleased(SubmenuView* source,
void MenuController::OnMouseMoved(SubmenuView* source,
const ui::MouseEvent& event) {
+ if (current_mouse_event_target_) {
+ ui::MouseEvent event_for_root(event);
+ ConvertLocatedEventForRootView(source, current_mouse_event_target_,
+ &event_for_root);
+ current_mouse_event_target_->ProcessMouseMoved(event_for_root);
+ return;
+ }
+
+ MenuHostRootView* root_view = GetRootView(source, event.location());
+ if (root_view)
+ root_view->ProcessMouseMoved(event);
HandleMouseLocation(source, event.location());
}
@@ -652,6 +724,23 @@ void MenuController::OnGestureEvent(SubmenuView* source,
part.submenu->OnGestureEvent(event);
}
+View* MenuController::GetTooltipHandlerForPoint(SubmenuView* source,
+ const gfx::Point& point) {
+ MenuHostRootView* root_view = GetRootView(source, point);
+ return root_view ? root_view->ProcessGetTooltipHandlerForPoint(point)
+ : nullptr;
+}
+
+void MenuController::ViewHierarchyChanged(
+ SubmenuView* source,
+ const View::ViewHierarchyChangedDetails& details) {
+ // If the current mouse handler is removed, remove it as the handler.
+ if (!details.is_add && details.child == current_mouse_event_target_) {
+ current_mouse_event_target_ = nullptr;
+ current_mouse_pressed_state_ = 0;
+ }
+}
+
bool MenuController::GetDropFormats(
SubmenuView* source,
int* formats,
@@ -805,6 +894,11 @@ void MenuController::OnDragWillStart() {
void MenuController::OnDragComplete(bool should_close) {
DCHECK(drag_in_progress_);
drag_in_progress_ = false;
+ // During a drag, mouse events are processed directly by the widget, and not
+ // sent to the MenuController. At drag completion, reset pressed state and
+ // the event target.
+ current_mouse_pressed_state_ = 0;
+ current_mouse_event_target_ = nullptr;
if (showing_ && should_close && GetActiveInstance() == this) {
CloseAllNestedMenus();
Cancel(EXIT_ALL);
@@ -1149,6 +1243,8 @@ MenuController::MenuController(ui::NativeTheme* theme,
menu_start_time_(base::TimeTicks()),
is_combobox_(false),
item_selected_by_touch_(false),
+ current_mouse_event_target_(nullptr),
+ current_mouse_pressed_state_(0),
message_loop_(MenuMessageLoop::Create()) {
active_instance_ = this;
}
@@ -1428,6 +1524,26 @@ bool MenuController::GetMenuPartByScreenCoordinateImpl(
return true;
}
+MenuHostRootView* MenuController::GetRootView(SubmenuView* submenu,
+ const gfx::Point& source_loc) {
+ MenuPart part = GetMenuPart(submenu, source_loc);
+ SubmenuView* view = part.submenu;
+ return view && view->GetWidget()
+ ? static_cast<MenuHostRootView*>(view->GetWidget()->GetRootView())
+ : nullptr;
+}
+
+void MenuController::ConvertLocatedEventForRootView(View* source,
+ View* dst,
+ ui::LocatedEvent* event) {
+ if (source->GetWidget()->GetRootView() == dst)
+ return;
+ gfx::Point new_location(event->location());
+ View::ConvertPointToScreen(source, &new_location);
+ View::ConvertPointFromScreen(dst, &new_location);
+ event->set_location(new_location);
+}
+
bool MenuController::DoesSubmenuContainLocation(SubmenuView* submenu,
const gfx::Point& screen_loc) {
gfx::Point view_loc = screen_loc;
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 1fa1e90..0b5344d 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -132,13 +132,16 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
//
// NOTE: the coordinates of the events are in that of the
// MenuScrollViewContainer.
- void OnMousePressed(SubmenuView* source, const ui::MouseEvent& event);
- void OnMouseDragged(SubmenuView* source, const ui::MouseEvent& event);
+ bool OnMousePressed(SubmenuView* source, const ui::MouseEvent& event);
+ bool OnMouseDragged(SubmenuView* source, const ui::MouseEvent& event);
void OnMouseReleased(SubmenuView* source, const ui::MouseEvent& event);
void OnMouseMoved(SubmenuView* source, const ui::MouseEvent& event);
void OnMouseEntered(SubmenuView* source, const ui::MouseEvent& event);
bool OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event);
void OnGestureEvent(SubmenuView* source, ui::GestureEvent* event);
+ View* GetTooltipHandlerForPoint(SubmenuView* source, const gfx::Point& point);
+ void ViewHierarchyChanged(SubmenuView* source,
+ const View::ViewHierarchyChangedDetails& details);
bool GetDropFormats(SubmenuView* source,
int* formats,
@@ -369,6 +372,17 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
const gfx::Point& screen_loc,
MenuPart* part);
+ // Returns the RootView of the target for the mouse event, if there is a
+ // target at |source_loc|.
+ MenuHostRootView* GetRootView(SubmenuView* source,
+ const gfx::Point& source_loc);
+
+ // Converts the located event from |source|'s geometry to |dst|'s geometry,
+ // iff the root view of source and dst differ.
+ void ConvertLocatedEventForRootView(View* source,
+ View* dst,
+ ui::LocatedEvent* event);
+
// Returns true if the SubmenuView contains the specified location. This does
// NOT included the scroll buttons, only the submenu view.
bool DoesSubmenuContainLocation(SubmenuView* submenu,
@@ -641,6 +655,15 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
// Set to true if the menu item was selected by touch.
bool item_selected_by_touch_;
+ // During mouse event handling, this is the RootView to forward mouse events
+ // to. We need this, because if we forward one event to it (e.g., mouse
+ // pressed), subsequent events (like dragging) should also go to it, even if
+ // the mouse is no longer over the view.
+ MenuHostRootView* current_mouse_event_target_;
+
+ // A mask of the EventFlags for the mouse buttons currently pressed.
+ int current_mouse_pressed_state_;
+
scoped_ptr<MenuMessageLoop> message_loop_;
DISALLOW_COPY_AND_ASSIGN(MenuController);
diff --git a/ui/views/controls/menu/menu_host_root_view.cc b/ui/views/controls/menu/menu_host_root_view.cc
index 1bc834a..362971f 100644
--- a/ui/views/controls/menu/menu_host_root_view.cc
+++ b/ui/views/controls/menu/menu_host_root_view.cc
@@ -10,42 +10,25 @@
namespace views {
-MenuHostRootView::MenuHostRootView(Widget* widget,
- SubmenuView* submenu)
- : internal::RootView(widget),
- submenu_(submenu),
- forward_drag_to_menu_controller_(true) {
-}
+MenuHostRootView::MenuHostRootView(Widget* widget, SubmenuView* submenu)
+ : internal::RootView(widget), submenu_(submenu) {}
bool MenuHostRootView::OnMousePressed(const ui::MouseEvent& event) {
- forward_drag_to_menu_controller_ =
- !GetLocalBounds().Contains(event.location()) ||
- !RootView::OnMousePressed(event) ||
- DoesEventTargetEmptyMenuItem(event);
-
- if (forward_drag_to_menu_controller_ && GetMenuController())
- GetMenuController()->OnMousePressed(submenu_, event);
- return true;
+ return GetMenuController() &&
+ GetMenuController()->OnMousePressed(submenu_, event);
}
bool MenuHostRootView::OnMouseDragged(const ui::MouseEvent& event) {
- if (forward_drag_to_menu_controller_ && GetMenuController()) {
- GetMenuController()->OnMouseDragged(submenu_, event);
- return true;
- }
- return RootView::OnMouseDragged(event);
+ return GetMenuController() &&
+ GetMenuController()->OnMouseDragged(submenu_, event);
}
void MenuHostRootView::OnMouseReleased(const ui::MouseEvent& event) {
- RootView::OnMouseReleased(event);
- if (forward_drag_to_menu_controller_ && GetMenuController()) {
- forward_drag_to_menu_controller_ = false;
+ if (GetMenuController())
GetMenuController()->OnMouseReleased(submenu_, event);
- }
}
void MenuHostRootView::OnMouseMoved(const ui::MouseEvent& event) {
- RootView::OnMouseMoved(event);
if (GetMenuController())
GetMenuController()->OnMouseMoved(submenu_, event);
}
@@ -55,6 +38,40 @@ bool MenuHostRootView::OnMouseWheel(const ui::MouseWheelEvent& event) {
GetMenuController()->OnMouseWheel(submenu_, event);
}
+View* MenuHostRootView::GetTooltipHandlerForPoint(const gfx::Point& point) {
+ return GetMenuController()
+ ? GetMenuController()->GetTooltipHandlerForPoint(submenu_, point)
+ : nullptr;
+}
+
+void MenuHostRootView::ViewHierarchyChanged(
+ const ViewHierarchyChangedDetails& details) {
+ if (GetMenuController())
+ GetMenuController()->ViewHierarchyChanged(submenu_, details);
+ RootView::ViewHierarchyChanged(details);
+}
+
+bool MenuHostRootView::ProcessMousePressed(const ui::MouseEvent& event) {
+ return RootView::OnMousePressed(event);
+}
+
+bool MenuHostRootView::ProcessMouseDragged(const ui::MouseEvent& event) {
+ return RootView::OnMouseDragged(event);
+}
+
+void MenuHostRootView::ProcessMouseReleased(const ui::MouseEvent& event) {
+ RootView::OnMouseReleased(event);
+}
+
+void MenuHostRootView::ProcessMouseMoved(const ui::MouseEvent& event) {
+ RootView::OnMouseMoved(event);
+}
+
+View* MenuHostRootView::ProcessGetTooltipHandlerForPoint(
+ const gfx::Point& point) {
+ return RootView::GetTooltipHandlerForPoint(point);
+}
+
void MenuHostRootView::OnEventProcessingFinished(ui::Event* event) {
RootView::OnEventProcessingFinished(event);
@@ -70,10 +87,4 @@ MenuController* MenuHostRootView::GetMenuController() {
return submenu_ ? submenu_->GetMenuItem()->GetMenuController() : NULL;
}
-bool MenuHostRootView::DoesEventTargetEmptyMenuItem(
- const ui::MouseEvent& event) {
- View* view = GetEventHandlerForPoint(event.location());
- return view && view->id() == MenuItemView::kEmptyMenuItemViewID;
-}
-
} // namespace views
diff --git a/ui/views/controls/menu/menu_host_root_view.h b/ui/views/controls/menu/menu_host_root_view.h
index cdb6b40..1a2aabd 100644
--- a/ui/views/controls/menu/menu_host_root_view.h
+++ b/ui/views/controls/menu/menu_host_root_view.h
@@ -30,6 +30,15 @@ class MenuHostRootView : public internal::RootView {
void OnMouseReleased(const ui::MouseEvent& event) override;
void OnMouseMoved(const ui::MouseEvent& event) override;
bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
+ View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
+ void ViewHierarchyChanged(const ViewHierarchyChangedDetails& details)
+ override;
+
+ bool ProcessMousePressed(const ui::MouseEvent& event);
+ bool ProcessMouseDragged(const ui::MouseEvent& event);
+ void ProcessMouseReleased(const ui::MouseEvent& event);
+ void ProcessMouseMoved(const ui::MouseEvent& event);
+ View* ProcessGetTooltipHandlerForPoint(const gfx::Point& point);
private:
// ui::EventProcessor:
@@ -38,15 +47,9 @@ class MenuHostRootView : public internal::RootView {
// Returns the MenuController for this MenuHostRootView.
MenuController* GetMenuController();
- // Returns true if event targets EmptyMenu.
- bool DoesEventTargetEmptyMenuItem(const ui::MouseEvent& event);
-
// The SubmenuView we contain.
SubmenuView* submenu_;
- // Whether mouse dragged/released should be forwarded to the MenuController.
- bool forward_drag_to_menu_controller_;
-
DISALLOW_COPY_AND_ASSIGN(MenuHostRootView);
};