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

Commit 8150b5a

Browse files
committed
PointerScrollInertiaCancel Win32
1 parent 365cdd4 commit 8150b5a

File tree

7 files changed

+166
-3
lines changed

7 files changed

+166
-3
lines changed

shell/platform/windows/direct_manipulation.cc

Lines changed: 21 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,28 @@ 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 && std::max(std::abs(last_pan_delta_x_), std::abs(last_pan_delta_y_)) > 0.01) {
68+
owner_->binding_handler_delegate->OnScrollInertiaCancel((int32_t) reinterpret_cast<int64_t>(this));
69+
}
6170
// Need to reset the content transform to its original position
6271
// so that we are ready for the next gesture.
6372
// Use during_synthesized_reset_ flag to prevent sending reset also to the
6473
// framework.
6574
during_synthesized_reset_ = true;
75+
last_pan_x_ = 0.0;
76+
last_pan_y_ = 0.0;
77+
last_pan_delta_x_ = 0.0;
78+
last_pan_delta_y_ = 0.0;
6679
RECT rect;
6780
HRESULT hr = viewport->GetViewportRect(&rect);
6881
if (FAILED(hr)) {
@@ -104,7 +117,11 @@ HRESULT DirectManipulationEventHandler::OnContentUpdated(
104117
float scale = c - (c - transform[0]);
105118
float pan_x = transform[4];
106119
float pan_y = transform[5];
107-
if (owner_->binding_handler_delegate) {
120+
last_pan_delta_x_ = pan_x - last_pan_x_;
121+
last_pan_delta_y_ = pan_y - last_pan_y_;
122+
last_pan_x_ = pan_x;
123+
last_pan_y_ = pan_y;
124+
if (owner_->binding_handler_delegate && !during_inertia_) {
108125
owner_->binding_handler_delegate->OnPointerPanZoomUpdate(
109126
(int32_t) reinterpret_cast<int64_t>(this), pan_x, pan_y, scale, 0);
110127
}
@@ -144,7 +161,8 @@ int DirectManipulationOwner::Init(unsigned int width, unsigned int height) {
144161
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
145162
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
146163
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
147-
DIRECTMANIPULATION_CONFIGURATION_SCALING;
164+
DIRECTMANIPULATION_CONFIGURATION_SCALING |
165+
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA;
148166
RETURN_IF_FAILED(viewport_->ActivateConfiguration(configuration));
149167
RETURN_IF_FAILED(viewport_->SetViewportOptions(
150168
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE));

shell/platform/windows/direct_manipulation.h

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

117125
} // namespace flutter

shell/platform/windows/direct_manipulation_unittests.cc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,5 +259,113 @@ 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(
291+
[](float* transform, DWORD size) {
292+
transform[0] = 1;
293+
transform[4] = 0;
294+
transform[5] = 100;
295+
return S_OK;
296+
}));
297+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
298+
(IDirectManipulationContent*)&content);
299+
EXPECT_CALL(content, GetContentTransform(_, 6))
300+
.WillOnce(::testing::Invoke(
301+
[](float* transform, DWORD size) {
302+
transform[0] = 1;
303+
transform[4] = 0;
304+
transform[5] = 110;
305+
return S_OK;
306+
}));
307+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
308+
(IDirectManipulationContent*)&content);
309+
// This looks like an interruption in the middle of synthetic inertia because of user input.
310+
EXPECT_CALL(delegate, OnScrollInertiaCancel(device_id));
311+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
312+
DIRECTMANIPULATION_READY,
313+
DIRECTMANIPULATION_INERTIA);
314+
}
315+
316+
TEST(DirectManipulationTest, TestInertiaCamcelNotSentAtInertiaEnd) {
317+
MockIDirectManipulationContent content;
318+
MockWindowBindingHandlerDelegate delegate;
319+
MockIDirectManipulationViewport viewport;
320+
const int DISPLAY_WIDTH = 800;
321+
const int DISPLAY_HEIGHT = 600;
322+
auto owner = std::make_unique<DirectManipulationOwner>(nullptr);
323+
owner->SetBindingHandlerDelegate(&delegate);
324+
auto handler =
325+
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
326+
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
327+
// No need to mock the actual gesture, just start at the end.
328+
EXPECT_CALL(viewport, GetViewportRect(_))
329+
.WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) {
330+
rect->left = 0;
331+
rect->top = 0;
332+
rect->right = DISPLAY_WIDTH;
333+
rect->bottom = DISPLAY_HEIGHT;
334+
return S_OK;
335+
}));
336+
EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false))
337+
.WillOnce(::testing::Return(S_OK));
338+
EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id));
339+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
340+
DIRECTMANIPULATION_INERTIA,
341+
DIRECTMANIPULATION_RUNNING);
342+
// Have no change in pan between events.
343+
EXPECT_CALL(content, GetContentTransform(_, 6))
344+
.WillOnce(::testing::Invoke(
345+
[](float* transform, DWORD size) {
346+
transform[0] = 1;
347+
transform[4] = 0;
348+
transform[5] = 140;
349+
return S_OK;
350+
}));
351+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
352+
(IDirectManipulationContent*)&content);
353+
EXPECT_CALL(content, GetContentTransform(_, 6))
354+
.WillOnce(::testing::Invoke(
355+
[](float* transform, DWORD size) {
356+
transform[0] = 1;
357+
transform[4] = 0;
358+
transform[5] = 140;
359+
return S_OK;
360+
}));
361+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
362+
(IDirectManipulationContent*)&content);
363+
// OnScrollInertiaCancel should not be called.
364+
EXPECT_CALL(delegate, OnScrollInertiaCancel(device_id)).Times(0);
365+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
366+
DIRECTMANIPULATION_READY,
367+
DIRECTMANIPULATION_INERTIA);
368+
}
369+
262370
} // namespace testing
263371
} // namespace flutter

