Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrolled composite with slow to repaint content looks ugly

I am implementing a Gantt component for SWT and this takes a bit to repaint (like, 200 ms for the whole visible part of the diagram).

Now, when I scroll, I only repaint what is needed regarding the clipping rectangle. This makes the application look very bad when I scroll fast, because then the still visible part after scrolling seems to be moved by the OS first, and when I finished painting the remaining part (the part which has become visible during scrolling), immediatly a new scrolling step begins, moving half of my diagram to the right and lets me repaint the other half. This effectively looks like my diagram flickers in the middle during scrolling.

This doesn't look really nice. Is there a way to get around this? Is this question understandable?

EDIT: Here is a "small" test program that shows exactly the behaviour described. You only need SWT in the classpath to run it.

package de.ikoffice.gui;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SlowRepaintProblem {

    public Color[] colors = new Color[501];

    public SlowRepaintProblem() {
        Display display = Display.getDefault();
        for( int i=0; i<=500; i++ ) {
            int r = ( i * 10 ) % 256;
            int g = ( i * 20 ) % 256;
            int b = ( i * 30 ) % 256;
            colors[i] = new Color(display,r,g,b);
        }

        Shell shell = new Shell(display);
        shell.setText("SlowRepaintTest");
        ScrolledComposite comp = new ScrolledComposite(shell,
                SWT.H_SCROLL | SWT.V_SCROLL | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND);
        SlowRepaintingCanvas canvas = new SlowRepaintingCanvas(comp,SWT.NONE| SWT.NO_BACKGROUND);
        comp.setContent(canvas);
        canvas.setSize(5000,5000);

        // Layouting
        shell.setLayout(new GridLayout());        
        comp.setLayoutData(new GridData(GridData.FILL_BOTH));
        shell.setBounds(50, 50, 800, 600);

        // Showing the control
        shell.open();
        while (!shell.isDisposed()) {
            try {
                if (!shell.getDisplay().readAndDispatch()) {
                    shell.getDisplay().sleep();
                }
            } catch (Throwable e) {
                String message = e.getMessage();
                if( message == null || !e.getMessage().equals("Widget is diposed") ) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    public static void main(String[] args) {
        new SlowRepaintProblem(); // Evil constructor call to start main program flow.
    }

    class SlowRepaintingCanvas extends Canvas {

        public SlowRepaintingCanvas(Composite parent, int style) {
            super(parent, style);

            addPaintListener(new PaintListener() {
                @Override
                public void paintControl(PaintEvent e) {
                    GC gc = e.gc;
                    Rectangle r = gc.getClipping();
                    gc.setAlpha(255);
//                    gc.setBackground(ColorUtils.WHITE);
//                    gc.fillRectangle(r);

                    int x = r.x - (r.x % 10);
                    int width = (r.width + r.x - x) - (r.width + r.x - x) % 10 + 10;
                    int y = r.y - (r.y % 10);
                    int height = (r.height + r.y - y) - (r.height + r.y - y) % 10 + 10;

                    gc.setAlpha(128);
                    for( int i = x; i < x+width; i+= 10 ) {
                        gc.setBackground(colors[i/10]);
                        gc.fillRectangle(i, r.y, 10, r.height);  
                    }
                    for( int j = y; j < y+height; j+= 10 ) {
                        gc.setBackground(colors[j/10]);
                        gc.fillRectangle(r.x, j, r.width, 10);  
                    }
                }
            });
        }

    }

}
like image 548
Daniel Avatar asked Aug 09 '11 06:08

Daniel


1 Answers

SWT painting is very fast and lacking UI can be usually tracked down to slow paint methods. Hence, try to optimize the algorithm that draws your diagram! One approach could be caching - draw the diagram contents into an Image:

Image cache = new Image(Display.getCurrent(), width, height);
GC gc = new GC(cache);

and repaint only the necessary image parts when scrolling:

gc.drawImage(cache, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight);

Once the diagram changes - and only then - repaint the cache image using your complex paint method.

HTH

like image 110
Henrik Avatar answered Nov 18 '22 17:11

Henrik