Bug 294300 - [GTK] Incorrect behaviour of tree rendering on GTK after removing paint listener
Summary: [GTK] Incorrect behaviour of tree rendering on GTK after removing paint listener
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.8   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 4.12 M1   Edit
Assignee: Eric Williams CLA
QA Contact: Praveen CLA
URL:
Whiteboard: RHT
Keywords: triaged
Depends on:
Blocks:
 
Reported: 2009-11-05 06:01 EST by Yuri Strot CLA
Modified: 2024-03-13 16:15 EDT (History)
7 users (show)

See Also:


Attachments
Screenshot (8.83 KB, image/png)
2010-01-06 11:57 EST, Kevin Barnes CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Yuri Strot CLA 2009-11-05 06:01:07 EST
If I'd like to add custom paint listener to my tree and than remove it, sometimes all text disappear. Actually, the problem is text foreground == white after removing paint listener.

Looks like the problem with Tree.drawForeground field: while custom rendering appear, Tree could cache this variable and then use invalid value when there are no custom painters (drawForeground value doesn't clear).

Following example reproduce my problem. You need to select the last one item in the tree, check "Enable custom painter" and then uncheck it -- all text will be white:

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class TreePainter implements Listener {

	private Tree tree;
	private TextLayout textLayout;

	TreePainter(Tree tree) {
		this.tree = tree;
	}

	public void setListeners(boolean add) {
		if (add) {
			tree.addListener(SWT.EraseItem, this);
			tree.addListener(SWT.PaintItem, this);
		} else {
			tree.removeListener(SWT.EraseItem, this);
			tree.removeListener(SWT.PaintItem, this);
		}
		redraw();
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.type) {
		case SWT.PaintItem:
			paint(event);
			break;
		case SWT.EraseItem:
			erase(event);
			break;
		}
	}

	private void erase(Event event) {
		event.detail &= ~(SWT.BACKGROUND | SWT.FOREGROUND | SWT.SELECTED | SWT.HOT);
	}

	private void paint(Event event) {
		TreeItem item = (TreeItem) event.item;
		GC gc = event.gc;
		// remember colors to restore the GC later
		Color oldForeground = gc.getForeground();
		Color oldBackground = gc.getBackground();

		int index = event.index;

		Color foreground = item.getForeground(index);
		if (foreground != null) {
			gc.setForeground(foreground);
		}

		Color background = item.getBackground(index);
		if (background != null) {
			gc.setBackground(background);
		}

		if ((event.detail & SWT.SELECTED) != 0) {
			gc.fillRectangle(item.getBounds(index));
		}

		Image image = item.getImage(index);
		if (image != null) {
			Rectangle imageBounds = item.getImageBounds(index);
			if (imageBounds != null) {
				Rectangle bounds = image.getBounds();

				// center the image in the given space
				int x = imageBounds.x
						+ Math.max(0, (imageBounds.width - bounds.width) / 2);
				int y = imageBounds.y
						+ Math.max(0, (imageBounds.height - bounds.height) / 2);
				gc.drawImage(image, x, y);
			}
		}

		Rectangle textBounds = item.getTextBounds(index);
		if (textBounds != null) {
			TextLayout layout = getTextLayout();
			layout.setText(item.getText(index));
			layout.setFont(item.getFont(index));

			Rectangle layoutBounds = layout.getBounds();

			int x = textBounds.x;
			int avg = (textBounds.height - layoutBounds.height) / 2;
			int y = textBounds.y + Math.max(0, avg);

			layout.draw(gc, x, y);
		}

		gc.setForeground(oldForeground);
		gc.setBackground(oldBackground);
	}

	public void redraw() {
		Rectangle rect = tree.getClientArea();
		tree.redraw(rect.x, rect.y, rect.width, rect.height, true);
	}

	private TextLayout getTextLayout() {
		if (textLayout == null) {
			int orientation = tree.getStyle()
					& (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
			textLayout = new TextLayout(tree.getDisplay());
			textLayout.setOrientation(orientation);
		} else {
			textLayout.setText("");
		}
		return textLayout;
	}

	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setBounds(10, 10, 800, 600);
		shell.setLayout(new GridLayout());
		final Tree tree = new Tree(shell, SWT.BORDER);
		tree.setLinesVisible(true);
		for (int i = 0; i < 5; i++) {
			TreeItem item = new TreeItem(tree, SWT.NONE);
			item.setText("item " + i);
			for (int j = 0; j < 5; j++) {
				TreeItem child = new TreeItem(item, SWT.NONE);
				child.setText("item " + i + "-" + j);
			}
		}
		tree.setLayoutData(new GridData(GridData.FILL_BOTH));

		final TreePainter painer = new TreePainter(tree);
		final Button button = new Button(shell, SWT.CHECK);
		button.setText("Enable custom painter");
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				painer.setListeners(button.getSelection());
			}
		});

		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}
}
Comment 1 Praveen CLA 2009-11-10 01:52:49 EST
which GTK version you are using ? I am not able to reproduce the problem on Ubuntu 9.04 (using GTK 2.16) - the text appears correctly after enabling/disabling the custom painter.
Comment 2 Kevin Barnes CLA 2010-01-06 11:57:41 EST
Created attachment 155426 [details]
Screenshot

