Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d985f0e

Browse files
committed
PointerScrollInertiaCancel Win32
1 parent f34a228 commit d985f0e

7 files changed

Lines changed: 169 additions & 3 deletions

shell/platform/windows/direct_manipulation.cc

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ HRESULT DirectManipulationEventHandler::OnViewportStatusChanged(
4343
IDirectManipulationViewport* viewport,
4444
DIRECTMANIPULATION_STATUS current,
4545
DIRECTMANIPULATION_STATUS previous) {
46+
during_inertia_ = current == DIRECTMANIPULATION_INERTIA;
4647
if (during_synthesized_reset_ && previous == DIRECTMANIPULATION_RUNNING) {
4748
during_synthesized_reset_ = false;
4849
} else if (current == DIRECTMANIPULATION_RUNNING) {
@@ -53,16 +54,31 @@ HRESULT DirectManipulationEventHandler::OnViewportStatusChanged(
5354
(int32_t) reinterpret_cast<int64_t>(this));
5455
}
5556
}
56-
} else if (previous == DIRECTMANIPULATION_RUNNING) {
57+
}
58+
if (previous == DIRECTMANIPULATION_RUNNING) {
59+
// Reset deltas to ensure only inertia values will be compared later.
60+
last_pan_delta_x_ = 0.0;
61+
last_pan_delta_y_ = 0.0;
5762
if (owner_->binding_handler_delegate) {
5863
owner_->binding_handler_delegate->OnPointerPanZoomEnd(
5964
(int32_t) reinterpret_cast<int64_t>(this));
6065
}
66+
} else if (previous == DIRECTMANIPULATION_INERTIA) {
67+
if (owner_->binding_handler_delegate &&
68+
std::max(std::abs(last_pan_delta_x_), std::abs(last_pan_delta_y_)) >
69+
0.01) {
70+
owner_->binding_handler_delegate->OnScrollInertiaCancel(
71+
(int32_t) reinterpret_cast<int64_t>(this));
72+
}
6173
// Need to reset the content transform to its original position
6274
// so that we are ready for the next gesture.
6375
// Use during_synthesized_reset_ flag to prevent sending reset also to the
6476
// framework.
6577
during_synthesized_reset_ = true;
78+
last_pan_x_ = 0.0;
79+
last_pan_y_ = 0.0;
80+
last_pan_delta_x_ = 0.0;
81+
last_pan_delta_y_ = 0.0;
6682
RECT rect;
6783
HRESULT hr = viewport->GetViewportRect(&rect);
6884
if (FAILED(hr)) {
@@ -104,7 +120,11 @@ HRESULT DirectManipulationEventHandler::OnContentUpdated(
104120
float scale = c - (c - transform[0]);
105121
float pan_x = transform[4];
106122
float pan_y = transform[5];
107-
if (owner_->binding_handler_delegate) {
123+
last_pan_delta_x_ = pan_x - last_pan_x_;
124+
last_pan_delta_y_ = pan_y - last_pan_y_;
125+
last_pan_x_ = pan_x;
126+
last_pan_y_ = pan_y;
127+
if (owner_->binding_handler_delegate && !during_inertia_) {
108128
owner_->binding_handler_delegate->OnPointerPanZoomUpdate(
109129
(int32_t) reinterpret_cast<int64_t>(this), pan_x, pan_y, scale, 0);
110130
}
@@ -144,7 +164,8 @@ int DirectManipulationOwner::Init(unsigned int width, unsigned int height) {
144164
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
145165
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
146166
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
147-
DIRECTMANIPULATION_CONFIGURATION_SCALING;
167+
DIRECTMANIPULATION_CONFIGURATION_SCALING |
168+
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA;
148169
RETURN_IF_FAILED(viewport_->ActivateConfiguration(configuration));
149170
RETURN_IF_FAILED(viewport_->SetViewportOptions(
150171
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE));

shell/platform/windows/direct_manipulation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ class DirectManipulationEventHandler
109109
// A flag is needed to ensure that false events created as the reset occurs
110110
// are not sent to the flutter framework.
111111
bool during_synthesized_reset_ = false;
112+
// Store whether current events are from synthetic inertia rather than user
113+
// input.
114+
bool during_inertia_ = false;
115+
// Store the difference between the last pan offsets to determine if inertia
116+
// has been cancelled in the middle of an animation.
117+
float last_pan_x_ = 0.0;
118+
float last_pan_y_ = 0.0;
119+
float last_pan_delta_x_ = 0.0;
120+
float last_pan_delta_y_ = 0.0;
112121
};
113122

114123
} // namespace flutter

shell/platform/windows/direct_manipulation_unittests.cc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,5 +259,110 @@ TEST(DirectManipulationTest, TestRounding) {
259259
DIRECTMANIPULATION_INERTIA);
260260
}
261261

262+
TEST(DirectManipulationTest, TestInertiaCancelSentForUserCancel) {
263+
MockIDirectManipulationContent content;
264+
MockWindowBindingHandlerDelegate delegate;
265+
MockIDirectManipulationViewport viewport;
266+
const int DISPLAY_WIDTH = 800;
267+
const int DISPLAY_HEIGHT = 600;
268+
auto owner = std::make_unique<DirectManipulationOwner>(nullptr);
269+
owner->SetBindingHandlerDelegate(&delegate);
270+
auto handler =
271+
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
272+
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
273+
// No need to mock the actual gesture, just start at the end.
274+
EXPECT_CALL(viewport, GetViewportRect(_))
275+
.WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) {
276+
rect->left = 0;
277+
rect->top = 0;
278+
rect->right = DISPLAY_WIDTH;
279+
rect->bottom = DISPLAY_HEIGHT;
280+
return S_OK;
281+
}));
282+
EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false))
283+
.WillOnce(::testing::Return(S_OK));
284+
EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id));
285+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
286+
DIRECTMANIPULATION_INERTIA,
287+
DIRECTMANIPULATION_RUNNING);
288+
// Have pan_y change by 10 between inertia updates.
289+
EXPECT_CALL(content, GetContentTransform(_, 6))
290+
.WillOnce(::testing::Invoke([](float* transform, DWORD size) {
291+
transform[0] = 1;
292+
transform[4] = 0;
293+
transform[5] = 100;
294+
return S_OK;
295+
}));
296+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
297+
(IDirectManipulationContent*)&content);
298+
EXPECT_CALL(content, GetContentTransform(_, 6))
299+
.WillOnce(::testing::Invoke([](float* transform, DWORD size) {
300+
transform[0] = 1;
301+
transform[4] = 0;
302+
transform[5] = 110;
303+
return S_OK;
304+
}));
305+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
306+
(IDirectManipulationContent*)&content);
307+
// This looks like an interruption in the middle of synthetic inertia because
308+
// of user input.
309+
EXPECT_CALL(delegate, OnScrollInertiaCancel(device_id));
310+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
311+
DIRECTMANIPULATION_READY,
312+
DIRECTMANIPULATION_INERTIA);
313+
}
314+
315+
TEST(DirectManipulationTest, TestInertiaCamcelNotSentAtInertiaEnd) {
316+
MockIDirectManipulationContent content;
317+
MockWindowBindingHandlerDelegate delegate;
318+
MockIDirectManipulationViewport viewport;
319+
const int DISPLAY_WIDTH = 800;
320+
const int DISPLAY_HEIGHT = 600;
321+
auto owner = std::make_unique<DirectManipulationOwner>(nullptr);
322+
owner->SetBindingHandlerDelegate(&delegate);
323+
auto handler =
324+
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
325+
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
326+
// No need to mock the actual gesture, just start at the end.
327+
EXPECT_CALL(viewport, GetViewportRect(_))
328+
.WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) {
329+
rect->left = 0;
330+
rect->top = 0;
331+
rect->right = DISPLAY_WIDTH;
332+
rect->bottom = DISPLAY_HEIGHT;
333+
return S_OK;
334+
}));
335+
EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false))
336+
.WillOnce(::testing::Return(S_OK));
337+
EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id));
338+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
339+
DIRECTMANIPULATION_INERTIA,
340+
DIRECTMANIPULATION_RUNNING);
341+
// Have no change in pan between events.
342+
EXPECT_CALL(content, GetContentTransform(_, 6))
343+
.WillOnce(::testing::Invoke([](float* transform, DWORD size) {
344+
transform[0] = 1;
345+
transform[4] = 0;
346+
transform[5] = 140;
347+
return S_OK;
348+
}));
349+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
350+
(IDirectManipulationContent*)&content);
351+
EXPECT_CALL(content, GetContentTransform(_, 6))
352+
.WillOnce(::testing::Invoke([](float* transform, DWORD size) {
353+
transform[0] = 1;
354+
transform[4] = 0;
355+
transform[5] = 140;
356+
return S_OK;
357+
}));
358+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
359+
(IDirectManipulationContent*)&content);
360+
// OnScrollInertiaCancel should not be called.
361+
EXPECT_CALL(delegate, OnScrollInertiaCancel(device_id)).Times(0);
362+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
363+
DIRECTMANIPULATION_READY,
364+
DIRECTMANIPULATION_INERTIA);
365+
}
366+
262367
} // namespace testing
263368
} // namespace flutter

