Bug 574938 - [GTK] Disabled button icon is not disabled anymore
Summary: [GTK] Disabled button icon is not disabled anymore
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.17   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 4.21 M2   Edit
Assignee: Simeon Andreev CLA
QA Contact:
URL:
Whiteboard:
Keywords: regression
Depends on:
Blocks:
 
Reported: 2021-07-21 04:09 EDT by Andrey Loskutov CLA
Modified: 2021-08-15 12:29 EDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrey Loskutov CLA 2021-07-21 04:09:46 EDT
The icon of the disabled Button is not disabled anymore.

This is a regression from bug 564097, gerrit https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/165440 , commit: http://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/?id=a05ee0e05540ae5f068f127d687828dc3cdf0341

Snippet to reproduce below.
Click on the second button - the icon of the first button doesn't change to grayed state, only the text.

package org.eclipse.swt.snippets;

import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class Snippet206 {

public static void main(String[] args) {
	Display display = new Display();
	Image image = display.getSystemImage(SWT.ICON_QUESTION);
	Shell shell = new Shell(display);
	shell.setText("Snippet 206");
	shell.setLayout (new GridLayout());
	Button button = new Button(shell, SWT.PUSH);
	button.setImage(image);
	button.setText("Will be disabled");

	Button button2 = new Button(shell, SWT.PUSH);
	button2.setImage(image);
	button2.setText("Click me!");
	SelectionListener listener = new SelectionListener() {
		@Override
		public void widgetSelected(SelectionEvent e) {
			button.setEnabled(!button.isEnabled());
		}
		@Override
		public void widgetDefaultSelected(SelectionEvent e) {
		}
	};
	button2.addSelectionListener(listener );
	shell.setSize(300, 300);
	shell.open();
	while (!shell.isDisposed ()) {
		if (!display.readAndDispatch ()) display.sleep ();
	}
	display.dispose ();
}
}
Comment 1 Andrey Loskutov CLA 2021-07-21 05:10:05 EDT
Looks like the change from gtk_image_set_from_gicon to gtk_image_set_from_surface causes missing state transition from enabled to disabled icon? I don't see other relevant code changes in patch https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/165440 .

@Paul, Sravan: could you please check, if we need to use some other feature / API from GTK3 to get the icon automatically disabled, or if we should now create "disposed" icon manually and switch between enabled / disabled image as soon as setEnabled() changes the Button state?
Comment 2 Simeon Andreev CLA 2021-07-22 07:18:36 EDT
(In reply to Andrey Loskutov from comment #1)
> Looks like the change from gtk_image_set_from_gicon to
> gtk_image_set_from_surface causes missing state transition from enabled to
> disabled icon?

I think this is correct, I can reproduce the same behaviour with just GTK3 snippets.

GTK+ example using a surface for a button image:

#include <gtk/gtk.h>

// gcc -g button_image.c  `pkg-config --cflags --libs gtk+-3.0`  -o ButtonImage && ./ButtonImage

static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

int
main (int argc, char **argv)
{
  gtk_init(&argc, &argv);

  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *button_box;
  GtkWidget *label;
  GtkWidget *image;
  cairo_surface_t *surface;
  cairo_t *cr;

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

  button = gtk_button_new ();
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);

  button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
  label = gtk_label_new ("button");
  image = gtk_image_new ();

  gtk_container_add (GTK_CONTAINER (window), button);
  gtk_container_add (GTK_CONTAINER (button), button_box);
  gtk_container_add (GTK_CONTAINER (button_box), image);
  gtk_container_add (GTK_CONTAINER (button_box), label);

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 32, 32);
  cr = cairo_create (surface);
  cairo_set_source_rgb(cr, 1, 0.0, 0.0);
  cairo_set_line_width(cr, 1);
  cairo_rectangle(cr, 0, 0, 32, 32);
  cairo_stroke_preserve(cr);
  cairo_fill(cr);
  cairo_destroy (cr);

  gtk_image_set_from_surface (GTK_IMAGE (image), surface);
  gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

  gtk_widget_show_all (window);

  gtk_main();

  return 0;
}

GTK+ example using a pixbuf:

#include <gtk/gtk.h>

// gcc -g button_image_gicon.c  `pkg-config --cflags --libs gtk+-3.0`  -o ButtonImageGIcon && ./ButtonImageGIcon

static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

