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
api-impl-jni: add workarounds for Gtk's non-graceful handling of layout changes during the snapshot phase
This commit is contained in:
@@ -65,7 +65,7 @@ static void bind_listitem_cb(GtkListItemFactory *factory, GtkListItem *list_item
|
||||
GtkWidget *label = gtk_list_item_get_child(list_item);
|
||||
ListEntry *entry = gtk_list_item_get_item(list_item);
|
||||
|
||||
gtk_label_set_text(GTK_LABEL(label), entry->text);
|
||||
atl_safe_gtk_label_set_text(GTK_LABEL(label), entry->text);
|
||||
}
|
||||
|
||||
static void activate_cb(GtkListView *list, guint position, struct click_callback_data *d)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "src/api-impl-jni/defines.h"
|
||||
|
||||
@@ -279,3 +281,92 @@ int get_nio_buffer_size(JNIEnv *env, jobject buffer)
|
||||
|
||||
return limit - position;
|
||||
}
|
||||
|
||||
|
||||
/* Calling these functions while snapshotting will cause Gtk to not snapshot the affected widgets.
|
||||
* Below are "safe" wrappers which will postpone the calls if inside a snapshot.
|
||||
* Specifically, gtk_widget_add_tick_callback will make sure the calls are made in the next
|
||||
* Update phase. */
|
||||
|
||||
/* callbacks */
|
||||
static gboolean queue_set_text(GtkWidget *label, GdkFrameClock *frame_clock, gpointer str)
|
||||
{
|
||||
gtk_label_set_text(GTK_LABEL(label), str);
|
||||
/* we always call strdup so we always want to free */
|
||||
free(str);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean queue_queue_allocate(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data)
|
||||
{
|
||||
gtk_widget_queue_allocate(widget);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean queue_queue_resize(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data)
|
||||
{
|
||||
gtk_widget_queue_resize(widget);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/* Some functions call gtk_widget_queue_allocate or similar internally.
|
||||
* To prevent that from breaking the snapshotting process, when called at the wrong time,
|
||||
* we have to follow those functions with this pile of hacks that will unset the problematic flags. */
|
||||
extern int snapshot_in_progress;
|
||||
void atl_ensure_widget_snapshotability(GtkWidget *widget)
|
||||
{
|
||||
if(snapshot_in_progress) {
|
||||
GtkAllocation allocation;
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
/* we probably don't need to use this deprecated function but it sure is convenient */
|
||||
gtk_widget_get_allocation(widget, &allocation);
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
/* this clears resize request, which seems to be necessary in some cases */
|
||||
gtk_widget_get_request_mode(widget);
|
||||
gtk_widget_size_allocate(widget, &allocation, gtk_widget_get_baseline(widget));
|
||||
gtk_widget_add_tick_callback(widget, queue_queue_allocate, NULL, NULL);
|
||||
|
||||
/* the problematic flags get set all the way up the hierarchy */
|
||||
GtkWidget *parent = gtk_widget_get_parent(widget);
|
||||
if (parent) {
|
||||
atl_ensure_widget_snapshotability(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void atl_safe_gtk_label_set_text(GtkLabel* label, const char* str)
|
||||
{
|
||||
if(!snapshot_in_progress) {
|
||||
gtk_label_set_text(label, str);
|
||||
} else {
|
||||
/* strdup since the string may not exist by the time the callback runs */
|
||||
gtk_widget_add_tick_callback(GTK_WIDGET(label), queue_set_text, (gpointer)strdup(str), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void atl_safe_gtk_widget_set_visible(GtkWidget *widget, gboolean visible)
|
||||
{
|
||||
gtk_widget_set_visible(widget, visible);
|
||||
GtkWidget *parent = gtk_widget_get_parent(widget);
|
||||
if (parent) {
|
||||
atl_ensure_widget_snapshotability(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void atl_safe_gtk_widget_queue_allocate(GtkWidget *widget)
|
||||
{
|
||||
if(!snapshot_in_progress) {
|
||||
gtk_widget_queue_allocate(widget);
|
||||
} else {
|
||||
gtk_widget_add_tick_callback(widget, queue_queue_allocate, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void atl_safe_gtk_widget_queue_resize(GtkWidget *widget)
|
||||
{
|
||||
if(!snapshot_in_progress) {
|
||||
gtk_widget_queue_resize(widget);
|
||||
} else {
|
||||
gtk_widget_add_tick_callback(widget, queue_queue_resize, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "defines.h"
|
||||
@@ -186,4 +188,10 @@ void *get_nio_buffer(JNIEnv *env, jobject buffer, jarray *array_ref, jbyte **arr
|
||||
void release_nio_buffer(JNIEnv *env, jarray array_ref, jbyte *array);
|
||||
int get_nio_buffer_size(JNIEnv *env, jobject buffer);
|
||||
|
||||
void atl_ensure_widget_snapshotability(GtkWidget *widget);
|
||||
void atl_safe_gtk_label_set_text(GtkLabel* label, const char* str);
|
||||
void atl_safe_gtk_widget_set_visible(GtkWidget *widget, gboolean visible);
|
||||
void atl_safe_gtk_widget_queue_allocate(GtkWidget *widget);
|
||||
void atl_safe_gtk_widget_queue_resize(GtkWidget *widget);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -446,6 +446,8 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setLayoutParams(JNIEnv *en
|
||||
android_layout_set_params(ATL_ANDROID_LAYOUT(layout_manager), width, height);
|
||||
|
||||
wrapper_widget_set_layout_params(WRAPPER_WIDGET(widget), width, height);
|
||||
|
||||
atl_ensure_widget_snapshotability(widget);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -473,7 +475,7 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setPadding(JNIEnv *env, jo
|
||||
JNIEXPORT void JNICALL Java_android_view_View_native_1setVisibility(JNIEnv *env, jobject this, jlong widget_ptr, jint visibility, jfloat alpha) {
|
||||
GtkWidget *widget = gtk_widget_get_parent(GTK_WIDGET(_PTR(widget_ptr)));
|
||||
|
||||
gtk_widget_set_visible(widget, visibility != android_view_View_GONE);
|
||||
atl_safe_gtk_widget_set_visible(widget, visibility != android_view_View_GONE);
|
||||
gtk_widget_set_opacity(widget, (visibility != android_view_View_INVISIBLE) * alpha);
|
||||
gtk_widget_set_sensitive(widget, visibility != android_view_View_INVISIBLE && alpha != 0.0f);
|
||||
}
|
||||
@@ -481,7 +483,7 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setVisibility(JNIEnv *env,
|
||||
/** JavaWidget:
|
||||
* Minimal gtk widget class which does nothing.
|
||||
* Drawing will be overwritten by WrapperWidget.
|
||||
* If it holds children, they will be layouted by AndroidLayout
|
||||
* If it holds children, they will be laid out by AndroidLayout
|
||||
*/
|
||||
struct _JavaWidget {GtkWidget parent_instance;};
|
||||
G_DECLARE_FINAL_TYPE(JavaWidget, java_widget, JAVA, WIDGET, GtkWidget)
|
||||
@@ -595,7 +597,7 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1layout(JNIEnv *env, jobjec
|
||||
wrapper->real_width = width;
|
||||
wrapper->real_height = height;
|
||||
if (!wrapper->needs_allocation)
|
||||
gtk_widget_queue_allocate(widget);
|
||||
atl_safe_gtk_widget_queue_allocate(widget);
|
||||
}
|
||||
if (wrapper->needs_allocation) {
|
||||
allocation.width = width;
|
||||
@@ -607,7 +609,7 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1layout(JNIEnv *env, jobjec
|
||||
JNIEXPORT void JNICALL Java_android_view_View_native_1requestLayout(JNIEnv *env, jobject this, jlong widget_ptr) {
|
||||
GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr));
|
||||
|
||||
gtk_widget_queue_resize(widget);
|
||||
atl_safe_gtk_widget_queue_resize(widget);
|
||||
}
|
||||
|
||||
/* we kinda need per-widget css */
|
||||
@@ -711,7 +713,7 @@ JNIEXPORT jboolean JNICALL Java_android_view_View_native_1getMatrix(JNIEnv *env,
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_view_View_native_1queueAllocate(JNIEnv *env, jobject this, jlong widget_ptr)
|
||||
{
|
||||
gtk_widget_queue_allocate(GTK_WIDGET(_PTR(widget_ptr)));
|
||||
atl_safe_gtk_widget_queue_allocate(GTK_WIDGET(_PTR(widget_ptr)));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_view_View_native_1drawBackground(JNIEnv *env, jobject this, jlong widget_ptr, jlong snapshot_ptr)
|
||||
|
||||
@@ -168,7 +168,7 @@ void wrapper_widget_allocate(GtkWidget *widget, int width, int height, int basel
|
||||
layout->real_width = width;
|
||||
layout->real_height = height;
|
||||
if (!layout->needs_allocation)
|
||||
gtk_widget_queue_allocate(wrapper->child);
|
||||
atl_safe_gtk_widget_queue_allocate(wrapper->child);
|
||||
}
|
||||
if (layout->needs_allocation)
|
||||
gtk_widget_size_allocate(wrapper->child, &allocation, baseline);
|
||||
@@ -181,8 +181,12 @@ void wrapper_widget_allocate(GtkWidget *widget, int width, int height, int basel
|
||||
gtk_widget_size_allocate(wrapper->background, &allocation, baseline);
|
||||
}
|
||||
|
||||
/* this is used to avoid queing layout changes in the middle of snapshotting */
|
||||
int snapshot_in_progress = 0;
|
||||
static void wrapper_widget_snapshot(GtkWidget *widget, GdkSnapshot *snapshot)
|
||||
{
|
||||
snapshot_in_progress++;
|
||||
|
||||
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));
|
||||
@@ -204,6 +208,8 @@ static void wrapper_widget_snapshot(GtkWidget *widget, GdkSnapshot *snapshot)
|
||||
if (wrapper->real_height > 0 && wrapper->real_width > 0) {
|
||||
gtk_snapshot_pop(snapshot);
|
||||
}
|
||||
|
||||
snapshot_in_progress--;
|
||||
}
|
||||
|
||||
static void wrapper_widget_class_init(WrapperWidgetClass *class)
|
||||
@@ -257,8 +263,9 @@ void wrapper_widget_queue_draw(WrapperWidget *wrapper)
|
||||
|
||||
if(wrapper->child)
|
||||
gtk_widget_queue_draw(wrapper->child);
|
||||
if (wrapper->computeScroll_method)
|
||||
gtk_widget_queue_allocate(GTK_WIDGET(wrapper));
|
||||
if (wrapper->computeScroll_method) {
|
||||
atl_safe_gtk_widget_queue_allocate(GTK_WIDGET(wrapper));
|
||||
}
|
||||
}
|
||||
|
||||
static bool on_click(GtkGestureClick *gesture, int n_press, double x, double y, jobject this)
|
||||
|
||||
@@ -36,7 +36,7 @@ JNIEXPORT void JNICALL Java_android_widget_Button_native_1setText(JNIEnv *env, j
|
||||
GtkButton *button = GTK_BUTTON(_PTR(widget_ptr));
|
||||
|
||||
const char *nativeText = ((*env)->GetStringUTFChars(env, text, NULL));
|
||||
gtk_label_set_text(box_get_label(env, gtk_button_get_child(button)), nativeText);
|
||||
atl_safe_gtk_label_set_text(box_get_label(env, gtk_button_get_child(button)), nativeText);
|
||||
((*env)->ReleaseStringUTFChars(env, text, nativeText));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_TextView_native_1constructor(JNIEnv
|
||||
JNIEXPORT void JNICALL Java_android_widget_TextView_native_1setText(JNIEnv *env, jobject this, jobject charseq)
|
||||
{
|
||||
const char *text = charseq ? (*env)->GetStringUTFChars(env, charseq, NULL) : NULL;
|
||||
gtk_label_set_text(box_get_label(env, _PTR(_GET_LONG_FIELD(this, "widget"))), text ?: "");
|
||||
atl_safe_gtk_label_set_text(box_get_label(env, _PTR(_GET_LONG_FIELD(this, "widget"))), text ?: "");
|
||||
if(text)
|
||||
(*env)->ReleaseStringUTFChars(env, charseq, text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user