shell/platform/windows/flutter_windows_view.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,11 @@ void FlutterWindowsView::OnScroll(double x,
262262
device_id);
263263
}
264264

265+
void FlutterWindowsView::OnScrollInertiaCancel(int32_t device_id) {
266+
PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
267+
SendScrollInertiaCancel(device_id, point.x, point.y);
268+
}
269+
265270
void FlutterWindowsView::OnUpdateSemanticsEnabled(bool enabled) {
266271
engine_->UpdateSemanticsEnabled(enabled);
267272
}
@@ -498,6 +503,21 @@ void FlutterWindowsView::SendScroll(double x,
498503
SendPointerEventWithData(event, state);
499504
}
500505

506+
void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
507+
double x,
508+
double y) {
509+
auto state =
510+
GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
511+
512+
FlutterPointerEvent event = {};
513+
event.x = x;
514+
event.y = y;
515+
event.signal_kind =
516+
FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
517+
SetEventPhaseFromCursorButtonState(&event, state);
518+
SendPointerEventWithData(event, state);
519+
}
520+
501521
void FlutterWindowsView::SendPointerEventWithData(
502522
const FlutterPointerEvent& event_data,
503523
PointerState* state) {

shell/platform/windows/flutter_windows_view.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
171171
FlutterPointerDeviceKind device_kind,
172172
int32_t device_id) override;
173173

