2022-10-02 23:06:56 +02:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
|
|
#include "../defines.h"
|
|
|
|
|
#include "../util.h"
|
|
|
|
|
|
2023-08-17 12:59:37 +02:00
|
|
|
#include "../widgets/WrapperWidget.h"
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
#include "../generated_headers/android_view_ViewGroup.h"
|
|
|
|
|
#include "../generated_headers/android_view_View.h"
|
|
|
|
|
|
2023-08-22 18:08:16 +02:00
|
|
|
#define SOURCE_CLASS_POINTER 0x2
|
|
|
|
|
|
2023-08-22 14:18:33 +02:00
|
|
|
struct _AndroidLayout {
|
|
|
|
|
GtkLayoutManager parent_instance;
|
|
|
|
|
jobject view;
|
|
|
|
|
};
|
|
|
|
|
G_DECLARE_FINAL_TYPE(AndroidLayout, android_layout, ATL, ANDROID_LAYOUT, GtkLayoutManager);
|
|
|
|
|
|
|
|
|
|
static void android_layout_measure(GtkLayoutManager *layout_manager, GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline) {
|
|
|
|
|
AndroidLayout *layout = ATL_ANDROID_LAYOUT(layout_manager);
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
|
|
|
|
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
|
|
|
|
|
*minimum = (*env)->CallIntMethod(env, layout->view, handle_cache.view.getSuggestedMinimumWidth);
|
|
|
|
|
*natural = (*env)->CallIntMethod(env, layout->view, handle_cache.view.getMeasuredWidth);
|
|
|
|
|
}
|
|
|
|
|
if (orientation == GTK_ORIENTATION_VERTICAL) {
|
|
|
|
|
*minimum = (*env)->CallIntMethod(env, layout->view, handle_cache.view.getSuggestedMinimumHeight);
|
|
|
|
|
*natural = (*env)->CallIntMethod(env, layout->view, handle_cache.view.getMeasuredHeight);
|
|
|
|
|
}
|
|
|
|
|
if (*natural < *minimum)
|
|
|
|
|
*natural = *minimum;
|
|
|
|
|
|
|
|
|
|
*minimum_baseline = -1;
|
|
|
|
|
*natural_baseline = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void android_layout_allocate(GtkLayoutManager *layout_manager, GtkWidget *widget, int width, int height, int baseline) {
|
|
|
|
|
AndroidLayout *layout = ATL_ANDROID_LAYOUT(layout_manager);
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
|
|
2023-10-28 22:38:43 +02:00
|
|
|
(*env)->CallVoidMethod(env, layout->view, handle_cache.view.layoutInternal, width, height);
|
2023-08-22 14:18:33 +02:00
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void android_layout_class_init(AndroidLayoutClass *klass) {
|
|
|
|
|
klass->parent_class.measure = android_layout_measure;
|
|
|
|
|
klass->parent_class.allocate = android_layout_allocate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void android_layout_init(AndroidLayout *self) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(AndroidLayout, android_layout, GTK_TYPE_LAYOUT_MANAGER)
|
|
|
|
|
|
|
|
|
|
static GtkLayoutManager *android_layout_new(jobject view) {
|
|
|
|
|
AndroidLayout *layout = g_object_new(android_layout_get_type(), NULL);
|
|
|
|
|
layout->view = view;
|
|
|
|
|
return &layout->parent_instance;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 14:24:27 +02:00
|
|
|
#define MAGIC_SCROLL_FACTOR 32
|
|
|
|
|
|
2023-08-22 18:08:16 +02:00
|
|
|
static gboolean scroll_cb(GtkEventControllerScroll* self, gdouble dx, gdouble dy, jobject this)
|
|
|
|
|
{
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
2023-09-01 14:24:27 +02:00
|
|
|
GdkScrollUnit scroll_unit = gtk_event_controller_scroll_get_unit (self);
|
|
|
|
|
|
|
|
|
|
if (scroll_unit == GDK_SCROLL_UNIT_SURFACE) {
|
|
|
|
|
dx /= MAGIC_SCROLL_FACTOR;
|
|
|
|
|
dy /= MAGIC_SCROLL_FACTOR;
|
|
|
|
|
}
|
2023-08-22 18:08:16 +02:00
|
|
|
jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_CLASS_POINTER, MOTION_EVENT_ACTION_SCROLL, dx, -dy);
|
|
|
|
|
|
|
|
|
|
gboolean ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onGenericMotionEvent, motion_event);
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 12:59:37 +02:00
|
|
|
/**
|
|
|
|
|
* Should be overwritten by ViewGroup subclasses.
|
|
|
|
|
* Fall back to vertical GtkBox if subclass is not implemented yet
|
|
|
|
|
*/
|
|
|
|
|
JNIEXPORT jlong JNICALL Java_android_view_ViewGroup_native_1constructor(JNIEnv *env, jobject this, jobject context, jobject attrs)
|
2022-10-02 23:06:56 +02:00
|
|
|
{
|
2023-08-22 13:49:09 +02:00
|
|
|
GtkWidget *wrapper = g_object_ref(wrapper_widget_new());
|
2023-08-17 12:59:37 +02:00
|
|
|
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); // spacing of 1
|
|
|
|
|
wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), box);
|
2023-10-30 22:35:31 +01:00
|
|
|
wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this);
|
2023-08-22 14:18:33 +02:00
|
|
|
|
|
|
|
|
const char *name = _CSTRING((*env)->CallObjectMethod(env, _CLASS(this),
|
|
|
|
|
_METHOD((*env)->FindClass(env, "java/lang/Class"), "getName", "()Ljava/lang/String;")));
|
|
|
|
|
gtk_widget_set_name(box, name);
|
|
|
|
|
|
2023-10-31 13:39:39 +01:00
|
|
|
/* this should better match default android behavior */
|
|
|
|
|
gtk_widget_set_overflow(wrapper, GTK_OVERFLOW_HIDDEN);
|
|
|
|
|
|
2023-08-22 14:18:33 +02:00
|
|
|
jmethodID measure_method = _METHOD(_CLASS(this), "onMeasure", "(II)V");
|
|
|
|
|
jmethodID layout_method = _METHOD(_CLASS(this), "onLayout", "(ZIIII)V");
|
|
|
|
|
if (measure_method != handle_cache.view.onMeasure || layout_method != handle_cache.view.onLayout) {
|
|
|
|
|
gtk_widget_set_layout_manager(box, android_layout_new(_REF(this)));
|
|
|
|
|
}
|
2023-08-22 18:08:16 +02:00
|
|
|
if (_METHOD(_CLASS(this), "onGenericMotionEvent", "(Landroid/view/MotionEvent;)Z") != handle_cache.view.onGenericMotionEvent) {
|
|
|
|
|
GtkEventController *controller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
|
|
|
|
|
|
|
|
|
|
g_signal_connect(controller, "scroll", G_CALLBACK(scroll_cb), _REF(this));
|
|
|
|
|
gtk_widget_add_controller(box, controller);
|
|
|
|
|
}
|
2023-08-22 14:18:33 +02:00
|
|
|
|
2023-08-17 12:59:37 +02:00
|
|
|
return _INTPTR(box);
|
|
|
|
|
}
|
2022-10-02 23:06:56 +02:00
|
|
|
|
2023-08-17 12:59:37 +02:00
|
|
|
JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1addView(JNIEnv *env, jobject this, jlong widget, jlong child, jint index, jobject layout_params)
|
|
|
|
|
{
|
2022-10-02 23:06:56 +02:00
|
|
|
if(layout_params) {
|
|
|
|
|
/*
|
2023-08-17 12:59:37 +02:00
|
|
|
GtkWidget *_child = gtk_widget_get_parent(GTK_WIDGET(_PTR(child)));
|
|
|
|
|
jint child_width = -1;
|
|
|
|
|
jint child_height = -1;
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
jint child_width = _GET_INT_FIELD(layout_params, "width");
|
|
|
|
|
jint child_height = _GET_INT_FIELD(layout_params, "height");
|
|
|
|
|
|
|
|
|
|
jint child_gravity = _GET_INT_FIELD(layout_params, "gravity");
|
|
|
|
|
|
|
|
|
|
if(child_width > 0)
|
|
|
|
|
g_object_set(G_OBJECT(_child), "width-request", child_width, NULL);
|
|
|
|
|
if(child_height > 0)
|
|
|
|
|
g_object_set(G_OBJECT(_child), "height-request", child_height, NULL);
|
|
|
|
|
|
|
|
|
|
if(child_gravity != -1) {
|
|
|
|
|
printf(":::-: setting child gravity: %d", child_gravity);
|
|
|
|
|
Java_android_view_View_setGravity(env, child, child_gravity);
|
|
|
|
|
}*/
|
|
|
|
|
}
|
2023-09-01 13:49:42 +02:00
|
|
|
GtkWidget *parent = _PTR(widget);
|
|
|
|
|
GtkWidget *iter = gtk_widget_get_first_child(parent);
|
|
|
|
|
for(int i = 0; i < index; i++) {
|
|
|
|
|
iter = gtk_widget_get_next_sibling(iter);
|
|
|
|
|
if(iter == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_widget_insert_before(gtk_widget_get_parent(GTK_WIDGET(_PTR(child))), parent, iter);
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-17 12:59:37 +02:00
|
|
|
JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1removeView(JNIEnv *env, jobject this, jlong widget, jlong child)
|
2022-10-02 23:06:56 +02:00
|
|
|
{
|
2023-09-01 13:49:42 +02:00
|
|
|
gtk_widget_unparent(gtk_widget_get_parent(GTK_WIDGET(_PTR(child))));
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|