Commit 77ff930c authored by chaopeng's avatar chaopeng Committed by Commit Bot

Pinch zoom support for Windows Precision Touchpad

In this patch, we covert the scale factor from Direct Manipulation
to GesturePinch event. The ctrl+wheel will create by WebViewImpl::
HandleSyntheticWheelFromTouchpadPinchEvent.

Bug: 647140
Change-Id: Ib041d7805daac7e71b2f43872c818a9f36b14abd
Reviewed-on: https://chromium-review.googlesource.com/786348Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDave Tapuska <dtapuska@chromium.org>
Commit-Queue: Jianpeng Chao <chaopeng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542719}
parent 6b591b59
......@@ -4,6 +4,8 @@
#include "ui/base/win/direct_manipulation.h"
#include <windows.h>
#include "base/macros.h"
#include "base/test/scoped_feature_list.h"
#include "base/win/windows_version.h"
......@@ -16,6 +18,9 @@
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/win/window_event_target.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/event_source.h"
#include "url/gurl.h"
namespace content {
......@@ -34,9 +39,20 @@ class DirectManipulationBrowserTest : public ContentBrowserTest {
return rwhva->legacy_render_widget_host_HWND_;
}
HWND GetSubWindowHWND() {
LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
return lrwhh->hwnd();
}
ui::WindowEventTarget* GetWindowEventTarget() {
LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
return lrwhh->GetWindowEventTarget(lrwhh->GetParent());
}
void SimulatePointerHitTest() {
LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
ASSERT_TRUE(lrwhh);
lrwhh->direct_manipulation_helper_->need_poll_events_ = true;
lrwhh->CreateAnimationObserver();
......@@ -44,7 +60,6 @@ class DirectManipulationBrowserTest : public ContentBrowserTest {
void UpdateParent(HWND hwnd) {
LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
ASSERT_TRUE(lrwhh);
lrwhh->UpdateParent(hwnd);
}
......@@ -90,4 +105,98 @@ IN_PROC_BROWSER_TEST_F(DirectManipulationBrowserTest, HWNDReparent) {
EXPECT_FALSE(HasCompositorAnimationObserver(lrwhh));
}
// EventLogger is to obserser the events sent from WindowEventTarget (the root
// window).
class EventLogger : public ui::EventRewriter {
public:
EventLogger() {}
~EventLogger() override {}
ui::Event* LastEvent() const { return last_event_.get(); }
private:
// ui::EventRewriter
ui::EventRewriteStatus RewriteEvent(
const ui::Event& event,
std::unique_ptr<ui::Event>* new_event) override {
last_event_ = ui::Event::Clone(event);
return ui::EVENT_REWRITE_CONTINUE;
}
// ui::EventRewriter
ui::EventRewriteStatus NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) override {
return ui::EVENT_REWRITE_CONTINUE;
}
std::unique_ptr<ui::Event> last_event_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(EventLogger);
};
// Check DirectManipulation events convert to ui::event correctly.
IN_PROC_BROWSER_TEST_F(DirectManipulationBrowserTest, EventConvert) {
if (base::win::GetVersion() < base::win::VERSION_WIN10)
return;
NavigateToURL(shell(), GURL(url::kAboutBlankURL));
LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND();
ASSERT_TRUE(lrwhh);
HWND hwnd =
shell()->window()->GetRootWindow()->GetHost()->GetAcceleratedWidget();
ui::EventSource* dwthw = static_cast<ui::EventSource*>(
aura::WindowTreeHost::GetForAcceleratedWidget(hwnd));
EventLogger event_logger;
dwthw->AddEventRewriter(&event_logger);
ui::WindowEventTarget* target = GetWindowEventTarget();
{
target->ApplyPanGestureScroll(1, 2);
ui::Event* event = event_logger.LastEvent();
EXPECT_TRUE(event);
EXPECT_EQ(ui::ET_MOUSEWHEEL, event->type());
ui::MouseWheelEvent* wheel_event = event->AsMouseWheelEvent();
EXPECT_EQ(1, wheel_event->x_offset());
EXPECT_EQ(2, wheel_event->y_offset());
}
{
target->ApplyPinchZoomBegin();
ui::Event* event = event_logger.LastEvent();
EXPECT_TRUE(event);
EXPECT_EQ(ui::ET_GESTURE_PINCH_BEGIN, event->type());
ui::GestureEvent* gesture_event = event->AsGestureEvent();
EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD,
gesture_event->details().device_type());
}
{
target->ApplyPinchZoomScale(1.1f);
ui::Event* event = event_logger.LastEvent();
EXPECT_TRUE(event);
EXPECT_EQ(ui::ET_GESTURE_PINCH_UPDATE, event->type());
ui::GestureEvent* gesture_event = event->AsGestureEvent();
EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD,
gesture_event->details().device_type());
EXPECT_EQ(1.1f, gesture_event->details().scale());
}
{
target->ApplyPinchZoomEnd();
ui::Event* event = event_logger.LastEvent();
EXPECT_TRUE(event);
EXPECT_EQ(ui::ET_GESTURE_PINCH_END, event->type());
ui::GestureEvent* gesture_event = event->AsGestureEvent();
EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD,
gesture_event->details().device_type());
}
dwthw->RemoveEventRewriter(&event_logger);
}
} // namespace content
......@@ -101,8 +101,7 @@ bool DirectManipulationHelper::Initialize() {
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
DIRECTMANIPULATION_CONFIGURATION_RAILS_X |
DIRECTMANIPULATION_CONFIGURATION_RAILS_Y |
DIRECTMANIPULATION_CONFIGURATION_SCALING |
DIRECTMANIPULATION_CONFIGURATION_SCALING_INERTIA;
DIRECTMANIPULATION_CONFIGURATION_SCALING;
hr = viewport_->ActivateConfiguration(configuration);
if (!SUCCEEDED(hr))
......@@ -205,6 +204,34 @@ DirectManipulationHandler::DirectManipulationHandler(
DirectManipulationHandler::~DirectManipulationHandler() {}
void DirectManipulationHandler::TransitionToState(Gesture new_gesture_state) {
if (gesture_state_ == new_gesture_state)
return;
Gesture previous_gesture_state = gesture_state_;
gesture_state_ = new_gesture_state;
if (new_gesture_state == Gesture::kPinch) {
// kScroll, kNone -> kPinch, PinchBegin.
// Pinch gesture may begin with some scroll events.
event_target_->ApplyPinchZoomBegin();
return;
}
if (new_gesture_state == Gesture::kNone) {
// kScroll -> kNone do nothing.
if (previous_gesture_state == Gesture::kScroll)
return;
// kPinch -> kNone, PinchEnd.
event_target_->ApplyPinchZoomEnd();
return;
}
// kNone -> kScroll do nothing. Not allow kPinch -> kScroll.
DCHECK_EQ(previous_gesture_state, Gesture::kNone);
DCHECK_EQ(new_gesture_state, Gesture::kScroll);
}
HRESULT DirectManipulationHandler::OnViewportStatusChanged(
IDirectManipulationViewport* viewport,
DIRECTMANIPULATION_STATUS current,
......@@ -226,6 +253,8 @@ HRESULT DirectManipulationHandler::OnViewportStatusChanged(
last_scale_ = 1.0f;
last_x_offset_ = 0.0f;
last_y_offset_ = 0.0f;
TransitionToState(Gesture::kNone);
}
return hr;
......@@ -237,6 +266,8 @@ HRESULT DirectManipulationHandler::OnViewportUpdated(
return S_OK;
}
namespace {
bool FloatEquals(float f1, float f2) {
// The idea behind this is to use this fraction of the larger of the
// two numbers as the limit of the difference. This breaks down near
......@@ -252,6 +283,8 @@ bool DifferentLessThanOne(int f1, int f2) {
return abs(f1 - f2) < 1;
}
} // namespace
HRESULT DirectManipulationHandler::OnContentUpdated(
IDirectManipulationViewport* viewport,
IDirectManipulationContent* content) {
......@@ -284,8 +317,22 @@ HRESULT DirectManipulationHandler::OnContentUpdated(
DCHECK_NE(last_scale_, 0.0f);
// DirectManipulation will send xy transform move to down-right which is noise
// when pinch zoom. We should consider the gesture either Scroll or Pinch at
// one sequence. But Pinch gesture may begin with some scroll transform since
// DirectManipulation recognition maybe wrong at start if the user pinch with
// slow motion. So we allow kScroll -> kPinch.
// Consider this is a Scroll when scale factor equals 1.0.
if (FloatEquals(scale, 1.0f)) {
if (gesture_state_ == Gesture::kNone)
TransitionToState(Gesture::kScroll);
} else {
// Pinch gesture may begin with some scroll events.
TransitionToState(Gesture::kPinch);
}
if (gesture_state_ == Gesture::kScroll) {
event_target_->ApplyPanGestureScroll(x_offset - last_x_offset_,
y_offset - last_y_offset_);
} else {
......
......@@ -52,6 +52,10 @@ class DirectManipulationHandler
DirectManipulationHandler();
~DirectManipulationHandler() override;
enum class Gesture { kNone, kScroll, kPinch };
void TransitionToState(Gesture gesture);
HRESULT STDMETHODCALLTYPE
OnViewportStatusChanged(_In_ IDirectManipulationViewport* viewport,
_In_ DIRECTMANIPULATION_STATUS current,
......@@ -71,6 +75,9 @@ class DirectManipulationHandler
int last_y_offset_ = 0;
bool first_ready_ = false;
// Current recognized gesture from Direct Manipulation.
Gesture gesture_state_ = Gesture::kNone;
DISALLOW_COPY_AND_ASSIGN(DirectManipulationHandler);
};
......
......@@ -239,18 +239,20 @@ class MockDirectManipulationContent
DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationContent);
};
enum class Gesture { kScroll, kScale };
enum class Gesture { kScroll, kScale, kScaleBegin, kScaleEnd };
struct Event {
explicit Event(float scale) : gesture_(Gesture::kScale), scale_(scale) {}
Event(float scroll_x, float scroll_y)
: gesture_(Gesture::kScroll), scroll_x_(scroll_x), scroll_y_(scroll_y) {}
explicit Event(Gesture gesture) : gesture_(gesture) {}
Gesture gesture_;
float scale_ = 0;
float scroll_x_ = 0;
float scroll_y_ = 0;
Event(float scale) : gesture_(Gesture::kScale), scale_(scale) {}
Event(float scroll_x, float scroll_y)
: gesture_(Gesture::kScroll), scroll_x_(scroll_x), scroll_y_(scroll_y) {}
};
class MockWindowEventTarget : public WindowEventTarget {
......@@ -263,6 +265,14 @@ class MockWindowEventTarget : public WindowEventTarget {
events_.push_back(Event(scale));
}
void ApplyPinchZoomBegin() override {
events_.push_back(Event(Gesture::kScaleBegin));
}
void ApplyPinchZoomEnd() override {
events_.push_back(Event(Gesture::kScaleEnd));
}
void ApplyPanGestureScroll(int scroll_x, int scroll_y) override {
events_.push_back(Event(scroll_x, scroll_y));
}
......@@ -411,9 +421,10 @@ TEST_F(DirectManipulationUnitTest, ReceiveSimpleScaleTransform) {
ContentUpdated(1.1f, 0, 0);
std::vector<Event> events = GetEvents();
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScale, events[0].gesture_);
EXPECT_EQ(1.1f, events[0].scale_);
EXPECT_EQ(2u, events.size());
EXPECT_EQ(Gesture::kScaleBegin, events[0].gesture_);
EXPECT_EQ(Gesture::kScale, events[1].gesture_);
EXPECT_EQ(1.1f, events[1].scale_);
// For next update, should only apply the difference.
ContentUpdated(1.21f, 0, 0);
......@@ -421,6 +432,11 @@ TEST_F(DirectManipulationUnitTest, ReceiveSimpleScaleTransform) {
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScale, events[0].gesture_);
EXPECT_EQ(1.1f, events[0].scale_);
ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING);
events = GetEvents();
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScaleEnd, events[0].gesture_);
}
TEST_F(DirectManipulationUnitTest, ReceiveScrollTransformLessThanOne) {
......@@ -472,9 +488,10 @@ TEST_F(DirectManipulationUnitTest,
// Scale factor more than float point error, apply.
ContentUpdated(1.00001f, 0, 0);
events = GetEvents();
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScale, events[0].gesture_);
EXPECT_EQ(1.00001f, events[0].scale_);
EXPECT_EQ(2u, events.size());
EXPECT_EQ(Gesture::kScaleBegin, events[0].gesture_);
EXPECT_EQ(Gesture::kScale, events[1].gesture_);
EXPECT_EQ(1.00001f, events[1].scale_);
// Scale factor difference less than float point error, ignore.
ContentUpdated(1.000011f, 0, 0);
......@@ -487,6 +504,11 @@ TEST_F(DirectManipulationUnitTest,
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScale, events[0].gesture_);
EXPECT_EQ(1.000021f / 1.00001f, events[0].scale_);
ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING);
events = GetEvents();
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScaleEnd, events[0].gesture_);
}
TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveBothScrollAndScale) {
......@@ -505,8 +527,9 @@ TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveBothScrollAndScale) {
// Second event comes with scale factor. Now the scroll offset only noise.
ContentUpdated(1.00001f, 5, 0);
events = GetEvents();
EXPECT_EQ(1u, events.size());
EXPECT_EQ(Gesture::kScale, events[0].gesture_);
EXPECT_EQ(2u, events.size());
EXPECT_EQ(Gesture::kScaleBegin, events[0].gesture_);
EXPECT_EQ(Gesture::kScale, events[1].gesture_);
}
TEST_F(DirectManipulationUnitTest,
......
......@@ -90,10 +90,19 @@ class UI_BASE_EXPORT WindowEventTarget {
virtual void HandleParentChanged() = 0;
// Apply the transform from Direct Manipulation API.
// ApplyPinchZoomScale is the pinch-zoom gesture. scale is the scale factor.
// ApplyPanGestureScroll is the pan gesture, scroll_x and scroll_y are pixel
// precison scroll offset.
// Calls ApplyPinchZoomScale() for pinch-zoom gesture. scale is the scale
// factor.
virtual void ApplyPinchZoomScale(float scale) = 0;
// Pinch gesture phase. The sequencing expected of these events.
// The sequence of calls is ApplyPinchZoomBegin(), any number of calls to
// ApplyPinchZoomScale() and finally ApplyPinchZoomEnd().
virtual void ApplyPinchZoomBegin() = 0;
virtual void ApplyPinchZoomEnd() = 0;
// Calls ApplyPanGestureScroll() for pan gesture, scroll_x and scroll_y are
// pixel precison scroll offset.
virtual void ApplyPanGestureScroll(int scroll_x, int scroll_y) = 0;
protected:
......
......@@ -955,6 +955,11 @@ bool DesktopWindowTreeHostWin::HandleScrollEvent(
return event.handled();
}
bool DesktopWindowTreeHostWin::HandleGestureEvent(ui::GestureEvent* event) {
SendEventToSink(event);
return event->handled();
}
void DesktopWindowTreeHostWin::HandleWindowSizeChanging() {
if (compositor())
compositor()->DisableSwapUntilResize();
......
......@@ -214,6 +214,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
LRESULT* result) override;
void PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) override;
bool HandleScrollEvent(const ui::ScrollEvent& event) override;
bool HandleGestureEvent(ui::GestureEvent* event) override;
void HandleWindowSizeChanging() override;
void HandleWindowSizeUnchanged() override;
void HandleWindowScaleFactorChanged(float window_scale_factor) override;
......
......@@ -1059,26 +1059,43 @@ void HWNDMessageHandler::HandleParentChanged() {
}
void HWNDMessageHandler::ApplyPinchZoomScale(float scale) {
// We fake a default ctrl+wheel event here. Offset 120 is the default scroll
// offset on Windows.
// TODO(chaopeng) Send pinch-zoom event here.
gfx::Vector2d offset =
scale > 1 ? gfx::Vector2d(120, 120) : gfx::Vector2d(-120, -120);
POINT cursor_pos = {0};
::GetCursorPos(&cursor_pos);
ScreenToClient(hwnd(), &cursor_pos);
POINT root_location = {0};
::GetCursorPos(&root_location);
ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_UPDATE);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
event_details.set_scale(scale);
POINT location = {root_location.x, root_location.y};
ScreenToClient(hwnd(), &root_location);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
gfx::Point cursor_location(location);
gfx::Point cursor_root_location(root_location);
void HWNDMessageHandler::ApplyPinchZoomBegin() {
POINT cursor_pos = {0};
::GetCursorPos(&cursor_pos);
ScreenToClient(hwnd(), &cursor_pos);
ui::MouseWheelEvent wheel_event(offset, cursor_location, cursor_root_location,
base::TimeTicks::Now(), ui::EF_CONTROL_DOWN,
ui::EF_PRECISION_SCROLLING_DELTA);
ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_BEGIN);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
delegate_->HandleMouseEvent(wheel_event);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
void HWNDMessageHandler::ApplyPinchZoomEnd() {
POINT cursor_pos = {0};
::GetCursorPos(&cursor_pos);
ScreenToClient(hwnd(), &cursor_pos);
ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_END);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
void HWNDMessageHandler::ApplyPanGestureScroll(int scroll_x, int scroll_y) {
......
......@@ -264,6 +264,8 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
bool* handled) override;
void HandleParentChanged() override;
void ApplyPinchZoomScale(float scale) override;
void ApplyPinchZoomBegin() override;
void ApplyPinchZoomEnd() override;
void ApplyPanGestureScroll(int scroll_x, int scroll_y) override;
// Returns the auto-hide edges of the appbar. See
......
......@@ -243,6 +243,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate {
// handled by the delegate.
virtual bool HandleScrollEvent(const ui::ScrollEvent& event) = 0;
// Called when a gesture event is received. Returns true if the event was
// handled by the delegate.
virtual bool HandleGestureEvent(ui::GestureEvent* event) = 0;
// Called when the window size is about to change.
virtual void HandleWindowSizeChanging() = 0;
......
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