diff --git a/src/api-impl-jni/defines.h b/src/api-impl-jni/defines.h index 887e4b0e..ebbeb701 100644 --- a/src/api-impl-jni/defines.h +++ b/src/api-impl-jni/defines.h @@ -13,6 +13,7 @@ #define _PTR(ptr)((void*)(intptr_t)(ptr)) #define _INTPTR(ptr)((jlong)(intptr_t)(ptr)) #define _REF(obj)((*env)->NewGlobalRef(env, obj)) +#define _UNREF(obj)((*env)->DeleteGlobalRef(env, obj)) #define _CLASS(object) ((*env)->GetObjectClass(env, object)) #define _SUPER(object) ((*env)->GetSuperclass(env, object)) #define _METHOD(class, method, attrs) ((*env)->GetMethodID(env, class, method, attrs)) diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 55c50a2d..f79817d3 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -104,6 +104,8 @@ void set_up_handle_cache(JNIEnv *env, char *apk_main_activity_class) handle_cache.view.setLayoutParams = _METHOD(handle_cache.view.class, "setLayoutParams", "(Landroid/view/ViewGroup$LayoutParams;)V"); if((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); + handle_cache.view.onDraw = _METHOD(handle_cache.view.class, "onDraw", "(Landroid/graphics/Canvas;)V"); + handle_cache.view.onMeasure = _METHOD(handle_cache.view.class, "onMeasure", "(II)V"); handle_cache.asset_manager.class = _REF((*env)->FindClass(env, "android/content/res/AssetManager")); handle_cache.asset_manager.extractFromAPK = _STATIC_METHOD(handle_cache.asset_manager.class, "extractFromAPK", "(Ljava/lang/String;Ljava/lang/String;)V"); diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index 911a8ada..59401e91 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -68,6 +68,8 @@ struct handle_cache { struct { jclass class; jmethodID setLayoutParams; + jmethodID onDraw; + jmethodID onMeasure; } view; struct { jclass class; diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index 37885e59..c56fc0ad 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -166,43 +166,11 @@ JNIEXPORT void JNICALL Java_android_view_View_setVisibility(JNIEnv *env, jobject } } -// --- the stuff below only applies to widgets that override the OnDraw() method; other widgets are created by class-specific constructors. -// FIXME: how do we handle someone subclassing something other then View and then overriding the onDraw/onMeasure method(s)? - -struct jni_callback_data { JavaVM *jvm; jobject this; jclass this_class; cairo_t *cached_cr; jobject canvas;}; - -static void draw_function(GtkDrawingArea *area, cairo_t *cr, int width, int height, struct jni_callback_data *d) -{ - JNIEnv *env; - (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - - if(d->cached_cr != cr) { - if(d->canvas == NULL) { - d->canvas = _REF((*env)->NewObject(env, handle_cache.canvas.class, handle_cache.canvas.constructor, _INTPTR(cr), _INTPTR(area))); - } else { - _SET_LONG_FIELD(d->canvas, "cairo_context", _INTPTR(cr)); - } - d->cached_cr = cr; - } - - (*env)->CallVoidMethod(env, d->this, _METHOD(d->this_class, "onDraw", "(Landroid/graphics/Canvas;)V"), d->canvas); - - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); -} - -static void on_mapped(GtkWidget* self, struct jni_callback_data *d) -{ - JNIEnv *env; - (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - - (*env)->CallVoidMethod(env, d->this, _METHOD(d->this_class, "onMeasure", "(II)V"), gtk_widget_get_width(self), gtk_widget_get_height(self)); -} - // FIXME: this is used in one other place as well, should probably go in util.c or gtk_util.c? gboolean tick_callback(GtkWidget* widget, GdkFrameClock* frame_clock, gpointer user_data) { gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(gtk_widget_get_parent(widget)); return G_SOURCE_CONTINUE; } @@ -211,24 +179,10 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1constructor(JNIEnv *env, j GtkWidget *wrapper = wrapper_widget_new(); GtkWidget *area = gtk_drawing_area_new(); wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), area); - - JavaVM *jvm; - (*env)->GetJavaVM(env, &jvm); - - struct jni_callback_data *callback_data = malloc(sizeof(struct jni_callback_data)); - callback_data->jvm = jvm; - callback_data->this = _REF(this); - callback_data->this_class = _REF(_CLASS(this)); - callback_data->cached_cr = NULL; - callback_data->canvas = NULL; - - gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(area), ( void(*)(GtkDrawingArea*,cairo_t*,int,int,gpointer) )draw_function, callback_data, NULL); + wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); gtk_widget_add_tick_callback(area, tick_callback, NULL, NULL); - // add a callback for when the widget is mapped, which will call onMeasure to figure out what size the widget wants to be - g_signal_connect(area, "map", G_CALLBACK(on_mapped), callback_data); - _SET_LONG_FIELD(this, "widget", (long)area); } diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c index 30e60e1e..1e7617f4 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.c +++ b/src/api-impl-jni/widgets/WrapperWidget.c @@ -1,5 +1,7 @@ #include +#include "../defines.h" +#include "../util.h" #include "../drawables/ninepatch.h" #include "WrapperWidget.h" @@ -52,6 +54,15 @@ 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); } @@ -78,6 +89,29 @@ void wrapper_snapshot(GtkWidget* widget, GtkSnapshot* snapshot) g_object_unref(texture); } gtk_widget_snapshot_child(widget, gtk_widget_get_first_child(widget), snapshot); + + // if onDraw method is overwritten call it now + WrapperWidget *wrapper_widget = WRAPPER_WIDGET(widget); + GtkAllocation alloc; + gtk_widget_get_allocation(widget, &alloc); + if (wrapper_widget->draw_method) { + cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT(0, 0, alloc.width, alloc.height)); + + 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(cr), 0)); + } else { + _SET_LONG_FIELD(wrapper_widget->canvas, "cairo_context", _INTPTR(cr)); + } + + (*env)->CallVoidMethod(env, wrapper_widget->jobj, wrapper_widget->draw_method, wrapper_widget->canvas); + + if((*env)->ExceptionCheck(env)) + (*env)->ExceptionDescribe(env); + + cairo_destroy (cr); + } } @@ -102,3 +136,32 @@ void wrapper_widget_set_child(WrapperWidget *parent, GtkWidget *child) // TODO: { gtk_widget_insert_before(child, GTK_WIDGET(parent), NULL); } + +static void on_mapped(GtkWidget* self, gpointer data) +{ + WrapperWidget *wrapper = WRAPPER_WIDGET(self); + if (wrapper->jvm) { + JNIEnv *env; + (*wrapper->jvm)->GetEnv(wrapper->jvm, (void**)&env, JNI_VERSION_1_6); + + (*env)->CallVoidMethod(env, wrapper->jobj, wrapper->measure_method, gtk_widget_get_width(self), gtk_widget_get_height(self)); + } +} + +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; + + jmethodID measure_method = _METHOD(_CLASS(jobj), "onMeasure", "(II)V"); + if (measure_method != handle_cache.view.onMeasure) { + wrapper->measure_method = measure_method; + // add a callback for when the widget is mapped, which will call onMeasure to figure out what size the widget wants to be + g_signal_connect(wrapper, "map", G_CALLBACK(on_mapped), NULL); + } +} diff --git a/src/api-impl-jni/widgets/WrapperWidget.h b/src/api-impl-jni/widgets/WrapperWidget.h index 901b8e64..3d3f1958 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.h +++ b/src/api-impl-jni/widgets/WrapperWidget.h @@ -1,11 +1,18 @@ #ifndef WRAPPER_WIDGET_H #define WRAPPER_WIDGET_H +#include + G_DECLARE_FINAL_TYPE (WrapperWidget, wrapper_widget, WRAPPER, WIDGET, GtkWidget) struct _WrapperWidget { GtkWidget parent_instance; + JavaVM *jvm; + jobject jobj; + jobject canvas; + jmethodID draw_method; + jmethodID measure_method; }; struct _WrapperWidgetClass @@ -15,5 +22,6 @@ struct _WrapperWidgetClass GtkWidget * wrapper_widget_new(void); void wrapper_widget_set_child(WrapperWidget *parent, GtkWidget *child); +void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject jobj); #endif diff --git a/src/api-impl-jni/widgets/android_widget_ImageView.c b/src/api-impl-jni/widgets/android_widget_ImageView.c index d3e8a30c..1e9f10e3 100644 --- a/src/api-impl-jni/widgets/android_widget_ImageView.c +++ b/src/api-impl-jni/widgets/android_widget_ImageView.c @@ -12,6 +12,7 @@ JNIEXPORT void JNICALL Java_android_widget_ImageView_native_1constructor__Landro GtkWidget *wrapper = wrapper_widget_new(); GtkWidget *image = gtk_picture_new_for_resource("/org/gtk/libgtk/icons/16x16/status/image-missing.png"); // show "broken image" icon wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), image); + wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); _SET_LONG_FIELD(this, "widget", _INTPTR(image));} JNIEXPORT void JNICALL Java_android_widget_ImageView_native_1constructor__Landroid_content_Context_2(JNIEnv *env, jobject this, jobject context) @@ -19,6 +20,7 @@ JNIEXPORT void JNICALL Java_android_widget_ImageView_native_1constructor__Landro GtkWidget *wrapper = wrapper_widget_new(); GtkWidget *image = gtk_picture_new_for_resource("/org/gtk/libgtk/icons/16x16/status/image-missing.png"); // show "broken image" icon wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), image); + wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); _SET_LONG_FIELD(this, "widget", _INTPTR(image)); }