174+
// |WindowBindingHandlerDelegate|
175+
void OnScrollInertiaCancel(int32_t device_id) override;
176+
174177
// |WindowBindingHandlerDelegate|
175178
virtual void OnUpdateSemanticsEnabled(bool enabled) override;
176179

@@ -322,6 +325,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
322325
FlutterPointerDeviceKind device_kind,
323326
int32_t device_id);
324327

328+
// Reports scroll inertia cancel events to Flutter engine.
329+
void SendScrollInertiaCancel(int32_t device_id, double x, double y);
330+
325331
// Creates a PointerState object unless it already exists.
326332
PointerState* GetOrCreatePointerState(FlutterPointerDeviceKind device_kind,
327333
int32_t device_id);

shell/platform/windows/testing/mock_window_binding_handler_delegate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
5959
int,
6060
FlutterPointerDeviceKind,
6161
int32_t));
62+
MOCK_METHOD1(OnScrollInertiaCancel, void(int32_t));
6263
MOCK_METHOD0(OnPlatformBrightnessChanged, void());
6364
};
6465

shell/platform/windows/window_binding_handler_delegate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ class WindowBindingHandlerDelegate {
118118
FlutterPointerDeviceKind device_kind,
119119
int32_t device_id) = 0;
120120

121+
// Notifies delegate that scroll inertia should be cancelled.
122+
// Typically called by DirectManipulationEventHandler
123+
virtual void OnScrollInertiaCancel(int32_t device_id) = 0;
124+
121125
// Notifies delegate that the Flutter semantics tree should be enabled or
122126
// disabled.
123127
virtual void OnUpdateSemanticsEnabled(bool enabled) = 0;

0 commit comments

Comments
 (0)