I can reproduce this on RHEL 5 (gtk 2.10). Attaching a screenshot.
Comment 3 Eric Williams CLA 2016-12-20 14:06:20 EST
I can reproduce this on GTK3.22, Fedora 25.
Comment 4 Ian Pun CLA 2017-06-21 14:37:32 EDT
Bug has been triaged as it occurs from Erics tests, see https://wiki.eclipse.org/SWT/Devel/Triage for more information.
Comment 5 Eclipse Genie CLA 2019-02-19 15:18:04 EST
New Gerrit change created: https://git.eclipse.org/r/137235
Comment 7 Eric Williams CLA 2019-03-11 15:06:27 EDT
(In reply to Eclipse Genie from comment #6)
> Gerrit change https://git.eclipse.org/r/137235 was merged to [master].
> Commit:
> http://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/
> ?id=eef687cb0ba0e614571caf5af7dc4ad829aeed1b

In master now.
Comment 8 Eric Williams CLA 2019-04-09 10:04:57 EDT
Verified in I20190409-0600.
Comment 9 Serhiy Davydiuk CLA 2024-03-13 16:15:41 EDT
I'm observing a regression after this issue has been fixed. My env: RHEL7, GTK3, org.eclipse.swt_v3.123.
In my example I want to provide RED foreground color for Tree and GREEN foreground color for child items. The example code is the same, but with small changes at the main method.
Run the application, enable custom painter, disable custom painter. The foreground color for the all items become RED, not RED-GREEN as on start-up.

With such issue we cannot provide TreeItem's custom foreground color on CommonViewer (CNF).


    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setBounds(10, 10, 800, 600);
        shell.setLayout(new GridLayout());
        final Tree tree = new Tree(shell, SWT.BORDER);
        tree.setLinesVisible(true);
        
        // new code
        tree.setForeground(display.getSystemColor(SWT.COLOR_RED));
        
        for (int i = 0; i < 5; i++) {
            TreeItem item = new TreeItem(tree, SWT.NONE);
            item.setText("item " + i);
            for (int j = 0; j < 5; j++) {
                TreeItem child = new TreeItem(item, SWT.NONE);
                child.setText("item " + i + "-" + j);
                
                // new code
                child.setForeground(display.getSystemColor(SWT.COLOR_GREEN));
            }
        }
        tree.setLayoutData(new GridData(GridData.FILL_BOTH));

        final TreePainter painer = new TreePainter(tree);
        final Button button = new Button(shell, SWT.CHECK);
        button.setText("Enable custom painter");
        button.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                painer.setListeners(button.getSelection());
            }
        });

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }