// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/touch_selection/touch_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/test/motion_event_test_utils.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/touch_selection/touch_handle_orientation.h" using ui::test::MockMotionEvent; namespace ui { namespace { const int kDefaultTapDurationMs = 200; const double kDefaultTapSlop = 10.; const float kDefaultDrawableSize = 10.f; const gfx::RectF kDefaultViewportRect(0, 0, 560, 1200); struct MockDrawableData { MockDrawableData() : orientation(TouchHandleOrientation::UNDEFINED), alpha(0.f), enabled(false), visible(false), rect(0, 0, kDefaultDrawableSize, kDefaultDrawableSize) {} TouchHandleOrientation orientation; float alpha; bool mirror_horizontal; bool mirror_vertical; bool enabled; bool visible; gfx::RectF rect; }; class MockTouchHandleDrawable : public TouchHandleDrawable { public: explicit MockTouchHandleDrawable(MockDrawableData* data) : data_(data) {} ~MockTouchHandleDrawable() override {} void SetEnabled(bool enabled) override { data_->enabled = enabled; } void SetOrientation(TouchHandleOrientation orientation, bool mirror_vertical, bool mirror_horizontal) override { data_->orientation = orientation; data_->mirror_horizontal = mirror_horizontal; data_->mirror_vertical = mirror_vertical; } void SetOrigin(const gfx::PointF& origin) override { data_->rect.set_origin(origin); } void SetAlpha(float alpha) override { data_->alpha = alpha; data_->visible = alpha > 0; } // TODO(AviD): Add unittests for non-zero values of padding ratio once the // code refactoring is completed. float GetDrawableHorizontalPaddingRatio() const override { return 0; } gfx::RectF GetVisibleBounds() const override { return data_->rect; } private: MockDrawableData* data_; }; } // namespace class TouchHandleTest : public testing::Test, public TouchHandleClient { public: TouchHandleTest() : dragging_(false), dragged_(false), tapped_(false), needs_animate_(false) {} ~TouchHandleTest() override {} // TouchHandleClient implementation. void OnDragBegin(const TouchSelectionDraggable& handler, const gfx::PointF& drag_position) override { dragging_ = true; } void OnDragUpdate(const TouchSelectionDraggable& handler, const gfx::PointF& drag_position) override { dragged_ = true; drag_position_ = drag_position; } void OnDragEnd(const TouchSelectionDraggable& handler) override { dragging_ = false; } bool IsWithinTapSlop(const gfx::Vector2dF& delta) const override { return delta.LengthSquared() < (kDefaultTapSlop * kDefaultTapSlop); } void OnHandleTapped(const TouchHandle& handle) override { tapped_ = true; } void SetNeedsAnimate() override { needs_animate_ = true; } scoped_ptr CreateDrawable() override { return make_scoped_ptr(new MockTouchHandleDrawable(&drawable_data_)); } base::TimeDelta GetMaxTapDuration() const override { return base::TimeDelta::FromMilliseconds(kDefaultTapDurationMs); } bool IsAdaptiveHandleOrientationEnabled() const override { // Enable adaptive handle orientation by default for unittests return true; } void Animate(TouchHandle& handle) { needs_animate_ = false; base::TimeTicks now = base::TimeTicks::Now(); while (handle.Animate(now)) now += base::TimeDelta::FromMilliseconds(16); } bool GetAndResetHandleDragged() { bool dragged = dragged_; dragged_ = false; return dragged; } bool GetAndResetHandleTapped() { bool tapped = tapped_; tapped_ = false; return tapped; } bool GetAndResetNeedsAnimate() { bool needs_animate = needs_animate_; needs_animate_ = false; return needs_animate; } void UpdateHandleFocus(TouchHandle& handle, gfx::PointF& top, gfx::PointF& bottom) { handle.SetFocus(top, bottom); handle.UpdateHandleLayout(); } void UpdateHandleOrientation(TouchHandle& handle, TouchHandleOrientation orientation) { handle.SetOrientation(orientation); handle.UpdateHandleLayout(); } void UpdateHandleVisibility(TouchHandle& handle, bool visible, TouchHandle::AnimationStyle animation_style) { handle.SetVisible(visible, animation_style); handle.UpdateHandleLayout(); } void UpdateViewportRect(TouchHandle& handle, gfx::RectF viewport_rect) { handle.SetViewportRect(viewport_rect); handle.UpdateHandleLayout(); } bool IsDragging() const { return dragging_; } const gfx::PointF& DragPosition() const { return drag_position_; } bool NeedsAnimate() const { return needs_animate_; } const MockDrawableData& drawable() { return drawable_data_; } private: gfx::PointF drag_position_; bool dragging_; bool dragged_; bool tapped_; bool needs_animate_; MockDrawableData drawable_data_; }; TEST_F(TouchHandleTest, Visibility) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); EXPECT_FALSE(drawable().visible); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); EXPECT_TRUE(drawable().visible); EXPECT_EQ(1.f, drawable().alpha); UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_NONE); EXPECT_FALSE(drawable().visible); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); EXPECT_TRUE(drawable().visible); EXPECT_EQ(1.f, drawable().alpha); } TEST_F(TouchHandleTest, VisibilityAnimation) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); ASSERT_FALSE(NeedsAnimate()); ASSERT_FALSE(drawable().visible); ASSERT_EQ(0.f, drawable().alpha); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_SMOOTH); EXPECT_TRUE(NeedsAnimate()); EXPECT_FALSE(drawable().visible); EXPECT_EQ(0.f, drawable().alpha); Animate(handle); EXPECT_TRUE(drawable().visible); EXPECT_EQ(1.f, drawable().alpha); ASSERT_FALSE(NeedsAnimate()); UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); EXPECT_TRUE(NeedsAnimate()); EXPECT_TRUE(drawable().visible); EXPECT_EQ(1.f, drawable().alpha); Animate(handle); EXPECT_FALSE(drawable().visible); EXPECT_EQ(0.f, drawable().alpha); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); EXPECT_EQ(1.f, drawable().alpha); EXPECT_FALSE(GetAndResetNeedsAnimate()); UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); EXPECT_EQ(1.f, drawable().alpha); EXPECT_TRUE(GetAndResetNeedsAnimate()); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_SMOOTH); EXPECT_EQ(1.f, drawable().alpha); EXPECT_FALSE(GetAndResetNeedsAnimate()); } TEST_F(TouchHandleTest, Orientation) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); EXPECT_EQ(TouchHandleOrientation::CENTER, drawable().orientation); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); UpdateHandleOrientation(handle, TouchHandleOrientation::LEFT); EXPECT_EQ(TouchHandleOrientation::LEFT, drawable().orientation); UpdateHandleOrientation(handle, TouchHandleOrientation::RIGHT); EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation); UpdateHandleOrientation(handle, TouchHandleOrientation::CENTER); EXPECT_EQ(TouchHandleOrientation::CENTER, drawable().orientation); } TEST_F(TouchHandleTest, Position) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); const gfx::Vector2dF koffset_vector(kDefaultDrawableSize / 2.f, 0); gfx::PointF focus_top; gfx::PointF focus_bottom; EXPECT_EQ(gfx::PointF() - koffset_vector, drawable().rect.origin()); focus_top = gfx::PointF(7.3f, -3.7f); focus_bottom = gfx::PointF(7.3f, -2.7f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_EQ(focus_bottom - koffset_vector, drawable().rect.origin()); focus_top = gfx::PointF(-7.3f, 3.7f); focus_bottom = gfx::PointF(-7.3f, 4.7f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_EQ(focus_bottom - koffset_vector, drawable().rect.origin()); } TEST_F(TouchHandleTest, PositionNotUpdatedWhileFadingOrInvisible) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); ASSERT_TRUE(drawable().visible); ASSERT_FALSE(NeedsAnimate()); const gfx::Vector2dF koffset_vector(kDefaultDrawableSize / 2.f, 0); gfx::PointF old_focus_top(7.3f, -3.7f); gfx::PointF old_focus_bottom(7.3f, -2.7f); UpdateHandleFocus(handle, old_focus_top, old_focus_bottom); ASSERT_EQ(old_focus_bottom - koffset_vector, drawable().rect.origin()); UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); ASSERT_TRUE(NeedsAnimate()); gfx::PointF new_position_top(3.7f, -3.7f); gfx::PointF new_position_bottom(3.7f, -2.7f); UpdateHandleFocus(handle, new_position_top, new_position_bottom); EXPECT_EQ(old_focus_bottom - koffset_vector, drawable().rect.origin()); EXPECT_TRUE(NeedsAnimate()); // While the handle is fading, the new position should not take affect. base::TimeTicks now = base::TimeTicks::Now(); while (handle.Animate(now)) { EXPECT_EQ(old_focus_bottom - koffset_vector, drawable().rect.origin()); now += base::TimeDelta::FromMilliseconds(16); } // Even after the animation terminates, the new position will not be pushed. EXPECT_EQ(old_focus_bottom - koffset_vector, drawable().rect.origin()); // As soon as the handle becomes visible, the new position will be pushed. UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_SMOOTH); EXPECT_EQ(new_position_bottom - koffset_vector, drawable().rect.origin()); } TEST_F(TouchHandleTest, Enabled) { // A newly created handle defaults to enabled. TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); EXPECT_TRUE(drawable().enabled); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_SMOOTH); EXPECT_TRUE(GetAndResetNeedsAnimate()); EXPECT_EQ(0.f, drawable().alpha); handle.SetEnabled(false); EXPECT_FALSE(drawable().enabled); // Dragging should not be allowed while the handle is disabled. base::TimeTicks event_time = base::TimeTicks::Now(); const float kOffset = kDefaultDrawableSize / 2.f; MockMotionEvent event( MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); // Disabling mid-animation should cancel the animation. handle.SetEnabled(true); UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); EXPECT_TRUE(drawable().visible); EXPECT_TRUE(GetAndResetNeedsAnimate()); handle.SetEnabled(false); EXPECT_FALSE(drawable().enabled); EXPECT_FALSE(drawable().visible); EXPECT_FALSE(handle.Animate(base::TimeTicks::Now())); // Disabling mid-drag should cancel the drag. handle.SetEnabled(true); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); handle.SetEnabled(false); EXPECT_FALSE(IsDragging()); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); } TEST_F(TouchHandleTest, Drag) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); base::TimeTicks event_time = base::TimeTicks::Now(); const float kOffset = kDefaultDrawableSize / 2.f; // The handle must be visible to trigger drag. MockMotionEvent event( MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); // ACTION_DOWN must fall within the drawable region to trigger drag. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 50); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); // Only ACTION_DOWN will trigger drag. event = MockMotionEvent( MockMotionEvent::ACTION_MOVE, event_time, kOffset, kOffset); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); // Start the drag. event = MockMotionEvent( MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); event = MockMotionEvent( MockMotionEvent::ACTION_MOVE, event_time, kOffset + 10, kOffset + 15); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(GetAndResetHandleDragged()); EXPECT_TRUE(IsDragging()); EXPECT_EQ(gfx::PointF(10, 15), DragPosition()); event = MockMotionEvent( MockMotionEvent::ACTION_MOVE, event_time, kOffset - 10, kOffset - 15); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(GetAndResetHandleDragged()); EXPECT_TRUE(IsDragging()); EXPECT_EQ(gfx::PointF(-10, -15), DragPosition()); event = MockMotionEvent(MockMotionEvent::ACTION_UP); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleDragged()); EXPECT_FALSE(IsDragging()); // Non-ACTION_DOWN events after the drag has terminated should not be handled. event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); } TEST_F(TouchHandleTest, DragDefersOrientationChange) { TouchHandle handle(this, TouchHandleOrientation::RIGHT, kDefaultViewportRect); ASSERT_EQ(drawable().orientation, TouchHandleOrientation::RIGHT); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); MockMotionEvent event(MockMotionEvent::ACTION_DOWN); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); // Orientation changes will be deferred until the drag ends. UpdateHandleOrientation(handle, TouchHandleOrientation::LEFT); EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation); event = MockMotionEvent(MockMotionEvent::ACTION_MOVE); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(GetAndResetHandleDragged()); EXPECT_TRUE(IsDragging()); EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation); event = MockMotionEvent(MockMotionEvent::ACTION_UP); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleDragged()); EXPECT_FALSE(IsDragging()); EXPECT_EQ(TouchHandleOrientation::LEFT, drawable().orientation); } TEST_F(TouchHandleTest, DragDefersFade) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); MockMotionEvent event(MockMotionEvent::ACTION_DOWN); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); // Fade will be deferred until the drag ends. UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); EXPECT_FALSE(NeedsAnimate()); EXPECT_TRUE(drawable().visible); EXPECT_EQ(1.f, drawable().alpha); event = MockMotionEvent(MockMotionEvent::ACTION_MOVE); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(NeedsAnimate()); EXPECT_TRUE(drawable().visible); event = MockMotionEvent(MockMotionEvent::ACTION_UP); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); EXPECT_TRUE(NeedsAnimate()); Animate(handle); EXPECT_FALSE(drawable().visible); EXPECT_EQ(0.f, drawable().alpha); } TEST_F(TouchHandleTest, DragTargettingUsesTouchSize) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); gfx::PointF focus_top(kDefaultDrawableSize / 2, 0); gfx::PointF focus_bottom(kDefaultDrawableSize / 2, 0); UpdateHandleFocus(handle, focus_top, focus_bottom); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); base::TimeTicks event_time = base::TimeTicks::Now(); const float kTouchSize = 24.f; const float kOffset = kDefaultDrawableSize + kTouchSize / 2.001f; MockMotionEvent event( MockMotionEvent::ACTION_DOWN, event_time, kOffset, 0); event.SetTouchMajor(0.f); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); event.SetTouchMajor(kTouchSize / 2.f); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); event.SetTouchMajor(kTouchSize); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); event.SetTouchMajor(kTouchSize * 2.f); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); // The touch hit test region should be circular. event = MockMotionEvent( MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); event.SetTouchMajor(kTouchSize); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); event.SetTouchMajor(kTouchSize * std::sqrt(2.f) - 0.1f); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); event.SetTouchMajor(kTouchSize * std::sqrt(2.f) + 0.1f); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); // Ensure a touch size of 0 can still register a hit. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, kDefaultDrawableSize / 2.f, kDefaultDrawableSize / 2.f); event.SetTouchMajor(0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); // Touches centered above the handle region should never register a hit, even // if the touch region intersects the handle region. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, kDefaultDrawableSize / 2.f, -kTouchSize / 3.f); event.SetTouchMajor(kTouchSize); EXPECT_FALSE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(IsDragging()); } TEST_F(TouchHandleTest, Tap) { TouchHandle handle(this, TouchHandleOrientation::CENTER, kDefaultViewportRect); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); base::TimeTicks event_time = base::TimeTicks::Now(); // ACTION_CANCEL shouldn't trigger a tap. MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event_time += base::TimeDelta::FromMilliseconds(50); event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleTapped()); // Long press shouldn't trigger a tap. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event_time += 2 * GetMaxTapDuration(); event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleTapped()); // Only a brief tap within the slop region should trigger a tap. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event_time += GetMaxTapDuration() / 2; event = MockMotionEvent( MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop / 2.f, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event = MockMotionEvent( MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop / 2.f, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(GetAndResetHandleTapped()); // Moving beyond the slop region shouldn't trigger a tap. event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event_time += GetMaxTapDuration() / 2; event = MockMotionEvent( MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop * 2.f, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); event = MockMotionEvent( MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop * 2.f, 0); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleTapped()); } TEST_F(TouchHandleTest, MirrorFocusChange) { TouchHandle handle(this, TouchHandleOrientation::LEFT, kDefaultViewportRect); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); gfx::PointF focus_top; gfx::PointF focus_bottom; EXPECT_EQ(gfx::PointF(), drawable().rect.origin()); // Moving the selection to the bottom of the screen // should mirror the handle vertically. focus_top = gfx::PointF(17.3f, 1199.0f); focus_bottom = gfx::PointF(17.3f, 1200.0f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_TRUE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); // Moving the left handle to the left edge of the viewport // should mirror the handle horizontally as well. focus_top = gfx::PointF(2.3f, 1199.0f); focus_bottom = gfx::PointF(2.3f, 1200.0f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_TRUE(drawable().mirror_vertical); EXPECT_TRUE(drawable().mirror_horizontal); // When the selection is not at the bottom, only the // horizontal mirror flag should be true. focus_top = gfx::PointF(2.3f, 7.3f); focus_bottom = gfx::PointF(2.3f, 8.3f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_TRUE(drawable().mirror_horizontal); // When selection handles intersects the viewport fully, // both mirror values should be false. focus_top = gfx::PointF(23.3f, 7.3f); focus_bottom = gfx::PointF(23.3f, 8.3f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); // Horizontal mirror should be true for Right handle when // the handle is at theright edge of the viewport. UpdateHandleOrientation(handle, TouchHandleOrientation::RIGHT); focus_top = gfx::PointF(560.0f, 7.3f); focus_bottom = gfx::PointF(560.0f, 8.3f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_TRUE(drawable().mirror_horizontal); } TEST_F(TouchHandleTest, DragDefersMirrorChange) { TouchHandle handle(this, TouchHandleOrientation::RIGHT, kDefaultViewportRect); ASSERT_EQ(drawable().orientation, TouchHandleOrientation::RIGHT); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); base::TimeTicks event_time = base::TimeTicks::Now(); const float kOffset = kDefaultDrawableSize / 2.f; // Start the drag. MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); UpdateHandleOrientation(handle, TouchHandleOrientation::LEFT); gfx::PointF focus_top(17.3f, 1199.0f); gfx::PointF focus_bottom(17.3f, 1200.0f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); // Mirror flag changes will be deferred until the drag ends. event = MockMotionEvent(MockMotionEvent::ACTION_UP); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_FALSE(GetAndResetHandleDragged()); EXPECT_FALSE(IsDragging()); EXPECT_TRUE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); } TEST_F(TouchHandleTest, ViewportSizeChange) { TouchHandle handle(this, TouchHandleOrientation::RIGHT, kDefaultViewportRect); ASSERT_EQ(drawable().orientation, TouchHandleOrientation::RIGHT); UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_NONE); gfx::PointF focus_top; gfx::PointF focus_bottom; EXPECT_EQ(gfx::PointF(), drawable().rect.origin()); focus_top = gfx::PointF(230.0f, 599.0f); focus_bottom = gfx::PointF(230.0f, 600.0f); UpdateHandleFocus(handle, focus_top, focus_bottom); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); UpdateViewportRect(handle, gfx::RectF(0, 0, 560, 600)); EXPECT_TRUE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); UpdateViewportRect(handle, gfx::RectF(0, 0, 230, 600)); EXPECT_TRUE(drawable().mirror_vertical); EXPECT_TRUE(drawable().mirror_horizontal); UpdateViewportRect(handle, kDefaultViewportRect); EXPECT_FALSE(drawable().mirror_vertical); EXPECT_FALSE(drawable().mirror_horizontal); } } // namespace ui