Commit 07596fa6 authored by Abhijeet Singh's avatar Abhijeet Singh Committed by Commit Bot

Prevent event-bubbling for Quick Answers views

The child buttons inside the Quick Answers view, which is itself a
button, do not work properly because of the way events are bubbled down
the view hierarchy for Quick Answers views.

To prevent that, this change will modify the event-handling of the
related view by propagating events in a bottom-up fashion instead and
stopping at the first child to handle the event to avoid simultaneous
firing of container buttons.

Bug: b:152922056
Test: On Chrome OS VM.
Change-Id: I5e309cf9cecf88e79651a138375025b7120ab2eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2140995
Commit-Queue: Abhijeet Singh <siabhijeet@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761176}
parent d6eb75c4
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "base/containers/adapters.h"
#include "chromeos/components/quick_answers/quick_answers_model.h" #include "chromeos/components/quick_answers/quick_answers_model.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -109,6 +110,7 @@ View* AddHorizontalUiElements( ...@@ -109,6 +110,7 @@ View* AddHorizontalUiElements(
// This class handles mouse events, and update background color or // This class handles mouse events, and update background color or
// dismiss quick answers view. // dismiss quick answers view.
// TODO (siabhijeet): Migrate to using two-phased event dispatching.
class QuickAnswersViewHandler : public ui::EventHandler { class QuickAnswersViewHandler : public ui::EventHandler {
public: public:
explicit QuickAnswersViewHandler(QuickAnswersView* quick_answers_view) explicit QuickAnswersViewHandler(QuickAnswersView* quick_answers_view)
...@@ -132,42 +134,69 @@ class QuickAnswersViewHandler : public ui::EventHandler { ...@@ -132,42 +134,69 @@ class QuickAnswersViewHandler : public ui::EventHandler {
if (!event->IsLocatedEvent()) if (!event->IsLocatedEvent())
return; return;
// Clone event and forward down the event-hierarchy. // Clone event to forward down the view-hierarchy.
ui::LocatedEvent* clone = auto clone = ui::Event::Clone(*event);
ui::Event::Clone(*event).release()->AsLocatedEvent(); ui::Event::DispatcherApi(clone.get()).set_target(event->target());
ui::Event::DispatcherApi(clone).set_target(event->target()); auto* to_dispatch = clone->AsLocatedEvent();
DoDispatchEvent(quick_answers_view_, clone); auto location = to_dispatch->target()->GetScreenLocation(*to_dispatch);
// `ET_MOUSE_MOVED` events outside the top-view's bounds are also dispatched
// to clear any set hover-state.
bool dispatch_event =
(quick_answers_view_->GetBoundsInScreen().Contains(location) ||
to_dispatch->type() == ui::EventType::ET_MOUSE_MOVED);
if (dispatch_event) {
// Convert to local coordinates and forward to the top-view.
views::View::ConvertPointFromScreen(quick_answers_view_, &location);
to_dispatch->set_location(location);
ui::Event::DispatcherApi(to_dispatch).set_target(quick_answers_view_);
DoDispatchEvent(quick_answers_view_, to_dispatch);
// Prevent the currently shown context-menu from receiving click events
// (which are outside its bounds) and thus getting dismissed.
if (event->type() == ui::EventType::ET_MOUSE_PRESSED)
event->StopPropagation();
}
// Show tooltips. // Show tooltips.
auto* tooltip_manager = auto* tooltip_manager =
quick_answers_view_->GetWidget()->GetTooltipManager(); quick_answers_view_->GetWidget()->GetTooltipManager();
if (tooltip_manager) if (tooltip_manager)
tooltip_manager->UpdateTooltip(); tooltip_manager->UpdateTooltip();
// Do not dismiss context menu for clicks inside the view.
auto location = clone->target()->GetScreenLocation(*clone);
if (quick_answers_view_->GetBoundsInScreen().Contains(location))
event->StopPropagation();
} }
private: private:
// Returns true if event was consumed by |view| or its children.
bool DoDispatchEvent(views::View* view, ui::LocatedEvent* event) { bool DoDispatchEvent(views::View* view, ui::LocatedEvent* event) {
if (event->handled()) DCHECK(view && event);
return true;
// Post-order dispatch the event on child views in reverse Z-order.
// Convert |event| to local coordinates of |view|. auto children = view->GetChildrenInZOrder();
gfx::Point location = event->target()->GetScreenLocation(*event); for (auto* child : base::Reversed(children)) {
views::View::ConvertPointFromScreen(view, &location); // Dispatch a fresh event to preserve the |event| for the parent target.
event->set_location(location); std::unique_ptr<ui::Event> to_dispatch;
ui::Event::DispatcherApi(event).set_target(view); if (event->IsMouseEvent()) {
to_dispatch = std::make_unique<ui::MouseEvent>(*event->AsMouseEvent(),
// Process event and dispatch on children recursively. view, child);
view->OnEvent(event); } else if (event->IsGestureEvent()) {
for (auto* child : view->children()) { to_dispatch = std::make_unique<ui::GestureEvent>(
if (DoDispatchEvent(child, event)) *event->AsGestureEvent(), view, child);
} else {
return false;
}
ui::Event::DispatcherApi(to_dispatch.get()).set_target(child);
if (DoDispatchEvent(child, to_dispatch.get()->AsLocatedEvent()))
return true; return true;
} }
return false; view->OnEvent(event);
// The deepest button explicitly consumes the events to avoid simultaneous
// firing of any enclosing buttons, since the |event| may not always be set
// as handled (like for `ET_MOUSE_RELEASED`).
// TODO (siabhijeet): Avoid housing a button inside another altogether.
bool button_pressed =
views::Button::AsButton(view) && view->HitTestPoint(event->location());
return (event->handled() || button_pressed);
} }
QuickAnswersView* const quick_answers_view_; QuickAnswersView* const quick_answers_view_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment