Blob Blame History Raw
From 46e387352f469e26ecedddc10ffec2cd16f11d82 Mon Sep 17 00:00:00 2001
From: Eric Williams
Date: Fri, 19 Feb 2016 15:59:12 -0500
Subject: Bug 488226: [GTK3] Content Assist using Table/Tree has wrong
 selection background colors

On GTK3.14 and above, SWT.COLOR_LIST_SELECTION was returning gray
instead of the default (and correct) blue. This causes bugs in
Table/Tree, making highlighted TableItems/TreeItems difficult to read.
COLOR_LIST_BACKGROUND is unaffected.

System background colors like SWT.COLOR_* are fetched using
gtk_style_context_get_background_color() when SWT loads. This approach
is no longer valid as 1) it is deprecated on the GTK side and 2) it is
unreliable/not guaranteed to work. This patch only changes this behavior
for SWT.COLOR_LIST_SELECTION, but in the future other system colors will
need to be changed as well.

The fix in this case is to fetch the GTK theme colors of the currently
running system theme from GTK. This can be done using GtkCssProvider.
Once the currently running theme is fetched, the correct selection
background color can be extracted. If there is no selection background
color specified in the theme, then the fall-back is to load the color
via SWT's COLOR_LIST_SELECTION. This ensures that the correct color is
chosen according to GTK, should a theme specify one.

Tested on GTK3.8, 3.14, 3.16, and 3.18. AllNonBrowser JUnit tests pass
on GTK3 and GTK2.

Change-Id: Ic9aeb4d35efef961be1f724a2ad7dcc852c06453
Signed-off-by: Eric Williams <ericwill@redhat.com>
---
 .../Eclipse SWT PI/gtk/library/os.c                |  27 ++++++
 .../Eclipse SWT PI/gtk/library/os_custom.h         |   1 +
 .../Eclipse SWT PI/gtk/library/os_stats.c          |   1 +
 .../Eclipse SWT PI/gtk/library/os_stats.h          |   1 +
 .../gtk/org/eclipse/swt/internal/gtk/OS.java       |  14 +++
 .../gtk/org/eclipse/swt/widgets/Control.java       |  17 +---
 .../gtk/org/eclipse/swt/widgets/Display.java       | 108 ++++++++++++++++++++-
 7 files changed, 153 insertions(+), 16 deletions(-)

diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
index 64e39f3..02dbadd 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c	
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c	
@@ -10056,6 +10056,33 @@ JNIEXPORT void JNICALL OS_NATIVE(_1gtk_1container_1set_1border_1width)
 }
 #endif
 
+#ifndef NO__1gtk_1css_1provider_1get_1named
+JNIEXPORT jintLong JNICALL OS_NATIVE(_1gtk_1css_1provider_1get_1named)
+	(JNIEnv *env, jclass that, jbyteArray arg0, jbyteArray arg1)
+{
+	jbyte *lparg0=NULL;
+	jbyte *lparg1=NULL;
+	jintLong rc = 0;
+	OS_NATIVE_ENTER(env, that, _1gtk_1css_1provider_1get_1named_FUNC);
+	if (arg0) if ((lparg0 = (*env)->GetByteArrayElements(env, arg0, NULL)) == NULL) goto fail;
+	if (arg1) if ((lparg1 = (*env)->GetByteArrayElements(env, arg1, NULL)) == NULL) goto fail;
+/*
+	rc = (jintLong)gtk_css_provider_get_named((const gchar *)lparg0, (const gchar *)lparg1);
+*/
+	{
+		OS_LOAD_FUNCTION(fp, gtk_css_provider_get_named)
+		if (fp) {
+			rc = (jintLong)((jintLong (CALLING_CONVENTION*)(const gchar *, const gchar *))fp)((const gchar *)lparg0, (const gchar *)lparg1);
+		}
+	}
+fail:
+	if (arg1 && lparg1) (*env)->ReleaseByteArrayElements(env, arg1, lparg1, 0);
+	if (arg0 && lparg0) (*env)->ReleaseByteArrayElements(env, arg0, lparg0, 0);
+	OS_NATIVE_EXIT(env, that, _1gtk_1css_1provider_1get_1named_FUNC);
+	return rc;
+}
+#endif
+
 #ifndef NO__1gtk_1css_1provider_1load_1from_1data
 JNIEXPORT jboolean JNICALL OS_NATIVE(_1gtk_1css_1provider_1load_1from_1data)
 	(JNIEnv *env, jclass that, jintLong arg0, jbyteArray arg1, jintLong arg2, jintLongArray arg3)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h
