2022-10-02 23:06:56 +02:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
2023-07-14 17:53:12 +02:00
|
|
|
#include "../defines.h"
|
|
|
|
|
#include "../util.h"
|
2022-10-02 23:06:56 +02:00
|
|
|
|
2023-10-17 21:33:59 +02:00
|
|
|
#include "../generated_headers/android_view_View.h"
|
|
|
|
|
|
2023-08-28 20:03:32 +02:00
|
|
|
#include "WrapperWidget.h"
|
2024-05-06 06:35:47 +02:00
|
|
|
#include "src/api-impl-jni/views/AndroidLayout.h"
|
2022-10-02 23:06:56 +02:00
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(WrapperWidget, wrapper_widget, GTK_TYPE_WIDGET)
|
|
|
|
|
|
2022-11-02 14:37:34 +01:00
|
|
|
static void wrapper_widget_init (WrapperWidget *wrapper_widget)
|
2022-10-02 23:06:56 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wrapper_widget_dispose(GObject *wrapper_widget)
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_unparent(gtk_widget_get_first_child(GTK_WIDGET(wrapper_widget)));
|
2023-07-14 17:53:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2022-10-02 23:06:56 +02:00
|
|
|
G_OBJECT_CLASS (wrapper_widget_parent_class)->dispose (wrapper_widget);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-04 07:49:38 +01:00
|
|
|
GtkSizeRequestMode wrapper_widget_get_request_mode(GtkWidget *widget)
|
2023-08-28 20:03:32 +02:00
|
|
|
{
|
2023-10-30 22:37:48 +01:00
|
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
|
2023-11-04 07:49:38 +01:00
|
|
|
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);
|
2023-11-10 23:36:57 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2023-11-04 07:49:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void wrapper_widget_allocate(GtkWidget *widget, int width, int height, int baseline)
|
|
|
|
|
{
|
|
|
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
|
2024-05-06 06:35:47 +02:00
|
|
|
if (!width && !height) {
|
|
|
|
|
width = wrapper->real_width;
|
|
|
|
|
height = wrapper->real_height;
|
|
|
|
|
}
|
2023-11-04 07:49:38 +01:00
|
|
|
GtkAllocation allocation = {
|
|
|
|
|
.x = 0,
|
|
|
|
|
.y = 0,
|
|
|
|
|
.width = width,
|
|
|
|
|
.height = height,
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-30 22:37:48 +01:00
|
|
|
if (wrapper->computeScroll_method) {
|
2023-11-04 07:49:38 +01:00
|
|
|
// 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);
|
|
|
|
|
|
2023-10-30 22:37:48 +01:00
|
|
|
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);
|
2023-11-04 07:49:38 +01:00
|
|
|
allocation.x = -(*env)->CallIntMethod(env, wrapper->jobj, handle_cache.view.getScrollX);
|
|
|
|
|
allocation.y = -(*env)->CallIntMethod(env, wrapper->jobj, handle_cache.view.getScrollY);
|
2023-10-30 22:37:48 +01:00
|
|
|
}
|
2023-11-04 07:49:38 +01:00
|
|
|
|
2024-05-06 06:35:47 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2023-12-29 16:55:11 +01:00
|
|
|
if (wrapper->background)
|
|
|
|
|
gtk_widget_size_allocate(wrapper->background, &allocation, baseline);
|
2023-10-30 22:37:48 +01:00
|
|
|
}
|
2023-08-28 20:03:32 +02:00
|
|
|
|
2024-05-25 10:38:49 +02:00
|
|
|
static void wrapper_widget_snapshot(GtkWidget *widget, GdkSnapshot *snapshot)
|
|
|
|
|
{
|
|
|
|
|
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
|
|
|
|
|
if (wrapper->real_height > 0 && wrapper->real_width > 0) {
|
|
|
|
|
gtk_snapshot_push_clip(snapshot, &GRAPHENE_RECT_INIT(0, 0, wrapper->real_width, wrapper->real_height));
|
|
|
|
|
}
|
|
|
|
|
GtkWidget *child = gtk_widget_get_first_child(widget);
|
|
|
|
|
while (child) {
|
|
|
|
|
gtk_widget_snapshot_child(widget, child, snapshot);
|
|
|
|
|
child = gtk_widget_get_next_sibling(child);
|
|
|
|
|
}
|
2024-05-25 19:25:33 +02:00
|
|
|
if (wrapper->draw_method) {
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
|
_SET_LONG_FIELD(wrapper->canvas, "snapshot", _INTPTR(snapshot));
|
|
|
|
|
(*env)->CallVoidMethod(env, wrapper->jobj, wrapper->draw_method, wrapper->canvas);
|
|
|
|
|
if ((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
}
|
2024-05-25 10:38:49 +02:00
|
|
|
if (wrapper->real_height > 0 && wrapper->real_width > 0) {
|
|
|
|
|
gtk_snapshot_pop(snapshot);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
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;
|
|
|
|
|
|
2023-11-04 07:49:38 +01:00
|
|
|
widget_class->get_request_mode = wrapper_widget_get_request_mode;
|
|
|
|
|
widget_class->measure = wrapper_widget_measure;
|
|
|
|
|
widget_class->size_allocate = wrapper_widget_allocate;
|
2024-05-25 10:38:49 +02:00
|
|
|
widget_class->snapshot = wrapper_widget_snapshot;
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2023-10-31 23:14:47 +01:00
|
|
|
parent->child = child;
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|
2023-07-14 17:53:12 +02:00
|
|
|
|
2024-05-25 19:25:33 +02:00
|
|
|
static guint queue_queue_redraw(GtkWidget *widget)
|
2023-08-28 20:03:32 +02:00
|
|
|
{
|
2024-05-25 19:25:33 +02:00
|
|
|
gtk_widget_queue_draw(widget);
|
2023-10-31 23:14:47 +01:00
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void wrapper_widget_queue_draw(WrapperWidget *wrapper)
|
|
|
|
|
{
|
2024-05-25 19:25:33 +02:00
|
|
|
if (wrapper->draw_method) {
|
|
|
|
|
/* schedule the call to gtk_widget_queue_draw for a future event loop pass in case we're currently inside the snapshot */
|
2023-10-31 23:14:47 +01:00
|
|
|
/* GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations. */
|
2024-05-25 19:25:33 +02:00
|
|
|
g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20, G_SOURCE_FUNC(queue_queue_redraw), &wrapper->parent_instance, NULL);
|
2023-10-31 23:14:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(wrapper->child)
|
|
|
|
|
gtk_widget_queue_draw(wrapper->child);
|
2023-11-04 07:49:38 +01:00
|
|
|
if (wrapper->computeScroll_method)
|
|
|
|
|
gtk_widget_queue_allocate(GTK_WIDGET(wrapper));
|
2023-08-28 20:03:32 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-09 10:05:22 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 17:53:12 +02:00
|
|
|
void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject jobj)
|
|
|
|
|
{
|
|
|
|
|
JavaVM *jvm;
|
|
|
|
|
(*env)->GetJavaVM(env, &jvm);
|
|
|
|
|
wrapper->jvm = jvm;
|
|
|
|
|
wrapper->jobj = _REF(jobj);
|
2024-05-19 15:04:15 +02:00
|
|
|
jmethodID on_draw_method = _METHOD(_CLASS(jobj), "onDraw", "(Landroid/graphics/Canvas;)V");
|
|
|
|
|
jmethodID draw_method = _METHOD(_CLASS(jobj), "draw", "(Landroid/graphics/Canvas;)V");
|
|
|
|
|
if (on_draw_method != handle_cache.view.onDraw || draw_method != handle_cache.view.draw) {
|
2023-07-14 17:53:12 +02:00
|
|
|
wrapper->draw_method = draw_method;
|
2024-05-25 19:25:33 +02:00
|
|
|
jclass canvas_class = (*env)->FindClass(env, "android/graphics/GskCanvas");
|
|
|
|
|
jmethodID canvas_constructor = _METHOD(canvas_class, "<init>", "(J)V");
|
|
|
|
|
wrapper->canvas = _REF((*env)->NewObject(env, canvas_class, canvas_constructor, 0));
|
|
|
|
|
(*env)->DeleteLocalRef(env, canvas_class);
|
2023-08-28 20:03:32 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-17 21:33:59 +02:00
|
|
|
jmethodID ontouchevent_method = _METHOD(_CLASS(jobj), "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
|
|
|
|
|
if (ontouchevent_method != handle_cache.view.onTouchEvent) {
|
2024-03-25 19:50:29 +01:00
|
|
|
_setOnTouchListener(env, jobj, GTK_WIDGET(wrapper), NULL);
|
2023-10-17 21:33:59 +02:00
|
|
|
}
|
2023-10-30 22:37:48 +01:00
|
|
|
|
|
|
|
|
jmethodID computeScroll_method = _METHOD(_CLASS(jobj), "computeScroll", "()V");
|
|
|
|
|
if (computeScroll_method != handle_cache.view.computeScroll) {
|
|
|
|
|
wrapper->computeScroll_method = computeScroll_method;
|
|
|
|
|
}
|
2024-02-09 10:05:22 +01:00
|
|
|
|
|
|
|
|
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);
|
2024-05-06 06:35:47 +02:00
|
|
|
widget_set_needs_allocation(wrapper->child);
|
2024-02-09 10:05:22 +01:00
|
|
|
}
|
2023-07-14 17:53:12 +02:00
|
|
|
}
|
2023-11-10 23:36:57 +01:00
|
|
|
|
|
|
|
|
void wrapper_widget_set_layout_params(WrapperWidget *wrapper, int width, int height)
|
|
|
|
|
{
|
|
|
|
|
wrapper->layout_width = width;
|
|
|
|
|
wrapper->layout_height = height;
|
|
|
|
|
}
|
2023-12-29 16:55:11 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|