shell/platform/windows/flutter_windows_view.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ void FlutterWindowsView::OnScroll(double x,
264264
device_id);
265265
}
266266

267+
void FlutterWindowsView::OnScrollInertiaCancel(int32_t device_id) {
268+
PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
269+
SendScrollInertiaCancel(device_id, point.x, point.y);
270+
}
271+
267272
void FlutterWindowsView::OnUpdateSemanticsEnabled(bool enabled) {
268273
engine_->UpdateSemanticsEnabled(enabled);
269274
}
@@ -500,6 +505,17 @@ void FlutterWindowsView::SendScroll(double x,
500505
SendPointerEventWithData(event, state);
501506
}
502507

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

shell/platform/windows/flutter_windows_view.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
180180
FlutterPointerDeviceKind device_kind,
181181
int32_t device_id) override;
182182

183+
// |WindowBindingHandlerDelegate|
184+
void OnScrollInertiaCancel(int32_t device_id) override;
185+
183186
// |WindowBindingHandlerDelegate|
184187
virtual void OnUpdateSemanticsEnabled(bool enabled) override;
185188

@@ -331,6 +334,11 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
331334
FlutterPointerDeviceKind device_kind,
332335
int32_t device_id);
333336

337+
// Reports scroll inertia cancel events to Flutter engine.
338+
void SendScrollInertiaCancel(int32_t device_id,
339+
double x,
340+
double y);
341+
334342
// Creates a PointerState object unless it already exists.
335343
PointerState* GetOrCreatePointerState(FlutterPointerDeviceKind device_kind,
336344
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
@@ -60,6 +60,7 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
6060
int,
6161
FlutterPointerDeviceKind,
6262
int32_t));
63+
MOCK_METHOD1(OnScrollInertiaCancel, void(int32_t));
6364
MOCK_METHOD0(OnPlatformBrightnessChanged, void());
6465
MOCK_METHOD1(UpdateHighContrastEnabled, void(bool enabled));
6566
};

shell/platform/windows/window_binding_handler_delegate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class WindowBindingHandlerDelegate {
122122
FlutterPointerDeviceKind device_kind,
123123
int32_t device_id) = 0;
124124

125+
// Notifies delegate that scroll inertia should be cancelled.
126+
// Typically called by DirectManipulationEventHandler
127+
virtual void OnScrollInertiaCancel(int32_t device_id) = 0;
128+
125129
// Notifies delegate that the Flutter semantics tree should be enabled or
126130
// disabled.
127131
virtual void OnUpdateSemanticsEnabled(bool enabled) = 0;

0 commit comments

Comments
 (0)