diff --git a/meson.build b/meson.build index ac1f7508..7d325cc1 100644 --- a/meson.build +++ b/meson.build @@ -167,10 +167,14 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ '-D_LARGEFILE64_SOURCE', ]) +# atl.gresources.xml +resources = gnome.compile_resources('com.gitlab.android_translation_layer.android_translation_layer', files('src/main-executable/atl.gresource.xml')) + executable('android-translation-layer', [ 'src/main-executable/main.c', 'src/main-executable/r_debug.c', - 'src/main-executable/back_button.c' + 'src/main-executable/back_button.c', + resources ], install: true, dependencies: [ diff --git a/src/api-impl-jni/generated_headers/android_view_View.h b/src/api-impl-jni/generated_headers/android_view_View.h index 93ef8521..33080eb9 100644 --- a/src/api-impl-jni/generated_headers/android_view_View.h +++ b/src/api-impl-jni/generated_headers/android_view_View.h @@ -295,6 +295,38 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setBackgroundDrawable JNIEXPORT void JNICALL Java_android_view_View_native_1queueAllocate (JNIEnv *, jobject, jlong); +/* + * Class: android_view_View + * Method: native_addClass + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_view_View_native_1addClass + (JNIEnv *, jobject, jlong, jstring); + +/* + * Class: android_view_View + * Method: native_removeClass + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_view_View_native_1removeClass + (JNIEnv *, jobject, jlong, jstring); + +/* + * Class: android_view_View + * Method: native_addClasses + * Signature: (J[Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_view_View_native_1addClasses + (JNIEnv *, jobject, jlong, jobjectArray); + +/* + * Class: android_view_View + * Method: native_removeClasses + * Signature: (J[Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_view_View_native_1removeClasses + (JNIEnv *, jobject, jlong, jobjectArray); + /* * Class: android_view_View * Method: native_drawBackground diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index 96e4b816..11f018db 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -617,3 +617,52 @@ JNIEXPORT void JNICALL Java_android_view_View_nativeSetFullscreen(JNIEnv *env, j gtk_window_unfullscreen(window); } } + + +JNIEXPORT void JNICALL Java_android_view_View_native_1addClass(JNIEnv *env, jobject this, jlong widget_ptr, jstring class_name_jstr){ + GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr)); + + const char *class_name = (*env)->GetStringUTFChars(env, class_name_jstr, NULL); + gtk_widget_add_css_class(widget, class_name); + + (*env)->ReleaseStringUTFChars(env, class_name_jstr, class_name); +} + +JNIEXPORT void JNICALL Java_android_view_View_native_1removeClass(JNIEnv *env, jobject this, jlong widget_ptr, jstring class_name_jstr){ + GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr)); + + const char *class_name = (*env)->GetStringUTFChars(env, class_name_jstr, NULL); + gtk_widget_remove_css_class(widget, class_name); + + (*env)->ReleaseStringUTFChars(env, class_name_jstr, class_name); +} + +JNIEXPORT void JNICALL Java_android_view_View_native_1addClasses(JNIEnv *env, jobject this, jlong widget_ptr, jobjectArray class_names_jarray){ + GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr)); + + int length = (*env)->GetArrayLength(env, class_names_jarray); + + for(int i = 0; i < length; i++){ + jstring class_name_jstr = (jstring) ((*env)->GetObjectArrayElement(env, class_names_jarray, i)); + + const char *class_name = (*env)->GetStringUTFChars(env, class_name_jstr, NULL); + gtk_widget_add_css_class(widget, class_name); + + (*env)->ReleaseStringUTFChars(env, class_name_jstr, class_name); + } +} + +JNIEXPORT void JNICALL Java_android_view_View_native_1removeClasses(JNIEnv *env, jobject this, jlong widget_ptr, jobjectArray class_names_jarray){ + GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr)); + + int length = (*env)->GetArrayLength(env, class_names_jarray); + + for(int i = 0; i < length; i++){ + jstring class_name_jstr = (jstring) ((*env)->GetObjectArrayElement(env, class_names_jarray, i)); + + const char *class_name = (*env)->GetStringUTFChars(env, class_name_jstr, NULL); + gtk_widget_remove_css_class(widget, class_name); + + (*env)->ReleaseStringUTFChars(env, class_name_jstr, class_name); + } +} \ No newline at end of file diff --git a/src/api-impl-jni/widgets/android_widget_Button.c b/src/api-impl-jni/widgets/android_widget_Button.c index 80da0fce..f8dd71b4 100644 --- a/src/api-impl-jni/widgets/android_widget_Button.c +++ b/src/api-impl-jni/widgets/android_widget_Button.c @@ -16,6 +16,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_Button_native_1constructor(JNIEnv *e wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), label); wrapper_widget_consume_touch_events(WRAPPER_WIDGET(wrapper)); // Android button consumes touch events wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this); + return _INTPTR(label); } diff --git a/src/api-impl-jni/widgets/android_widget_ImageButton.c b/src/api-impl-jni/widgets/android_widget_ImageButton.c index d4dc4ea5..f246e501 100644 --- a/src/api-impl-jni/widgets/android_widget_ImageButton.c +++ b/src/api-impl-jni/widgets/android_widget_ImageButton.c @@ -51,4 +51,6 @@ JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setDrawable(JNIEn GtkPicture *picture = GTK_PICTURE(gtk_button_get_child(GTK_BUTTON(button))); GdkPaintable *paintable = _PTR(paintable_ptr); gtk_picture_set_paintable(picture, paintable); + + gtk_widget_add_css_class(GTK_WIDGET(button), "ATL-no-border"); } diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java index 6177cb85..7afa122a 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -946,6 +946,11 @@ public class View implements Drawable.Callback { if (a.hasValue(com.android.internal.R.styleable.View_tag)) { tag = a.getText(com.android.internal.R.styleable.View_tag); } + if(a.hasValue(com.android.internal.R.styleable.View_textAlignment)) { + int textAlignment = a.getInt(com.android.internal.R.styleable.View_textAlignment, 0); + setTextAlignment(textAlignment); + } + a.recycle(); } onCreateDrawableState(0); @@ -1063,6 +1068,12 @@ public class View implements Drawable.Callback { protected native void native_requestLayout(long widget); protected native void native_setBackgroundDrawable(long widget, long paintable); protected native void native_queueAllocate(long widget); + + protected native void native_addClass(long widget, String className); + protected native void native_removeClass(long widget, String className); + + protected native void native_addClasses(long widget, String[] classNames); + protected native void native_removeClasses(long widget, String[] classNames); protected native void native_drawBackground(long widget, long snapshot); protected native void native_drawContent(long widget, long snapshot); @@ -1855,7 +1866,24 @@ public class View implements Drawable.Callback { public boolean hasOnClickListeners() {return false;} - public void setTextAlignment(int textAlignment) {} + public void setTextAlignment(int textAlignment) { + String[] classesToRemove = {"ATL-text-align-left", "ATL-text-align-center", "ATL-text-align-right"}; + native_removeClasses(widget, classesToRemove); + + switch(textAlignment) { + case TEXT_ALIGNMENT_CENTER: + native_addClass(widget, "ATL-text-align-center"); + break; + case TEXT_ALIGNMENT_TEXT_START: + case TEXT_ALIGNMENT_VIEW_START: + native_addClass(widget, "ATL-text-align-left"); + break; + case TEXT_ALIGNMENT_TEXT_END: + case TEXT_ALIGNMENT_VIEW_END: + native_addClass(widget, "ATL-text-align-right"); + break; + } + } public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {} diff --git a/src/api-impl/android/widget/Button.java b/src/api-impl/android/widget/Button.java index 0b6369b0..4e7508b2 100644 --- a/src/api-impl/android/widget/Button.java +++ b/src/api-impl/android/widget/Button.java @@ -18,6 +18,10 @@ public class Button extends TextView { if (a.hasValue(com.android.internal.R.styleable.TextView_text)) { setText(a.getText(com.android.internal.R.styleable.TextView_text)); } + + if(getBackground() != null){ + native_addClass(widget, "ATL-no-border"); + } a.recycle(); } diff --git a/src/api-impl/android/widget/TextView.java b/src/api-impl/android/widget/TextView.java index 82cebf1b..40d809ee 100644 --- a/src/api-impl/android/widget/TextView.java +++ b/src/api-impl/android/widget/TextView.java @@ -61,7 +61,18 @@ public class TextView extends View { if (a.hasValue(com.android.internal.R.styleable.TextView_textSize)) { setTextSize(a.getDimensionPixelSize(com.android.internal.R.styleable.TextView_textSize, 10)); } + + if(a.hasValue(com.android.internal.R.styleable.TextView_textStyle)) { + int textStyle = a.getInt(com.android.internal.R.styleable.TextView_textStyle, 0); + setTypeface(getTypeface(), textStyle); + } + + if(a.hasValue(com.android.internal.R.styleable.TextView_textAllCaps)) { + boolean allCaps = a.getBoolean(com.android.internal.R.styleable.TextView_textAllCaps, false); + setAllCaps(allCaps); + } } catch(java.lang.Exception e) { System.out.println("exception while inflating TextView:"); e.printStackTrace(); } + a.recycle(); haveCustomMeasure = false; } @@ -103,7 +114,25 @@ public class TextView extends View { setTextColor(colors.getDefaultColor()); // TODO: do this properly } } - public void setTypeface(Typeface tf, int style) {} + public void setTypeface(Typeface tf, int style) { + String[] classesToRemove = {"ATL-font-bold", "ATL-font-italic"}; + native_removeClasses(widget, classesToRemove); + + switch(style) { + case Typeface.BOLD: + native_addClass(widget, "ATL-font-bold"); + break; + case Typeface.ITALIC: + native_addClass(widget, "ATL-font-italic"); + break; + case Typeface.BOLD_ITALIC: + native_addClass(widget, "ATL-font-bold"); + native_addClass(widget, "ATL-font-italic"); + break; + default: + break; + } + } public void setTypeface(Typeface tf) {} public void setLineSpacing(float add, float mult) {} public final void setLinksClickable(boolean whether) {} @@ -188,7 +217,14 @@ public class TextView extends View { drawableBottom = bottom; } - public void setAllCaps(boolean allCaps) {} + public void setAllCaps(boolean allCaps) { + String[] classesToRemove = {"ATL-text-uppercase"}; + native_removeClasses(widget, classesToRemove); + + if(allCaps){ + native_addClass(widget, "ATL-text-uppercase"); + } + } public void setSaveEnabled(boolean enabled) {} diff --git a/src/main-executable/atl.gresource.xml b/src/main-executable/atl.gresource.xml new file mode 100644 index 00000000..3cf46437 --- /dev/null +++ b/src/main-executable/atl.gresource.xml @@ -0,0 +1,6 @@ + + + + src/main-executable/css/default-stylesheet.css + + \ No newline at end of file diff --git a/src/main-executable/css/default-stylesheet.css b/src/main-executable/css/default-stylesheet.css new file mode 100644 index 00000000..e12d129e --- /dev/null +++ b/src/main-executable/css/default-stylesheet.css @@ -0,0 +1,31 @@ +/* Border Styling */ +.ATL-no-border { + border: unset; +} + +/* Font styling */ +.ATL-font-bold > * { + font-weight: bold; +} + +.ATL-font-italic > * { + font-style: italic; +} + +/* Text styling */ +.ATL-text-uppercase > * { + text-transform: uppercase; +} + +.ATL-text-align-left > * { + text-align: left; +} + +.ATL-text-align-center > * { + text-align: center; +} + +.ATL-text-align-right > * { + text-align: right; +} + diff --git a/src/main-executable/main.c b/src/main-executable/main.c index 1f1d2573..67d0f4d1 100644 --- a/src/main-executable/main.c +++ b/src/main-executable/main.c @@ -417,6 +417,11 @@ static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *h if (getenv("ATL_FORCE_FULLSCREEN")) gtk_window_fullscreen(GTK_WINDOW(window)); + // Load default css stylesheet + GtkCssProvider * cssProvider = gtk_css_provider_new(); + gtk_css_provider_load_from_resource(cssProvider, "/com/gitlab/android-translation-layer/android-translation-layer/default-stylesheet.css"); + gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + prepare_main_looper(env); // construct Application