/*******************************************************************************
* Copyright (c) 2015, 2017 QNX Software Systems and others.
* 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:
* Alena Laskavaia - initial API and implementation
* Ralf M Petter<ralf.petter@gmail.com> - Bug 510241
*******************************************************************************/
package org.eclipse.ui.tests.forms.widgets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.IExpansionListener;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.tests.forms.layout.ControlFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for expandable composite
*/
public class ExpandableCompositeTest {
private static final int SHORT_CONTROL_WIDTH = 58;
private int defaultFlags = ExpandableComposite.TWISTIE;
private static Display display;
private Shell shell;
private ExpandableCompositeForTest ec;
private Rectangle ecbounds;
private static String shortText = "Hedgehog";
private static String longText = "A hedgehog is any of the spiny mammals of the subfamily Erinaceinae, in the order Erinaceomorpha. " //$NON-NLS-1$
+ "There are seventeen species of hedgehog in five genera, found through parts of Europe, Asia, Africa and New Zealand. " //$NON-NLS-1$
;
private Font font;
// change this to true if you want to see test is slow motion
private boolean humanWatching = false;
private class ExpandableCompositeForTest extends ExpandableComposite {
private Control separator;
private Control description;
public ExpandableCompositeForTest(Composite parent, int style, int expansionStyle) {
super(parent, style, expansionStyle);
}
public void setSeparatorControl(Control separator) {
this.separator = separator;
}
@Override
public Control getSeparatorControl() {
return separator;
}
@Override
public Control getDescriptionControl() {
return description;
}
public void setDescriptionControl(Control description) {
this.description = description;
}
}
static {
try {
display = PlatformUI.getWorkbench().getDisplay();
} catch (Throwable e) {
// this is to run without eclipse
display = new Display();
}
}
private Point getTextExtend(String str) {
GC gc = new GC(display);
gc.setFont(font);
try {
return gc.stringExtent(str);
} finally {
gc.dispose();
}
}
@Before
public void setUp() throws Exception {
font = new Font(display, "Arial", 12, SWT.NORMAL);
shell = new Shell(display);
shell.setSize(600, 400);
GridLayoutFactory.fillDefaults().applyTo(shell);
shell.setFont(font);
shell.open();
}
@After
public void tearDown() throws Exception {
if (humanWatching)
dispatch(1000);
shell.dispose();
font.dispose();
}
private static void dispatch() {
while (display.readAndDispatch()) {
}
}
private static void dispatch(int msec) {
long cur = System.currentTimeMillis();
do {
dispatch();
long pass = System.currentTimeMillis() - cur;
if (pass < msec) {
// not doing display.sleep() because its automated tests,
// nothing will cause any display events
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
} else
break;
} while (true);
}
private Composite rectangleComposite(final Composite parent, final int x, final int y) {
return ControlFactory.create(parent, x, y);
}
private Composite createClient() {
Composite client = rectangleComposite(ec, 200, 100);
client.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
ec.setClient(client);
return client;
}
private Composite createTextClient(int w, int h) {
Composite textClient = rectangleComposite(ec, w, h);
textClient.setBackground(display.getSystemColor(SWT.COLOR_DARK_YELLOW));
ec.setTextClient(textClient);
return textClient;
}
private Composite createSeparator(int w) {
Composite sep = rectangleComposite(ec, w, 20);
sep.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
ec.setSeparatorControl(sep);
return sep;
}
private Composite createDescriptionControl(int w, int h) {
Composite sep = rectangleComposite(ec, w, 20);
sep.setBackground(display.getSystemColor(SWT.COLOR_DARK_GREEN));
ec.setDescriptionControl(sep);
return sep;
}
private void createExtendableComposite(String text, int flags) {
ec = new ExpandableCompositeForTest(shell, SWT.NONE, flags);
ec.setFont(font);
ec.setText(text);
ec.setBackground(display.getSystemColor(SWT.COLOR_GRAY));
ec.addExpansionListener(new IExpansionListener() {
@Override
public void expansionStateChanging(ExpansionEvent e) {
}
@Override
public void expansionStateChanged(ExpansionEvent e) {
ec.getParent().layout(true);
}
});
}
private Rectangle update() {
shell.layout(true, true);
if (humanWatching)
dispatch(300);
else
dispatch();
ecbounds = ec.getBounds();
return ecbounds;
}
@Test
public void testExpCompNoClient() {
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.TITLE_BAR);
Rectangle bounds1 = update();
ec.setExpanded(true);
Rectangle bounds2 = update();
assertTrue(bounds1.width == bounds2.width);
assertEquals(bounds1, bounds2);
createExtendableComposite(shortText, defaultFlags);
Rectangle bounds3 = update();
assertTrue(bounds3.width < bounds2.width);
}
@Test
public void testExpCompWithClient() {
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.TITLE_BAR);
createClient();
Rectangle bounds1 = update();
ec.setExpanded(true);
Rectangle bounds2 = update();
// our client is 100 tall + ver spacing
bounds2.height -= 100 + ec.clientVerticalSpacing;
assertEquals(bounds1, bounds2);
createExtendableComposite(shortText, defaultFlags);
createClient();
Rectangle bounds3 = update();
assertTrue(bounds3.width < bounds2.width);
}
@Test
public void testExpCompWithClientAndCompact() {
createExtendableComposite(shortText, defaultFlags);
// no client
Rectangle bounds1 = update();
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.COMPACT);
createClient(); // add client
Rectangle bounds2 = update();
assertTrue(bounds1.width == bounds2.width);
ec.setExpanded(true); // now it should be bigger
Rectangle bounds3 = update();
assertTrue(bounds3.width > bounds2.width);
}
@Test
public void testExpCompWithAndWithoutClientCompact() {
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.COMPACT);
// no client
Rectangle bounds1 = update();
createClient(); // client
Rectangle bounds2 = update();
assertEquals(bounds1, bounds2);
}
@Test
public void testExpCompWithTextClient() {
int fontSize = getTextExtend(shortText).y;
final int SMALL_BOX_H = 3;
final int BIG_BOX_H = fontSize * 2;
final int BIG_W = 80;
createExtendableComposite(shortText, defaultFlags);
Rectangle bounds1 = update();
// text client height less then text height
createExtendableComposite(shortText, defaultFlags);
createTextClient(BIG_W, SMALL_BOX_H);
Rectangle bounds2 = update();
assertTrue(bounds2.width >= bounds1.width + BIG_W);
assertTrue(bounds2.height == bounds1.height);
// text client height more then text height
createExtendableComposite(shortText, defaultFlags);
createTextClient(BIG_W, BIG_BOX_H);
Rectangle bounds3 = update();
assertTrue(bounds2.width == bounds3.width);
assertTrue(bounds3.height >= BIG_BOX_H);
// text client height more then text height, left alignment
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT);
createTextClient(BIG_W, BIG_BOX_H);
Rectangle bounds3l = update();
assertTrue(bounds2.width == bounds3l.width);
assertTrue(bounds3l.height >= BIG_BOX_H);
// no title
createExtendableComposite(shortText, defaultFlags | ExpandableComposite.NO_TITLE);
Rectangle bounds4 = update();
assertTrue(bounds4.width < bounds1.width);
assertTrue(bounds4.height < bounds1.height);
// pure only toggle header
createExtendableComposite(shortText, ExpandableComposite.TWISTIE | ExpandableComposite.NO_TITLE);
Rectangle boundsToggle = update();
assertTrue(boundsToggle.width > 0);
assertTrue(boundsToggle.height > 0);
createExtendableComposite(shortText, ExpandableComposite.TWISTIE | ExpandableComposite.NO_TITLE);
createTextClient(BIG_W, SMALL_BOX_H); // text client is small
Rectangle bounds5 = update();
assertTrue(bounds5.width >= boundsToggle.width + BIG_W);
assertTrue(bounds5.height == boundsToggle.height);
createExtendableComposite(shortText, ExpandableComposite.TWISTIE | ExpandableComposite.NO_TITLE
| ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT);
createTextClient(BIG_W, SMALL_BOX_H); // text client is small
Rectangle bounds5l = update();
assertTrue(bounds5l.width >= boundsToggle.width + BIG_W);
assertTrue(bounds5l.height == boundsToggle.height);
createExtendableComposite(shortText, ExpandableComposite.TWISTIE | ExpandableComposite.NO_TITLE);
createTextClient(BIG_W, BIG_BOX_H); // text client bigger then font size
// and toggle
Rectangle bounds6 = update();
assertEquals(BIG_BOX_H, bounds6.height);
ec.setExpanded(true);
Rectangle bounds7 = update();
assertEquals(bounds6, bounds7);
// no toggle
createExtendableComposite(shortText, ExpandableComposite.NO_TITLE);
createTextClient(BIG_W, BIG_BOX_H);
Rectangle bounds8 = update();
assertEquals(BIG_BOX_H, bounds8.height);
assertEquals(BIG_W, bounds8.width);
}
@Test
public void testExpCompWithTextSeparator() {
createExtendableComposite(shortText, ExpandableComposite.TWISTIE);
createSeparator(10);
checkSeparator();
ec.setExpanded(true);
checkSeparator();
// with client
createExtendableComposite(shortText, ExpandableComposite.TWISTIE);
createSeparator(10);
createClient();
checkSeparator();
ec.setExpanded(true);
checkSeparator();
// with client and description
createExtendableComposite(shortText, ExpandableComposite.TWISTIE);
createSeparator(10);
createDescriptionControl(50, 20);
createClient();
checkSeparator();
ec.setExpanded(true);
update();
Rectangle bounds = ec.getBounds();
Rectangle sepBounds = ec.getSeparatorControl().getBounds();
assertTrue(sepBounds.width == bounds.width);
Rectangle cb = ec.getClient().getBounds();
Rectangle db = ec.getDescriptionControl().getBounds();
assertEquals(bounds.height - db.height - 3 - cb.height - ec.clientVerticalSpacing,
sepBounds.y + sepBounds.height);
}
private void checkSeparator() {
update();
Rectangle bounds = ec.getBounds();
Rectangle sepBounds = ec.getSeparatorControl().getBounds();
assertTrue(sepBounds.width == bounds.width);
if (ec.isExpanded() && ec.getClient() != null) {
Rectangle cb = ec.getClient().getBounds();
assertEquals(bounds.height - cb.height - ec.clientVerticalSpacing, sepBounds.y + sepBounds.height);
} else
assertEquals(bounds.height, sepBounds.y + sepBounds.height);
}
private void width500() {
GridData layoutData = new GridData();
layoutData.widthHint = 500;
ec.setLayoutData(layoutData);
}
@Test
public void testLabelLong() {
createExtendableComposite(longText, 0);
width500();
Rectangle bounds = update();
assertEquals(500, bounds.width);
assertTextLines(4, bounds);
}
@Test
public void testLinkLong() {
createExtendableComposite(longText, ExpandableComposite.FOCUS_TITLE);
width500();
Rectangle bounds = update();
assertAround("Width", 500, bounds.width, 8);
assertTextLines(4, bounds);
}
private void assertTextLines(int lines, Rectangle bounds) {
Point textExtend = getTextExtend(shortText);
// it will be around "lines" lines of text
assertAround("Expected " + lines + " lines of text", (textExtend.y * lines), bounds.height, textExtend.y * 2);
}
private Label createLabel(Composite comp, String text) {
Label l = new Label(comp, SWT.WRAP);
l.setText(text);
l.setFont(font);
return l;
}
@Test
public void testLabelLongAndTextClientLabel() {
createExtendableComposite(longText, 0);
width500();
Label client = createLabel(ec, longText);
ec.setTextClient(client);
Rectangle bounds = update();
assertEquals(500, bounds.width);
assertAround("Text Client Width", 500 / 2, client.getBounds().width, 3);
assertTextLines(7, bounds);
}
private Composite createComposite(Composite parent) {
Composite comp = new Composite(parent, SWT.NONE);
comp.setBackground(comp.getDisplay().getSystemColor(SWT.COLOR_MAGENTA));
return comp;
}
private Composite createFillComp(Composite parent) {
Composite comp = createComposite(parent);
GridLayoutFactory.fillDefaults().applyTo(comp);
Label l = createLabel(comp, longText);
GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(l);
return comp;
}
private Composite createFixedComp(Composite parent) {
Composite comp = createComposite(parent);
GridLayoutFactory.fillDefaults().applyTo(comp);
Control control = ControlFactory.create(comp, SHORT_CONTROL_WIDTH, 15);
GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(control);
return comp;
}
@Test
public void testLabelLongAndTextClientComp() {
createExtendableComposite(longText, 0);
width500();
Control client = createFillComp(ec);
ec.setTextClient(client);
Rectangle bounds = update();
assertEquals("Width", 500, bounds.width);
assertAround("Text Client width", 500 / 2, client.getBounds().width, 3);
assertTextLines(7, bounds);
}
@Test
public void testLabelShortAndTextClientComp() {
createExtendableComposite(shortText, 0);
width500();
Control client = createFillComp(ec);
ec.setTextClient(client);
Rectangle bounds = update();
assertEquals("Width", 500, bounds.width);
int w = getTextExtend(shortText).x;
assertAround("Text Client width", 500 - w, client.getBounds().width, 8);
assertTextLines(4, bounds);
}
private void assertAround(String prefix, int len1, int len2, int delta) {
assertTrue(prefix + ": expected around " + len1 + " pixes +/- " + delta + " but was " + len2,
len1 - delta <= len2 && len2 <= len1 + delta);
}
@Test
public void testLabelLongAndTextClientCompFixed() {
createExtendableComposite(longText, 0);
width500();
Control client = createFixedComp(ec);
ec.setTextClient(client);
Rectangle bounds = update();
assertEquals(SHORT_CONTROL_WIDTH, client.getBounds().width);
assertTextLines(4, bounds);
assertAround("Width", 500, bounds.width, 8);
}
@Test
public void testLabelLongAndTextClientCompFixedL() {
createExtendableComposite(longText, ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT);
width500();
Control client = createFixedComp(ec);
ec.setTextClient(client);
Rectangle bounds = update();
// not sure +8
assertAround("Text Client width", SHORT_CONTROL_WIDTH, client.getBounds().width, 8);
assertTextLines(4, bounds);
assertAround("Width", 500, bounds.width, 2);
}
@Test
public void testTwistieIsVerticallyCentered() {
createExtendableComposite(shortText,
ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT | ExpandableComposite.TWISTIE);
width500();
update();
Control[] children = ec.getChildren();
int textCenter = Geometry.centerPoint(children[1].getBounds()).y;
int twistieCenter = Geometry.centerPoint(children[0].getBounds()).y;
assertAround("Twisty position", textCenter, twistieCenter, 1);
}
}