index 7a27475..8e5844b 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h	
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h	
@@ -530,6 +530,7 @@
 #define gtk_css_provider_load_from_data_LIB LIB_GTK
 #define gtk_css_provider_new_LIB LIB_GTK
 #define gtk_css_provider_to_string_LIB LIB_GTK
+#define gtk_css_provider_get_named_LIB LIB_GTK
 #define gtk_icon_set_render_icon_pixbuf_LIB LIB_GTK
 #define gtk_drag_set_icon_surface_LIB LIB_GTK
 #define gtk_accel_label_set_accel_LIB LIB_GTK
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
index 5d77479..670adf8 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c	
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c	
@@ -729,6 +729,7 @@ char * OS_nativeFunctionNames[] = {
 	"_1gtk_1container_1remove",
 	"_1gtk_1container_1resize_1children",
 	"_1gtk_1container_1set_1border_1width",
+	"_1gtk_1css_1provider_1get_1named",
 	"_1gtk_1css_1provider_1load_1from_1data",
 	"_1gtk_1css_1provider_1new",
 	"_1gtk_1css_1provider_1to_1string",
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
index a0a30b7..11fdfe4 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h	
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h	
@@ -739,6 +739,7 @@ typedef enum {
 	_1gtk_1container_1remove_FUNC,
 	_1gtk_1container_1resize_1children_FUNC,
 	_1gtk_1container_1set_1border_1width_FUNC,
+	_1gtk_1css_1provider_1get_1named_FUNC,
 	_1gtk_1css_1provider_1load_1from_1data_FUNC,
 	_1gtk_1css_1provider_1new_FUNC,
 	_1gtk_1css_1provider_1to_1string_FUNC,
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java
index d3e52c3..353d4b7 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java	
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java	
@@ -650,6 +650,7 @@ public class OS extends C {
 	public static final byte[] gtk_show_input_method_menu = ascii("gtk-show-input-method-menu");
 	public static final byte[] gtk_menu_bar_accel = ascii("gtk-menu-bar-accel");
 	public static final byte[] gtk_menu_images = ascii("gtk-menu-images");
+	public static final byte[] gtk_theme_name = ascii("gtk-theme-name");
 	public static final byte[] inner_border = ascii("inner-border");
 	public static final byte[] has_backward_stepper = ascii("has-backward-stepper");
 	public static final byte[] has_secondary_backward_stepper = ascii("has-secondary-backward-stepper");
@@ -9299,6 +9300,19 @@ public static final long /*int*/gtk_css_provider_to_string(long /*int*/ provider
 		lock.unlock();
 	}
 }
+/** @method flags=dynamic
+ *  @param name cast=(const gchar *)
+ *  @param variant cast=(const gchar *)
+ */
+public static final native long /*int*/ _gtk_css_provider_get_named (byte[] name, byte[] variant);
+public static final long /*int*/gtk_css_provider_get_named(byte[] name, byte[] variant) {
+	lock.lock();
+	try {
+		return _gtk_css_provider_get_named(name, variant);
+	} finally {
+		lock.unlock();
+	}
+}
 /**
  * @method flags=dynamic
  * @param screen cast=(GdkScreen *)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java
index d296192..741a4e2 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java	
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java	
@@ -4178,7 +4178,7 @@ GdkColor gtk_css_parse_background (long /*int*/ provider) {
 		shortOutput = cssOutput.substring (startIndex + 18);
 		// Double check to make sure with have a valid rgb/rgba property
 		if (shortOutput.contains ("rgba") || shortOutput.contains ("rgb")) {
-			rgba = gtk_css_property_to_rgba (shortOutput);
+			rgba = display.gtk_css_property_to_rgba (shortOutput);
 		} else {
 			return display.COLOR_WIDGET_BACKGROUND;
 		}
@@ -4187,7 +4187,7 @@ GdkColor gtk_css_parse_background (long /*int*/ provider) {
 		shortOutput = cssOutput.substring (startIndex + 13);
 		// Double check to make sure with have a valid rgb/rgba property
 		if (shortOutput.contains ("rgba") || shortOutput.contains ("rgb")) {
-			rgba = gtk_css_property_to_rgba (shortOutput);
+			rgba = display.gtk_css_property_to_rgba (shortOutput);
 		} else {
 			return display.COLOR_WIDGET_BACKGROUND;
 		}
@@ -4195,18 +4195,6 @@ GdkColor gtk_css_parse_background (long /*int*/ provider) {
 	return color;
 }
 
-GdkRGBA gtk_css_property_to_rgba(String property) {
-	/* Here we convert rgb(...) or rgba(...) properties
-	 * into GdkRGBA objects using gdk_rgba_parse(). Note
-	 * that we still need to remove the ";" character from the
-	 * input string.
-	 */
-	GdkRGBA rgba = new GdkRGBA ();
-	String [] propertyParsed = new String [1];
-	propertyParsed = property.split (";");
-	OS.gdk_rgba_parse (rgba, Converter.wcsToMbcs (null, propertyParsed[0], true));
-	return rgba;
-}
 
 void gtk_css_provider_load_from_css (long /*int*/ context, String css) {
 	/* Utility function. */
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
index 49c0e0d..7dee6d7 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java	
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java	
@@ -1693,6 +1693,99 @@ long /*int*/ gtk_cell_renderer_toggle_get_type () {
 	return toggle_renderer_type;
 }
 
+String gtk_css_default_theme_values (int swt) {
+	/*
+	 * This method fetches GTK theme values/properties. This is accomplished
+	 * by determining the name of the current system theme loaded, giving that
+	 * name to GTK, and then parsing values from the returned theme contents.
+	 *
+	 * The idea here is that SWT variables that have corresponding GTK theme
+	 * elements can be fetched easily by supplying the SWT variable as an
+	 * parameter to this method.
+	 */
+
+	// Find CSS theme name
+	byte [] buffer;
+	int length;
+	long /*int*/ settings = OS.gtk_settings_get_default ();
+	long /*int*/ [] ptr = new long /*int*/ [1];
+	long /*int*/ str;
+	OS.g_object_get (settings, OS.gtk_theme_name, ptr, 0);
+	if (ptr [0] == 0) {
+		return "";
+	}
+	length = OS.strlen (ptr [0]);
+	if (length == 0) {
+		return "";
+	}
+	buffer = new byte [length];
+	OS.memmove (buffer, ptr [0], length);
+	OS.g_free (ptr [0]);
+
+	// Fetch the actual theme in char/string format
+	long /*int*/ themeProvider = OS.gtk_css_provider_get_named(buffer, null);
+	str = OS.gtk_css_provider_to_string (themeProvider);
+	length = OS.strlen (str);
+	if (length == 0) {
+		return "";
+	}
+	buffer = new byte [length];
+	OS.memmove (buffer, str, length);
+	String cssOutput = new String (Converter.mbcsToWcs (null, buffer));
+
+	// Parse the theme values based on the corresponding SWT value
+	// i.e. theme_selected_bg_color in GTK is SWT.COLOR_LIST_SELECTION in SWT
+	String color;
+	switch (swt) {
+		case SWT.COLOR_LIST_SELECTION:
+			/*
+			 * These strings are the GTK named colors we are looking for.
+			 *
+			 * NOTE: we need to be careful of cases where one is being assigned
+			 * to the other. For example we do NOT want to parse:
+			 * @define-color theme_selected_bg_color selected_bg_color
+			 * Instead we want the actual value for selected_bg_color, i.e.
+			 * @define-color selected_bg_color rgb(255, 255, 255)
+			 *
+			 * We also want to filter out any color formats other than #xxyyzz,
+			 * rgb(xxx,yyy,zzz) and rgba(www,xxx,yyy,zzz) since gdk_rgba_parse()
+			 * can only handle strings in this format.
+			 */
+			int tSelected = cssOutput.indexOf ("@define-color theme_selected_bg_color");
+			int selected = cssOutput.indexOf ("@define-color selected_bg_color");
+			if (tSelected != -1) {
+				color = cssOutput.substring(tSelected + 38);
+				if (color.startsWith("#") || color.startsWith("rgb")) {
+					return color;
+				}
+			}
+			if (selected != -1) {
+				color = cssOutput.substring(selected + 32);
+				if (color.startsWith("#") || color.startsWith("rgb")) {
+					return color;
+				}
+			}
+			else {
+				return "";
+			}
+		default:
+			return "";
+	}
+}
+
+GdkRGBA gtk_css_property_to_rgba(String property) {
+	/* Here we convert rgb(...) or rgba(...) properties
+	 * into GdkRGBA objects using gdk_rgba_parse(). Note
+	 * that we still need to remove the ";" character from the
+	 * input string.
+	 */
+	GdkRGBA rgba = new GdkRGBA ();
+	String [] propertyParsed = new String [1];
+	propertyParsed = property.split (";");
+	OS.gdk_rgba_parse (rgba, Converter.wcsToMbcs (null, propertyParsed[0], true));
+	return rgba;
+}
+
 /**
  * Returns the default display. One is created (making the
  * thread that invokes this method its user-interface thread)
@@ -2409,7 +2502,20 @@ void initializeSystemColors () {
 		COLOR_LIST_BACKGROUND = toGdkColor (rgba);
 		OS.gtk_style_context_restore (context);
 		COLOR_LIST_SELECTION_TEXT = toGdkColor (styleContextGetColor (context, OS.GTK_STATE_FLAG_SELECTED, rgba));
-		OS.gtk_style_context_get_background_color (context, OS.GTK_STATE_FLAG_SELECTED, rgba);
+
+		// SWT.COLOR_LIST_SELECTION will be fetched using GTK CSS for GTK3.14+.
+		// TODO: convert other system colors to this method and re-factor.
+		if (OS.GTK_VERSION >= OS.VERSION(3, 14, 0)) {
+			String colorListSelection = gtk_css_default_theme_values(SWT.COLOR_LIST_SELECTION);
+			if (!colorListSelection.isEmpty()) {
+				rgba = gtk_css_property_to_rgba (colorListSelection);
+			} else {
+				OS.gtk_style_context_get_background_color (context, OS.GTK_STATE_FLAG_SELECTED, rgba);
+			}
+		} else {
+			OS.gtk_style_context_get_background_color (context, OS.GTK_STATE_FLAG_SELECTED, rgba);
+		}
+
 		COLOR_LIST_SELECTION = toGdkColor (rgba);
 		COLOR_LIST_SELECTION_TEXT_INACTIVE = toGdkColor (styleContextGetColor (context, OS.GTK_STATE_FLAG_ACTIVE, rgba));
 		OS.gtk_style_context_get_background_color (context, OS.GTK_STATE_FLAG_ACTIVE, rgba);
-- 
cgit v0.11.2-4-g4a35