int
main (int argc, char **argv)
{
  gtk_init(&argc, &argv);

  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *button_box;
  GtkWidget *label;
  GtkWidget *image;

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

  button = gtk_button_new ();
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);

  button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
  label = gtk_label_new ("button");
  image = gtk_image_new ();

  gtk_container_add (GTK_CONTAINER (window), button);
  gtk_container_add (GTK_CONTAINER (button), button_box);
  gtk_container_add (GTK_CONTAINER (button_box), image);
  gtk_container_add (GTK_CONTAINER (button_box), label);

  GdkPixbuf *pixbuf;
  GError *error = NULL;
  pixbuf = gdk_pixbuf_new_from_file ("/home/sandreev/tmp/testicon.png", &error);

  gtk_image_set_from_gicon (GTK_IMAGE (image), (GIcon *) pixbuf, GTK_ICON_SIZE_BUTTON);

  gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

  g_object_unref (pixbuf);

  gtk_widget_show_all (window);

  gtk_main();

  return 0;
}

("/home/sandreev/tmp/testicon.png" is a 32x32 pixel red (RGB 255,0,0) square, matching the surface created in the surface based snippet above)

From discussions in related SWT tickets, I understood that pixbuf use is deprecated and should be replaced with surface.

Sravan, what is your recommendation here?

1. Should we open a bug for GTK+ developers (it seems odd to me, considering how the snippets above/SWT code are structured, that the behaviour with a surface/gicon should be different)?
2. Is there something we can do on the client code side (I'm not aware of being able to provide disabled state icons for buttons)?
3. Should SWT use a different API here when passing the button image to GTK+? I believe a revert of the fix for bug 564097 is a no go here.
Comment 3 Simeon Andreev CLA 2021-07-23 07:20:39 EDT
Looking at GTK+ code, I can't determine why pixbuf images receive extra handling. Possibly pixbuf implementing the GIcon interface is enough to result in the extra handling (greyed out button image).

We did however find extra SWT code for ToolItem (as toolbars still have the expected behaviour for disabled items): org.eclipse.swt.widgets.ToolItem.setEnabled(boolean)

/**
 * Enables the receiver if the argument is <code>true</code>,
 * and disables it otherwise.
 * <p>
 * A disabled control is typically
 * not selectable from the user interface and draws with an
 * inactive or "grayed" look.
 * </p>
 *
 * @param enabled the new enabled state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setEnabled (boolean enabled) {
	checkWidget();
	long topHandle = topHandle();
	if (this.enabled == enabled) return;
	this.enabled = enabled;

	GTK.gtk_widget_set_sensitive(topHandle, enabled);
	if (!enabled) {
		if (disabledImage == null) {
			if (defaultDisableImage == null && image != null) {
				defaultDisableImage = new Image(getDisplay(), image, SWT.IMAGE_DISABLE);
			}
			_setImage(defaultDisableImage);
		} else {
			_setImage(disabledImage);
		}
	}
	if (enabled && image != null) _setImage(image);

}


The extra code seems to be coming from the fix for bug 568771, which is also a regression from 564097.

I.e. it looks like the fix for bug 568771 covered only toolbar button images, but not images.

IMO we should apply the same fix to Button.setEnabled(). Sravan, Paul, Andrey? The  change is probably simple, looking at: https://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/?id=5b6474909194a96accf81dc255ae51cd4c018c34
Comment 4 Andrey Loskutov CLA 2021-07-23 07:30:20 EDT
(In reply to Simeon Andreev from comment #3)
> I.e. it looks like the fix for bug 568771 covered only toolbar button
> images, but not images.
> 
> IMO we should apply the same fix to Button.setEnabled().

Sounds like there is no other way to restore previous functionality. Please push gerrit with the proposed patch, we can discuss further details on gerrit.
Comment 5 Eclipse Genie CLA 2021-07-23 08:10:30 EDT
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/183329
Comment 6 Eclipse Genie CLA 2021-07-26 07:39:31 EDT
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/183378
Comment 7 Simeon Andreev CLA 2021-07-26 08:58:04 EDT
MenuItem has the same problem, see bug 575028.

The remaining callers of ImageList (changed in bug 564097, introducing the bug) are TrayItem, TreeItem and TableItem.

Neither TrayItem nor Tray have methods to disable the widgets.

Tree/Table can be disabled, but still store a pixbuf for each cell (on top of the surface). The greying-out of TreeItem/TableItem images is still handled correctly.
Comment 9 Andrey Loskutov CLA 2021-07-28 03:31:36 EDT
Works as expected in I20210727-1800, except the image quality, which will be addressed by 575069.