diff --git a/src/api-impl-jni/generated_headers/android_view_ViewGroup.h b/src/api-impl-jni/generated_headers/android_view_ViewGroup.h index c60d66f9..f5ac7150 100644 --- a/src/api-impl-jni/generated_headers/android_view_ViewGroup.h +++ b/src/api-impl-jni/generated_headers/android_view_ViewGroup.h @@ -231,6 +231,14 @@ JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChildren JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChild (JNIEnv *, jobject, jlong, jlong, jlong); +/* + * Class: android_view_ViewGroup + * Method: native_dispatchTouchEvent + * Signature: (JLandroid/view/MotionEvent;DD)Z + */ +JNIEXPORT jboolean JNICALL Java_android_view_ViewGroup_native_1dispatchTouchEvent + (JNIEnv *, jobject, jlong, jobject, jdouble, jdouble); + #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 8fa829df..ba1716be 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -132,6 +132,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.view.getScrollY = _METHOD(handle_cache.view.class, "getScrollY", "()I"); handle_cache.view.performClick = _METHOD(handle_cache.view.class, "performClick", "()Z"); handle_cache.view.onTouchEvent = _METHOD(handle_cache.view.class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z"); + handle_cache.view.onTouchEventInternal = _METHOD(handle_cache.view.class, "onTouchEventInternal", "(Landroid/view/MotionEvent;)Z"); handle_cache.view.dispatchTouchEvent = _METHOD(handle_cache.view.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z"); handle_cache.view.onInterceptTouchEvent = _METHOD(handle_cache.view.class, "onInterceptTouchEvent", "(Landroid/view/MotionEvent;)Z"); handle_cache.view.layoutInternal = _METHOD(handle_cache.view.class, "layoutInternal", "(II)V"); @@ -143,6 +144,9 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.view.dispatchKeyEvent = _METHOD(handle_cache.view.class, "dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z"); handle_cache.view.onKeyDown = _METHOD(handle_cache.view.class, "onKeyDown", "(ILandroid/view/KeyEvent;)Z"); + handle_cache.view_group.class = _REF((*env)->FindClass(env, "android/view/ViewGroup")); + handle_cache.view_group.dispatchTouchEvent = _METHOD(handle_cache.view_group.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z"); + 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 a8f92048..5d343344 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -77,6 +77,7 @@ struct handle_cache { jmethodID getScrollY; jmethodID performClick; jmethodID onTouchEvent; + jmethodID onTouchEventInternal; jmethodID dispatchTouchEvent; jmethodID onInterceptTouchEvent; jmethodID layoutInternal; @@ -88,6 +89,10 @@ struct handle_cache { jmethodID dispatchKeyEvent; jmethodID onKeyDown; } view; + struct { + jclass class; + jmethodID dispatchTouchEvent; + } view_group; struct { jclass class; jmethodID extractFromAPK; diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index f6e4eea3..191167cc 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -46,11 +46,40 @@ static WrapperWidget *cancel_triggerer = NULL; static struct pointer pointers[MAX_POINTERS] = {}; +bool view_dispatch_motionevent(JNIEnv *env, WrapperWidget *wrapper, GtkPropagationPhase phase, jobject motion_event, GdkEvent *event) { + int ret; + + jobject this = wrapper->jobj; + + if (wrapper->custom_dispatch_touch) { + ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.dispatchTouchEvent, motion_event); + } else if (phase == GTK_PHASE_CAPTURE && !wrapper->intercepting_touch) { + wrapper->intercepting_touch = (*env)->CallBooleanMethod(env, this, handle_cache.view.onInterceptTouchEvent, motion_event); + if (wrapper->intercepting_touch) { + if(event) { + // store the event that was canceled and let it propagate to the child widgets + canceled_event = event; + cancel_triggerer = wrapper; + } else { + /* this function is also called to synthesize an event, in which case there is no GdkEvent so not sure what to do */ + fprintf(stderr, "view_dispatch_motionevent: onInterceptTouchEvent returned true but this is a synthesized event, please investigate\n"); + } + } + ret = false; + } else { + ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onTouchEventInternal, motion_event); + } + + if((*env)->ExceptionCheck(env)) + (*env)->ExceptionDescribe(env); + + return ret; +} + static bool call_ontouch_callback(WrapperWidget *wrapper, int action, struct pointer pointers[MAX_POINTERS], GPtrArray *pointer_indices, GtkPropagationPhase phase, guint32 timestamp, GdkEvent *event) { bool ret; JNIEnv *env = get_jni_env(); - jobject this = wrapper->jobj; int num_pointers = pointer_indices->len; jintArray ids = (*env)->NewIntArray(env, num_pointers); @@ -62,24 +91,9 @@ static bool call_ontouch_callback(WrapperWidget *wrapper, int action, struct poi (*env)->SetFloatArrayRegion(env, coords, 4 * i, 4, &pointer->coord_x); } - jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, (long)timestamp, ids, coords); + jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, timestamp, ids, coords); - if (wrapper->custom_dispatch_touch) { - ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.dispatchTouchEvent, motion_event); - } else if (phase == GTK_PHASE_CAPTURE && !wrapper->intercepting_touch) { - wrapper->intercepting_touch = (*env)->CallBooleanMethod(env, this, handle_cache.view.onInterceptTouchEvent, motion_event); - if (wrapper->intercepting_touch) { - // store the event that was canceled and let it propagate to the child widgets - canceled_event = event; - cancel_triggerer = wrapper; - } - ret = false; - } else { - ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onTouchEvent, motion_event); - } - - if((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); + ret = view_dispatch_motionevent(env, wrapper, phase, motion_event, event); (*env)->DeleteLocalRef(env, motion_event); diff --git a/src/api-impl-jni/views/android_view_ViewGroup.c b/src/api-impl-jni/views/android_view_ViewGroup.c index 5cf6996c..d8be5d43 100644 --- a/src/api-impl-jni/views/android_view_ViewGroup.c +++ b/src/api-impl-jni/views/android_view_ViewGroup.c @@ -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); +} diff --git a/src/api-impl-jni/views/gdkarrayimpl.c b/src/api-impl-jni/views/gdkarrayimpl.c new file mode 100644 index 00000000..dfde3ce4 --- /dev/null +++ b/src/api-impl-jni/views/gdkarrayimpl.c @@ -0,0 +1,326 @@ +/* lifted from Gtk for gtk_propagate_event_internal derived function */ +/* + * Copyright © 2020 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include + +G_BEGIN_DECLS + +#ifndef GDK_ARRAY_TYPE_NAME +#define GDK_ARRAY_TYPE_NAME GdkArray +#endif + +#ifndef GDK_ARRAY_NAME +#define GDK_ARRAY_NAME gdk_array +#endif + +#ifndef GDK_ARRAY_ELEMENT_TYPE +#define GDK_ARRAY_ELEMENT_TYPE gpointer +#endif + +#ifdef GDK_ARRAY_PREALLOC +#if GDK_ARRAY_PREALLOC == 0 +#undef GDK_ARRAY_PREALLOC +#endif +#endif + +#ifdef GDK_ARRAY_NULL_TERMINATED +#define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1) +#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof(_T_) - 1) +#else +#define GDK_ARRAY_REAL_SIZE(_size) (_size) +#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof(_T_)) +#endif + +/* make this readable */ +#define _T_ GDK_ARRAY_ELEMENT_TYPE +#define GdkArray GDK_ARRAY_TYPE_NAME +#define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME##_##func_name +#define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more(GDK_ARRAY_NAME, func_name) +#define gdk_array(func_name) gdk_array_paste(GDK_ARRAY_NAME, func_name) + +typedef struct GdkArray GdkArray; + +struct GdkArray { + _T_ *start; + _T_ *end; + _T_ *end_allocation; +#ifdef GDK_ARRAY_PREALLOC + _T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)]; +#endif +}; + +/* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */ +static inline void +gdk_array(init)(GdkArray *self) +{ +#ifdef GDK_ARRAY_PREALLOC + self->start = self->preallocated; + self->end = self->start; + self->end_allocation = self->start + GDK_ARRAY_PREALLOC; +#ifdef GDK_ARRAY_NULL_TERMINATED + *self->start = *(_T_[1]){0}; +#endif +#else + self->start = NULL; + self->end = NULL; + self->end_allocation = NULL; +#endif +} + +G_GNUC_UNUSED static inline gsize +gdk_array(get_capacity)(const GdkArray *self) +{ + return self->end_allocation - self->start; +} + +G_GNUC_UNUSED static inline gsize +gdk_array(get_size)(const GdkArray *self) +{ + return self->end - self->start; +} + +static inline void +gdk_array(free_elements)(_T_ *start, + _T_ *end) +{ +#ifdef GDK_ARRAY_FREE_FUNC + _T_ *e; + for (e = start; e < end; e++) +#ifdef GDK_ARRAY_BY_VALUE + GDK_ARRAY_FREE_FUNC(e); +#else + GDK_ARRAY_FREE_FUNC(*e); +#endif +#endif +} + +/* no G_GNUC_UNUSED here */ +static inline void +gdk_array(clear)(GdkArray *self) +{ + gdk_array(free_elements)(self->start, self->end); + +#ifdef GDK_ARRAY_PREALLOC + if (self->start != self->preallocated) +#endif + g_free(self->start); + gdk_array(init)(self); +} + +/* + * gdk_array_steal: + * @self: the array + * + * Steals all data in the array and clears the array. + * + * If you need to know the size of the data, you should query it + * beforehand. + * + * Returns: The array's data + **/ +G_GNUC_UNUSED static inline _T_ * +gdk_array(steal)(GdkArray *self) +{ + _T_ *result; + +#ifdef GDK_ARRAY_PREALLOC + if (self->start == self->preallocated) { + gsize size = GDK_ARRAY_REAL_SIZE(gdk_array(get_size)(self)); + result = g_new(_T_, size); + memcpy(result, self->preallocated, sizeof(_T_) * size); + } else +#endif + result = self->start; + + gdk_array(init)(self); + + return result; +} + +G_GNUC_UNUSED static inline _T_ * +gdk_array(get_data)(const GdkArray *self) +{ + return self->start; +} + +G_GNUC_UNUSED static inline _T_ * +gdk_array(index)(const GdkArray *self, + gsize pos) +{ + return self->start + pos; +} + +G_GNUC_UNUSED static inline gboolean +gdk_array(is_empty)(const GdkArray *self) +{ + return self->end == self->start; +} + +G_GNUC_UNUSED static inline void +gdk_array(reserve)(GdkArray *self, + gsize n) +{ + gsize new_capacity, size, capacity; + + if (G_UNLIKELY(n > GDK_ARRAY_MAX_SIZE)) + g_error("requesting array size of %zu, but maximum size is %zu", n, GDK_ARRAY_MAX_SIZE); + + capacity = gdk_array(get_capacity)(self); + if (n <= capacity) + return; + + size = gdk_array(get_size)(self); + /* capacity * 2 can overflow, that's why we MAX() */ + new_capacity = MAX(GDK_ARRAY_REAL_SIZE(n), capacity * 2); + +#ifdef GDK_ARRAY_PREALLOC + if (self->start == self->preallocated) { + self->start = g_new(_T_, new_capacity); + memcpy(self->start, self->preallocated, sizeof(_T_) * GDK_ARRAY_REAL_SIZE(size)); + } else +#endif +#ifdef GDK_ARRAY_NULL_TERMINATED + if (self->start == NULL) { + self->start = g_new(_T_, new_capacity); + *self->start = *(_T_[1]){0}; + } else +#endif + self->start = g_renew(_T_, self->start, new_capacity); + + self->end = self->start + size; + self->end_allocation = self->start + new_capacity; +#ifdef GDK_ARRAY_NULL_TERMINATED + self->end_allocation--; +#endif +} + +G_GNUC_UNUSED static inline void +gdk_array(splice)(GdkArray *self, + gsize pos, + gsize removed, + gboolean stolen, +#ifdef GDK_ARRAY_BY_VALUE + const _T_ *additions, +#else + _T_ *additions, +#endif + gsize added) +{ + gsize size; + gsize remaining; + + size = gdk_array(get_size)(self); + g_assert(pos + removed <= size); + remaining = size - pos - removed; + + if (!stolen) + gdk_array(free_elements)(gdk_array(index)(self, pos), + gdk_array(index)(self, pos + removed)); + + gdk_array(reserve)(self, size - removed + added); + + if (GDK_ARRAY_REAL_SIZE(remaining) && removed != added) + memmove(gdk_array(index)(self, pos + added), + gdk_array(index)(self, pos + removed), + GDK_ARRAY_REAL_SIZE(remaining) * sizeof(_T_)); + + if (added) { + if (additions) + memcpy(gdk_array(index)(self, pos), + additions, + added * sizeof(_T_)); +#ifndef GDK_ARRAY_NO_MEMSET + else + memset(gdk_array(index)(self, pos), 0, added * sizeof(_T_)); +#endif + } + + /* might overflow, but does the right thing */ + self->end += added - removed; +} + +G_GNUC_UNUSED static void +gdk_array(set_size)(GdkArray *self, + gsize new_size) +{ + gsize old_size = gdk_array(get_size)(self); + if (new_size > old_size) + gdk_array(splice)(self, old_size, 0, FALSE, NULL, new_size - old_size); + else + gdk_array(splice)(self, new_size, old_size - new_size, FALSE, NULL, 0); +} + +G_GNUC_UNUSED static void +gdk_array(append)(GdkArray *self, +#ifdef GDK_ARRAY_BY_VALUE + _T_ *value) +#else + _T_ value) +#endif +{ + gdk_array(splice)(self, + gdk_array(get_size)(self), + 0, + FALSE, +#ifdef GDK_ARRAY_BY_VALUE + value, +#else + &value, +#endif + 1); +} + +#ifdef GDK_ARRAY_BY_VALUE +G_GNUC_UNUSED static _T_ * +gdk_array(get)(const GdkArray *self, + gsize pos) +{ + return gdk_array(index)(self, pos); +} +#else +G_GNUC_UNUSED static _T_ +gdk_array(get)(const GdkArray *self, + gsize pos) +{ + return *gdk_array(index)(self, pos); +} +#endif + +#ifndef GDK_ARRAY_NO_UNDEF + +#undef _T_ +#undef GdkArray +#undef gdk_array_paste_more +#undef gdk_array_paste +#undef gdk_array +#undef GDK_ARRAY_REAL_SIZE +#undef GDK_ARRAY_MAX_SIZE + +#undef GDK_ARRAY_BY_VALUE +#undef GDK_ARRAY_ELEMENT_TYPE +#undef GDK_ARRAY_FREE_FUNC +#undef GDK_ARRAY_NAME +#undef GDK_ARRAY_NULL_TERMINATED +#undef GDK_ARRAY_PREALLOC +#undef GDK_ARRAY_TYPE_NAME +#undef GDK_ARRAY_NO_MEMSET +#endif + +G_END_DECLS diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c index 82858880..cb418cfb 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.c +++ b/src/api-impl-jni/widgets/WrapperWidget.c @@ -23,7 +23,7 @@ static void wrapper_widget_set_property (GObject *object, guint property_id, con } } -static void wrapper_widget_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +static void wrapper_widget_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WrapperWidget *self = WRAPPER_WIDGET(object); @@ -91,7 +91,7 @@ static void wrapper_widget_get_property (GObject *object, guint property_id, GVa } } -static void wrapper_widget_init (WrapperWidget *wrapper_widget) +static void wrapper_widget_init(WrapperWidget *wrapper_widget) { } @@ -386,7 +386,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job jmethodID ontouchevent_method = _METHOD(_CLASS(jobj), "onTouchEvent", "(Landroid/view/MotionEvent;)Z"); jmethodID dispatchtouchevent_method = _METHOD(_CLASS(jobj), "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z"); - wrapper->custom_dispatch_touch = dispatchtouchevent_method != handle_cache.view.dispatchTouchEvent; + wrapper->custom_dispatch_touch = (dispatchtouchevent_method != handle_cache.view.dispatchTouchEvent && dispatchtouchevent_method != handle_cache.view_group.dispatchTouchEvent); if (ontouchevent_method != handle_cache.view.onTouchEvent || wrapper->custom_dispatch_touch) { _setOnTouchListener(env, jobj, GTK_WIDGET(wrapper)); } diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java index 6033818a..d502d818 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -1075,13 +1075,22 @@ public class View implements Drawable.Callback { } private OnTouchListener on_touch_listener = null; - public boolean onTouchEvent(MotionEvent event) { + + public boolean onTouchEventInternal(MotionEvent event) { boolean handled = false; if (on_touch_listener != null) handled = on_touch_listener.onTouch(this, event); + + if (!handled) + handled = onTouchEvent(event); + return handled; } + public boolean onTouchEvent(MotionEvent event) { + return false; + } + public void setOnTouchListener(OnTouchListener l) { nativeSetOnTouchListener(widget); on_touch_listener = l; @@ -1206,12 +1215,21 @@ public class View implements Drawable.Callback { public native void setBackgroundColor(int color); public native void native_setVisibility(long widget, int visibility, float alpha); + + protected void onVisibilityChanged(View changedView, int visibility) { + } + + protected void dispatchVisibilityChanged(View changedView, int visibility) { + onVisibilityChanged(changedView, visibility); + } + public void setVisibility(int visibility) { native_setVisibility(widget, visibility, alpha); if ((visibility == View.GONE) != (this.visibility == View.GONE)) { requestLayout(); } this.visibility = visibility; + dispatchVisibilityChanged(this, visibility); } public void setPadding(int left, int top, int right, int bottom) { diff --git a/src/api-impl/android/view/ViewGroup.java b/src/api-impl/android/view/ViewGroup.java index b8defa1d..e9b2b9bb 100644 --- a/src/api-impl/android/view/ViewGroup.java +++ b/src/api-impl/android/view/ViewGroup.java @@ -61,6 +61,10 @@ public class ViewGroup extends View implements ViewParent, ViewManager { addView(child, params); } + public void addView(View child, int index, LayoutParams params) { + addViewInternal(child, index, params); + } + protected void addViewInternal(View child, int index, LayoutParams params) { if (child.parent == this) return; @@ -81,10 +85,13 @@ public class ViewGroup extends View implements ViewParent, ViewManager { requestLayout(); } - public void addView(View child, int index, LayoutParams params) { - addViewInternal(child, index, params); + /* We never call this ourselves */ + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + return native_dispatchTouchEvent(widget, event, event.getX(), event.getY()); } + protected boolean addViewInLayout(View child, int index, LayoutParams params) { addViewInternal(child, index, params); return true; @@ -169,6 +176,16 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } } + @Override + protected void dispatchVisibilityChanged(View changedView, int visibility) { + if (children == null) // happens if this gets called during super constructor + return; + + for (View child: children) { + child.dispatchVisibilityChanged(changedView, visibility); + } + } + protected native void native_addView(long widget, long child, int index, LayoutParams params); protected native void native_removeView(long widget, long child); @Override @@ -636,4 +653,6 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } public void requestChildFocus(View child, View focused) {} + + public native boolean native_dispatchTouchEvent(long widget, MotionEvent event, double x, double y); }