Skip to content

Commit

Permalink
Avoid excessive repaint operations when figure is not modified
Browse files Browse the repository at this point in the history
When updating the border or the background/foreground color of a figure,
it may happen what we whole figure is repainted, even though it hasn't
been modified.
Additional checks have been added to check for equality in the setter
methods, to limit the amount of times repaint() and revalidate() is
called.
  • Loading branch information
ptziegler authored and azoitl committed Aug 25, 2023
1 parent 50882ce commit 196bee5
Show file tree
Hide file tree
Showing 5 changed files with 476 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
PrecisionPointTest.class,
PrecisionRectangleTest.class,
ThumbnailTest.class,
FigurePaintingTest.class,
FigureUtilitiesTest.class,
RectangleTest.class,
ColorConstantTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
/*******************************************************************************
* Copyright (c) 2011, 2023 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.draw2d.test;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.test.utils.TestFigure;
import org.eclipse.draw2d.test.utils.TestLogger;

import org.junit.Before;
import org.junit.Test;

@SuppressWarnings("nls")
public class FigurePaintingTest extends BaseTestCase {
private TestLogger expectedLogger;
private TestLogger actualLogger;
private Figure testFigure;

@Before
public void setUp() {
expectedLogger = new TestLogger();
actualLogger = new TestLogger();
testFigure = new TestFigure(actualLogger);
}

@Test
public void test_repaint() throws Exception {
testFigure.setBounds(new Rectangle(10, 11, 50, 78));
actualLogger.clear();
//
// check repaint from figure fully
testFigure.repaint();
expectedLogger.log("repaint(10, 11, 50, 78)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint from invisible figure
testFigure.setVisible(false);
actualLogger.clear();
//
testFigure.repaint();
actualLogger.assertEmpty();
}

@Test
public void test_add() throws Exception {
//
// check reset state during add child figure with empty bounds
testFigure.add(new Figure());
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check reset state during add(Figure) child figure with not empty bounds
Figure testChildFigure = new Figure();
testChildFigure.setBounds(new Rectangle(1, 2, 3, 4));
testFigure.add(testChildFigure);
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(1, 2, 3, 4)");
actualLogger.assertEquals(expectedLogger);
}

@Test
public void test_remove() throws Exception {
testFigure.setBounds(new Rectangle(10, 11, 50, 78));
actualLogger.clear();
//
Figure testChildFigure = new Figure();
testChildFigure.setBounds(new Rectangle(21, 17, 25, 24));
testFigure.add(testChildFigure);
testFigure.add(new Figure());
actualLogger.clear();
//
// check reset state during remove child figure
testFigure.remove(testChildFigure);
expectedLogger.log("repaint(21, 17, 25, 24)");
expectedLogger.log("invalidate()");
actualLogger.assertEquals(expectedLogger);
//
// check reset state during remove all children figures
testFigure.removeAll();
expectedLogger.log("repaint(0, 0, 0, 0)");
expectedLogger.log("invalidate()");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during remove if not childrens
testFigure.removeAll();
actualLogger.assertEmpty();
}

@Test
public void test_bounds() throws Exception {
// check reset state during setBounds()
testFigure.setBounds(new Rectangle(1, 2, 3, 4));
expectedLogger.log("erase()");
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(1, 2, 3, 4)");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setBounds() if bounds not change
testFigure.setBounds(new Rectangle(1, 2, 3, 4));
actualLogger.assertEmpty();
//
// check no reset state during setSize(int, int) if bounds not change
testFigure.setSize(3, 4);
actualLogger.assertEmpty();
//
// check reset state during setSize(int, int)
testFigure.setSize(1, 5);
expectedLogger.log("erase()");
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(1, 2, 1, 5)");
actualLogger.assertEquals(expectedLogger);
//
// check reset state during setSize(Dimension)
testFigure.setSize(new Dimension(11, 12));
expectedLogger.log("erase()");
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(1, 2, 11, 12)");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setSize(Dimension) if bounds not change
testFigure.setSize(new Dimension(11, 12));
actualLogger.assertEmpty();
//
// check no reset state during setLocation(Point) if bounds not change
testFigure.setLocation(new Point(1, 2));
actualLogger.assertEmpty();
//
// check reset state during setLocation(Point)
testFigure.setLocation(new Point(3, 7));
expectedLogger.log("erase()");
expectedLogger.log("repaint(3, 7, 11, 12)");
actualLogger.assertEquals(expectedLogger);
//
// check reset state during setLocation(Point)
testFigure.setLocation(new Point());
expectedLogger.log("erase()");
expectedLogger.log("repaint(0, 0, 11, 12)");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setLocation(Point) if bounds not change
testFigure.setLocation(new Point());
actualLogger.assertEmpty();
}

@Test
public void test_border() throws Exception {
// check repaint during setBorder()
LineBorder border = new LineBorder();
testFigure.setBorder(border);
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setBorder() if border not change
testFigure.setBorder(border);
actualLogger.assertEmpty();
//
// check repaint during setBorder()
testFigure.setBorder(new LineBorder(7));
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check repaint during setBorder()
testFigure.setBorder(null);
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setBorder() if border not change
testFigure.setBorder(null);
actualLogger.assertEmpty();
}

@Test
public void test_background() throws Exception {
// check repaint during setBackgroundColor()
testFigure.setBackgroundColor(ColorConstants.red);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setBackgroundColor() if color not change
testFigure.setBackgroundColor(ColorConstants.red);
actualLogger.assertEmpty();
//
// check repaint during setBackgroundColor()
testFigure.setBackgroundColor(ColorConstants.green);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check repaint during setBackgroundColor()
testFigure.setBackgroundColor(null);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setBackgroundColor() if color not change
testFigure.setBackgroundColor(null);
actualLogger.assertEmpty();
}

@Test
public void test_foreground() throws Exception {
// check repaint during setForegroundColor()
testFigure.setForegroundColor(ColorConstants.red);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setForegroundColor() if color not change
testFigure.setForegroundColor(ColorConstants.red);
actualLogger.assertEmpty();
//
// check repaint during setForegroundColor()
testFigure.setForegroundColor(ColorConstants.green);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check repaint during setForegroundColor()
testFigure.setForegroundColor(null);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setForegroundColor() if color not change
testFigure.setForegroundColor(null);
actualLogger.assertEmpty();
}

@Test
public void test_font() throws Exception {
// check reset state during setFont()
testFigure.setFont(new Font(null, "Courier New", 12, SWT.BOLD));
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check reset state during setFont()
testFigure.setFont(Display.getCurrent().getSystemFont());
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setFont() if font not change
testFigure.setFont(Display.getCurrent().getSystemFont());
actualLogger.assertEmpty();
//
// check reset state during setFont()
testFigure.setFont(null);
expectedLogger.log("invalidate()");
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setFont() if font not change
testFigure.setFont(null);
actualLogger.assertEmpty();
}

@Test
public void test_cursor() throws Exception {
// check invoke updateCursor() during setCursor()
testFigure.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_HELP));
expectedLogger.log("updateCursor()");
actualLogger.assertEquals(expectedLogger);
//
// check invoke updateCursor() during setCursor()
testFigure.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_CROSS));
expectedLogger.log("updateCursor()");
actualLogger.assertEquals(expectedLogger);
//
// check not invoke updateCursor() during setCursor() if cursor not change
testFigure.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_CROSS));
actualLogger.assertEmpty();
//
// check invoke updateCursor() during setCursor()
testFigure.setCursor(null);
expectedLogger.log("updateCursor()");
actualLogger.assertEquals(expectedLogger);
//
// check not invoke updateCursor() during setCursor() if cursor not change
testFigure.setCursor(null);
actualLogger.assertEmpty();
}

@Test
public void test_opaque() throws Exception {
// check repaint during setOpaque()
testFigure.setOpaque(true);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setOpaque() if opaque not change
testFigure.setOpaque(true);
actualLogger.assertEmpty();
//
// check repaint during setOpaque()
testFigure.setOpaque(false);
expectedLogger.log("repaint(0, 0, 0, 0)");
actualLogger.assertEquals(expectedLogger);
//
// check no repaint during setOpaque() if opaque not change
testFigure.setOpaque(false);
actualLogger.assertEmpty();
}

@Test
public void test_visible() throws Exception {
// check reset state during setVisible()
testFigure.setVisible(false);
expectedLogger.log("erase()");
expectedLogger.log("invalidate()");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setVisible() if visible not change
testFigure.setVisible(false);
actualLogger.assertEmpty();
//
// check reset state during setVisible()
testFigure.setVisible(true);
expectedLogger.log("repaint(0, 0, 0, 0)");
expectedLogger.log("invalidate()");
actualLogger.assertEquals(expectedLogger);
//
// check no reset state during setVisible() if visible not change
testFigure.setVisible(true);
actualLogger.assertEmpty();
}
}
Loading

0 comments on commit 196bee5

Please sign in to comment.