Scrolled composite with slow redrawing content looks ugly

I am implementing a Gantt component for SWT and it redraws a bit (e.g. 200 ms for the entire visible part of the chart).

Now when I scroll, I just redraw what is needed regarding the cropping rectangle. This makes the application really bad when I scroll quickly, because then the still visible part after scrolling seems to move the OS first, and when I finish drawing the rest (the part that became visible during scrolling), a new scroll step starts immediately by moving half of my chart to the right and letting me repaint the other half. It looks like my chart flickers in the middle while scrolling.

It doesn’t look very good. Is there any way around this? Is this question understandable?

EDIT : here is a small test program that shows exactly the described behavior. To run it, you only need a SWT in the classpath.

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 = rx - (rx % 10); int width = (r.width + rx - x) - (r.width + rx - x) % 10 + 10; int y = ry - (ry % 10); int height = (r.height + ry - y) - (r.height + ry - y) % 10 + 10; gc.setAlpha(128); for( int i = x; i < x+width; i+= 10 ) { gc.setBackground(colors[i/10]); gc.fillRectangle(i, ry, 10, r.height); } for( int j = y; j < y+height; j+= 10 ) { gc.setBackground(colors[j/10]); gc.fillRectangle(rx, j, r.width, 10); } } }); } } } 
+7
source share
2 answers

SWT painting is very fast and a lack of user interface can usually be tracked to slow drawing methods. Therefore, try optimizing the algorithm that draws your chart! One approach could be caching - draw the contents of the diagram into an image:

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

and scroll only the necessary parts of the image when scrolling:

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

As soon as the diagram changes - and only then - redraw the cache image using a sophisticated drawing method.

NTN

+7
source

I suggested Henrik use Image to buffer the picture and implement it in your SSCCE. Now I see much less flicker in my system.

  addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { GC gc = e.gc; Rectangle r = gc.getClipping(); int x = rx - (rx % 10); int width = (r.width + rx - x) - (r.width + rx - x) % 10 + 10; int y = ry - (ry % 10); int height = (r.height + ry - y) - (r.height + ry - y) % 10 + 10; Image image = new Image(gc.getDevice(),width,height); GC igc = new GC(image); // Without buffering, this code was necessary to prevent "ghost" // scrollbars on window resize, but with buffering it is no longer // required...it does affect the visual results however. //igc.setAlpha(255); //igc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_BLACK)); //igc.fillRectangle(image.getBounds()); igc.setAlpha(128); for( int i = x; i < x+width; i+= 10 ) { igc.setBackground(colors[i/10]); igc.fillRectangle(ix, 0, 10, height); } for( int j = y; j < y+height; j+= 10 ) { igc.setBackground(colors[j/10]); igc.fillRectangle(0, jy, width, 10); } gc.drawImage(image, x, y); igc.dispose(); image.dispose(); } }); 
0
source

All Articles