View: add basic implemntation of dispatchTouchEvent

Some Views block all events going to their descendants, and
then synthesize new events themselves. Gtk doesn't allow event
synthesization, which makes this quite annoying to deal with.
This initial implementation definiely has some problems,
for example it seems to cause infinite loops in some cases,
however it surprisingly works well enough.
This commit is contained in:
Mis012
2025-03-26 20:06:09 +01:00
parent b19f2c35d2
commit 4d12ad5c90
9 changed files with 518 additions and 24 deletions

View File

@@ -63,3 +63,103 @@ JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChild(JNIEnv *env
gtk_widget_queue_draw(child); // FIXME: why didn't compose UI invalidate the child?
gtk_widget_snapshot_child(widget, child, snapshot);
}
/* FIXME: put this in a header */
G_DECLARE_FINAL_TYPE(JavaWidget, java_widget, JAVA, WIDGET, GtkWidget)
bool view_dispatch_motionevent(JNIEnv *env, WrapperWidget *wrapper, GtkPropagationPhase phase, jobject motion_event, GdkEvent *event);
static bool dispatch_motionevent_if_JavaWidget(GtkWidget *widget, GtkPropagationPhase phase, jobject motion_event)
{
if(!JAVA_IS_WIDGET(widget))
return false;
return view_dispatch_motionevent(get_jni_env(), WRAPPER_WIDGET(gtk_widget_get_parent(widget)), phase, motion_event, NULL);
}
/* used by atl_propagate_synthetic_motionevent */
#define GDK_ARRAY_ELEMENT_TYPE GtkWidget *
#define GDK_ARRAY_TYPE_NAME GtkWidgetStack
#define GDK_ARRAY_NAME gtk_widget_stack
#define GDK_ARRAY_FREE_FUNC g_object_unref
#define GDK_ARRAY_PREALLOC 16
#include "gdkarrayimpl.c"
/* based on gtk_propagate_event_internal © GTK Team */
bool atl_propagate_synthetic_motionevent(GtkWidget *widget, jobject motionevent, GtkWidget *toplevel)
{
int handled_event = false;
GtkWidgetStack widget_array;
int i;
/* First, propagate event down */
gtk_widget_stack_init(&widget_array);
gtk_widget_stack_append(&widget_array, g_object_ref(widget));
for (;;) {
widget = gtk_widget_get_parent(widget);
if (!widget)
break;
if (widget == toplevel)
break;
gtk_widget_stack_append(&widget_array, g_object_ref(widget));
}
i = gtk_widget_stack_get_size(&widget_array) - 1;
for (;;) {
widget = gtk_widget_stack_get(&widget_array, i);
if (!gtk_widget_is_sensitive(widget)) {
handled_event = true;
} else if (gtk_widget_get_realized(widget))
handled_event = dispatch_motionevent_if_JavaWidget(widget, GTK_PHASE_CAPTURE, motionevent);
handled_event |= !gtk_widget_get_realized(widget);
if (handled_event)
break;
if (i == 0)
break;
i--;
}
/* If not yet handled, also propagate back up */
if (!handled_event) {
/* Propagate event up the widget tree so that
* parents can see the button and motion
* events of the children.
*/
for (i = 0; i < gtk_widget_stack_get_size(&widget_array); i++) {
widget = gtk_widget_stack_get(&widget_array, i);
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
* to have children of the viewport eat the scroll
* event
*/
if (!gtk_widget_is_sensitive(widget))
handled_event = true;
else if (gtk_widget_get_realized(widget))
handled_event = dispatch_motionevent_if_JavaWidget(widget, GTK_PHASE_BUBBLE, motionevent);
handled_event |= !gtk_widget_get_realized(widget);
if (handled_event)
break;
}
}
gtk_widget_stack_clear(&widget_array);
return handled_event;
}
JNIEXPORT jboolean JNICALL Java_android_view_ViewGroup_native_1dispatchTouchEvent(JNIEnv *env, jobject this, jlong widget_ptr, jobject motion_event, jdouble x, jdouble y)
{
GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr));
GtkWidget *picked_child = gtk_widget_pick(widget, x, y, GTK_PICK_DEFAULT);
return atl_propagate_synthetic_motionevent(picked_child, motion_event, widget);
}