From f82af6ecb771fc9310c3ee143f183dbbe56047c5 Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Mon, 6 May 2024 06:35:47 +0200 Subject: [PATCH] allocate transparent {WrapperWidget / JavaWidget} 0x0 and draw children in overflow area This prevents overlay widgets from blocking touch events. Androids ViewGroup.dispatchTouchEvent() loops over all children and only breaks if an eventhandler returns true. Gtk on the other hand stops at the first sensitive child. Even if the eventhandler returned false. --- src/api-impl-jni/views/AndroidLayout.c | 13 +++++++++ src/api-impl-jni/views/AndroidLayout.h | 5 ++++ src/api-impl-jni/views/android_view_View.c | 31 +++++++++++++++++++--- src/api-impl-jni/widgets/WrapperWidget.c | 22 ++++++++++++++- src/api-impl-jni/widgets/WrapperWidget.h | 3 +++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/api-impl-jni/views/AndroidLayout.c b/src/api-impl-jni/views/AndroidLayout.c index c54e7f36..2100230a 100644 --- a/src/api-impl-jni/views/AndroidLayout.c +++ b/src/api-impl-jni/views/AndroidLayout.c @@ -64,6 +64,10 @@ static void android_layout_allocate(GtkLayoutManager *layout_manager, GtkWidget { AndroidLayout *layout = ATL_ANDROID_LAYOUT(layout_manager); JNIEnv *env = get_jni_env(); + if (!width && !height) { + width = layout->real_width; + height = layout->real_height; + } (*env)->CallVoidMethod(env, layout->view, handle_cache.view.layoutInternal, width, height); if((*env)->ExceptionCheck(env)) @@ -108,3 +112,12 @@ void android_layout_set_params(AndroidLayout *layout, int width, int height) layout->width = width; layout->height = height; } + +void widget_set_needs_allocation(GtkWidget *widget) { + if (ATL_IS_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget))) { + AndroidLayout *layout = ATL_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget)); + if (!layout->needs_allocation && (layout->real_width || layout->real_height)) + gtk_widget_size_allocate(widget, &(GtkAllocation){.x = 0, .y = 0, .width = layout->real_width, .height = layout->real_height}, 0); + layout->needs_allocation = true; + } +} diff --git a/src/api-impl-jni/views/AndroidLayout.h b/src/api-impl-jni/views/AndroidLayout.h index 9f90cb74..7fc1461e 100644 --- a/src/api-impl-jni/views/AndroidLayout.h +++ b/src/api-impl-jni/views/AndroidLayout.h @@ -19,9 +19,14 @@ struct _AndroidLayout { jobject view; int width; int height; + int real_width; + int real_height; + gboolean needs_allocation; }; GtkLayoutManager *android_layout_new(jobject view); void android_layout_set_params(AndroidLayout *layout, int width, int height); +void widget_set_needs_allocation(GtkWidget *widget); + #endif // ANDROID_LAYOUT_H \ No newline at end of file diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index ca4b48b4..412ea602 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -181,6 +181,10 @@ void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget, jobject o gtk_widget_add_controller(widget, controller); g_object_set_data(G_OBJECT(widget), "on_intercept_touch_listener", controller); } + WrapperWidget *wrapper = WRAPPER_WIDGET(widget); + if (!wrapper->needs_allocation && (wrapper->layout_width || wrapper->layout_height)) + gtk_widget_size_allocate(GTK_WIDGET(wrapper), &(GtkAllocation){.x = 0, .y = 0, .width = wrapper->layout_width, .height = wrapper->layout_height}, 0); + wrapper->needs_allocation = true; } JNIEXPORT void JNICALL Java_android_view_View_setOnTouchListener(JNIEnv *env, jobject this, jobject on_touch_listener) @@ -214,6 +218,7 @@ JNIEXPORT void JNICALL Java_android_view_View_setOnClickListener(JNIEnv *env, jo g_signal_connect(controller, "released", G_CALLBACK(on_click), callback_data); // the release completes the click, I guess? gtk_widget_add_controller(widget, controller); g_object_set_data(G_OBJECT(widget), "on_click_listener", controller); + widget_set_needs_allocation(widget); } JNIEXPORT jint JNICALL Java_android_view_View_getWidth(JNIEnv *env, jobject this) @@ -225,6 +230,10 @@ JNIEXPORT jint JNICALL Java_android_view_View_getWidth(JNIEnv *env, jobject this gtk_widget_get_allocation(widget, &alloc); printf("widget size is currently %dx%d\n", alloc.width, alloc.height); */ + if (ATL_IS_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget))) { + AndroidLayout *layout = ATL_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget)); + return layout->real_width; + } return gtk_widget_get_width(widget); } @@ -232,6 +241,10 @@ JNIEXPORT jint JNICALL Java_android_view_View_getHeight(JNIEnv *env, jobject thi { GtkWidget *widget = GTK_WIDGET(_PTR(_GET_LONG_FIELD(this, "widget"))); + if (ATL_IS_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget))) { + AndroidLayout *layout = ATL_ANDROID_LAYOUT(gtk_widget_get_layout_manager(widget)); + return layout->real_height; + } return gtk_widget_get_height(widget); } @@ -358,7 +371,7 @@ JNIEXPORT jlong JNICALL Java_android_view_View_native_1constructor(JNIEnv *env, (*env)->ReleaseStringUTFChars(env, nameObj, name); /* this should better match default android behavior */ - gtk_widget_set_overflow(wrapper, GTK_OVERFLOW_HIDDEN); + gtk_widget_set_overflow(wrapper, GTK_OVERFLOW_VISIBLE); jmethodID measure_method = _METHOD(class, "onMeasure", "(II)V"); jmethodID layout_method = _METHOD(class, "onLayout", "(ZIIII)V"); @@ -437,9 +450,20 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1layout(JNIEnv *env, jobjec GtkAllocation allocation = { .x=l, .y=t, - .width=r-l, - .height=b-t, }; + int width = r-l; + int height = b-t; + WrapperWidget *wrapper = WRAPPER_WIDGET(widget); + if (wrapper->real_width != width || wrapper->real_height != height) { + wrapper->real_width = width; + wrapper->real_height = height; + if (!wrapper->needs_allocation) + gtk_widget_queue_allocate(widget); + } + if (wrapper->needs_allocation) { + allocation.width = width; + allocation.height = height; + } gtk_widget_size_allocate(widget, &allocation, -1); } @@ -474,6 +498,7 @@ JNIEXPORT void JNICALL Java_android_view_View_setBackgroundColor(JNIEnv *env, jo gtk_style_context_add_provider(style_context, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_set_data(G_OBJECT(widget), "background_color_style_provider", css_provider); + widget_set_needs_allocation(widget); } #pragma GCC diagnostic pop diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c index ff15888c..5526d93b 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.c +++ b/src/api-impl-jni/widgets/WrapperWidget.c @@ -8,6 +8,7 @@ #include "../generated_headers/android_view_View.h" #include "WrapperWidget.h" +#include "src/api-impl-jni/views/AndroidLayout.h" G_DEFINE_TYPE(WrapperWidget, wrapper_widget, GTK_TYPE_WIDGET) @@ -68,6 +69,10 @@ void wrapper_widget_measure(GtkWidget *widget, GtkOrientation orientation, int f void wrapper_widget_allocate(GtkWidget *widget, int width, int height, int baseline) { WrapperWidget *wrapper = WRAPPER_WIDGET(widget); + if (!width && !height) { + width = wrapper->real_width; + height = wrapper->real_height; + } GtkAllocation allocation = { .x = 0, .y = 0, @@ -89,7 +94,21 @@ void wrapper_widget_allocate(GtkWidget *widget, int width, int height, int basel allocation.y = -(*env)->CallIntMethod(env, wrapper->jobj, handle_cache.view.getScrollY); } - gtk_widget_size_allocate(wrapper->child, &allocation, baseline); + if (ATL_IS_ANDROID_LAYOUT(gtk_widget_get_layout_manager(wrapper->child))) { + AndroidLayout *layout = ATL_ANDROID_LAYOUT(gtk_widget_get_layout_manager(wrapper->child)); + if (layout->real_width != width || layout->real_height != height) { + layout->real_width = width; + layout->real_height = height; + if (!layout->needs_allocation) + gtk_widget_queue_allocate(wrapper->child); + } + if (layout->needs_allocation) + gtk_widget_size_allocate(wrapper->child, &allocation, baseline); + else + gtk_widget_size_allocate(wrapper->child, &(GtkAllocation){.x = allocation.x, .y = allocation.y}, baseline); + } else { + gtk_widget_size_allocate(wrapper->child, &allocation, baseline); + } if (wrapper->sk_area) gtk_widget_size_allocate(wrapper->sk_area, &allocation, baseline); if (wrapper->background) @@ -184,6 +203,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job g_signal_connect(controller, "released", G_CALLBACK(on_click), _REF(jobj)); gtk_widget_add_controller(wrapper->child, controller); + widget_set_needs_allocation(wrapper->child); } } diff --git a/src/api-impl-jni/widgets/WrapperWidget.h b/src/api-impl-jni/widgets/WrapperWidget.h index 01fceb90..ce22153f 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.h +++ b/src/api-impl-jni/widgets/WrapperWidget.h @@ -20,6 +20,9 @@ struct _WrapperWidget jmethodID computeScroll_method; int layout_width; int layout_height; + int real_width; + int real_height; + gboolean needs_allocation; }; struct _WrapperWidgetClass