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 =
130 switch (
event->button) {
132 button = kFlutterPointerButtonMousePrimary;
135 button = kFlutterPointerButtonMouseMiddle;
138 button = kFlutterPointerButtonMouseSecondary;
143 int old_button_state =
self->button_state;
144 FlutterPointerPhase phase = kMove;
145 if (
event->type == GDK_BUTTON_PRESS) {
147 if ((
self->button_state & button) != 0) {
150 self->button_state ^= button;
152 phase = old_button_state == 0 ? kDown : kMove;
153 }
else if (
event->type == GDK_BUTTON_RELEASE) {
155 if ((
self->button_state & button) == 0) {
158 self->button_state ^= button;
160 phase =
self->button_state == 0 ? kUp : kMove;
163 if (
self->engine ==
nullptr) {
167 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
169 event->x * scale_factor,
170 event->y * scale_factor);
175 event->x * scale_factor,
event->y * scale_factor, 0, 0,
183 if (!
self->pointer_inside) {
184 self->pointer_inside =
TRUE;
187 if (gdk_event_get_coords(
event, &x, &y)) {
188 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
193 x * scale_factor, y * scale_factor, 0, 0,
self->button_state);
200 GtkAllocation allocation;
201 gtk_widget_get_allocation(GTK_WIDGET(
self), &allocation);
202 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
204 self->engine, allocation.width * scale_factor,
205 allocation.height * scale_factor, scale_factor);
213 if (allocation.width > 1 && allocation.height > 1 &&
214 gtk_widget_get_realized(GTK_WIDGET(
self))) {
216 allocation.height * scale_factor);
222 const FlutterSemanticsNode*
node,
238 g_clear_object(&
self->keyboard_manager);
239 g_clear_object(&
self->text_input_plugin);
240 g_clear_object(&
self->scrolling_manager);
244 gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(
self)));
249 FlPluginRegistry* registry,
251 FlView*
self = FL_VIEW(registry);
259 FlPluginRegistryInterface* iface) {
264 FlKeyboardViewDelegateInterface* iface) {
265 iface->send_key_event =
266 [](FlKeyboardViewDelegate* view_delegate,
const FlutterKeyEvent*
event,
268 FlView*
self = FL_VIEW(view_delegate);
269 if (
self->engine !=
nullptr) {
274 iface->text_filter_key_press = [](FlKeyboardViewDelegate* view_delegate,
276 FlView*
self = FL_VIEW(view_delegate);
280 iface->get_messenger = [](FlKeyboardViewDelegate* view_delegate) {
281 FlView*
self = FL_VIEW(view_delegate);
285 iface->redispatch_event = [](FlKeyboardViewDelegate* view_delegate,
286 std::unique_ptr<FlKeyEvent> in_event) {
288 GdkEvent* gdk_event =
reinterpret_cast<GdkEvent*
>(
event->origin);
289 GdkEventType type = gdk_event->type;
290 g_return_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE);
291 gdk_event_put(gdk_event);
295 iface->subscribe_to_layout_change = [](FlKeyboardViewDelegate* view_delegate,
297 FlView*
self = FL_VIEW(view_delegate);
298 self->keyboard_layout_notifier = std::move(notifier);
301 iface->lookup_key = [](FlKeyboardViewDelegate* view_delegate,
302 const GdkKeymapKey* key) -> guint {
303 FlView*
self = FL_VIEW(view_delegate);
304 g_return_val_if_fail(
self->keymap !=
nullptr, 0);
305 return gdk_keymap_lookup_key(
self->keymap, key);
308 iface->get_keyboard_state =
309 [](FlKeyboardViewDelegate* view_delegate) -> GHashTable* {
310 FlView*
self = FL_VIEW(view_delegate);
316 FlScrollingViewDelegateInterface* iface) {
317 iface->send_mouse_pointer_event =
318 [](FlScrollingViewDelegate* view_delegate, FlutterPointerPhase phase,
319 size_t timestamp,
double x,
double y,
double scroll_delta_x,
320 double scroll_delta_y, int64_t buttons) {
321 FlView*
self = FL_VIEW(view_delegate);
322 if (
self->engine !=
nullptr) {
324 y, scroll_delta_x, scroll_delta_y,
328 iface->send_pointer_pan_zoom_event =
329 [](FlScrollingViewDelegate* view_delegate,
size_t timestamp,
double x,
330 double y, FlutterPointerPhase phase,
double pan_x,
double pan_y,
331 double scale,
double rotation) {
332 FlView*
self = FL_VIEW(view_delegate);
333 if (
self->engine !=
nullptr) {
335 phase, pan_x, pan_y, scale,
342 FlTextInputViewDelegateInterface* iface) {
343 iface->translate_coordinates = [](FlTextInputViewDelegate* delegate,
344 gint view_x, gint view_y, gint* window_x,
346 FlView*
self = FL_VIEW(delegate);
347 gtk_widget_translate_coordinates(GTK_WIDGET(
self),
348 gtk_widget_get_toplevel(GTK_WIDGET(
self)),
349 view_x, view_y, window_x, window_y);
355 GdkEventButton*
event,
358 if (
event->type == GDK_DOUBLE_BUTTON_PRESS ||
359 event->type == GDK_TRIPLE_BUTTON_PRESS) {
370 GdkEventButton*
event,
377 GdkEventScroll*
event,
384 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
390 GdkEventMotion*
event,
392 if (
self->engine ==
nullptr) {
398 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
403 self->engine,
self->button_state != 0 ? kMove : kHover,
405 event->y * scale_factor, 0, 0,
self->button_state);
412 GdkEventCrossing*
event,
414 if (
self->engine ==
nullptr) {
425 GdkEventCrossing*
event,
427 if (
event->mode != GDK_CROSSING_NORMAL) {
431 if (
self->engine ==
nullptr) {
438 if (
self->pointer_inside &&
self->button_state == 0) {
439 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
442 event->x * scale_factor,
event->y * scale_factor, 0, 0,
444 self->pointer_inside = FALSE;
451 if (
self->keyboard_layout_notifier ==
nullptr) {
455 self->keyboard_layout_notifier();
459 GdkEventSequence* sequence,
473 GdkEventSequence* sequence,
479 GdkEventSequence* sequence,
491 GdkEventSequence* sequence,
499 g_return_val_if_fail(FL_IS_VIEW(
user_data), FALSE);
502 GdkWindowState
state =
event->window_state.new_window_state;
503 GdkWindowState previous_state =
self->window_state;
504 self->window_state =
state;
505 bool was_visible = !((previous_state & GDK_WINDOW_STATE_WITHDRAWN) ||
506 (previous_state & GDK_WINDOW_STATE_ICONIFIED));
507 bool is_visible = !((
state & GDK_WINDOW_STATE_WITHDRAWN) ||
508 (
state & GDK_WINDOW_STATE_ICONIFIED));
509 bool was_focused = (previous_state & GDK_WINDOW_STATE_FOCUSED);
510 bool is_focused = (
state & GDK_WINDOW_STATE_FOCUSED);
511 if (was_visible != is_visible || was_focused != is_focused) {
512 if (
self->engine !=
nullptr) {
521 FlView*
self = FL_VIEW(widget);
522 g_autoptr(GError)
error =
nullptr;
525 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
528 self->window_state_cb_id =
529 g_signal_connect(toplevel_window,
"window-state-event",
532 g_signal_connect(toplevel_window,
"delete-event",
533 G_CALLBACK(window_delete_event_cb),
self);
538 g_warning(
"Failed to start Flutter renderer: %s",
error->message);
543 g_warning(
"Failed to start Flutter engine: %s",
error->message);
551 FlView*
self = FL_VIEW(widget);
556 FlView*
self = FL_VIEW(
object);
566 self->keymap = gdk_keymap_get_for_display(gdk_display_get_default());
575 self->event_box = gtk_event_box_new();
576 gtk_widget_set_hexpand(
self->event_box,
TRUE);
577 gtk_widget_set_vexpand(
self->event_box,
TRUE);
578 gtk_container_add(GTK_CONTAINER(
self),
self->event_box);
579 gtk_widget_show(
self->event_box);
580 gtk_widget_add_events(
self->event_box,
581 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
582 GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
583 GDK_SMOOTH_SCROLL_MASK);
585 g_signal_connect(
self->event_box,
"button-press-event",
587 g_signal_connect(
self->event_box,
"button-release-event",
591 g_signal_connect(
self->event_box,
"motion-notify-event",
593 g_signal_connect(
self->event_box,
"enter-notify-event",
595 g_signal_connect(
self->event_box,
"leave-notify-event",
597 self->keymap_keys_changed_cb_id = g_signal_connect(
599 GtkGesture* zoom = gtk_gesture_zoom_new(
self->event_box);
604 GtkGesture* rotate = gtk_gesture_rotate_new(
self->event_box);
607 g_signal_connect(rotate,
"angle-changed",
611 g_signal_connect(
self,
"realize", G_CALLBACK(
realize_cb),
self);
612 g_signal_connect(
self,
"size-allocate", G_CALLBACK(
configure_cb),
self);
619 FlView*
self = FL_VIEW(
object);
623 g_set_object(&
self->project,
624 static_cast<FlDartProject*
>(g_value_get_object(
value)));
627 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
636 FlView*
self = FL_VIEW(
object);
640 g_value_set_object(
value,
self->project);
643 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
649 FlView*
self = FL_VIEW(
object);
651 if (strcmp(pspec->name,
"scale-factor") == 0) {
655 if (G_OBJECT_CLASS(fl_view_parent_class)->notify !=
nullptr) {
656 G_OBJECT_CLASS(fl_view_parent_class)->notify(
object, pspec);
661 FlView*
self = FL_VIEW(
object);
663 if (
self->engine !=
nullptr) {
670 if (
self->window_state_cb_id != 0) {
671 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
672 g_signal_handler_disconnect(toplevel_window,
self->window_state_cb_id);
673 self->window_state_cb_id = 0;
676 g_clear_object(&
self->project);
677 g_clear_object(&
self->renderer);
678 g_clear_object(&
self->engine);
679 g_clear_object(&
self->accessibility_plugin);
680 g_clear_object(&
self->keyboard_manager);
681 if (
self->keymap_keys_changed_cb_id != 0) {
682 g_signal_handler_disconnect(
self->keymap,
self->keymap_keys_changed_cb_id);
683 self->keymap_keys_changed_cb_id = 0;
685 g_clear_object(&
self->mouse_cursor_plugin);
686 g_clear_object(&
self->platform_plugin);
688 G_OBJECT_CLASS(fl_view_parent_class)->dispose(
object);
693 FlView*
self = FL_VIEW(widget);
697 reinterpret_cast<GdkEvent*
>(
event))));
702 GdkEventKey*
event) {
703 FlView*
self = FL_VIEW(widget);
706 reinterpret_cast<GdkEvent*
>(
event))));
710 GObjectClass* object_class = G_OBJECT_CLASS(klass);
717 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
721 g_object_class_install_property(
724 "flutter-project",
"flutter-project",
"Flutter project in use",
725 fl_dart_project_get_type(),
726 static_cast<GParamFlags
>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
727 G_PARAM_STATIC_STRINGS)));
729 gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
730 fl_view_accessible_get_type());
734 gtk_widget_set_can_focus(GTK_WIDGET(
self),
TRUE);
735 self->window_state = gdk_window_get_state(
736 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self))));
740 return static_cast<FlView*
>(
741 g_object_new(fl_view_get_type(),
"flutter-project", project,
nullptr));
745 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);
750 GdkGLContext* context,
751 GPtrArray* textures) {
752 g_return_if_fail(FL_IS_VIEW(
self));
754 if (
self->gl_area ==
nullptr) {
756 gtk_widget_show(GTK_WIDGET(
self->gl_area));
757 gtk_container_add(GTK_CONTAINER(
self->event_box),
758 GTK_WIDGET(
self->gl_area));
765 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);