Skip to content

Commit a650790

Browse files
authored
Add scroll wheel support to desktop GLFW shell (flutter#8416)
Sends scroll events from GLFW to the Flutter engine, allowing scrolling of Scrollables via a scroll wheel.
1 parent 77d8e12 commit a650790

File tree

1 file changed

+76
-30
lines changed

1 file changed

+76
-30
lines changed

shell/platform/glfw/flutter_glfw.cc

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -155,57 +155,86 @@ static void GLFWFramebufferSizeCallback(GLFWwindow* window,
155155
FlutterEngineSendWindowMetricsEvent(state->engine, &event);
156156
}
157157

158-
// Sends a pointer event to the Flutter engine with the given phase.
159-
static void SendPointerEventWithPhase(GLFWwindow* window,
160-
FlutterPointerPhase phase,
161-
double x,
162-
double y) {
163-
auto state = GetSavedWindowState(window);
158+
// Sends a pointer event to the Flutter engine based on the given data.
159+
//
160+
// Any coordinate/distance values in |event_data| should be in screen
161+
// coordinates; they will be adjusted to pixel values before being sent.
162+
static void SendPointerEventWithData(GLFWwindow* window,
163+
const FlutterPointerEvent& event_data) {
164+
auto* state = GetSavedWindowState(window);
164165
// If sending anything other than an add, and the pointer isn't already added,
165166
// synthesize an add to satisfy Flutter's expectations about events.
166-
if (!state->pointer_currently_added && phase != FlutterPointerPhase::kAdd) {
167-
SendPointerEventWithPhase(window, FlutterPointerPhase::kAdd, x, y);
167+
if (!state->pointer_currently_added &&
168+
event_data.phase != FlutterPointerPhase::kAdd) {
169+
FlutterPointerEvent event = {};
170+
event.phase = FlutterPointerPhase::kAdd;
171+
event.x = event_data.x;
172+
event.y = event_data.y;
173+
SendPointerEventWithData(window, event);
168174
}
169175
// Don't double-add (e.g., if events are delivered out of order, so an add has
170176
// already been synthesized).
171-
if (state->pointer_currently_added && phase == FlutterPointerPhase::kAdd) {
177+
if (state->pointer_currently_added &&
178+
event_data.phase == FlutterPointerPhase::kAdd) {
172179
return;
173180
}
174181

175-
FlutterPointerEvent event = {};
182+
FlutterPointerEvent event = event_data;
183+
// Set metadata that's always the same regardless of the event.
176184
event.struct_size = sizeof(event);
177-
event.phase = phase;
178-
event.x = x * state->window_pixels_per_screen_coordinate;
179-
event.y = y * state->window_pixels_per_screen_coordinate;
180185
event.timestamp =
181186
std::chrono::duration_cast<std::chrono::microseconds>(
182187
std::chrono::high_resolution_clock::now().time_since_epoch())
183188
.count();
189+
// Convert all screen coordinates to pixel coordinates.
190+
event.x *= state->window_pixels_per_screen_coordinate;
191+
event.y *= state->window_pixels_per_screen_coordinate;
192+
event.scroll_delta_x *= state->window_pixels_per_screen_coordinate;
193+
event.scroll_delta_y *= state->window_pixels_per_screen_coordinate;
194+
184195
FlutterEngineSendPointerEvent(state->engine, &event, 1);
185196

186-
if (phase == FlutterPointerPhase::kAdd) {
197+
if (event_data.phase == FlutterPointerPhase::kAdd) {
187198
state->pointer_currently_added = true;
188-
} else if (phase == FlutterPointerPhase::kRemove) {
199+
} else if (event_data.phase == FlutterPointerPhase::kRemove) {
189200
state->pointer_currently_added = false;
190201
}
191202
}
192203

204+
// Updates |event_data| with the current location of the mouse cursor.
205+
static void SetEventLocationFromCursorPosition(
206+
GLFWwindow* window,
207+
FlutterPointerEvent* event_data) {
208+
glfwGetCursorPos(window, &event_data->x, &event_data->y);
209+
}
210+
211+
// Set's |event_data|'s phase to either kMove or kHover depending on the current
212+
// primary mouse button state.
213+
static void SetEventPhaseFromCursorButtonState(
214+
GLFWwindow* window,
215+
FlutterPointerEvent* event_data) {
216+
event_data->phase =
217+
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS
218+
? FlutterPointerPhase::kMove
219+
: FlutterPointerPhase::kHover;
220+
}
221+
193222
// Reports the mouse entering or leaving the Flutter view.
194223
static void GLFWCursorEnterCallback(GLFWwindow* window, int entered) {
195-
double x, y;
196-
glfwGetCursorPos(window, &x, &y);
197-
FlutterPointerPhase phase =
224+
FlutterPointerEvent event = {};
225+
event.phase =
198226
entered ? FlutterPointerPhase::kAdd : FlutterPointerPhase::kRemove;
199-
SendPointerEventWithPhase(window, phase, x, y);
227+
SetEventLocationFromCursorPosition(window, &event);
228+
SendPointerEventWithData(window, event);
200229
}
201230

202231
// Reports mouse movement to the Flutter engine.
203232
static void GLFWCursorPositionCallback(GLFWwindow* window, double x, double y) {
204-
bool button_down =
205-
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
206-
FlutterPointerPhase phase =
207-
button_down ? FlutterPointerPhase::kMove : FlutterPointerPhase::kHover;
208-
SendPointerEventWithPhase(window, phase, x, y);
233+
FlutterPointerEvent event = {};
234+
event.x = x;
235+
event.y = y;
236+
SetEventPhaseFromCursorButtonState(window, &event);
237+
SendPointerEventWithData(window, event);
209238
}
210239

211240
// Reports mouse button press to the Flutter engine.
@@ -219,12 +248,11 @@ static void GLFWMouseButtonCallback(GLFWwindow* window,
219248
return;
220249
}
221250

222-
double x, y;
223-
glfwGetCursorPos(window, &x, &y);
224-
FlutterPointerPhase phase = (action == GLFW_PRESS)
225-
? FlutterPointerPhase::kDown
226-
: FlutterPointerPhase::kUp;
227-
SendPointerEventWithPhase(window, phase, x, y);
251+
FlutterPointerEvent event = {};
252+
event.phase = (action == GLFW_PRESS) ? FlutterPointerPhase::kDown
253+
: FlutterPointerPhase::kUp;
254+
SetEventLocationFromCursorPosition(window, &event);
255+
SendPointerEventWithData(window, event);
228256

229257
// If mouse tracking isn't already enabled, turn it on for the duration of
230258
// the drag to generate kMove events.
@@ -242,6 +270,22 @@ static void GLFWMouseButtonCallback(GLFWwindow* window,
242270
}
243271
}
244272

273+
// Reports scroll wheel events to the Flutter engine.
274+
static void GLFWScrollCallback(GLFWwindow* window,
275+
double delta_x,
276+
double delta_y) {
277+
FlutterPointerEvent event = {};
278+
SetEventLocationFromCursorPosition(window, &event);
279+
SetEventPhaseFromCursorButtonState(window, &event);
280+
event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
281+
// TODO: See if this can be queried from the OS; this value is chosen
282+
// arbitrarily to get something that feels reasonable.
283+
const int kScrollOffsetMultiplier = 20;
284+
event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
285+
event.scroll_delta_y = -delta_y * kScrollOffsetMultiplier;
286+
SendPointerEventWithData(window, event);
287+
}
288+
245289
// Passes character input events to registered handlers.
246290
static void GLFWCharCallback(GLFWwindow* window, unsigned int code_point) {
247291
for (const auto& handler :
@@ -276,6 +320,7 @@ static void GLFWAssignEventCallbacks(GLFWwindow* window) {
276320
glfwSetKeyCallback(window, GLFWKeyCallback);
277321
glfwSetCharCallback(window, GLFWCharCallback);
278322
glfwSetMouseButtonCallback(window, GLFWMouseButtonCallback);
323+
glfwSetScrollCallback(window, GLFWScrollCallback);
279324
if (GetSavedWindowState(window)->hover_tracking_enabled) {
280325
SetHoverCallbacksEnabled(window, true);
281326
}
@@ -286,6 +331,7 @@ static void GLFWClearEventCallbacks(GLFWwindow* window) {
286331
glfwSetKeyCallback(window, nullptr);
287332
glfwSetCharCallback(window, nullptr);
288333
glfwSetMouseButtonCallback(window, nullptr);
334+
glfwSetScrollCallback(window, nullptr);
289335
SetHoverCallbacksEnabled(window, false);
290336
}
291337

0 commit comments

Comments
 (0)