Move views event targeting into ViewTargeterDelegate::TargetForRect()

Move the default event-targeting implementation for views from
View::GetEventHandlerForRect() into the new method
ViewTargeterDelegate::TargetForRect() and make the method
View::GetEventHandlerForRect() call into its effective ViewTargeter
instead. The "effective" targeter for a View is the
ViewTargeter installed on that View if one exists, otherwise
it is the ViewTargeter installed on its root view.

Once this change has landed, the overrides of
View::GetEventHandlerForRect() can be removed and their
implementations moved into overrides of
ViewTargeterDelegate::TargetForRect().
View::GetEventHandlerForRect() can then be made
non-virtual.

BUG=391845
TEST=existing coverage in ViewTest.GetEventHandlerForRect

Review URL: https://codereview.chromium.org/401933002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284342 0039d316-1c4b-4281-b951-d872f2087c98
parent 4eef7525
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include "ui/gfx/path.h" #include "ui/gfx/path.h"
#include "ui/gfx/point3_f.h" #include "ui/gfx/point3_f.h"
#include "ui/gfx/point_conversions.h" #include "ui/gfx/point_conversions.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/scoped_canvas.h" #include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/screen.h" #include "ui/gfx/screen.h"
#include "ui/gfx/skia_util.h" #include "ui/gfx/skia_util.h"
...@@ -42,7 +41,6 @@ ...@@ -42,7 +41,6 @@
#include "ui/views/drag_controller.h" #include "ui/views/drag_controller.h"
#include "ui/views/focus/view_storage.h" #include "ui/views/focus/view_storage.h"
#include "ui/views/layout/layout_manager.h" #include "ui/views/layout/layout_manager.h"
#include "ui/views/rect_based_targeting_utils.h"
#include "ui/views/views_delegate.h" #include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/root_view.h" #include "ui/views/widget/root_view.h"
...@@ -61,11 +59,6 @@ const bool kContextMenuOnMousePress = false; ...@@ -61,11 +59,6 @@ const bool kContextMenuOnMousePress = false;
const bool kContextMenuOnMousePress = true; const bool kContextMenuOnMousePress = true;
#endif #endif
// The minimum percentage of a view's area that needs to be covered by a rect
// representing a touch region in order for that view to be considered by the
// rect-based targeting algorithm.
static const float kRectTargetOverlap = 0.6f;
// Default horizontal drag threshold in pixels. // Default horizontal drag threshold in pixels.
// Same as what gtk uses. // Same as what gtk uses.
const int kDefaultHorizontalDragThreshold = 8; const int kDefaultHorizontalDragThreshold = 8;
...@@ -850,77 +843,7 @@ View* View::GetEventHandlerForPoint(const gfx::Point& point) { ...@@ -850,77 +843,7 @@ View* View::GetEventHandlerForPoint(const gfx::Point& point) {
} }
View* View::GetEventHandlerForRect(const gfx::Rect& rect) { View* View::GetEventHandlerForRect(const gfx::Rect& rect) {
// |rect_view| represents the current best candidate to return return GetEffectiveViewTargeter()->TargetForRect(this, rect);
// if rect-based targeting (i.e., fuzzing) is used.
// |rect_view_distance| is used to keep track of the distance
// between the center point of |rect_view| and the center
// point of |rect|.
View* rect_view = NULL;
int rect_view_distance = INT_MAX;
// |point_view| represents the view that would have been returned
// from this function call if point-based targeting were used.
View* point_view = NULL;
for (int i = child_count() - 1; i >= 0; --i) {
View* child = child_at(i);
if (!child->CanProcessEventsWithinSubtree())
continue;
// Ignore any children which are invisible or do not intersect |rect|.
if (!child->visible())
continue;
gfx::RectF rect_in_child_coords_f(rect);
ConvertRectToTarget(this, child, &rect_in_child_coords_f);
gfx::Rect rect_in_child_coords = gfx::ToEnclosingRect(
rect_in_child_coords_f);
if (!child->HitTestRect(rect_in_child_coords))
continue;
View* cur_view = child->GetEventHandlerForRect(rect_in_child_coords);
if (views::UsePointBasedTargeting(rect))
return cur_view;
gfx::RectF cur_view_bounds_f(cur_view->GetLocalBounds());
ConvertRectToTarget(cur_view, this, &cur_view_bounds_f);
gfx::Rect cur_view_bounds = gfx::ToEnclosingRect(
cur_view_bounds_f);
if (views::PercentCoveredBy(cur_view_bounds, rect) >= kRectTargetOverlap) {
// |cur_view| is a suitable candidate for rect-based targeting.
// Check to see if it is the closest suitable candidate so far.
gfx::Point touch_center(rect.CenterPoint());
int cur_dist = views::DistanceSquaredFromCenterToPoint(touch_center,
cur_view_bounds);
if (!rect_view || cur_dist < rect_view_distance) {
rect_view = cur_view;
rect_view_distance = cur_dist;
}
} else if (!rect_view && !point_view) {
// Rect-based targeting has not yielded any candidates so far. Check
// if point-based targeting would have selected |cur_view|.
gfx::Point point_in_child_coords(rect_in_child_coords.CenterPoint());
if (child->HitTestPoint(point_in_child_coords))
point_view = child->GetEventHandlerForPoint(point_in_child_coords);
}
}
if (views::UsePointBasedTargeting(rect) || (!rect_view && !point_view))
return this;
// If |this| is a suitable candidate for rect-based targeting, check to
// see if it is closer than the current best suitable candidate so far.
gfx::Rect local_bounds(GetLocalBounds());
if (views::PercentCoveredBy(local_bounds, rect) >= kRectTargetOverlap) {
gfx::Point touch_center(rect.CenterPoint());
int cur_dist = views::DistanceSquaredFromCenterToPoint(touch_center,
local_bounds);
if (!rect_view || cur_dist < rect_view_distance)
rect_view = this;
}
return rect_view ? rect_view : point_view;
} }
bool View::CanProcessEventsWithinSubtree() const { bool View::CanProcessEventsWithinSubtree() const {
...@@ -928,6 +851,7 @@ bool View::CanProcessEventsWithinSubtree() const { ...@@ -928,6 +851,7 @@ bool View::CanProcessEventsWithinSubtree() const {
} }
View* View::GetTooltipHandlerForPoint(const gfx::Point& point) { View* View::GetTooltipHandlerForPoint(const gfx::Point& point) {
// TODO(tdanderson): Move this implementation into ViewTargetDelegate.
if (!HitTestPoint(point) || !CanProcessEventsWithinSubtree()) if (!HitTestPoint(point) || !CanProcessEventsWithinSubtree())
return NULL; return NULL;
...@@ -963,11 +887,7 @@ bool View::HitTestPoint(const gfx::Point& point) const { ...@@ -963,11 +887,7 @@ bool View::HitTestPoint(const gfx::Point& point) const {
} }
bool View::HitTestRect(const gfx::Rect& rect) const { bool View::HitTestRect(const gfx::Rect& rect) const {
ViewTargeter* view_targeter = targeter(); return GetEffectiveViewTargeter()->DoesIntersectRect(this, rect);
if (!view_targeter)
view_targeter = GetWidget()->GetRootView()->targeter();
CHECK(view_targeter);
return view_targeter->DoesIntersectRect(this, rect);
} }
bool View::IsMouseHovered() { bool View::IsMouseHovered() {
...@@ -2350,6 +2270,14 @@ void View::ProcessMouseReleased(const ui::MouseEvent& event) { ...@@ -2350,6 +2270,14 @@ void View::ProcessMouseReleased(const ui::MouseEvent& event) {
// WARNING: we may have been deleted. // WARNING: we may have been deleted.
} }
ViewTargeter* View::GetEffectiveViewTargeter() const {
ViewTargeter* view_targeter = targeter();
if (!view_targeter)
view_targeter = GetWidget()->GetRootView()->targeter();
CHECK(view_targeter);
return view_targeter;
}
// Accelerators ---------------------------------------------------------------- // Accelerators ----------------------------------------------------------------
void View::RegisterPendingAccelerators() { void View::RegisterPendingAccelerators() {
......
...@@ -551,17 +551,15 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -551,17 +551,15 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// The points, rects, mouse locations, and touch locations in the following // The points, rects, mouse locations, and touch locations in the following
// functions are in the view's coordinates, except for a RootView. // functions are in the view's coordinates, except for a RootView.
// Convenience functions which calls into GetEventHandler() with // A convenience function which calls into GetEventHandlerForRect() with
// a 1x1 rect centered at |point|. // a 1x1 rect centered at |point|. |point| is in the local coordinate
// space of |this|.
View* GetEventHandlerForPoint(const gfx::Point& point); View* GetEventHandlerForPoint(const gfx::Point& point);
// If point-based targeting should be used, return the deepest visible // Returns the View that should be the target of an event having |rect| as
// descendant that contains the center point of |rect|. // its location, or NULL if no such target exists. |rect| is in the local
// If rect-based targeting (i.e., fuzzing) should be used, return the // coordinate space of |this|.
// closest visible descendant having at least kRectTargetOverlap of // TODO(tdanderson): Make this function non-virtual.
// its area covered by |rect|. If no such descendant exists, return the
// deepest visible descendant that contains the center point of |rect|.
// See http://goo.gl/3Jp2BD for more information about rect-based targeting.
virtual View* GetEventHandlerForRect(const gfx::Rect& rect); virtual View* GetEventHandlerForRect(const gfx::Rect& rect);
// Returns the deepest visible descendant that contains the specified point // Returns the deepest visible descendant that contains the specified point
...@@ -577,12 +575,12 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -577,12 +575,12 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event); virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event);
// A convenience function which calls HitTestRect() with a rect of size // A convenience function which calls HitTestRect() with a rect of size
// 1x1 and an origin of |point|. // 1x1 and an origin of |point|. |point| is in the local coordinate space
// of |this|.
bool HitTestPoint(const gfx::Point& point) const; bool HitTestPoint(const gfx::Point& point) const;
// Tests whether |rect| intersects this view's bounds using the ViewTargeter // Returns true if |rect| intersects this view's bounds. |rect| is in the
// installed on |this|. If there is no ViewTargeter installed on |this|, the // local coordinate space of |this|.
// ViewTargeter installed on the root view is used instead.
bool HitTestRect(const gfx::Rect& rect) const; bool HitTestRect(const gfx::Rect& rect) const;
// Returns true if this view or any of its descendants are permitted to // Returns true if this view or any of its descendants are permitted to
...@@ -1393,6 +1391,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -1393,6 +1391,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
bool ProcessMouseDragged(const ui::MouseEvent& event); bool ProcessMouseDragged(const ui::MouseEvent& event);
void ProcessMouseReleased(const ui::MouseEvent& event); void ProcessMouseReleased(const ui::MouseEvent& event);
// Returns the ViewTargeter installed on |this| if one exists,
// otherwise returns the ViewTargeter installed on our root view.
// The return value is guaranteed to be non-null.
ViewTargeter* GetEffectiveViewTargeter() const;
// Accelerators -------------------------------------------------------------- // Accelerators --------------------------------------------------------------
// Registers this view's keyboard accelerators that are not registered to // Registers this view's keyboard accelerators that are not registered to
......
...@@ -13,16 +13,20 @@ namespace views { ...@@ -13,16 +13,20 @@ namespace views {
ViewTargeter::ViewTargeter(ViewTargeterDelegate* delegate) ViewTargeter::ViewTargeter(ViewTargeterDelegate* delegate)
: delegate_(delegate) { : delegate_(delegate) {
DCHECK(delegate_);
} }
ViewTargeter::~ViewTargeter() {} ViewTargeter::~ViewTargeter() {}
bool ViewTargeter::DoesIntersectRect(const View* target, bool ViewTargeter::DoesIntersectRect(const View* target,
const gfx::Rect& rect) const { const gfx::Rect& rect) const {
DCHECK(delegate_);
return delegate_->DoesIntersectRect(target, rect); return delegate_->DoesIntersectRect(target, rect);
} }
View* ViewTargeter::TargetForRect(View* root, const gfx::Rect& rect) const {
return delegate_->TargetForRect(root, rect);
}
gfx::RectF ViewTargeter::BoundsForEvent(const ui::LocatedEvent& event) const { gfx::RectF ViewTargeter::BoundsForEvent(const ui::LocatedEvent& event) const {
gfx::RectF event_bounds(event.location_f(), gfx::SizeF(1, 1)); gfx::RectF event_bounds(event.location_f(), gfx::SizeF(1, 1));
if (event.IsGestureEvent()) { if (event.IsGestureEvent()) {
......
...@@ -18,6 +18,8 @@ class ViewTargeterDelegate; ...@@ -18,6 +18,8 @@ class ViewTargeterDelegate;
// derived classes) is installed on a View to specify the // derived classes) is installed on a View to specify the
// targeting behaviour to be used for the subtree rooted at // targeting behaviour to be used for the subtree rooted at
// that View. // that View.
// TODO(tdanderson): Remove overrides of all EventHandler methods except for
// FindTargetForEvent() and FindNextBestTarget().
class VIEWS_EXPORT ViewTargeter : public ui::EventTargeter { class VIEWS_EXPORT ViewTargeter : public ui::EventTargeter {
public: public:
explicit ViewTargeter(ViewTargeterDelegate* delegate); explicit ViewTargeter(ViewTargeterDelegate* delegate);
...@@ -26,6 +28,9 @@ class VIEWS_EXPORT ViewTargeter : public ui::EventTargeter { ...@@ -26,6 +28,9 @@ class VIEWS_EXPORT ViewTargeter : public ui::EventTargeter {
// A call-through to DoesIntersectRect() on |delegate_|. // A call-through to DoesIntersectRect() on |delegate_|.
bool DoesIntersectRect(const View* target, const gfx::Rect& rect) const; bool DoesIntersectRect(const View* target, const gfx::Rect& rect) const;
// A call-through to TargetForRect() on |delegate_|.
View* TargetForRect(View* root, const gfx::Rect& rect) const;
protected: protected:
// Returns the location of |event| represented as a rect. If |event| is // Returns the location of |event| represented as a rect. If |event| is
// a gesture event, its bounding box is returned. Otherwise, a 1x1 rect // a gesture event, its bounding box is returned. Otherwise, a 1x1 rect
......
...@@ -2,14 +2,103 @@ ...@@ -2,14 +2,103 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "ui/views/view.h"
#include "ui/views/view_targeter_delegate.h" #include "ui/views/view_targeter_delegate.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/views/rect_based_targeting_utils.h"
#include "ui/views/view.h"
namespace {
// The minimum percentage of a view's area that needs to be covered by a rect
// representing a touch region in order for that view to be considered by the
// rect-based targeting algorithm.
static const float kRectTargetOverlap = 0.6f;
} // namespace
namespace views { namespace views {
// TODO(tdanderson): Move the contents of rect_based_targeting_utils.(h|cc)
// into here.
bool ViewTargeterDelegate::DoesIntersectRect(const View* target, bool ViewTargeterDelegate::DoesIntersectRect(const View* target,
const gfx::Rect& rect) const { const gfx::Rect& rect) const {
return target->GetLocalBounds().Intersects(rect); return target->GetLocalBounds().Intersects(rect);
} }
View* ViewTargeterDelegate::TargetForRect(View* root, const gfx::Rect& rect) {
// |rect_view| represents the current best candidate to return
// if rect-based targeting (i.e., fuzzing) is used.
// |rect_view_distance| is used to keep track of the distance
// between the center point of |rect_view| and the center
// point of |rect|.
View* rect_view = NULL;
int rect_view_distance = INT_MAX;
// |point_view| represents the view that would have been returned
// from this function call if point-based targeting were used.
View* point_view = NULL;
for (int i = root->child_count() - 1; i >= 0; --i) {
View* child = root->child_at(i);
if (!child->CanProcessEventsWithinSubtree())
continue;
// Ignore any children which are invisible or do not intersect |rect|.
if (!child->visible())
continue;
gfx::RectF rect_in_child_coords_f(rect);
View::ConvertRectToTarget(root, child, &rect_in_child_coords_f);
gfx::Rect rect_in_child_coords = gfx::ToEnclosingRect(
rect_in_child_coords_f);
if (!child->HitTestRect(rect_in_child_coords))
continue;
View* cur_view = child->GetEventHandlerForRect(rect_in_child_coords);
if (views::UsePointBasedTargeting(rect))
return cur_view;
gfx::RectF cur_view_bounds_f(cur_view->GetLocalBounds());
View::ConvertRectToTarget(cur_view, root, &cur_view_bounds_f);
gfx::Rect cur_view_bounds = gfx::ToEnclosingRect(
cur_view_bounds_f);
if (views::PercentCoveredBy(cur_view_bounds, rect) >= kRectTargetOverlap) {
// |cur_view| is a suitable candidate for rect-based targeting.
// Check to see if it is the closest suitable candidate so far.
gfx::Point touch_center(rect.CenterPoint());
int cur_dist = views::DistanceSquaredFromCenterToPoint(touch_center,
cur_view_bounds);
if (!rect_view || cur_dist < rect_view_distance) {
rect_view = cur_view;
rect_view_distance = cur_dist;
}
} else if (!rect_view && !point_view) {
// Rect-based targeting has not yielded any candidates so far. Check
// if point-based targeting would have selected |cur_view|.
gfx::Point point_in_child_coords(rect_in_child_coords.CenterPoint());
if (child->HitTestPoint(point_in_child_coords))
point_view = child->GetEventHandlerForPoint(point_in_child_coords);
}
}
if (views::UsePointBasedTargeting(rect) || (!rect_view && !point_view))
return root;
// If |root| is a suitable candidate for rect-based targeting, check to
// see if it is closer than the current best suitable candidate so far.
gfx::Rect local_bounds(root->GetLocalBounds());
if (views::PercentCoveredBy(local_bounds, rect) >= kRectTargetOverlap) {
gfx::Point touch_center(rect.CenterPoint());
int cur_dist = views::DistanceSquaredFromCenterToPoint(touch_center,
local_bounds);
if (!rect_view || cur_dist < rect_view_distance)
rect_view = root;
}
return rect_view ? rect_view : point_view;
}
} // namespace views } // namespace views
...@@ -29,6 +29,16 @@ class VIEWS_EXPORT ViewTargeterDelegate { ...@@ -29,6 +29,16 @@ class VIEWS_EXPORT ViewTargeterDelegate {
virtual bool DoesIntersectRect(const View* target, virtual bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const; const gfx::Rect& rect) const;
// If point-based targeting should be used, return the deepest visible
// descendant of |root| that contains the center point of |rect|.
// If rect-based targeting (i.e., fuzzing) should be used, return the
// closest visible descendant of |root| having at least kRectTargetOverlap of
// its area covered by |rect|. If no such descendant exists, return the
// deepest visible descendant of |root| that contains the center point of
// |rect|. See http://goo.gl/3Jp2BD for more information about rect-based
// targeting.
virtual View* TargetForRect(View* root, const gfx::Rect& rect);
private: private:
DISALLOW_COPY_AND_ASSIGN(ViewTargeterDelegate); DISALLOW_COPY_AND_ASSIGN(ViewTargeterDelegate);
}; };
......
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