You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
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.
224 lines
7.7 KiB
C
224 lines
7.7 KiB
C
#include <gtk/gtk.h>
|
|
|
|
#include "../defines.h"
|
|
#include "../util.h"
|
|
|
|
#include "../sk_area/sk_area.h"
|
|
|
|
#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)
|
|
|
|
static void wrapper_widget_init (WrapperWidget *wrapper_widget)
|
|
{
|
|
|
|
}
|
|
|
|
static void wrapper_widget_dispose(GObject *wrapper_widget)
|
|
{
|
|
gtk_widget_unparent(gtk_widget_get_first_child(GTK_WIDGET(wrapper_widget)));
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(wrapper_widget);
|
|
if (wrapper->jvm) {
|
|
JNIEnv *env;
|
|
(*wrapper->jvm)->GetEnv(wrapper->jvm, (void**)&env, JNI_VERSION_1_6);
|
|
if (wrapper->jobj)
|
|
_UNREF(wrapper->jobj);
|
|
if (wrapper->canvas)
|
|
_UNREF(wrapper->canvas);
|
|
}
|
|
G_OBJECT_CLASS (wrapper_widget_parent_class)->dispose (wrapper_widget);
|
|
}
|
|
|
|
void skia_draw_func(SKArea *sk_area, sk_canvas_t *canvas, void *user_data)
|
|
{
|
|
WrapperWidget *wrapper_widget = WRAPPER_WIDGET(user_data);
|
|
JNIEnv *env;
|
|
(*wrapper_widget->jvm)->GetEnv(wrapper_widget->jvm, (void**)&env, JNI_VERSION_1_6);
|
|
if(wrapper_widget->canvas == NULL) {
|
|
wrapper_widget->canvas = _REF((*env)->NewObject(env, handle_cache.canvas.class, handle_cache.canvas.constructor, _INTPTR(canvas), 0));
|
|
} else {
|
|
_SET_LONG_FIELD(wrapper_widget->canvas, "skia_canvas", _INTPTR(canvas));
|
|
}
|
|
|
|
(*env)->CallVoidMethod(env, wrapper_widget->jobj, wrapper_widget->draw_method, wrapper_widget->canvas);
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
(*env)->ExceptionDescribe(env);
|
|
}
|
|
|
|
GtkSizeRequestMode wrapper_widget_get_request_mode(GtkWidget *widget)
|
|
{
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
|
|
return gtk_widget_get_request_mode(wrapper->child);
|
|
}
|
|
|
|
void wrapper_widget_measure(GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline)
|
|
{
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
|
|
gtk_widget_measure(wrapper->child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL && (wrapper->layout_width > 0)) {
|
|
*minimum = *natural = wrapper->layout_width;
|
|
} else if (orientation == GTK_ORIENTATION_VERTICAL && (wrapper->layout_height > 0)) {
|
|
*minimum = *natural = wrapper->layout_height;
|
|
}
|
|
}
|
|
|
|
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,
|
|
.width = width,
|
|
.height = height,
|
|
};
|
|
|
|
if (wrapper->computeScroll_method) {
|
|
// The child needs to know its size before calling computeScroll, so we allocate it twice.
|
|
// second allocate will not trigger onLayout, because of unchanged size
|
|
gtk_widget_size_allocate(wrapper->child, &allocation, baseline);
|
|
|
|
JNIEnv *env;
|
|
(*wrapper->jvm)->GetEnv(wrapper->jvm, (void**)&env, JNI_VERSION_1_6);
|
|
(*env)->CallVoidMethod(env, wrapper->jobj, wrapper->computeScroll_method);
|
|
if((*env)->ExceptionCheck(env))
|
|
(*env)->ExceptionDescribe(env);
|
|
allocation.x = -(*env)->CallIntMethod(env, wrapper->jobj, handle_cache.view.getScrollX);
|
|
allocation.y = -(*env)->CallIntMethod(env, wrapper->jobj, handle_cache.view.getScrollY);
|
|
}
|
|
|
|
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)
|
|
gtk_widget_size_allocate(wrapper->background, &allocation, baseline);
|
|
}
|
|
|
|
static void wrapper_widget_class_init(WrapperWidgetClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
|
|
object_class->dispose = wrapper_widget_dispose;
|
|
|
|
widget_class->get_request_mode = wrapper_widget_get_request_mode;
|
|
widget_class->measure = wrapper_widget_measure;
|
|
widget_class->size_allocate = wrapper_widget_allocate;
|
|
}
|
|
|
|
GtkWidget * wrapper_widget_new(void)
|
|
{
|
|
return g_object_new (wrapper_widget_get_type(), NULL);
|
|
}
|
|
|
|
void wrapper_widget_set_child(WrapperWidget *parent, GtkWidget *child) // TODO: make sure there can only be one child
|
|
{
|
|
gtk_widget_insert_before(child, GTK_WIDGET(parent), NULL);
|
|
parent->child = child;
|
|
}
|
|
|
|
static guint sk_area_queue_queue_redraw(GtkWidget *sk_area)
|
|
{
|
|
gtk_widget_queue_draw(sk_area);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void wrapper_widget_queue_draw(WrapperWidget *wrapper)
|
|
{
|
|
if(wrapper->sk_area) {
|
|
/* schedule the call to gtk_widget_queue_draw for a future event loop pass in case we're currently inside the sk_area's snapshot */
|
|
/* GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations. */
|
|
g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20, G_SOURCE_FUNC(sk_area_queue_queue_redraw), wrapper->sk_area, NULL);
|
|
}
|
|
|
|
if(wrapper->child)
|
|
gtk_widget_queue_draw(wrapper->child);
|
|
if (wrapper->computeScroll_method)
|
|
gtk_widget_queue_allocate(GTK_WIDGET(wrapper));
|
|
}
|
|
|
|
static bool on_click(GtkGestureClick *gesture, int n_press, double x, double y, jobject this)
|
|
{
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
bool ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.performClick);
|
|
if((*env)->ExceptionCheck(env))
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject jobj)
|
|
{
|
|
JavaVM *jvm;
|
|
(*env)->GetJavaVM(env, &jvm);
|
|
wrapper->jvm = jvm;
|
|
wrapper->jobj = _REF(jobj);
|
|
jmethodID draw_method = _METHOD(_CLASS(jobj), "onDraw", "(Landroid/graphics/Canvas;)V");
|
|
if (draw_method != handle_cache.view.onDraw) {
|
|
wrapper->draw_method = draw_method;
|
|
|
|
GtkWidget *sk_area = sk_area_new();
|
|
gtk_widget_set_sensitive(sk_area, false);
|
|
sk_area_set_draw_func(SK_AREA_WIDGET(sk_area), skia_draw_func, wrapper);
|
|
gtk_widget_insert_before(sk_area, GTK_WIDGET(wrapper), NULL);
|
|
wrapper->sk_area = sk_area;
|
|
// gtk_widget_add_tick_callback(sk_area, tick_callback, NULL, NULL);
|
|
}
|
|
|
|
jmethodID ontouchevent_method = _METHOD(_CLASS(jobj), "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
|
|
if (ontouchevent_method != handle_cache.view.onTouchEvent) {
|
|
_setOnTouchListener(env, jobj, GTK_WIDGET(wrapper), NULL);
|
|
}
|
|
|
|
jmethodID computeScroll_method = _METHOD(_CLASS(jobj), "computeScroll", "()V");
|
|
if (computeScroll_method != handle_cache.view.computeScroll) {
|
|
wrapper->computeScroll_method = computeScroll_method;
|
|
}
|
|
|
|
jmethodID performClick_method = _METHOD(_CLASS(jobj), "performClick", "()Z");
|
|
if (performClick_method != handle_cache.view.performClick) {
|
|
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
|
|
|
|
g_signal_connect(controller, "released", G_CALLBACK(on_click), _REF(jobj));
|
|
gtk_widget_add_controller(wrapper->child, controller);
|
|
widget_set_needs_allocation(wrapper->child);
|
|
}
|
|
}
|
|
|
|
void wrapper_widget_set_layout_params(WrapperWidget *wrapper, int width, int height)
|
|
{
|
|
wrapper->layout_width = width;
|
|
wrapper->layout_height = height;
|
|
}
|
|
|
|
void wrapper_widget_set_background(WrapperWidget *wrapper, GdkPaintable *paintable)
|
|
{
|
|
if (!wrapper->background) {
|
|
wrapper->background = gtk_picture_new();
|
|
gtk_widget_insert_after(wrapper->background, GTK_WIDGET(wrapper), NULL);
|
|
}
|
|
gtk_picture_set_paintable(GTK_PICTURE(wrapper->background), paintable);
|
|
}
|