73 FlPluginRegistryInterface* iface);
76 FlKeyboardViewDelegateInterface* iface);
79 FlScrollingViewDelegateInterface* iface);
82 FlTextInputViewDelegateInterface* iface);
88 G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
90 G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
92 G_IMPLEMENT_INTERFACE(fl_scrolling_view_delegate_get_type(),
94 G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(),
98 static gboolean window_delete_event_cb(GtkWidget* widget,
111 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
112 g_return_if_fail(GDK_IS_WINDOW(window));
113 g_autoptr(GtkIMContext) im_context = gtk_im_multicontext_new();
114 gtk_im_context_set_client_window(im_context, window);
117 messenger, im_context, FL_TEXT_INPUT_VIEW_DELEGATE(
self));
118 self->keyboard_manager =
123 self->scrolling_manager =
129 guint event_time = gdk_event_get_time(
event);
130 GdkEventType event_type = gdk_event_get_event_type(
event);
131 GdkModifierType event_state =
static_cast<GdkModifierType
>(0);
132 gdk_event_get_state(
event, &event_state);
133 guint event_button = 0;
134 gdk_event_get_button(
event, &event_button);
135 gdouble event_x = 0.0, event_y = 0.0;
136 gdk_event_get_coords(
event, &event_x, &event_y);
139 switch (event_button) {
141 button = kFlutterPointerButtonMousePrimary;
144 button = kFlutterPointerButtonMouseMiddle;
147 button = kFlutterPointerButtonMouseSecondary;
152 int old_button_state =
self->button_state;
153 FlutterPointerPhase phase = kMove;
154 if (event_type == GDK_BUTTON_PRESS) {
156 if ((
self->button_state & button) != 0) {
159 self->button_state ^= button;
161 phase = old_button_state == 0 ? kDown : kMove;
162 }
else if (event_type == GDK_BUTTON_RELEASE) {
164 if ((
self->button_state & button) == 0) {
167 self->button_state ^= button;
169 phase =
self->button_state == 0 ? kUp : kMove;
172 if (
self->engine ==
nullptr) {
176 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
178 self->scrolling_manager, event_x * scale_factor, event_y * scale_factor);
180 event_state, event_time);
183 event_x * scale_factor, event_y * scale_factor, 0, 0,
self->button_state);
190 if (!
self->pointer_inside) {
191 self->pointer_inside =
TRUE;
194 if (gdk_event_get_coords(
event, &x, &y)) {
195 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
200 x * scale_factor, y * scale_factor, 0, 0,
self->button_state);
207 GtkAllocation allocation;
208 gtk_widget_get_allocation(GTK_WIDGET(
self), &allocation);
209 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
211 self->engine, allocation.width * scale_factor,
212 allocation.height * scale_factor, scale_factor);
220 if (allocation.width > 1 && allocation.height > 1 &&
221 gtk_widget_get_realized(GTK_WIDGET(
self))) {
223 allocation.height * scale_factor);
229 const FlutterSemanticsNode*
node,
245 g_clear_object(&
self->keyboard_manager);
246 g_clear_object(&
self->text_input_plugin);
247 g_clear_object(&
self->scrolling_manager);
254 FlPluginRegistry* registry,
256 FlView*
self = FL_VIEW(registry);
264 FlPluginRegistryInterface* iface) {
269 FlKeyboardViewDelegateInterface* iface) {
270 iface->send_key_event =
271 [](FlKeyboardViewDelegate* view_delegate,
const FlutterKeyEvent*
event,
273 FlView*
self = FL_VIEW(view_delegate);
274 if (
self->engine !=
nullptr) {
279 iface->text_filter_key_press = [](FlKeyboardViewDelegate* view_delegate,
281 FlView*
self = FL_VIEW(view_delegate);
285 iface->get_messenger = [](FlKeyboardViewDelegate* view_delegate) {
286 FlView*
self = FL_VIEW(view_delegate);
290 iface->redispatch_event = [](FlKeyboardViewDelegate* view_delegate,
291 std::unique_ptr<FlKeyEvent> in_event) {
293 GdkEvent* gdk_event =
reinterpret_cast<GdkEvent*
>(
event->origin);
294 GdkEventType event_type = gdk_event_get_event_type(gdk_event);
295 g_return_if_fail(event_type == GDK_KEY_PRESS ||
296 event_type == GDK_KEY_RELEASE);
297 gdk_event_put(gdk_event);
301 iface->subscribe_to_layout_change = [](FlKeyboardViewDelegate* view_delegate,
303 FlView*
self = FL_VIEW(view_delegate);
304 self->keyboard_layout_notifier = std::move(notifier);
307 iface->lookup_key = [](FlKeyboardViewDelegate* view_delegate,
308 const GdkKeymapKey* key) -> guint {
309 FlView*
self = FL_VIEW(view_delegate);
310 g_return_val_if_fail(
self->keymap !=
nullptr, 0);
311 return gdk_keymap_lookup_key(
self->keymap, key);
314 iface->get_keyboard_state =
315 [](FlKeyboardViewDelegate* view_delegate) -> GHashTable* {
316 FlView*
self = FL_VIEW(view_delegate);
322 FlScrollingViewDelegateInterface* iface) {
323 iface->send_mouse_pointer_event =
324 [](FlScrollingViewDelegate* view_delegate, FlutterPointerPhase phase,
325 size_t timestamp,
double x,
double y,
double scroll_delta_x,
326 double scroll_delta_y, int64_t buttons) {
327 FlView*
self = FL_VIEW(view_delegate);
328 if (
self->engine !=
nullptr) {
330 y, scroll_delta_x, scroll_delta_y,
334 iface->send_pointer_pan_zoom_event =
335 [](FlScrollingViewDelegate* view_delegate,
size_t timestamp,
double x,
336 double y, FlutterPointerPhase phase,
double pan_x,
double pan_y,
337 double scale,
double rotation) {
338 FlView*
self = FL_VIEW(view_delegate);
339 if (
self->engine !=
nullptr) {
341 phase, pan_x, pan_y, scale,
348 FlTextInputViewDelegateInterface* iface) {
349 iface->translate_coordinates = [](FlTextInputViewDelegate* delegate,
350 gint view_x, gint view_y, gint* window_x,
352 FlView*
self = FL_VIEW(delegate);
353 gtk_widget_translate_coordinates(GTK_WIDGET(
self),
354 gtk_widget_get_toplevel(GTK_WIDGET(
self)),
355 view_x, view_y, window_x, window_y);
361 GdkEventButton* button_event,
363 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
366 GdkEventType event_type = gdk_event_get_event_type(
event);
367 if (event_type == GDK_DOUBLE_BUTTON_PRESS ||
368 event_type == GDK_TRIPLE_BUTTON_PRESS) {
379 GdkEventButton* button_event,
381 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
387 GdkEventScroll*
event,
394 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
400 GdkEventMotion* motion_event,
402 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(motion_event);
404 if (
self->engine ==
nullptr) {
408 guint event_time = gdk_event_get_time(
event);
409 GdkModifierType event_state =
static_cast<GdkModifierType
>(0);
410 gdk_event_get_state(
event, &event_state);
411 gdouble event_x = 0.0, event_y = 0.0;
412 gdk_event_get_coords(
event, &event_x, &event_y);
416 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
419 event_state, event_time);
421 self->engine,
self->button_state != 0 ? kMove : kHover,
423 event_y * scale_factor, 0, 0,
self->button_state);
430 GdkEventCrossing* crossing_event,
432 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
434 if (
self->engine ==
nullptr) {
445 GdkEventCrossing* crossing_event,
447 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
449 guint event_time = gdk_event_get_time(
event);
450 gdouble event_x = 0.0, event_y = 0.0;
451 gdk_event_get_coords(
event, &event_x, &event_y);
453 if (crossing_event->mode != GDK_CROSSING_NORMAL) {
457 if (
self->engine ==
nullptr) {
464 if (
self->pointer_inside &&
self->button_state == 0) {
465 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
468 event_x * scale_factor, event_y * scale_factor, 0, 0,
470 self->pointer_inside = FALSE;
477 if (
self->keyboard_layout_notifier ==
nullptr) {
481 self->keyboard_layout_notifier();
485 GdkEventSequence* sequence,
499 GdkEventSequence* sequence,
505 GdkEventSequence* sequence,
517 GdkEventSequence* sequence,
525 g_return_val_if_fail(FL_IS_VIEW(
user_data), FALSE);
528 GdkWindowState
state =
event->window_state.new_window_state;
529 GdkWindowState previous_state =
self->window_state;
530 self->window_state =
state;
531 bool was_visible = !((previous_state & GDK_WINDOW_STATE_WITHDRAWN) ||
532 (previous_state & GDK_WINDOW_STATE_ICONIFIED));
533 bool is_visible = !((
state & GDK_WINDOW_STATE_WITHDRAWN) ||
534 (
state & GDK_WINDOW_STATE_ICONIFIED));
535 bool was_focused = (previous_state & GDK_WINDOW_STATE_FOCUSED);
536 bool is_focused = (
state & GDK_WINDOW_STATE_FOCUSED);
537 if (was_visible != is_visible || was_focused != is_focused) {
538 if (
self->engine !=
nullptr) {
547 FlView*
self = FL_VIEW(widget);
548 g_autoptr(GError)
error =
nullptr;
551 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
554 self->window_state_cb_id =
555 g_signal_connect(toplevel_window,
"window-state-event",
558 gdk_window_get_state(gtk_widget_get_window(toplevel_window));
560 g_signal_connect(toplevel_window,
"delete-event",
561 G_CALLBACK(window_delete_event_cb),
self);
566 g_warning(
"Failed to start Flutter renderer: %s",
error->message);
571 g_warning(
"Failed to start Flutter engine: %s",
error->message);
579 FlView*
self = FL_VIEW(widget);
584 FlView*
self = FL_VIEW(
object);
594 self->keymap = gdk_keymap_get_for_display(gdk_display_get_default());
603 self->event_box = gtk_event_box_new();
604 gtk_widget_set_hexpand(
self->event_box,
TRUE);
605 gtk_widget_set_vexpand(
self->event_box,
TRUE);
606 gtk_container_add(GTK_CONTAINER(
self),
self->event_box);
607 gtk_widget_show(
self->event_box);
608 gtk_widget_add_events(
self->event_box,
609 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
610 GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
611 GDK_SMOOTH_SCROLL_MASK);
613 g_signal_connect(
self->event_box,
"button-press-event",
615 g_signal_connect(
self->event_box,
"button-release-event",
619 g_signal_connect(
self->event_box,
"motion-notify-event",
621 g_signal_connect(
self->event_box,
"enter-notify-event",
623 g_signal_connect(
self->event_box,
"leave-notify-event",
625 self->keymap_keys_changed_cb_id = g_signal_connect(
627 GtkGesture* zoom = gtk_gesture_zoom_new(
self->event_box);
632 GtkGesture* rotate = gtk_gesture_rotate_new(
self->event_box);
635 g_signal_connect(rotate,
"angle-changed",
639 g_signal_connect(
self,
"realize", G_CALLBACK(
realize_cb),
self);
640 g_signal_connect(
self,
"size-allocate", G_CALLBACK(
configure_cb),
self);
647 FlView*
self = FL_VIEW(
object);
651 g_set_object(&
self->project,
652 static_cast<FlDartProject*
>(g_value_get_object(
value)));
655 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
664 FlView*
self = FL_VIEW(
object);
668 g_value_set_object(
value,
self->project);
671 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
677 FlView*
self = FL_VIEW(
object);
679 if (strcmp(pspec->name,
"scale-factor") == 0) {
683 if (G_OBJECT_CLASS(fl_view_parent_class)->notify !=
nullptr) {
684 G_OBJECT_CLASS(fl_view_parent_class)->notify(
object, pspec);
689 FlView*
self = FL_VIEW(
object);
691 if (
self->engine !=
nullptr) {
698 if (
self->window_state_cb_id != 0) {
699 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
700 g_signal_handler_disconnect(toplevel_window,
self->window_state_cb_id);
701 self->window_state_cb_id = 0;
704 g_clear_object(&
self->project);
705 g_clear_object(&
self->renderer);
706 g_clear_object(&
self->engine);
707 g_clear_object(&
self->accessibility_plugin);
708 g_clear_object(&
self->keyboard_manager);
709 if (
self->keymap_keys_changed_cb_id != 0) {
710 g_signal_handler_disconnect(
self->keymap,
self->keymap_keys_changed_cb_id);
711 self->keymap_keys_changed_cb_id = 0;
713 g_clear_object(&
self->mouse_cursor_plugin);
714 g_clear_object(&
self->platform_plugin);
716 G_OBJECT_CLASS(fl_view_parent_class)->dispose(
object);
721 FlView*
self = FL_VIEW(widget);
725 reinterpret_cast<GdkEvent*
>(
event))));
730 GdkEventKey*
event) {
731 FlView*
self = FL_VIEW(widget);
734 reinterpret_cast<GdkEvent*
>(
event))));
738 GObjectClass* object_class = G_OBJECT_CLASS(klass);
745 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
749 g_object_class_install_property(
752 "flutter-project",
"flutter-project",
"Flutter project in use",
753 fl_dart_project_get_type(),
754 static_cast<GParamFlags
>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
755 G_PARAM_STATIC_STRINGS)));
757 gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
758 fl_view_accessible_get_type());
762 gtk_widget_set_can_focus(GTK_WIDGET(
self),
TRUE);
766 return static_cast<FlView*
>(
767 g_object_new(fl_view_get_type(),
"flutter-project", project,
nullptr));
771 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);
776 GdkGLContext* context,
777 GPtrArray* textures) {
778 g_return_if_fail(FL_IS_VIEW(
self));
780 if (
self->gl_area ==
nullptr) {
782 gtk_widget_show(GTK_WIDGET(
self->gl_area));
783 gtk_container_add(GTK_CONTAINER(
self->event_box),
784 GTK_WIDGET(
self->gl_area));
791 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);