mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Windows: Add multi-touch support (flutter/engine#27863)
This commit is contained in:
parent
d944c38cf8
commit
f6977e39d8
@ -1662,6 +1662,9 @@ FILE: ../../../flutter/shell/platform/windows/platform_handler_win32.h
|
||||
FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.h
|
||||
FILE: ../../../flutter/shell/platform/windows/public/flutter_windows.h
|
||||
FILE: ../../../flutter/shell/platform/windows/sequential_id_generator.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/sequential_id_generator.h
|
||||
FILE: ../../../flutter/shell/platform/windows/sequential_id_generator_unittests.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/string_conversion.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/string_conversion.h
|
||||
FILE: ../../../flutter/shell/platform/windows/string_conversion_unittests.cc
|
||||
|
||||
@ -74,6 +74,8 @@ source_set("flutter_windows_source") {
|
||||
"keyboard_key_handler.h",
|
||||
"platform_handler.cc",
|
||||
"platform_handler.h",
|
||||
"sequential_id_generator.cc",
|
||||
"sequential_id_generator.h",
|
||||
"system_utils.h",
|
||||
"task_runner.h",
|
||||
"text_input_plugin.cc",
|
||||
@ -221,6 +223,7 @@ executable("flutter_windows_unittests") {
|
||||
sources = [
|
||||
# "flutter_project_bundle_unittests.cc", //TODO failing due to switches test failing. Blocked on https://github.com/flutter/flutter/issues/74153
|
||||
# "flutter_windows_engine_unittests.cc", //TODO failing to send / receive platform message get plugins working first. Blocked on https://github.com/flutter/flutter/issues/74155
|
||||
"sequential_id_generator_unittests.cc",
|
||||
"string_conversion_unittests.cc",
|
||||
"system_utils_unittests.cc",
|
||||
"testing/engine_modifier.h",
|
||||
|
||||
@ -128,24 +128,6 @@ static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This method is only valid during a window message related to mouse/touch
|
||||
// input.
|
||||
// See
|
||||
// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN#distinguishing-pen-input-from-mouse-and-touch.
|
||||
static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
|
||||
constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
|
||||
constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
|
||||
constexpr LPARAM kSignatureMask = 0xFFFFFF00;
|
||||
LPARAM info = GetMessageExtraInfo();
|
||||
if ((info & kSignatureMask) == kTouchOrPenSignature) {
|
||||
if ((info & kTouchSignature) == kTouchSignature) {
|
||||
return kFlutterPointerDeviceKindTouch;
|
||||
}
|
||||
return kFlutterPointerDeviceKindStylus;
|
||||
}
|
||||
return kFlutterPointerDeviceKindMouse;
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnDpiScale(unsigned int dpi){};
|
||||
|
||||
// When DesktopWindow notifies that a WM_Size message has come in
|
||||
@ -156,30 +138,42 @@ void FlutterWindowWin32::OnResize(unsigned int width, unsigned int height) {
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnPointerMove(double x, double y) {
|
||||
binding_handler_delegate_->OnPointerMove(x, y, GetFlutterPointerDeviceKind());
|
||||
void FlutterWindowWin32::OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id);
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnPointerDown(double x, double y, UINT button) {
|
||||
void FlutterWindowWin32::OnPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) {
|
||||
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
|
||||
if (flutter_button != 0) {
|
||||
binding_handler_delegate_->OnPointerDown(
|
||||
x, y, GetFlutterPointerDeviceKind(),
|
||||
x, y, device_kind, device_id,
|
||||
static_cast<FlutterPointerMouseButtons>(flutter_button));
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnPointerUp(double x, double y, UINT button) {
|
||||
void FlutterWindowWin32::OnPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) {
|
||||
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
|
||||
if (flutter_button != 0) {
|
||||
binding_handler_delegate_->OnPointerUp(
|
||||
x, y, GetFlutterPointerDeviceKind(),
|
||||
x, y, device_kind, device_id,
|
||||
static_cast<FlutterPointerMouseButtons>(flutter_button));
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnPointerLeave() {
|
||||
binding_handler_delegate_->OnPointerLeave(GetFlutterPointerDeviceKind());
|
||||
void FlutterWindowWin32::OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
binding_handler_delegate_->OnPointerLeave(device_kind, device_id);
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnSetCursor() {
|
||||
@ -217,13 +211,17 @@ void FlutterWindowWin32::OnComposeChange(const std::u16string& text,
|
||||
binding_handler_delegate_->OnComposeChange(text, cursor_pos);
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnScroll(double delta_x, double delta_y) {
|
||||
void FlutterWindowWin32::OnScroll(double delta_x,
|
||||
double delta_y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
POINT point;
|
||||
GetCursorPos(&point);
|
||||
|
||||
ScreenToClient(GetWindowHandle(), &point);
|
||||
binding_handler_delegate_->OnScroll(point.x, point.y, delta_x, delta_y,
|
||||
kScrollOffsetMultiplier);
|
||||
kScrollOffsetMultiplier, device_kind,
|
||||
device_id);
|
||||
}
|
||||
|
||||
void FlutterWindowWin32::OnCursorRectUpdated(const Rect& rect) {
|
||||
|
||||
@ -37,16 +37,28 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler {
|
||||
void OnResize(unsigned int width, unsigned int height) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnPointerMove(double x, double y) override;
|
||||
void OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnPointerDown(double x, double y, UINT button) override;
|
||||
void OnPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnPointerUp(double x, double y, UINT button) override;
|
||||
void OnPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnPointerLeave() override;
|
||||
void OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnSetCursor() override;
|
||||
@ -78,7 +90,10 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler {
|
||||
void OnCursorRectUpdated(const Rect& rect) override;
|
||||
|
||||
// |WindowWin32|
|
||||
void OnScroll(double delta_x, double delta_y) override;
|
||||
void OnScroll(double delta_x,
|
||||
double delta_y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) override;
|
||||
|
||||
// |FlutterWindowBindingHandler|
|
||||
void SetView(WindowBindingHandlerDelegate* view) override;
|
||||
|
||||
@ -28,6 +28,7 @@ namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
static constexpr int32_t kDefaultPointerDeviceId = 0;
|
||||
|
||||
// A key event handler that can be spied on while it forwards calls to the real
|
||||
// key event handler.
|
||||
@ -141,12 +142,16 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32,
|
||||
|
||||
MOCK_METHOD1(OnDpiScale, void(unsigned int));
|
||||
MOCK_METHOD2(OnResize, void(unsigned int, unsigned int));
|
||||
MOCK_METHOD2(OnPointerMove, void(double, double));
|
||||
MOCK_METHOD3(OnPointerDown, void(double, double, UINT));
|
||||
MOCK_METHOD3(OnPointerUp, void(double, double, UINT));
|
||||
MOCK_METHOD0(OnPointerLeave, void());
|
||||
MOCK_METHOD4(OnPointerMove,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD5(OnPointerDown,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t, UINT));
|
||||
MOCK_METHOD5(OnPointerUp,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t, UINT));
|
||||
MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD0(OnSetCursor, void());
|
||||
MOCK_METHOD2(OnScroll, void(double, double));
|
||||
MOCK_METHOD4(OnScroll,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD0(GetDpiScale, float());
|
||||
MOCK_METHOD0(IsVisible, bool());
|
||||
MOCK_METHOD1(UpdateCursorRect, void(const Rect&));
|
||||
@ -188,25 +193,35 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
|
||||
MockWindowBindingHandlerDelegate const&) = delete;
|
||||
|
||||
MOCK_METHOD2(OnWindowSizeChanged, void(size_t, size_t));
|
||||
MOCK_METHOD3(OnPointerMove, void(double, double, FlutterPointerDeviceKind));
|
||||
MOCK_METHOD4(OnPointerDown,
|
||||
MOCK_METHOD4(OnPointerMove,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD5(OnPointerDown,
|
||||
void(double,
|
||||
double,
|
||||
FlutterPointerDeviceKind,
|
||||
int32_t,
|
||||
FlutterPointerMouseButtons));
|
||||
MOCK_METHOD4(OnPointerUp,
|
||||
MOCK_METHOD5(OnPointerUp,
|
||||
void(double,
|
||||
double,
|
||||
FlutterPointerDeviceKind,
|
||||
int32_t,
|
||||
FlutterPointerMouseButtons));
|
||||
MOCK_METHOD1(OnPointerLeave, void(FlutterPointerDeviceKind));
|
||||
MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD1(OnText, void(const std::u16string&));
|
||||
MOCK_METHOD6(OnKey, bool(int, int, int, char32_t, bool, bool));
|
||||
MOCK_METHOD0(OnComposeBegin, void());
|
||||
MOCK_METHOD0(OnComposeCommit, void());
|
||||
MOCK_METHOD0(OnComposeEnd, void());
|
||||
MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int));
|
||||
MOCK_METHOD5(OnScroll, void(double, double, double, double, int));
|
||||
MOCK_METHOD7(OnScroll,
|
||||
void(double,
|
||||
double,
|
||||
double,
|
||||
double,
|
||||
int,
|
||||
FlutterPointerDeviceKind,
|
||||
int32_t));
|
||||
};
|
||||
|
||||
// A FlutterWindowsView that overrides the RegisterKeyboardHandlers function
|
||||
@ -517,69 +532,88 @@ TEST(FlutterWindowWin32Test, OnPointerStarSendsDeviceType) {
|
||||
win32window.SetView(&delegate);
|
||||
// Move
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse))
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch))
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus))
|
||||
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
|
||||
// Down
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
EXPECT_CALL(
|
||||
delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
EXPECT_CALL(
|
||||
delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
EXPECT_CALL(
|
||||
delegate,
|
||||
OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
|
||||
// Up
|
||||
EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId,
|
||||
kFlutterPointerButtonMousePrimary))
|
||||
.Times(1);
|
||||
|
||||
// Leave
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindMouse))
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindTouch))
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindStylus))
|
||||
EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId))
|
||||
.Times(1);
|
||||
|
||||
win32window.OnPointerMove(10.0, 10.0);
|
||||
win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave();
|
||||
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId);
|
||||
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave(kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId);
|
||||
|
||||
// Touch
|
||||
LPARAM original_lparam = SetMessageExtraInfo(0xFF51578b);
|
||||
win32window.OnPointerMove(10.0, 10.0);
|
||||
win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave();
|
||||
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId);
|
||||
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave(kFlutterPointerDeviceKindTouch,
|
||||
kDefaultPointerDeviceId);
|
||||
|
||||
// Pen
|
||||
SetMessageExtraInfo(0xFF515700);
|
||||
win32window.OnPointerMove(10.0, 10.0);
|
||||
win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave();
|
||||
|
||||
// Reset extra info for other tests.
|
||||
SetMessageExtraInfo(original_lparam);
|
||||
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId);
|
||||
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
|
||||
win32window.OnPointerLeave(kFlutterPointerDeviceKindStylus,
|
||||
kDefaultPointerDeviceId);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
|
||||
@ -9,6 +9,11 @@ namespace flutter {
|
||||
// Multipler used to map controller velocity to an appropriate scroll input.
|
||||
static constexpr double kControllerScrollMultiplier = 3;
|
||||
|
||||
// TODO(clarkezone): Determine pointer ID in
|
||||
// OnPointerPressed/OnPointerReleased/OnPointerMoved in order to support multi
|
||||
// touch. See https://github.com/flutter/flutter/issues/70201
|
||||
static constexpr int32_t kDefaultPointerDeviceId = 0;
|
||||
|
||||
FlutterWindowWinUWP::FlutterWindowWinUWP(
|
||||
ABI::Windows::ApplicationModel::Core::CoreApplicationView*
|
||||
applicationview) {
|
||||
@ -146,7 +151,7 @@ void FlutterWindowWinUWP::OnPointerPressed(
|
||||
FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args);
|
||||
|
||||
binding_handler_delegate_->OnPointerDown(
|
||||
x, y, device_kind,
|
||||
x, y, device_kind, kDefaultPointerDeviceId,
|
||||
FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary);
|
||||
}
|
||||
|
||||
@ -158,7 +163,7 @@ void FlutterWindowWinUWP::OnPointerReleased(
|
||||
FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args);
|
||||
|
||||
binding_handler_delegate_->OnPointerUp(
|
||||
x, y, device_kind,
|
||||
x, y, device_kind, kDefaultPointerDeviceId,
|
||||
FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary);
|
||||
}
|
||||
|
||||
@ -169,7 +174,8 @@ void FlutterWindowWinUWP::OnPointerMoved(
|
||||
double y = GetPosY(args);
|
||||
FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args);
|
||||
|
||||
binding_handler_delegate_->OnPointerMove(x, y, device_kind);
|
||||
binding_handler_delegate_->OnPointerMove(x, y, device_kind,
|
||||
kDefaultPointerDeviceId);
|
||||
}
|
||||
|
||||
void FlutterWindowWinUWP::OnPointerWheelChanged(
|
||||
@ -177,8 +183,10 @@ void FlutterWindowWinUWP::OnPointerWheelChanged(
|
||||
winrt::Windows::UI::Core::PointerEventArgs const& args) {
|
||||
double x = GetPosX(args);
|
||||
double y = GetPosY(args);
|
||||
FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args);
|
||||
int delta = args.CurrentPoint().Properties().MouseWheelDelta();
|
||||
binding_handler_delegate_->OnScroll(x, y, 0, -delta, 1);
|
||||
binding_handler_delegate_->OnScroll(x, y, 0, -delta, 1, device_kind,
|
||||
kDefaultPointerDeviceId);
|
||||
}
|
||||
|
||||
double FlutterWindowWinUWP::GetPosX(
|
||||
|
||||
@ -172,19 +172,21 @@ void FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) {
|
||||
|
||||
void FlutterWindowsView::OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) {
|
||||
SendPointerMove(x, y, device_kind);
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
|
||||
}
|
||||
|
||||
void FlutterWindowsView::OnPointerDown(
|
||||
double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons flutter_button) {
|
||||
if (flutter_button != 0) {
|
||||
uint64_t mouse_buttons = mouse_state_.buttons | flutter_button;
|
||||
SetMouseButtons(mouse_buttons);
|
||||
SendPointerDown(x, y, device_kind);
|
||||
auto state = GetOrCreatePointerState(device_kind, device_id);
|
||||
state->buttons |= flutter_button;
|
||||
SendPointerDown(x, y, state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,16 +194,18 @@ void FlutterWindowsView::OnPointerUp(
|
||||
double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons flutter_button) {
|
||||
if (flutter_button != 0) {
|
||||
uint64_t mouse_buttons = mouse_state_.buttons & ~flutter_button;
|
||||
SetMouseButtons(mouse_buttons);
|
||||
SendPointerUp(x, y, device_kind);
|
||||
auto state = GetOrCreatePointerState(device_kind, device_id);
|
||||
state->buttons &= ~flutter_button;
|
||||
SendPointerUp(x, y, state);
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterWindowsView::OnPointerLeave(FlutterPointerDeviceKind device_kind) {
|
||||
SendPointerLeave(device_kind);
|
||||
void FlutterWindowsView::OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
SendPointerLeave(GetOrCreatePointerState(device_kind, device_id));
|
||||
}
|
||||
|
||||
void FlutterWindowsView::OnText(const std::u16string& text) {
|
||||
@ -238,8 +242,11 @@ void FlutterWindowsView::OnScroll(double x,
|
||||
double y,
|
||||
double delta_x,
|
||||
double delta_y,
|
||||
int scroll_offset_multiplier) {
|
||||
SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier);
|
||||
int scroll_offset_multiplier,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
|
||||
device_id);
|
||||
}
|
||||
|
||||
void FlutterWindowsView::OnCursorRectUpdated(const Rect& rect) {
|
||||
@ -265,18 +272,38 @@ void FlutterWindowsView::SendInitialBounds() {
|
||||
binding_handler_->GetDpiScale());
|
||||
}
|
||||
|
||||
FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
// Create a virtual pointer ID that is unique across all device types
|
||||
// to prevent pointers from clashing in the engine's converter
|
||||
// (lib/ui/window/pointer_data_packet_converter.cc)
|
||||
int32_t pointer_id = (static_cast<int32_t>(device_kind) << 28) | device_id;
|
||||
|
||||
auto [it, added] = pointer_states_.try_emplace(pointer_id, nullptr);
|
||||
if (added) {
|
||||
auto state = std::make_unique<PointerState>();
|
||||
state->device_kind = device_kind;
|
||||
state->pointer_id = pointer_id;
|
||||
it->second = std::move(state);
|
||||
}
|
||||
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// Set's |event_data|'s phase to either kMove or kHover depending on the current
|
||||
// primary mouse button state.
|
||||
void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
|
||||
FlutterPointerEvent* event_data) const {
|
||||
FlutterPointerEvent* event_data,
|
||||
const PointerState* state) const {
|
||||
// For details about this logic, see FlutterPointerPhase in the embedder.h
|
||||
// file.
|
||||
if (mouse_state_.buttons == 0) {
|
||||
event_data->phase = mouse_state_.flutter_state_is_down
|
||||
if (state->buttons == 0) {
|
||||
event_data->phase = state->flutter_state_is_down
|
||||
? FlutterPointerPhase::kUp
|
||||
: FlutterPointerPhase::kHover;
|
||||
} else {
|
||||
event_data->phase = mouse_state_.flutter_state_is_down
|
||||
event_data->phase = state->flutter_state_is_down
|
||||
? FlutterPointerPhase::kMove
|
||||
: FlutterPointerPhase::kDown;
|
||||
}
|
||||
@ -284,47 +311,46 @@ void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
|
||||
|
||||
void FlutterWindowsView::SendPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) {
|
||||
PointerState* state) {
|
||||
FlutterPointerEvent event = {};
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = device_kind;
|
||||
SetEventPhaseFromCursorButtonState(&event);
|
||||
SendPointerEventWithData(event);
|
||||
|
||||
SetEventPhaseFromCursorButtonState(&event, state);
|
||||
SendPointerEventWithData(event, state);
|
||||
}
|
||||
|
||||
void FlutterWindowsView::SendPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) {
|
||||
PointerState* state) {
|
||||
FlutterPointerEvent event = {};
|
||||
SetEventPhaseFromCursorButtonState(&event);
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = device_kind;
|
||||
SendPointerEventWithData(event);
|
||||
SetMouseFlutterStateDown(true);
|
||||
|
||||
SetEventPhaseFromCursorButtonState(&event, state);
|
||||
SendPointerEventWithData(event, state);
|
||||
|
||||
state->flutter_state_is_down = true;
|
||||
}
|
||||
|
||||
void FlutterWindowsView::SendPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) {
|
||||
PointerState* state) {
|
||||
FlutterPointerEvent event = {};
|
||||
SetEventPhaseFromCursorButtonState(&event);
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = device_kind;
|
||||
SendPointerEventWithData(event);
|
||||
|
||||
SetEventPhaseFromCursorButtonState(&event, state);
|
||||
SendPointerEventWithData(event, state);
|
||||
if (event.phase == FlutterPointerPhase::kUp) {
|
||||
SetMouseFlutterStateDown(false);
|
||||
state->flutter_state_is_down = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterWindowsView::SendPointerLeave(
|
||||
FlutterPointerDeviceKind device_kind) {
|
||||
void FlutterWindowsView::SendPointerLeave(PointerState* state) {
|
||||
FlutterPointerEvent event = {};
|
||||
event.device_kind = device_kind;
|
||||
event.phase = FlutterPointerPhase::kRemove;
|
||||
SendPointerEventWithData(event);
|
||||
SendPointerEventWithData(event, state);
|
||||
}
|
||||
|
||||
void FlutterWindowsView::SendText(const std::u16string& text) {
|
||||
@ -378,39 +404,47 @@ void FlutterWindowsView::SendScroll(double x,
|
||||
double y,
|
||||
double delta_x,
|
||||
double delta_y,
|
||||
int scroll_offset_multiplier) {
|
||||
int scroll_offset_multiplier,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) {
|
||||
auto state = GetOrCreatePointerState(device_kind, device_id);
|
||||
|
||||
FlutterPointerEvent event = {};
|
||||
SetEventPhaseFromCursorButtonState(&event);
|
||||
event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
|
||||
event.scroll_delta_x = delta_x * scroll_offset_multiplier;
|
||||
event.scroll_delta_y = delta_y * scroll_offset_multiplier;
|
||||
SendPointerEventWithData(event);
|
||||
SetEventPhaseFromCursorButtonState(&event, state);
|
||||
SendPointerEventWithData(event, state);
|
||||
}
|
||||
|
||||
void FlutterWindowsView::SendPointerEventWithData(
|
||||
const FlutterPointerEvent& event_data) {
|
||||
const FlutterPointerEvent& event_data,
|
||||
PointerState* state) {
|
||||
// If sending anything other than an add, and the pointer isn't already added,
|
||||
// synthesize an add to satisfy Flutter's expectations about events.
|
||||
if (!mouse_state_.flutter_state_is_added &&
|
||||
if (!state->flutter_state_is_added &&
|
||||
event_data.phase != FlutterPointerPhase::kAdd) {
|
||||
FlutterPointerEvent event = {};
|
||||
event.phase = FlutterPointerPhase::kAdd;
|
||||
event.x = event_data.x;
|
||||
event.y = event_data.y;
|
||||
event.buttons = 0;
|
||||
SendPointerEventWithData(event);
|
||||
SendPointerEventWithData(event, state);
|
||||
}
|
||||
|
||||
// Don't double-add (e.g., if events are delivered out of order, so an add has
|
||||
// already been synthesized).
|
||||
if (mouse_state_.flutter_state_is_added &&
|
||||
if (state->flutter_state_is_added &&
|
||||
event_data.phase == FlutterPointerPhase::kAdd) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event = event_data;
|
||||
event.buttons = mouse_state_.buttons;
|
||||
event.device_kind = state->device_kind;
|
||||
event.device = state->pointer_id;
|
||||
event.buttons = state->buttons;
|
||||
|
||||
// Set metadata that's always the same regardless of the event.
|
||||
event.struct_size = sizeof(event);
|
||||
@ -422,10 +456,12 @@ void FlutterWindowsView::SendPointerEventWithData(
|
||||
engine_->SendPointerEvent(event);
|
||||
|
||||
if (event_data.phase == FlutterPointerPhase::kAdd) {
|
||||
SetMouseFlutterStateAdded(true);
|
||||
state->flutter_state_is_added = true;
|
||||
} else if (event_data.phase == FlutterPointerPhase::kRemove) {
|
||||
SetMouseFlutterStateAdded(false);
|
||||
ResetMouseState();
|
||||
auto it = pointer_states_.find(state->pointer_id);
|
||||
if (it != pointer_states_.end()) {
|
||||
pointer_states_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h"
|
||||
@ -94,22 +96,26 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
// |WindowBindingHandlerDelegate|
|
||||
void OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) override;
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) override;
|
||||
|
||||
// |WindowBindingHandlerDelegate|
|
||||
void OnPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons button) override;
|
||||
|
||||
// |WindowBindingHandlerDelegate|
|
||||
void OnPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons button) override;
|
||||
|
||||
// |WindowBindingHandlerDelegate|
|
||||
void OnPointerLeave(FlutterPointerDeviceKind device_kind) override;
|
||||
void OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id = 0) override;
|
||||
|
||||
// |WindowBindingHandlerDelegate|
|
||||
void OnText(const std::u16string&) override;
|
||||
@ -139,7 +145,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
double y,
|
||||
double delta_x,
|
||||
double delta_y,
|
||||
int scroll_offset_multiplier) override;
|
||||
int scroll_offset_multiplier,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) override;
|
||||
|
||||
// |TextInputPluginDelegate|
|
||||
void OnCursorRectUpdated(const Rect& rect) override;
|
||||
@ -161,15 +169,21 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
std::unique_ptr<flutter::KeyboardHandlerBase> handler);
|
||||
|
||||
private:
|
||||
// Struct holding the mouse state. The engine doesn't keep track of which
|
||||
// mouse buttons have been pressed, so it's the embedding's responsibility.
|
||||
struct MouseState {
|
||||
// True if the last event sent to Flutter had at least one mouse button.
|
||||
// pressed.
|
||||
// Struct holding the state of an individual pointer. The engine doesn't keep
|
||||
// track of which buttons have been pressed, so it's the embedding's
|
||||
// responsibility.
|
||||
struct PointerState {
|
||||
// The device kind.
|
||||
FlutterPointerDeviceKind device_kind = kFlutterPointerDeviceKindMouse;
|
||||
|
||||
// A virtual pointer ID that is unique across all device kinds.
|
||||
int32_t pointer_id = 0;
|
||||
|
||||
// True if the last event sent to Flutter had at least one button pressed.
|
||||
bool flutter_state_is_down = false;
|
||||
|
||||
// True if kAdd has been sent to Flutter. Used to determine whether
|
||||
// to send a kAdd event before sending an incoming mouse event, since
|
||||
// to send a kAdd event before sending an incoming pointer event, since
|
||||
// Flutter expects pointers to be added before events are sent for them.
|
||||
bool flutter_state_is_added = false;
|
||||
|
||||
@ -195,24 +209,20 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
void SendWindowMetrics(size_t width, size_t height, double dpiscale) const;
|
||||
|
||||
// Reports a mouse movement to Flutter engine.
|
||||
void SendPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind);
|
||||
void SendPointerMove(double x, double y, PointerState* state);
|
||||
|
||||
// Reports mouse press to Flutter engine.
|
||||
void SendPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind);
|
||||
void SendPointerDown(double x, double y, PointerState* state);
|
||||
|
||||
// Reports mouse release to Flutter engine.
|
||||
void SendPointerUp(double x, double y, FlutterPointerDeviceKind device_kind);
|
||||
void SendPointerUp(double x, double y, PointerState* state);
|
||||
|
||||
// Reports mouse left the window client area.
|
||||
//
|
||||
// Win32 api doesn't have "mouse enter" event. Therefore, there is no
|
||||
// SendPointerEnter method. A mouse enter event is tracked then the "move"
|
||||
// event is called.
|
||||
void SendPointerLeave(FlutterPointerDeviceKind device_kind);
|
||||
void SendPointerLeave(PointerState* state);
|
||||
|
||||
// Reports a keyboard character to Flutter engine.
|
||||
void SendText(const std::u16string&);
|
||||
@ -255,35 +265,24 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
double y,
|
||||
double delta_x,
|
||||
double delta_y,
|
||||
int scroll_offset_multiplier);
|
||||
int scroll_offset_multiplier,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id);
|
||||
|
||||
// Creates a PointerState object unless it already exists.
|
||||
PointerState* GetOrCreatePointerState(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id);
|
||||
|
||||
// Sets |event_data|'s phase to either kMove or kHover depending on the
|
||||
// current primary mouse button state.
|
||||
void SetEventPhaseFromCursorButtonState(
|
||||
FlutterPointerEvent* event_data) const;
|
||||
void SetEventPhaseFromCursorButtonState(FlutterPointerEvent* event_data,
|
||||
const PointerState* state) const;
|
||||
|
||||
// Sends a pointer event to the Flutter engine based on given data. Since
|
||||
// all input messages are passed in physical pixel values, no translation is
|
||||
// needed before passing on to engine.
|
||||
void SendPointerEventWithData(const FlutterPointerEvent& event_data);
|
||||
|
||||
// Resets the mouse state to its default values.
|
||||
void ResetMouseState() { mouse_state_ = MouseState(); }
|
||||
|
||||
// Updates the mouse state to whether the last event to Flutter had at least
|
||||
// one mouse button pressed.
|
||||
void SetMouseFlutterStateDown(bool is_down) {
|
||||
mouse_state_.flutter_state_is_down = is_down;
|
||||
}
|
||||
|
||||
// Updates the mouse state to whether the last event to Flutter was a kAdd
|
||||
// event.
|
||||
void SetMouseFlutterStateAdded(bool is_added) {
|
||||
mouse_state_.flutter_state_is_added = is_added;
|
||||
}
|
||||
|
||||
// Updates the currently pressed buttons.
|
||||
void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; }
|
||||
void SendPointerEventWithData(const FlutterPointerEvent& event_data,
|
||||
PointerState* state);
|
||||
|
||||
// Currently configured WindowsRenderTarget for this view used by
|
||||
// surface_manager for creation of render surfaces and bound to the physical
|
||||
@ -293,8 +292,8 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
|
||||
// The engine associated with this view.
|
||||
std::unique_ptr<FlutterWindowsEngine> engine_;
|
||||
|
||||
// Keeps track of mouse state in relation to the window.
|
||||
MouseState mouse_state_;
|
||||
// Keeps track of pointer states in relation to the window.
|
||||
std::map<int32_t, std::unique_ptr<PointerState>> pointer_states_;
|
||||
|
||||
// The plugin registrar managing internal plugins.
|
||||
std::unique_ptr<flutter::PluginRegistrar> internal_plugin_registrar_;
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
static constexpr int32_t kDefaultPointerDeviceId = 0;
|
||||
|
||||
GamepadCursorWinUWP::GamepadCursorWinUWP(
|
||||
WindowBindingHandlerDelegate* view,
|
||||
DisplayHelperWinUWP* displayhelper,
|
||||
@ -162,16 +164,18 @@ void GamepadCursorWinUWP::OnGamepadLeftStickMoved(double x, double y) {
|
||||
// TODO(dnfield): Support for gamepad as a distinct device type?
|
||||
// https://github.com/flutter/flutter/issues/80472
|
||||
binding_handler_delegate_->OnPointerMove(scaled.x, scaled.y,
|
||||
kFlutterPointerDeviceKindMouse);
|
||||
kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId);
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadCursorWinUWP::OnGamepadRightStickMoved(double x, double y) {
|
||||
winrt::Windows::Foundation::Numerics::float3 scaled =
|
||||
GetScaledInput(cursor_visual_.Offset());
|
||||
binding_handler_delegate_->OnScroll(scaled.x, scaled.y,
|
||||
x * kControllerScrollMultiplier,
|
||||
y * kControllerScrollMultiplier, 1);
|
||||
binding_handler_delegate_->OnScroll(
|
||||
scaled.x, scaled.y, x * kControllerScrollMultiplier,
|
||||
y * kControllerScrollMultiplier, 1, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId);
|
||||
}
|
||||
|
||||
void GamepadCursorWinUWP::OnGamepadButtonPressed(
|
||||
@ -190,6 +194,7 @@ void GamepadCursorWinUWP::OnGamepadButtonPressed(
|
||||
// https://github.com/flutter/flutter/issues/80472
|
||||
binding_handler_delegate_->OnPointerDown(
|
||||
scaled.x, scaled.y, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId,
|
||||
FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary);
|
||||
} else if ((buttons &
|
||||
winrt::Windows::Gaming::Input::GamepadButtons::DPadLeft) ==
|
||||
@ -234,6 +239,7 @@ void GamepadCursorWinUWP::OnGamepadButtonReleased(
|
||||
// https://github.com/flutter/flutter/issues/80472
|
||||
binding_handler_delegate_->OnPointerUp(
|
||||
scaled.x, scaled.y, kFlutterPointerDeviceKindMouse,
|
||||
kDefaultPointerDeviceId,
|
||||
FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary);
|
||||
} else if ((buttons &
|
||||
winrt::Windows::Gaming::Input::GamepadButtons::DPadLeft) ==
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/sequential_id_generator.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
namespace {
|
||||
|
||||
// Removes |key| from |first|, and |first[key]| from |second|.
|
||||
template <typename T>
|
||||
void Remove(uint32_t key, T* first, T* second) {
|
||||
auto iter = first->find(key);
|
||||
if (iter == first->end())
|
||||
return;
|
||||
|
||||
uint32_t second_key = iter->second;
|
||||
first->erase(iter);
|
||||
|
||||
iter = second->find(second_key);
|
||||
second->erase(iter);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SequentialIdGenerator::SequentialIdGenerator(uint32_t min_id, uint32_t max_id)
|
||||
: min_id_(min_id), min_available_id_(min_id), max_id_(max_id) {}
|
||||
|
||||
SequentialIdGenerator::~SequentialIdGenerator() {}
|
||||
|
||||
uint32_t SequentialIdGenerator::GetGeneratedId(uint32_t number) {
|
||||
auto it = number_to_id_.find(number);
|
||||
if (it != number_to_id_.end())
|
||||
return it->second;
|
||||
|
||||
auto id = GetNextAvailableId();
|
||||
number_to_id_.emplace(number, id);
|
||||
id_to_number_.emplace(id, number);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool SequentialIdGenerator::HasGeneratedIdFor(uint32_t number) const {
|
||||
return number_to_id_.find(number) != number_to_id_.end();
|
||||
}
|
||||
|
||||
void SequentialIdGenerator::ReleaseNumber(uint32_t number) {
|
||||
if (number_to_id_.count(number) > 0U) {
|
||||
UpdateNextAvailableIdAfterRelease(number_to_id_[number]);
|
||||
Remove(number, &number_to_id_, &id_to_number_);
|
||||
}
|
||||
}
|
||||
|
||||
void SequentialIdGenerator::ReleaseId(uint32_t id) {
|
||||
if (id_to_number_.count(id) > 0U) {
|
||||
UpdateNextAvailableIdAfterRelease(id);
|
||||
Remove(id_to_number_[id], &number_to_id_, &id_to_number_);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t SequentialIdGenerator::GetNextAvailableId() {
|
||||
while (id_to_number_.count(min_available_id_) > 0 &&
|
||||
min_available_id_ < max_id_) {
|
||||
++min_available_id_;
|
||||
}
|
||||
if (min_available_id_ >= max_id_)
|
||||
min_available_id_ = min_id_;
|
||||
return min_available_id_;
|
||||
}
|
||||
|
||||
void SequentialIdGenerator::UpdateNextAvailableIdAfterRelease(uint32_t id) {
|
||||
if (id < min_available_id_) {
|
||||
min_available_id_ = id;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
@ -0,0 +1,59 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_SEQUENTIAL_ID_GENERATOR_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_SEQUENTIAL_ID_GENERATOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// This is used to generate a series of sequential ID numbers in a way that a
|
||||
// new ID is always the lowest possible ID in the sequence.
|
||||
//
|
||||
// based on
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:ui/gfx/sequential_id_generator.h
|
||||
class SequentialIdGenerator {
|
||||
public:
|
||||
// Creates a new generator with the specified lower bound and uppoer bound for
|
||||
// the IDs.
|
||||
explicit SequentialIdGenerator(uint32_t min_id, uint32_t max_id);
|
||||
~SequentialIdGenerator();
|
||||
|
||||
// Generates a unique ID to represent |number|. The generated ID is the
|
||||
// smallest available ID greater than or equal to the |min_id| specified
|
||||
// during creation of the generator.
|
||||
uint32_t GetGeneratedId(uint32_t number);
|
||||
|
||||
// Checks to see if the generator currently has a unique ID generated for
|
||||
// |number|.
|
||||
bool HasGeneratedIdFor(uint32_t number) const;
|
||||
|
||||
// Removes the ID previously generated for |number| by calling
|
||||
// |GetGeneratedID()| - does nothing if the number is not mapped.
|
||||
void ReleaseNumber(uint32_t number);
|
||||
|
||||
// Releases ID previously generated by calling |GetGeneratedID()|. Does
|
||||
// nothing if the ID is not mapped.
|
||||
void ReleaseId(uint32_t id);
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<uint32_t, uint32_t> IdMap;
|
||||
|
||||
uint32_t GetNextAvailableId();
|
||||
|
||||
void UpdateNextAvailableIdAfterRelease(uint32_t id);
|
||||
|
||||
IdMap number_to_id_;
|
||||
IdMap id_to_number_;
|
||||
|
||||
const uint32_t min_id_;
|
||||
const uint32_t max_id_;
|
||||
uint32_t min_available_id_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_SEQUENTIAL_ID_GENERATOR_H_
|
||||
@ -0,0 +1,49 @@
|
||||
// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/sequential_id_generator.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
TEST(SequentialIdGeneratorTest, RemoveMultipleNumbers) {
|
||||
const uint32_t kMinId = 4;
|
||||
const uint32_t kMaxId = 128;
|
||||
|
||||
SequentialIdGenerator generator(kMinId, kMaxId);
|
||||
|
||||
EXPECT_EQ(4U, generator.GetGeneratedId(45));
|
||||
EXPECT_EQ(5U, generator.GetGeneratedId(55));
|
||||
EXPECT_EQ(6U, generator.GetGeneratedId(15));
|
||||
|
||||
generator.ReleaseNumber(45);
|
||||
EXPECT_FALSE(generator.HasGeneratedIdFor(45));
|
||||
generator.ReleaseNumber(15);
|
||||
EXPECT_FALSE(generator.HasGeneratedIdFor(15));
|
||||
|
||||
EXPECT_EQ(5U, generator.GetGeneratedId(55));
|
||||
EXPECT_EQ(4U, generator.GetGeneratedId(12));
|
||||
|
||||
generator.ReleaseNumber(12);
|
||||
generator.ReleaseNumber(55);
|
||||
EXPECT_EQ(4U, generator.GetGeneratedId(0));
|
||||
}
|
||||
|
||||
TEST(SequentialIdGeneratorTest, MaybeRemoveNumbers) {
|
||||
const uint32_t kMinId = 0;
|
||||
const uint32_t kMaxId = 128;
|
||||
|
||||
SequentialIdGenerator generator(kMinId, kMaxId);
|
||||
|
||||
EXPECT_EQ(0U, generator.GetGeneratedId(42));
|
||||
|
||||
generator.ReleaseNumber(42);
|
||||
EXPECT_FALSE(generator.HasGeneratedIdFor(42));
|
||||
generator.ReleaseNumber(42);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
@ -34,14 +34,18 @@ class MockWin32Window : public WindowWin32, public MockMessageQueue {
|
||||
|
||||
MOCK_METHOD1(OnDpiScale, void(unsigned int));
|
||||
MOCK_METHOD2(OnResize, void(unsigned int, unsigned int));
|
||||
MOCK_METHOD2(OnPointerMove, void(double, double));
|
||||
MOCK_METHOD3(OnPointerDown, void(double, double, UINT));
|
||||
MOCK_METHOD3(OnPointerUp, void(double, double, UINT));
|
||||
MOCK_METHOD0(OnPointerLeave, void());
|
||||
MOCK_METHOD4(OnPointerMove,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD5(OnPointerDown,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t, UINT));
|
||||
MOCK_METHOD5(OnPointerUp,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t, UINT));
|
||||
MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD0(OnSetCursor, void());
|
||||
MOCK_METHOD1(OnText, void(const std::u16string&));
|
||||
MOCK_METHOD6(OnKey, bool(int, int, int, char32_t, bool, bool));
|
||||
MOCK_METHOD2(OnScroll, void(double, double));
|
||||
MOCK_METHOD4(OnScroll,
|
||||
void(double, double, FlutterPointerDeviceKind, int32_t));
|
||||
MOCK_METHOD0(OnComposeBegin, void());
|
||||
MOCK_METHOD0(OnComposeCommit, void());
|
||||
MOCK_METHOD0(OnComposeEnd, void());
|
||||
|
||||
@ -21,13 +21,15 @@ class WindowBindingHandlerDelegate {
|
||||
// Typically called by currently configured WindowBindingHandler
|
||||
virtual void OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind) = 0;
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
|
||||
// Notifies delegate that backing window mouse pointer button has been
|
||||
// pressed. Typically called by currently configured WindowBindingHandler
|
||||
virtual void OnPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons button) = 0;
|
||||
|
||||
// Notifies delegate that backing window mouse pointer button has been
|
||||
@ -35,11 +37,13 @@ class WindowBindingHandlerDelegate {
|
||||
virtual void OnPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
FlutterPointerMouseButtons button) = 0;
|
||||
|
||||
// Notifies delegate that backing window mouse pointer has left the window.
|
||||
// Typically called by currently configured WindowBindingHandler
|
||||
virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind) = 0;
|
||||
virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
|
||||
// Notifies delegate that backing window has received text.
|
||||
// Typically called by currently configured WindowBindingHandler
|
||||
@ -88,7 +92,9 @@ class WindowBindingHandlerDelegate {
|
||||
double y,
|
||||
double delta_x,
|
||||
double delta_y,
|
||||
int scroll_offset_multiplier) = 0;
|
||||
int scroll_offset_multiplier,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -13,13 +13,39 @@
|
||||
namespace flutter {
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr int32_t kDefaultPointerDeviceId = 0;
|
||||
|
||||
// This method is only valid during a window message related to mouse/touch
|
||||
// input.
|
||||
// See
|
||||
// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN#distinguishing-pen-input-from-mouse-and-touch.
|
||||
static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
|
||||
constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
|
||||
constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
|
||||
constexpr LPARAM kSignatureMask = 0xFFFFFF00;
|
||||
LPARAM info = GetMessageExtraInfo();
|
||||
if ((info & kSignatureMask) == kTouchOrPenSignature) {
|
||||
if ((info & kTouchSignature) == kTouchSignature) {
|
||||
return kFlutterPointerDeviceKindTouch;
|
||||
}
|
||||
return kFlutterPointerDeviceKindStylus;
|
||||
}
|
||||
return kFlutterPointerDeviceKindMouse;
|
||||
}
|
||||
|
||||
char32_t CodePointFromSurrogatePair(wchar_t high, wchar_t low) {
|
||||
return 0x10000 + ((static_cast<char32_t>(high) & 0x000003FF) << 10) +
|
||||
(low & 0x3FF);
|
||||
}
|
||||
|
||||
static const int kMinTouchDeviceId = 0;
|
||||
static const int kMaxTouchDeviceId = 128;
|
||||
|
||||
} // namespace
|
||||
|
||||
WindowWin32::WindowWin32() {
|
||||
WindowWin32::WindowWin32()
|
||||
: touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId) {
|
||||
// Get the DPI of the primary monitor as the initial DPI. If Per-Monitor V2 is
|
||||
// supported, |current_dpi_| should be updated in the
|
||||
// kWmDpiChangedBeforeParent message.
|
||||
@ -94,6 +120,7 @@ LRESULT CALLBACK WindowWin32::WndProc(HWND const window,
|
||||
auto that = static_cast<WindowWin32*>(cs->lpCreateParams);
|
||||
that->window_handle_ = window;
|
||||
that->text_input_manager_.SetWindowHandle(window);
|
||||
RegisterTouchWindow(window, 0);
|
||||
} else if (WindowWin32* that = GetThisFromHandle(window)) {
|
||||
return that->HandleMessage(message, wparam, lparam);
|
||||
}
|
||||
@ -204,6 +231,7 @@ WindowWin32::HandleMessage(UINT const message,
|
||||
int xPos = 0, yPos = 0;
|
||||
UINT width = 0, height = 0;
|
||||
UINT button_pressed = 0;
|
||||
FlutterPointerDeviceKind device_kind;
|
||||
|
||||
switch (message) {
|
||||
case kWmDpiChangedBeforeParent:
|
||||
@ -218,15 +246,56 @@ WindowWin32::HandleMessage(UINT const message,
|
||||
current_height_ = height;
|
||||
HandleResize(width, height);
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
TrackMouseLeaveEvent(window_handle_);
|
||||
case WM_TOUCH: {
|
||||
UINT num_points = LOWORD(wparam);
|
||||
touch_points_.resize(num_points);
|
||||
auto touch_input_handle = reinterpret_cast<HTOUCHINPUT>(lparam);
|
||||
if (GetTouchInputInfo(touch_input_handle, num_points,
|
||||
touch_points_.data(), sizeof(TOUCHINPUT))) {
|
||||
for (const auto& touch : touch_points_) {
|
||||
// Generate a mapped ID for the Windows-provided touch ID
|
||||
auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
|
||||
|
||||
xPos = GET_X_LPARAM(lparam);
|
||||
yPos = GET_Y_LPARAM(lparam);
|
||||
OnPointerMove(static_cast<double>(xPos), static_cast<double>(yPos));
|
||||
POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
|
||||
TOUCH_COORD_TO_PIXEL(touch.y)};
|
||||
ScreenToClient(window_handle_, &pt);
|
||||
auto x = static_cast<double>(pt.x);
|
||||
auto y = static_cast<double>(pt.y);
|
||||
|
||||
if (touch.dwFlags & TOUCHEVENTF_DOWN) {
|
||||
OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
|
||||
WM_LBUTTONDOWN);
|
||||
} else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
|
||||
OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id);
|
||||
} else if (touch.dwFlags & TOUCHEVENTF_UP) {
|
||||
OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
|
||||
WM_LBUTTONDOWN);
|
||||
OnPointerLeave(kFlutterPointerDeviceKindTouch, touch_id);
|
||||
touch_id_generator_.ReleaseNumber(touch.dwID);
|
||||
}
|
||||
}
|
||||
CloseTouchInputHandle(touch_input_handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEMOVE:
|
||||
device_kind = GetFlutterPointerDeviceKind();
|
||||
if (device_kind == kFlutterPointerDeviceKindMouse) {
|
||||
TrackMouseLeaveEvent(window_handle_);
|
||||
|
||||
xPos = GET_X_LPARAM(lparam);
|
||||
yPos = GET_Y_LPARAM(lparam);
|
||||
|
||||
OnPointerMove(static_cast<double>(xPos), static_cast<double>(yPos),
|
||||
device_kind, kDefaultPointerDeviceId);
|
||||
}
|
||||
break;
|
||||
case WM_MOUSELEAVE:;
|
||||
OnPointerLeave();
|
||||
case WM_MOUSELEAVE:
|
||||
device_kind = GetFlutterPointerDeviceKind();
|
||||
if (device_kind == kFlutterPointerDeviceKindMouse) {
|
||||
OnPointerLeave(device_kind, kDefaultPointerDeviceId);
|
||||
}
|
||||
|
||||
// Once the tracked event is received, the TrackMouseEvent function
|
||||
// resets. Set to false to make sure it's called once mouse movement is
|
||||
// detected again.
|
||||
@ -250,6 +319,11 @@ WindowWin32::HandleMessage(UINT const message,
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_XBUTTONDOWN:
|
||||
device_kind = GetFlutterPointerDeviceKind();
|
||||
if (device_kind != kFlutterPointerDeviceKindMouse) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (message == WM_LBUTTONDOWN) {
|
||||
// Capture the pointer in case the user drags outside the client area.
|
||||
// In this case, the "mouse leave" event is delayed until the user
|
||||
@ -265,12 +339,17 @@ WindowWin32::HandleMessage(UINT const message,
|
||||
xPos = GET_X_LPARAM(lparam);
|
||||
yPos = GET_Y_LPARAM(lparam);
|
||||
OnPointerDown(static_cast<double>(xPos), static_cast<double>(yPos),
|
||||
button_pressed);
|
||||
device_kind, kDefaultPointerDeviceId, button_pressed);
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONUP:
|
||||
device_kind = GetFlutterPointerDeviceKind();
|
||||
if (device_kind != kFlutterPointerDeviceKindMouse) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (message == WM_LBUTTONUP) {
|
||||
ReleaseCapture();
|
||||
}
|
||||
@ -281,16 +360,18 @@ WindowWin32::HandleMessage(UINT const message,
|
||||
xPos = GET_X_LPARAM(lparam);
|
||||
yPos = GET_Y_LPARAM(lparam);
|
||||
OnPointerUp(static_cast<double>(xPos), static_cast<double>(yPos),
|
||||
button_pressed);
|
||||
device_kind, kDefaultPointerDeviceId, button_pressed);
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
OnScroll(0.0, -(static_cast<short>(HIWORD(wparam)) /
|
||||
static_cast<double>(WHEEL_DELTA)));
|
||||
OnScroll(0.0,
|
||||
-(static_cast<short>(HIWORD(wparam)) /
|
||||
static_cast<double>(WHEEL_DELTA)),
|
||||
kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
|
||||
break;
|
||||
case WM_MOUSEHWHEEL:
|
||||
OnScroll((static_cast<short>(HIWORD(wparam)) /
|
||||
static_cast<double>(WHEEL_DELTA)),
|
||||
0.0);
|
||||
0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
|
||||
break;
|
||||
case WM_INPUTLANGCHANGE:
|
||||
// TODO(cbracken): pass this to TextInputManager to aid with
|
||||
|
||||
@ -11,7 +11,10 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "flutter/shell/platform/windows/sequential_id_generator.h"
|
||||
#include "flutter/shell/platform/windows/text_input_manager_win32.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -54,17 +57,18 @@ class WindowWin32 {
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
LRESULT
|
||||
HandleMessage(UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
LRESULT HandleMessage(UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// When WM_DPICHANGE process it using |hWnd|, |wParam|. If
|
||||
// |top_level| is set, extract the suggested new size from |lParam| and resize
|
||||
// the window to the new suggested size. If |top_level| is not set, the
|
||||
// |lParam| will not contain a suggested size hence ignore it.
|
||||
LRESULT
|
||||
HandleDpiChange(HWND hWnd, WPARAM wParam, LPARAM lParam, bool top_level);
|
||||
LRESULT HandleDpiChange(HWND hWnd,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam,
|
||||
bool top_level);
|
||||
|
||||
// Called when the DPI changes either when a
|
||||
// user drags the window between monitors of differing DPI or when the user
|
||||
@ -76,17 +80,29 @@ class WindowWin32 {
|
||||
|
||||
// Called when the pointer moves within the
|
||||
// window bounds.
|
||||
virtual void OnPointerMove(double x, double y) = 0;
|
||||
virtual void OnPointerMove(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
|
||||
// Called when the a mouse button, determined by |button|, goes down.
|
||||
virtual void OnPointerDown(double x, double y, UINT button) = 0;
|
||||
virtual void OnPointerDown(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) = 0;
|
||||
|
||||
// Called when the a mouse button, determined by |button|, goes from
|
||||
// down to up
|
||||
virtual void OnPointerUp(double x, double y, UINT button) = 0;
|
||||
virtual void OnPointerUp(double x,
|
||||
double y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id,
|
||||
UINT button) = 0;
|
||||
|
||||
// Called when the mouse leaves the window.
|
||||
virtual void OnPointerLeave() = 0;
|
||||
virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
|
||||
// Called when the cursor should be set for the client area.
|
||||
virtual void OnSetCursor() = 0;
|
||||
@ -151,7 +167,10 @@ class WindowWin32 {
|
||||
virtual void UpdateCursorRect(const Rect& rect);
|
||||
|
||||
// Called when mouse scrollwheel input occurs.
|
||||
virtual void OnScroll(double delta_x, double delta_y) = 0;
|
||||
virtual void OnScroll(double delta_x,
|
||||
double delta_y,
|
||||
FlutterPointerDeviceKind device_kind,
|
||||
int32_t device_id) = 0;
|
||||
|
||||
UINT GetCurrentDPI();
|
||||
|
||||
@ -226,6 +245,12 @@ class WindowWin32 {
|
||||
|
||||
// Manages IME state.
|
||||
TextInputManagerWin32 text_input_manager_;
|
||||
|
||||
// Used for temporarily storing the WM_TOUCH-provided touch points.
|
||||
std::vector<TOUCHINPUT> touch_points_;
|
||||
|
||||
// Generates touch point IDs for touch events.
|
||||
SequentialIdGenerator touch_id_generator_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -25,7 +25,9 @@ TEST(MockWin32Window, VerticalScroll) {
|
||||
const int scroll_amount = 10;
|
||||
// Vertical scroll should be passed along, adjusted for scroll tick size
|
||||
// and direction.
|
||||
EXPECT_CALL(window, OnScroll(0, -scroll_amount / 120.0)).Times(1);
|
||||
EXPECT_CALL(window, OnScroll(0, -scroll_amount / 120.0,
|
||||
kFlutterPointerDeviceKindMouse, 0))
|
||||
.Times(1);
|
||||
|
||||
window.InjectWindowMessage(WM_MOUSEWHEEL, MAKEWPARAM(0, scroll_amount), 0);
|
||||
}
|
||||
@ -34,7 +36,9 @@ TEST(MockWin32Window, HorizontalScroll) {
|
||||
MockWin32Window window;
|
||||
const int scroll_amount = 10;
|
||||
// Vertical scroll should be passed along, adjusted for scroll tick size.
|
||||
EXPECT_CALL(window, OnScroll(scroll_amount / 120.0, 0)).Times(1);
|
||||
EXPECT_CALL(window, OnScroll(scroll_amount / 120.0, 0,
|
||||
kFlutterPointerDeviceKindMouse, 0))
|
||||
.Times(1);
|
||||
|
||||
window.InjectWindowMessage(WM_MOUSEHWHEEL, MAKEWPARAM(0, scroll_amount), 0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user