diff options
| author | Stefan Xenos | 2016-03-26 17:14:49 +0000 |
|---|---|---|
| committer | Stefan Xenos | 2017-05-04 20:02:40 +0000 |
| commit | 1e645458b2ef61bd7d83b1f5c7fb28dcde7ace58 (patch) | |
| tree | 9f48338f95f62689b1ee452f747db1522e163121 | |
| parent | e09eb0f702ecafa189017550bb8f1ca0edddd125 (diff) | |
| download | eclipse.platform.ui-1e645458b2ef61bd7d83b1f5c7fb28dcde7ace58.tar.gz eclipse.platform.ui-1e645458b2ef61bd7d83b1f5c7fb28dcde7ace58.tar.xz eclipse.platform.ui-1e645458b2ef61bd7d83b1f5c7fb28dcde7ace58.zip | |
Bug 196692 - Forms cannot handle most kinds of wrapping controlsI20170504-2000
Note that if this change is reverted or cherry-picked, you must also
include the previous change elaskavaia.cdt@gmail.com as well.
Change-Id: I781fab28ce427874f9d41662a98a1ebfa3840b18
22 files changed, 1200 insertions, 423 deletions
diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.java index 6c7bd804be1..fa3642bf43d 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.java @@ -18,6 +18,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; import org.eclipse.ui.internal.forms.widgets.ColumnLayoutUtils; +import org.eclipse.ui.internal.forms.widgets.FormUtil; /** * This layout manager arranges children of the composite parent in vertical * columns. All the columns are identical size and children are stretched @@ -71,52 +72,101 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { */ public int rightMargin = 5; + private LayoutCache cache = new LayoutCache(); + + private final static int MIN_SIZE = -2; + /** * Creates a new instance of the column layout. */ public ColumnLayout() { } + private void updateCache(Composite composite, boolean flushCache) { + Control[] children = composite.getChildren(); + if (flushCache) { + cache.flush(); + } + cache.setControls(children); + } + @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { - if (wHint == 0) - return computeSize(composite, wHint, hHint, minNumColumns); - else if (wHint == SWT.DEFAULT) - return computeSize(composite, wHint, hHint, maxNumColumns); - else - return computeSize(composite, wHint, hHint, -1); + updateCache(composite, flushCache); + return computeSize(composite, wHint, hHint); + } + + /** + * Given a desired number of columns, this returns a clamped result that falls + * within the range specified by the minimum and maximum number of columns. + */ + private int clampNumColumns(Composite parent, int desiredNumColumns) { + int ncolumns = desiredNumColumns; + ncolumns = Math.min(ncolumns, parent.getChildren().length); + ncolumns = Math.min(ncolumns, maxNumColumns); + ncolumns = Math.max(ncolumns, minNumColumns); + ncolumns = Math.max(ncolumns, 1); + return ncolumns; } - private Point computeSize(Composite parent, int wHint, int hHint, int ncolumns) { + private int computeOptimalNumColumnsForWidth(Composite parent, int width) { + if (minNumColumns >= maxNumColumns || parent.getChildren().length <= minNumColumns) { + return clampNumColumns(parent, minNumColumns); + } + + Control[] children = parent.getChildren(); + int minColWidth = 0; + + for (int i = 0; i < children.length; i++) { + // To maximize the number of columns: + int nextWidth = computeMinimumWidth(i); + + // To minimize the number of columns: + // int nextWidth = computeControlSize(i, SWT.DEFAULT).x; + + minColWidth = Math.max(minColWidth, nextWidth); + } + + return clampNumColumns(parent, + (width - leftMargin - rightMargin + horizontalSpacing) / (minColWidth + horizontalSpacing)); + } + + private int computeColumnWidthForNumColumns(int layoutWidth, int numColumns) { + return ((layoutWidth - leftMargin - rightMargin) - (numColumns - 1) * horizontalSpacing) / numColumns; + } + + private Point computeSize(Composite parent, int wHint, int hHint) { Control[] children = parent.getChildren(); - int cwidth = 0; int cheight = 0; Point[] sizes = new Point[children.length]; - int cwHint = SWT.DEFAULT; - if (ncolumns != -1) { - cwHint = wHint - leftMargin - rightMargin - (ncolumns - 1) * horizontalSpacing; - if (cwHint <= 0) - cwHint = 0; - else - cwHint /= ncolumns; + int columnWidth = 0; + int nColumns; + if (wHint == SWT.DEFAULT) { + nColumns = clampNumColumns(parent, maxNumColumns); + + for (int i = 0; i < children.length; i++) { + columnWidth = Math.max(columnWidth, computeControlSize(i, SWT.DEFAULT).x); + } + } else if (wHint == MIN_SIZE) { + nColumns = clampNumColumns(parent, 0); + + for (int i = 0; i < children.length; i++) { + columnWidth = Math.max(columnWidth, computeMinimumWidth(i)); + } + } else { + nColumns = computeOptimalNumColumnsForWidth(parent, wHint); + columnWidth = computeColumnWidthForNumColumns(wHint, nColumns); } for (int i = 0; i < children.length; i++) { - sizes[i] = computeControlSize(children[i], cwHint); - cwidth = Math.max(cwidth, sizes[i].x); + sizes[i] = computeControlSize(i, columnWidth); cheight += sizes[i].y; } - if (ncolumns == -1) { - // must compute - ncolumns = (wHint - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing); - ncolumns = Math.min(ncolumns, children.length); - ncolumns = Math.max(ncolumns, minNumColumns); - ncolumns = Math.min(ncolumns, maxNumColumns); - } - int perColHeight = ColumnLayoutUtils.computeColumnHeight(ncolumns, sizes, cheight, verticalSpacing); + + int perColHeight = ColumnLayoutUtils.computeColumnHeight(nColumns, sizes, cheight, verticalSpacing); int colHeight = 0; - int[] heights = new int[ncolumns]; + int[] heights = new int[nColumns]; int ncol = 0; boolean fillIn = false; @@ -126,7 +176,7 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { if (i>0 && colHeight + childHeight > perColHeight) { heights[ncol] = colHeight; ncol++; - if (ncol == ncolumns || fillIn) { + if (ncol == nColumns || fillIn) { // overflow - start filling in fillIn = true; ncol = findShortestColumn(heights); @@ -140,21 +190,33 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { heights[ncol] = Math.max(heights[ncol],colHeight); Point size = new Point(0, 0); - for (int i = 0; i < ncolumns; i++) { + for (int i = 0; i < nColumns; i++) { size.y = Math.max(size.y, heights[i]); } - size.x = cwidth * ncolumns + (ncolumns - 1) * horizontalSpacing; + size.x = columnWidth * nColumns + (nColumns - 1) * horizontalSpacing; size.x += leftMargin + rightMargin; - //System.out.println("ColumnLayout: whint="+wHint+", size.x="+size.x); size.y += topMargin + bottomMargin; + if (hHint != SWT.DEFAULT) { + size.y = hHint; + } return size; } - private Point computeControlSize(Control c, int wHint) { + private int computeMinimumWidth(int i) { + SizeCache sc = cache.getCache(i); + return sc.computeMinimumWidth(); + } + + private Point computeControlSize(int controlIndex, int wHint) { + SizeCache sizeCache = cache.getCache(controlIndex); + Control c = sizeCache.getControl(); ColumnLayoutData cd = (ColumnLayoutData) c.getLayoutData(); - int widthHint = cd != null ? cd.widthHint : wHint; - int heightHint = cd != null ? cd.heightHint : SWT.DEFAULT; - return c.computeSize(widthHint, heightHint); + + if (cd != null) { + return FormUtil.computeControlSize(sizeCache, wHint, cd.widthHint, cd.heightHint, + cd.horizontalAlignment == ColumnLayoutData.FILL); + } + return FormUtil.computeControlSize(sizeCache, wHint, SWT.DEFAULT, SWT.DEFAULT, true); } private int findShortestColumn(int[] heights) { @@ -171,32 +233,23 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { @Override protected void layout(Composite parent, boolean flushCache) { + updateCache(parent, flushCache); Control[] children = parent.getChildren(); Rectangle carea = parent.getClientArea(); - int cwidth = 0; + int nColumns = computeOptimalNumColumnsForWidth(parent, carea.width); + int columnWidth = computeColumnWidthForNumColumns(carea.width, nColumns); + int cheight = 0; Point[] sizes = new Point[children.length]; for (int i = 0; i < children.length; i++) { - sizes[i] = computeControlSize(children[i], SWT.DEFAULT); - cwidth = Math.max(cwidth, sizes[i].x); + sizes[i] = computeControlSize(i, columnWidth); cheight += sizes[i].y; } - int ncolumns = (carea.width - leftMargin - rightMargin + horizontalSpacing) / (cwidth + horizontalSpacing); - ncolumns = Math.min(ncolumns, children.length); - ncolumns = Math.max(ncolumns, minNumColumns); - ncolumns = Math.min(ncolumns, maxNumColumns); - int realWidth = (carea.width - leftMargin - rightMargin + horizontalSpacing) / ncolumns - horizontalSpacing; -// int childrenPerColumn = children.length / ncolumns; -// if (children.length % ncolumns != 0) -// childrenPerColumn++; -// int colWidth = 0; - - int fillWidth = Math.max(cwidth, realWidth); - int perColHeight = ColumnLayoutUtils.computeColumnHeight(ncolumns, sizes, cheight, verticalSpacing); + int perColHeight = ColumnLayoutUtils.computeColumnHeight(nColumns, sizes, cheight, verticalSpacing); int colHeight = 0; - int[] heights = new int[ncolumns]; + int[] heights = new int[nColumns]; int ncol = 0; int x = leftMargin; boolean fillIn = false; @@ -206,21 +259,21 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { Point csize = sizes[i]; ColumnLayoutData cd = (ColumnLayoutData) child.getLayoutData(); int align = cd != null ? cd.horizontalAlignment : ColumnLayoutData.FILL; - int childWidth = align == ColumnLayoutData.FILL ? fillWidth : csize.x; + int childWidth = csize.x; if (i>0 && colHeight + csize.y > perColHeight) { heights[ncol] = colHeight; - if (fillIn || ncol == ncolumns-1) { + if (fillIn || ncol == nColumns - 1) { // overflow - start filling in fillIn = true; ncol = findShortestColumn(heights); - x = leftMargin + ncol * (fillWidth + horizontalSpacing); + x = leftMargin + ncol * (columnWidth + horizontalSpacing); } else { ncol++; - x += fillWidth + horizontalSpacing; + x += columnWidth + horizontalSpacing; } colHeight = heights[ncol]; } @@ -234,10 +287,10 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { child.setBounds(x, topMargin+colHeight, childWidth, csize.y); break; case ColumnLayoutData.RIGHT : - child.setBounds(x + fillWidth - childWidth, topMargin+colHeight, childWidth, csize.y); + child.setBounds(x + columnWidth - childWidth, topMargin + colHeight, childWidth, csize.y); break; case ColumnLayoutData.CENTER : - child.setBounds(x + fillWidth / 2 - childWidth / 2, topMargin+colHeight, childWidth, csize.y); + child.setBounds(x + columnWidth / 2 - childWidth / 2, topMargin + colHeight, childWidth, csize.y); break; } @@ -252,6 +305,7 @@ public final class ColumnLayout extends Layout implements ILayoutExtension { @Override public int computeMinimumWidth(Composite parent, boolean changed) { - return computeSize(parent, 0, SWT.DEFAULT, changed).x; + updateCache(parent, changed); + return computeSize(parent, MIN_SIZE, SWT.DEFAULT).x; } } diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.java index f732fa5562a..1cd4f385b5d 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.java @@ -225,6 +225,8 @@ public class ExpandableComposite extends Canvas { private class ExpandableLayout extends Layout implements ILayoutExtension { + private static final int MIN_WIDTH = -2; + private SizeCache toggleCache = new SizeCache(); private SizeCache textClientCache = new SizeCache(); @@ -318,10 +320,8 @@ public class ExpandableComposite extends Canvas { boolean leftAlignment = textClient != null && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0; if (toggle != null) { - // if label control is absent we vcenter the toggle, because - // text client is usually a lot thicker - // before it was using leftAlignment flag for that which I think - // is not related to this at all + // if label control is absent we vertically center the toggle, + // because the text client is usually a lot thicker int ty = size.x == 0 ? (height - toggleSize.y) / 2 : 0; ty = Math.max(ty, 0); ty += marginHeight + tvmargin; @@ -418,7 +418,7 @@ public class ExpandableComposite extends Canvas { Point labelDefault = this.textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); int width = 0; - if (wHint == SWT.DEFAULT) { + if (wHint == SWT.DEFAULT || wHint == MIN_WIDTH) { width += toggleWidthPlusGap; width += labelDefault.x; width += gapBetweenTcAndLabel; @@ -463,17 +463,23 @@ public class ExpandableComposite extends Canvas { if ((expansionStyle & CLIENT_INDENT) != 0) clientIndent = toggleWidthPlusGap; - if (cwHint != SWT.DEFAULT) { + if (cwHint != SWT.DEFAULT && cwHint != MIN_WIDTH) { cwHint -= marginWidth + marginWidth + thmargin + thmargin; if ((expansionStyle & CLIENT_INDENT) != 0) if (tcsize.x > 0) cwHint -= toggleWidthPlusGap; } Point dsize = null; - Point csize = clientCache.computeSize(cwHint, SWT.DEFAULT); + Point csize; + if (cwHint == MIN_WIDTH) { + int minWidth = clientCache.computeMinimumWidth(); + csize = clientCache.computeSize(minWidth, SWT.DEFAULT); + } else { + csize = clientCache.computeSize(cwHint, SWT.DEFAULT); + } if (getDescriptionControl() != null) { int dwHint = cwHint; - if (dwHint == SWT.DEFAULT) { + if (dwHint == SWT.DEFAULT || dwHint == MIN_WIDTH) { dwHint = csize.x; if ((expansionStyle & CLIENT_INDENT) != 0) dwHint -= toggleWidthPlusGap; @@ -496,7 +502,7 @@ public class ExpandableComposite extends Canvas { int resultWidth = width + marginWidth + marginWidth + thmargin + thmargin; - if (wHint != SWT.DEFAULT) { + if (wHint != SWT.DEFAULT && wHint != MIN_WIDTH) { resultWidth = wHint; } @@ -512,7 +518,7 @@ public class ExpandableComposite extends Canvas { @Override public int computeMinimumWidth(Composite parent, boolean changed) { - return computeSize(parent, 0, SWT.DEFAULT, changed).x; + return computeSize(parent, MIN_WIDTH, SWT.DEFAULT, changed).x; } @Override diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.java index cb4c167ccc4..7caef61b7f1 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.java @@ -108,7 +108,8 @@ public class Form extends Composite { private class FormLayout extends Layout implements ILayoutExtension { @Override public int computeMinimumWidth(Composite composite, boolean flushCache) { - return computeSize(composite, 5, SWT.DEFAULT, flushCache).x; + initCaches(flushCache); + return Math.max(headCache.computeMinimumWidth(), bodyCache.computeMinimumWidth()); } @Override @@ -119,18 +120,12 @@ public class Form extends Composite { @Override public Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { - if (flushCache) { - bodyCache.flush(); - headCache.flush(); - } - bodyCache.setControl(body); - headCache.setControl(head); + initCaches(flushCache); int width = 0; int height = 0; - Point hsize = headCache.computeSize(FormUtil.getWidthHint(wHint, - head), SWT.DEFAULT); + Point hsize = headCache.computeSize(wHint, SWT.DEFAULT); width = Math.max(hsize.x, width); height = hsize.y; @@ -140,8 +135,7 @@ public class Form extends Composite { if (ignoreBody) bsize = new Point(0,0); else - bsize = bodyCache.computeSize(FormUtil.getWidthHint(wHint, - body), SWT.DEFAULT); + bsize = bodyCache.computeSize(wHint, SWT.DEFAULT); width = Math.max(bsize.x, width); height += bsize.y; return new Point(width, height); @@ -149,12 +143,7 @@ public class Form extends Composite { @Override protected void layout(Composite composite, boolean flushCache) { - if (flushCache) { - bodyCache.flush(); - headCache.flush(); - } - bodyCache.setControl(body); - headCache.setControl(head); + initCaches(flushCache); Rectangle carea = composite.getClientArea(); Point hsize = headCache.computeSize(carea.width, SWT.DEFAULT); @@ -162,6 +151,15 @@ public class Form extends Composite { bodyCache .setBounds(0, hsize.y, carea.width, carea.height - hsize.y); } + + private void initCaches(boolean flushCache) { + if (flushCache) { + bodyCache.flush(); + headCache.flush(); + } + bodyCache.setControl(body); + headCache.setControl(head); + } } /** @@ -175,7 +173,7 @@ public class Form extends Composite { super.setLayout(new FormLayout()); head = new FormHeading(this, SWT.NULL); head.setMenu(parent.getMenu()); - body = new LayoutComposite(this, SWT.NULL); + body = new Composite(this, SWT.NULL); body.setMenu(parent.getMenu()); } @@ -193,15 +191,6 @@ public class Form extends Composite { } /** - * Fully delegates the size computation to the internal layout manager. - */ - @Override - public final Point computeSize(int wHint, int hHint, boolean changed) { - return ((FormLayout) getLayout()).computeSize(this, wHint, hHint, - changed); - } - - /** * Prevents from changing the custom control layout. */ @Override diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.java index edeeada57ec..b3cacba24da 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.java @@ -309,7 +309,7 @@ public class FormToolkit { * @return the composite widget */ public Composite createComposite(Composite parent, int style) { - Composite composite = new LayoutComposite(parent, style | orientation); + Composite composite = new Composite(parent, style | orientation); adapt(composite); return composite; } diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.java index 4b53e79ec61..2d6fa5d5b5d 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.java @@ -156,15 +156,20 @@ public class ImageHyperlink extends Hyperlink { @Override public Point computeSize(int wHint, int hHint, boolean changed) { checkWidget(); + Rectangle trim = computeTrim(0, 0, 0, 0); Point isize = computeMaxImageSize(); - int spacing = isize.x>0?textSpacing:0; + int spacing = isize.x > 0 ? textSpacing : 0; Point textSize = null; if (getText() != null) { int innerWHint = wHint; if (wHint != SWT.DEFAULT) { - innerWHint = wHint - 2 * marginWidth - isize.x - spacing; + innerWHint = wHint - 2 * marginWidth - isize.x - spacing - trim.width; } - textSize = super.computeSize(innerWHint, hHint, changed); + int innerHHint = SWT.DEFAULT; + if (hHint != SWT.DEFAULT) { + innerHHint = hHint - trim.height; + } + textSize = super.computeSize(innerWHint, innerHHint, changed); } int width = isize.x; int height = isize.y; @@ -175,7 +180,13 @@ public class ImageHyperlink extends Hyperlink { } width += 2 * marginWidth; height += 2 * marginHeight; - return new Point(width, height); + + if (wHint != SWT.DEFAULT) + width = wHint; + if (hHint != SWT.DEFAULT) + height = hHint; + + return new Point(width + trim.width, height + trim.height); } @Override diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutComposite.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutComposite.java deleted file mode 100644 index 7dbde00e0d7..00000000000 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutComposite.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation 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: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.ui.forms.widgets; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Layout; -/** - * The class overrides default method for computing size in Composite by - * accepting size returned from layout managers as-is. The default code accepts - * width or height hint assuming it is correct. However, it is possible that - * the computation using the provided width hint results in a real size that is - * larger. This can result in wrapped text widgets being clipped, asking to - * render in bounds narrower than the longest word. - */ -/* package */class LayoutComposite extends Composite { - public LayoutComposite(Composite parent, int style) { - super(parent, style); - setMenu(parent.getMenu()); - } - @Override - public Point computeSize(int wHint, int hHint, boolean changed) { - Layout layout = getLayout(); - if (layout instanceof TableWrapLayout) - return ((TableWrapLayout) layout).computeSize(this, wHint, hHint, - changed); - if (layout instanceof ColumnLayout) - return ((ColumnLayout) layout).computeSize(this, wHint, hHint, - changed); - return super.computeSize(wHint, hHint, changed); - } -} diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.java index 900786857ce..15f0850e8f1 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.java @@ -78,7 +78,15 @@ public class ScrolledPageBook extends SharedScrolledComposite { */ @Override public Point computeSize(int wHint, int hHint, boolean changed) { - Rectangle trim = computeTrim(0, 0, 10, 10); + int width = 10; + int height = 10; + if (wHint != SWT.DEFAULT) { + width = wHint; + } + if (hHint != SWT.DEFAULT) { + height = hHint; + } + Rectangle trim = computeTrim(0, 0, width, height); return new Point(trim.width, trim.height); } /** @@ -209,7 +217,7 @@ public class ScrolledPageBook extends SharedScrolledComposite { return currentPage; } private Composite createPage() { - Composite page = new LayoutComposite(pageBook, SWT.NULL); + Composite page = new Composite(pageBook, SWT.NULL); page.setBackground(getBackground()); page.setForeground(getForeground()); page.setMenu(pageBook.getMenu()); diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.java index b64b460b25e..349b63030f4 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.java @@ -180,15 +180,17 @@ public abstract class SharedScrolledComposite extends ScrolledComposite { if (flushCache) { contentCache.flush(); } - Point newSize = contentCache.computeSize(FormUtil.getWidthHint( - clientArea.width, c), FormUtil.getHeightHint(clientArea.height, - c)); + + int minWidth = contentCache.computeMinimumWidth(); + int minHeight = contentCache.computeSize(minWidth, SWT.DEFAULT).y; if (!(expandHorizontal && expandVertical)) { - c.setSize(newSize); + Point preferredSize = contentCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); + + c.setSize(preferredSize); } - setMinSize(newSize); + setMinSize(new Point(minWidth, minHeight)); FormUtil.updatePageIncrement(this); // reduce vertical scroll increment if necessary diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.java index 958e2e3abba..0ae267338c8 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.java @@ -29,7 +29,6 @@ import org.eclipse.swt.widgets.Slider; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.Tree; -import org.eclipse.ui.internal.forms.widgets.FormUtil; /** * Caches the preferred size of an SWT control @@ -48,6 +47,7 @@ public class SizeCache { private Point cachedHeight; private int minimumWidth; + private int heightAtMinimumWidth; private int maximumWidth; /** @@ -115,6 +115,8 @@ public class SizeCache { independentDimensions = independentLengthAndWidth(control); preferredWidthOrLargerIsMinimumHeight = isPreferredWidthMaximum(control); computeHintOffset(control); + // TODO: We could probably speed things up quite a bit by using flush(false). + // Doing a recursive flush is probably not necessary here. flush(); } } @@ -144,6 +146,7 @@ public class SizeCache { minimumWidth = -1; maximumWidth = -1; minimumHeight = -1; + heightAtMinimumWidth = -1; if (recursive || dirtySize != null) { if (control == null || control.isDisposed()) { @@ -183,7 +186,7 @@ public class SizeCache { // This may not be what control returns but this means control won't // fit in these dimensions and exactly how much it does not fit it // probably not a concern of layout - return new Point(widthHint + widthAdjustment, heightHint + heightAdjustment); + return new Point(widthHint, heightHint); } // No hints given -- find the preferred size @@ -197,11 +200,11 @@ public class SizeCache { Point result = Geometry.copy(getPreferredSize()); if (widthHint != SWT.DEFAULT) { - result.x = widthHint + widthAdjustment; + result.x = widthHint; } if (heightHint != SWT.DEFAULT) { - result.y = heightHint + heightAdjustment; + result.y = heightHint; } return result; @@ -212,11 +215,15 @@ public class SizeCache { // If we know the control's preferred size if (preferredSize != null) { // If the given width is the preferred width, then return the preferred size - if (widthHint + widthAdjustment == preferredSize.x) { + if (widthHint == preferredSize.x) { return Geometry.copy(preferredSize); } } + if (minimumWidth != -1 && heightAtMinimumWidth != -1 && widthHint == minimumWidth) { + return new Point(widthHint, heightAtMinimumWidth); + } + // If we have a cached height measurement if (cachedHeightQuery != -1) { // If this was measured with the same width hint @@ -230,12 +237,12 @@ public class SizeCache { // we can compute the result based on the preferred height if (preferredWidthOrLargerIsMinimumHeight) { // Computed the preferred size (if we don't already know it) - getPreferredSize(); + Point preferred = getPreferredSize(); // If the width hint is larger than the preferred width, then // we can compute the result from the preferred width - if (widthHint + widthAdjustment >= preferredSize.x) { - return new Point(widthHint + widthAdjustment, preferredSize.y); + if (widthHint >= preferred.x) { + return new Point(widthHint, preferred.y); } } @@ -243,6 +250,11 @@ public class SizeCache { // it from scratch. cachedHeight = controlComputeSize(widthHint, heightHint); cachedHeightQuery = widthHint; + + if (minimumWidth != -1 && widthHint == minimumWidth) { + heightAtMinimumWidth = cachedHeight.y; + } + return Geometry.copy(cachedHeight); } @@ -251,7 +263,7 @@ public class SizeCache { // If we know the control's preferred size if (preferredSize != null) { // If the given height is the preferred height, then return the preferred size - if (heightHint + heightAdjustment == preferredSize.y) { + if (heightHint == preferredSize.y) { return Geometry.copy(preferredSize); } } @@ -285,16 +297,29 @@ public class SizeCache { * @return the control's size */ public Point computeAdjustedSize(int widthHint, int heightHint) { + return computeSize(widthHint, heightHint); + } + + private Point controlComputeSize(int widthHint, int heightHint) { int adjustedWidthHint = widthHint == SWT.DEFAULT ? SWT.DEFAULT : Math .max(0, widthHint - widthAdjustment); int adjustedHeightHint = heightHint == SWT.DEFAULT ? SWT.DEFAULT : Math .max(0, heightHint - heightAdjustment); - Point result = computeSize(adjustedWidthHint, adjustedHeightHint); + Point result = control.computeSize(adjustedWidthHint, adjustedHeightHint, flushChildren); + flushChildren = false; // If the amounts we subtracted off the widthHint and heightHint didn't do the trick, then // manually adjust the result to ensure that a non-default hint will return that result verbatim. + if (widthHint != SWT.DEFAULT) { + result.x = widthHint; + } + + if (heightHint != SWT.DEFAULT) { + result.y = heightHint; + } + return result; } @@ -361,13 +386,6 @@ public class SizeCache { } } - private Point controlComputeSize(int widthHint, int heightHint) { - Point result = control.computeSize(widthHint, heightHint, flushChildren); - flushChildren = false; - - return result; - } - /** * Returns true only if the control will return a constant height for any * width hint larger than the preferred width. Returns false if there is any @@ -400,9 +418,14 @@ public class SizeCache { } } + // TODO: Check for forms-specific control types that know how to compute + // their minimum width. Possibly allow + // the controls to implement ILayoutExtension directly. + if (minimumWidth == -1) { - Point minWidth = controlComputeSize(FormUtil.getWidthHint(5, control), SWT.DEFAULT); + Point minWidth = computeSize(SWT.DEFAULT, SWT.DEFAULT); minimumWidth = minWidth.x; + heightAtMinimumWidth = minWidth.y; } return minimumWidth; @@ -419,6 +442,12 @@ public class SizeCache { } } + // TODO: Check for forms-specific control types that know how to compute + // their minimum width. Possibly allow + // the controls to implement ILayoutExtension directly. + + // TODO: Fix the following branch. + if (maximumWidth == -1) { maximumWidth = getPreferredSize().x; } @@ -427,6 +456,7 @@ public class SizeCache { } private int computeMinimumHeight() { + // TODO: Fix the following branch if (minimumHeight == -1) { Point sizeAtMinHeight = controlComputeSize(SWT.DEFAULT, 0); diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.java index 352749aa33f..41e23e17736 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.java @@ -20,6 +20,7 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; +import org.eclipse.ui.internal.forms.widgets.FormUtil; /** * This implementation of the layout algorithm attempts to position controls in @@ -318,7 +319,9 @@ public final class TableWrapLayout extends Layout implements ILayoutExtension { if (k < j + span - 1) cwidth += horizontalSpacing; } - Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight); + Point size = FormUtil.computeControlSize(cache.getCache(td.childIndex), cwidth - td.indent, td.maxWidth, + td.maxHeight, td.align == TableWrapData.FILL); + size.x += td.indent; td.compWidth = cwidth; if (td.heightHint != SWT.DEFAULT) { size = new Point(size.x, td.heightHint); @@ -392,20 +395,6 @@ public final class TableWrapLayout extends Layout implements ILayoutExtension { return widths; } - Point computeSize(int childIndex, int width, int indent, int maxWidth, int maxHeight) { - int widthArg = width - indent; - SizeCache controlCache = cache.getCache(childIndex); - if (!isWrap(controlCache.getControl())) - widthArg = SWT.DEFAULT; - Point size = controlCache.computeSize(widthArg, SWT.DEFAULT); - if (maxWidth!=SWT.DEFAULT) - size.x = Math.min(size.x, maxWidth); - if (maxHeight!=SWT.DEFAULT) - size.y = Math.min(size.y, maxHeight); - size.x += indent; - return size; - } - void placeControl(Control control, TableWrapData td, int x, int y, int[] rowHeights, int row) { int xloc = x + td.indent; @@ -426,7 +415,7 @@ public final class TableWrapLayout extends Layout implements ILayoutExtension { } // align horizontally if (td.align == TableWrapData.CENTER) { - xloc = x + colWidth / 2 - width / 2; + xloc = x + (colWidth - width) / 2; } else if (td.align == TableWrapData.RIGHT) { xloc = x + colWidth - width; } else if (td.align == TableWrapData.FILL) { @@ -434,7 +423,7 @@ public final class TableWrapLayout extends Layout implements ILayoutExtension { } // align vertically if (td.valign == TableWrapData.MIDDLE) { - yloc = y + slotHeight / 2 - height / 2; + yloc = y + (slotHeight - height) / 2; } else if (td.valign == TableWrapData.BOTTOM) { yloc = y + slotHeight - height; } else if (td.valign == TableWrapData.FILL) { @@ -669,7 +658,9 @@ public final class TableWrapLayout extends Layout implements ILayoutExtension { } int cy = td.heightHint; if (cy == SWT.DEFAULT) { - Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight); + SizeCache controlCache = cache.getCache(td.childIndex); + Point size = FormUtil.computeControlSize(controlCache, cwidth - td.indent, td.maxWidth, + td.maxHeight, td.align == TableWrapData.FILL); cy = size.y; } RowSpan rowspan = rowspans.get(child); diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.java index 966fa6debb8..850c5240fdb 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.java @@ -141,18 +141,15 @@ public abstract class ToggleHyperlink extends AbstractHyperlink { */ @Override public Point computeSize(int wHint, int hHint, boolean changed) { - int width, height; - /* + int width = innerWidth + 2 * marginWidth; + int height = innerHeight + 2 * marginHeight; if (wHint != SWT.DEFAULT) width = wHint; - else */ - width = innerWidth + 2 * marginWidth; - /* if (hHint != SWT.DEFAULT) height = hHint; - else */ - height = innerHeight + 2 * marginHeight; - return new Point(width, height); + + Rectangle trim = computeTrim(0, 0, width, height); + return new Point(trim.width, trim.height); } /** * Returns the expansion state of the toggle control. When toggle is in the diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.java index ce9accfd4ae..133010ec9df 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.java @@ -50,15 +50,14 @@ public class ControlSegment extends ObjectSegment implements IFocusSelectable { } @Override - protected Point getObjectSize(Hashtable<String, Object> resourceTable, int wHint) { + protected Point getObjectSize(Hashtable<String, Object> resourceTable, int widthHint) { Control control = getControl(resourceTable); if (control==null) return new Point(0,0); - int realWhint = FormUtil.getWidthHint(wHint, control); - Point size = control.computeSize(realWhint, SWT.DEFAULT); - if (realWhint!=SWT.DEFAULT && fill) - size.x = Math.max(size.x, realWhint); - if (width !=SWT.DEFAULT) + Point size = control.computeSize(widthHint, SWT.DEFAULT); + if (widthHint!=SWT.DEFAULT && fill) + size.x = Math.max(size.x, widthHint); + if (width != SWT.DEFAULT) size.x = width; if (height != SWT.DEFAULT) size.y = height; diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.java index 85964d973a1..6d88f42db15 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.java @@ -87,6 +87,8 @@ public class FormHeading extends Canvas { private SizeCache messageCache = new SizeCache(); + private SizeCache titleRegionCache = new SizeCache(); + private TitleRegion titleRegion; private MessageRegion messageRegion; @@ -123,9 +125,11 @@ public class FormHeading extends Canvas { } private class FormHeadingLayout extends Layout implements ILayoutExtension { + private static final int MIN_WIDTH = -2; + @Override public int computeMinimumWidth(Composite composite, boolean flushCache) { - return computeSize(composite, 5, SWT.DEFAULT, flushCache).x; + return layout(composite, false, 0, 0, MIN_WIDTH, SWT.DEFAULT, flushCache).x; } @Override @@ -148,6 +152,8 @@ public class FormHeading extends Canvas { private Point layout(Composite composite, boolean move, int x, int y, int width, int height, boolean flushCache) { + titleRegionCache.setControl(titleRegion); + Point tsize = null; Point msize = null; Point tbsize = null; @@ -157,6 +163,7 @@ public class FormHeading extends Canvas { clientCache.flush(); messageCache.flush(); toolbarCache.flush(); + titleRegionCache.flush(); } if (hasToolBar()) { ToolBar tb = toolBarManager.getControl(); @@ -165,17 +172,17 @@ public class FormHeading extends Canvas { } if (headClient != null) { clientCache.setControl(headClient); - int cwhint = width; - if (cwhint != SWT.DEFAULT) { - cwhint -= HMARGIN * 2; + int clientWidthHint = width; + if (clientWidthHint != SWT.DEFAULT && clientWidthHint != MIN_WIDTH) { + clientWidthHint -= HMARGIN * 2; if (tbsize != null && getToolBarAlignment() == SWT.BOTTOM) - cwhint -= tbsize.x + SPACING; + clientWidthHint -= tbsize.x + SPACING; } - clsize = clientCache.computeSize(cwhint, SWT.DEFAULT); + clsize = computeSize(clientCache, clientWidthHint); } int totalFlexWidth = width; int flexWidth = totalFlexWidth; - if (totalFlexWidth != SWT.DEFAULT) { + if (totalFlexWidth != SWT.DEFAULT && totalFlexWidth != MIN_WIDTH) { totalFlexWidth -= TITLE_HMARGIN * 2; // complete right margin if (hasToolBar() && getToolBarAlignment() == SWT.TOP @@ -207,11 +214,11 @@ public class FormHeading extends Canvas { * SWT.DEFAULT); } } */ if (!hasMessageRegion()) { - tsize = titleRegion.computeSize(flexWidth, SWT.DEFAULT); + tsize = computeSize(titleRegionCache, flexWidth); } else { // Total flexible area in the first row is flexWidth. // Try natural widths of title and - Point tsizeNatural = titleRegion.computeSize(SWT.DEFAULT, + Point tsizeNatural = titleRegionCache.computeSize(SWT.DEFAULT, SWT.DEFAULT); messageCache.setControl(messageRegion.getMessageControl()); Point msizeNatural = messageCache.computeSize(SWT.DEFAULT, @@ -219,7 +226,7 @@ public class FormHeading extends Canvas { // try to fit all tsize = tsizeNatural; msize = msizeNatural; - if (flexWidth != SWT.DEFAULT) { + if (flexWidth != SWT.DEFAULT && flexWidth != MIN_WIDTH) { int needed = tsizeNatural.x + msizeNatural.x; if (needed > flexWidth) { // too big - try to limit the message @@ -337,6 +344,24 @@ public class FormHeading extends Canvas { } return size; } + + /** + * Computes the preferred or minimum size of the given client cache. + * + * @param clientCache + * size cache for the control whose size is being computed + * @param wHint + * the width of the control, in pixels, or SWT.DEFAULT if the + * preferred size is being computed, or MIN_WIDTH if the minimum size + * is being computed + */ + private Point computeSize(SizeCache clientCache, int wHint) { + if (wHint == MIN_WIDTH) { + int minWidth = clientCache.computeMinimumWidth(); + return clientCache.computeSize(minWidth, SWT.DEFAULT); + } + return clientCache.computeSize(wHint, SWT.DEFAULT); + } } @Override diff --git a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.java b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.java index 3115ced00af..2b072647ab3 100644 --- a/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.java +++ b/bundles/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.java @@ -33,7 +33,7 @@ import org.eclipse.ui.forms.widgets.ColumnLayout; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormText; import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.ILayoutExtension; +import org.eclipse.ui.forms.widgets.SizeCache; import com.ibm.icu.text.BreakIterator; @@ -55,6 +55,58 @@ public class FormUtil { public static final String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$ + /** + * Computes the preferred of a control, given the (optional) fixed width of the + * column it needs to fit within, the width and height hints from its layout + * data, and its horizontal alignment. + * + * @param cache + * the size cache for the control whose size is being computed + * @param constrainedWidth + * the width of the control's cell or SWT.DEFAULT if unconstrained + * @param widthHintFromLayoutData + * the width hint associated with the control's layout data or + * SWT.DEFAULT if none + * @param heightHintFromLayoutData + * the height hint associated with the control's layout data or + * SWT.DEFAULT if none + * @param isFillAligned + * true if and only if the control is horizontally fill-aligned + * within its cell + */ + public static Point computeControlSize(SizeCache cache, int constrainedWidth, int widthHintFromLayoutData, + int heightHintFromLayoutData, boolean isFillAligned) { + int widthHint; + int heightHint = heightHintFromLayoutData; + // If using non-fill alignment and there is a width hint specified in both the + // argument and the layout data, + // use whichever one is smaller. + if (isFillAligned) { + // If width is unbounded, apply a width hint iff requested in the layout data + if (constrainedWidth == SWT.DEFAULT) { + widthHint = widthHintFromLayoutData; + } else { + widthHint = constrainedWidth; + } + } else { + // If not using fill alignment, make a first attempt at computing the size + // without considering the + // column width + widthHint = widthHintFromLayoutData; + } + + Point result = cache.computeSize(widthHint, heightHint); + + // If using non-fill alignment, it's possible that the result is larger than the + // column width. In that case, + // constrain the width and try again. + if (!isFillAligned && constrainedWidth != SWT.DEFAULT && result.x > constrainedWidth) { + result = cache.computeSize(constrainedWidth, heightHint); + } + + return result; + } + public static Text createText(Composite parent, String label, FormToolkit factory) { return createText(parent, label, factory, 1); @@ -406,20 +458,6 @@ public class FormUtil { } } - public static boolean isWrapControl(Control c) { - if ((c.getStyle() & SWT.WRAP) != 0) - return true; - if (c instanceof Composite) { - return ((Composite) c).getLayout() instanceof ILayoutExtension; - } - return false; - } - - public static int getWidthHint(int wHint, Control c) { - boolean wrap = isWrapControl(c); - return wrap ? wHint : SWT.DEFAULT; - } - public static int getHeightHint(int hHint, Control c) { if (c instanceof Composite) { Layout layout = ((Composite) c).getLayout(); @@ -429,26 +467,6 @@ public class FormUtil { return SWT.DEFAULT; } - public static int computeMinimumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); - if (layout instanceof ILayoutExtension) - return ((ILayoutExtension) layout).computeMinimumWidth( - (Composite) c, changed); - } - return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x; - } - - public static int computeMaximumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); - if (layout instanceof ILayoutExtension) - return ((ILayoutExtension) layout).computeMaximumWidth( - (Composite) c, changed); - } - return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x; - } - public static Form getForm(Control c) { Composite parent = c.getParent(); while (parent != null) { diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java index c04fc122ea2..4fb9455f111 100755 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java @@ -16,7 +16,6 @@ package org.eclipse.ui.tests.forms; import org.eclipse.ui.tests.forms.layout.AllLayoutTests; import org.eclipse.ui.tests.forms.util.AllUtilityTests; import org.eclipse.ui.tests.forms.widgets.AllWidgetsTests; -import org.eclipse.ui.tests.forms.widgets.SizeCacheTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -27,8 +26,7 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ AllLayoutTests.class, AllUtilityTests.class, - AllWidgetsTests.class, - SizeCacheTest.class + AllWidgetsTests.class }) public class AllFormsTests { diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/AllLayoutTests.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/AllLayoutTests.java index 99a6a972542..c642c1fb5a9 100755 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/AllLayoutTests.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/AllLayoutTests.java @@ -12,6 +12,8 @@ package org.eclipse.ui.tests.forms.layout; +import org.eclipse.ui.tests.forms.widgets.HintAdjustmentTest; +import org.eclipse.ui.tests.forms.widgets.SizeCacheTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -20,8 +22,10 @@ import org.junit.runners.Suite; */ @RunWith(Suite.class) @Suite.SuiteClasses({ + HintAdjustmentTest.class, + SizeCacheTest.class, TestColumnWrapLayout.class, - TestTableWrapLayout.class + TestTableWrapLayout.class, }) public class AllLayoutTests { diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/ControlFactory.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/ControlFactory.java new file mode 100644 index 00000000000..b3e87e79f29 --- /dev/null +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/ControlFactory.java @@ -0,0 +1,130 @@ +package org.eclipse.ui.tests.forms.layout; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.ui.forms.widgets.ILayoutExtension; + +/** + * Factory for creating test controls which try to maintain a constant area when + * their width changes. + */ +public final class ControlFactory { + + /** + * Implements a layout intended for use within unit tests. The layout simulates + * a wrapping control by attempting to maintain a constant area. When the layout + * is compressed or stretched, its preferred height will increase or decrease in + * order to preserve the layout's area. + * <p> + * This layout also records whether or not its cache was ever flushed, allowing + * unit tests to test that the flush flags propagate correctly. + * <p> + * This layout does not reposition its children. It is meant to be used with + * Composites that have no children. + */ + public static class TestLayout extends Layout { + final int maxWidth; + final int desiredArea; + public boolean wasChanged = false; + public Rectangle bounds = new Rectangle(0, 0, 0, 0); + + public TestLayout(int maxWidth, int desiredArea) { + super(); + this.maxWidth = maxWidth; + this.desiredArea = desiredArea; + } + + protected void recordChanged(boolean changed) { + if (changed) { + this.wasChanged = changed; + } + } + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + recordChanged(flushCache); + + if (wHint == SWT.DEFAULT) { + wHint = maxWidth; + } + + if (hHint == SWT.DEFAULT) { + hHint = wHint <= 0 ? desiredArea : (desiredArea / wHint); + } + + return new Point(wHint, hHint); + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + recordChanged(flushCache); + + bounds = composite.getBounds(); + } + } + + private static class TestLayoutWithExtension extends TestLayout implements ILayoutExtension { + + final int minWidth; + + public TestLayoutWithExtension(int minWidth, int maxWidth, int desiredArea) { + super(maxWidth, desiredArea); + this.minWidth = minWidth; + } + + @Override + public int computeMinimumWidth(Composite parent, boolean changed) { + recordChanged(changed); + return minWidth; + } + + @Override + public int computeMaximumWidth(Composite parent, boolean changed) { + recordChanged(changed); + return maxWidth; + } + + } + + /** + * Creates a wrapping layout that does not implement ILayoutExtension and + * attempts to maintain a constant area. + */ + public static TestLayout createLayout(int maxWidth, int heightAtMaxWidth) { + int area = heightAtMaxWidth * maxWidth; + return new TestLayout(maxWidth, area); + } + + /** + * Creates a wrapping layout that implements ILayoutExtension and attempts to maintain a constant area. + */ + public static TestLayout createLayout(int minWidth, int maxWidth, int heightAtMaxWidth) { + int area = heightAtMaxWidth * maxWidth; + return new TestLayoutWithExtension(minWidth, maxWidth, area); + } + + /** + * Creates a new composite that attempts to maintain a constant area. The + * composite's layout implements ILayoutExtension. It will not lay out its + * children. + */ + public static Composite create(Composite parent, int minWidth, int maxWidth, int heightAtMaxWidth) { + Composite newControl = new Composite(parent, SWT.NONE); + newControl.setLayout(createLayout(minWidth, maxWidth, heightAtMaxWidth)); + return newControl; + } + + /** + * Creates a new composite that attempts to maintain a constant area. The + * composite's layout does not implement ILayoutExtension. It will not lay + * out its children. + */ + public static Composite create(Composite parent, int maxWidth, int heightAtMaxWidth) { + Composite newControl = new Composite(parent, SWT.NONE); + newControl.setLayout(createLayout(maxWidth, heightAtMaxWidth)); + return newControl; + } +} diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestColumnWrapLayout.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestColumnWrapLayout.java index ca22811d8ff..0dd299ab3dd 100644 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestColumnWrapLayout.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestColumnWrapLayout.java @@ -16,13 +16,18 @@ import static org.junit.Assert.assertEquals; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.widgets.ColumnLayout; +import org.eclipse.ui.forms.widgets.ColumnLayoutData; import org.eclipse.ui.internal.forms.widgets.ColumnLayoutUtils; +import org.eclipse.ui.tests.forms.layout.ControlFactory.TestLayout; +import org.junit.After; +import org.junit.Before; import org.junit.Test; public class TestColumnWrapLayout { @@ -33,6 +38,32 @@ public class TestColumnWrapLayout { private final Point p100 = new Point(100, 100); private final Point p200 = new Point(100, 200); + private Display display; + private Shell shell; + private Composite inner; + private ColumnLayout layout; + + @Before + public void setUp() { + display = PlatformUI.getWorkbench().getDisplay(); + shell = new Shell(display); + inner = new Composite(shell, SWT.NULL); + inner.setSize(100, 300); + layout = new ColumnLayout(); + layout.leftMargin = 0; + layout.rightMargin = 0; + layout.topMargin = 0; + layout.bottomMargin = 0; + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + inner.setLayout(layout); + } + + @After + public void tearDown() { + shell.dispose(); + } + @Test public void testEqualSizeColumns() { Point[] sizes = { p20, p30, p30, p20, p20, p30 }; @@ -63,44 +94,258 @@ public class TestColumnWrapLayout { assertEquals(260, ColumnLayoutUtils.computeColumnHeight(3, sizes, 100, 100)); } - private class SizedComposite extends Composite { - - int height; - - public SizedComposite(Composite parent, int style, int height) { - super(parent, style); - this.height = height; - } - - @Override - public Point computeSize(int wHint, int hHint, boolean changed) { - return new Point( 20, height); - } - } - /** * Test that labels with the WRAP property set do indeed wrap. */ @Test public void testColumnLayoutInShell() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(100, 300); - shell.setLayout(new GridLayout()); - Composite inner = new Composite(shell, SWT.NULL); - ColumnLayout layout = new ColumnLayout(); layout.verticalSpacing = 5; + layout.horizontalSpacing = 5; layout.minNumColumns = 2; layout.maxNumColumns = 2; layout.topMargin=2; layout.bottomMargin=3; - inner.setLayout(layout); - new SizedComposite(inner, SWT.NULL, 30); - new SizedComposite(inner, SWT.NULL, 40); - new SizedComposite(inner, SWT.NULL, 20); - shell.layout(true); - assertEquals(70, inner.getSize().y); - shell.dispose(); + layout.leftMargin = 5; + layout.rightMargin = 5; + ControlFactory.create(inner, 20, 20, 30); + ControlFactory.create(inner, 20, 20, 40); + ControlFactory.create(inner, 20, 20, 20); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(70, size.y); + inner.setSize(size); + inner.layout(true); + assertEquals(new Rectangle(5, 2, 20, 30), inner.getChildren()[0].getBounds()); + assertEquals(new Rectangle(30, 2, 20, 40), inner.getChildren()[1].getBounds()); + } + + @Test + public void testHorizontalSpacingHasNoEffectWhenOnlyOneColumn() { + layout.horizontalSpacing = 1000; + Composite control = ControlFactory.create(inner, 20, 20, 30); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(20, size.x); + inner.pack(true); + assertEquals(new Rectangle(0, 0, 20, 30), control.getBounds()); + } + + @Test + public void testHorizontalSpacing() { + layout.horizontalSpacing = 1000; + ControlFactory.create(inner, 20, 20, 30); + Composite secondControl = ControlFactory.create(inner, 20, 20, 30); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(1040, size.x); + inner.pack(true); + assertEquals(new Rectangle(1020, 0, 20, 30), secondControl.getBounds()); + } + + @Test + public void testHorizontalMargins() { + layout.leftMargin = 100; + layout.rightMargin = 10; + Control leftControl = ControlFactory.create(inner, 20, 20, 30); + Control rightControl = ControlFactory.create(inner, 20, 20, 40); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(150, size.x); + inner.pack(true); + assertEquals(new Rectangle(100, 0, 20, 30), leftControl.getBounds()); + assertEquals(new Rectangle(120, 0, 20, 40), rightControl.getBounds()); + assertEquals(new Rectangle(0, 0, 150, 40), inner.getBounds()); + } + + @Test + public void testVerticalSpacingHasNoEffectWhenOnlyOneColumn() { + layout.verticalSpacing = 1000; + Composite control = ControlFactory.create(inner, 20, 20, 30); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(20, size.x); + inner.pack(true); + assertEquals(new Rectangle(0, 0, 20, 30), control.getBounds()); + } + + @Test + public void testVerticalSpacing() { + layout.verticalSpacing = 1000; + layout.maxNumColumns = 1; + ControlFactory.create(inner, 20, 20, 30); + Composite secondControl = ControlFactory.create(inner, 20, 20, 30); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(1060, size.y); + inner.pack(true); + assertEquals(new Rectangle(0, 1030, 20, 30), secondControl.getBounds()); + } + + @Test + public void testVerticalMargins() { + layout.topMargin = 100; + layout.bottomMargin = 10; + layout.maxNumColumns = 1; + Control control1 = ControlFactory.create(inner, 20, 20, 30); + Control control2 = ControlFactory.create(inner, 20, 20, 40); + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(180, size.y); + inner.pack(); + assertEquals(new Rectangle(0, 100, 20, 30), control1.getBounds()); + assertEquals(new Rectangle(0, 130, 20, 40), control2.getBounds()); + assertEquals(new Rectangle(0, 0, 20, 180), inner.getBounds()); + } + + @Test + public void testSelectsCorrectNumberOfColumns() { + layout.horizontalSpacing = 10; + layout.leftMargin = 10; + layout.rightMargin = 10; + + ControlFactory.create(inner, 21, 30, 50); + ControlFactory.create(inner, 22, 40, 50); + // This last control will have a preferred height of 108 when compressed to its + // minimum width + ControlFactory.create(inner, 23, 50, 50); + + // Should always use the maximum number of columns when the width is + // unconstrained, and the preferred size + // should be based on the maximum size of the children + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + assertEquals(190, size.x); + assertEquals(50, size.y); + + // Should still use the maximum number of columns if the minimum size of the + // children will fit in the area + size = inner.computeSize(109, SWT.DEFAULT); + assertEquals(109, size.x); + assertEquals(108, size.y); + + // Lay out with the default width + inner.pack(true); + assertAllChildrenHaveWidth(50); + + for (Control next : inner.getChildren()) { + assertEquals(0, next.getBounds().y); + } + + // If we're one pixel less than the minimum size for the children, the number of + // columns should be reduced + size = inner.computeSize(108, SWT.DEFAULT); + assertEquals(108, size.x); + assertEquals(89, size.y); + + // Ensure that it falls back to the minimum number of columns when we're + // requesting the minimum width + int minWidth = layout.computeMinimumWidth(inner, false); + assertEquals(43, minWidth); + } + + @Test + public void testFillAlignment() { + layout.maxNumColumns = 1; + + // Control1 has a min size of 100, a default size of 800, and a width hint of + // 400. When computing the default size of the column, the width hint should be + // used rather than the control's preferred width and the control should end up + // with a size 400x100. + Composite control1 = ControlFactory.create(inner, 100, 800, 200); + ColumnLayoutData data1 = new ColumnLayoutData(); + data1.widthHint = 400; + control1.setLayoutData(data1); + + // Control2 is narrower than control1, but since it's fill-aligned it should be + // stretched to match the size of control1. + Composite control2 = ControlFactory.create(inner, 50, 100, 1200); + ColumnLayoutData data2 = new ColumnLayoutData(); + data2.widthHint = 200; + control2.setLayoutData(data2); + + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + + // Both control should be resized to match the first control's width hint since + // it's the widest and both are fill-aligned. + assertEquals(400, size.x); + + // 400 pixels for the first control, 300 pixels for the second. + assertEquals(700, size.y); + + inner.pack(); + + assertEquals(new Rectangle(0, 0, 400, 400), control1.getBounds()); + assertEquals(new Rectangle(0, 400, 400, 300), control2.getBounds()); + } + + @Test + public void testLeftAlignment() { + layout.maxNumColumns = 1; + + // Create a large control to dominate the width of the columns + Composite control1 = ControlFactory.create(inner, 200, 200, 200); + + // Control2 is narrower than control1, but since it's fill-aligned it + // should be + // stretched to match the size of control1. + Composite control2 = ControlFactory.create(inner, 10, 100, 100); + ColumnLayoutData data2 = new ColumnLayoutData(); + data2.horizontalAlignment = ColumnLayoutData.LEFT; + data2.widthHint = 50; + control2.setLayoutData(data2); + + Composite control3 = ControlFactory.create(inner, 10, 50, 100); + ColumnLayoutData data3 = new ColumnLayoutData(); + data3.widthHint = 10; + data3.horizontalAlignment = ColumnLayoutData.LEFT; + control3.setLayoutData(data3); + + Point size = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + + assertEquals(200, size.x); + assertEquals(900, size.y); + + inner.pack(true); + + assertEquals(new Rectangle(0, 0, 200, 200), control1.getBounds()); + assertEquals(new Rectangle(0, 200, 50, 200), control2.getBounds()); + assertEquals(new Rectangle(0, 400, 10, 500), control3.getBounds()); + + // Verify that if we shrink a left-aligned wrapping control to a size + // smaller than its preferred width, it will still wrap correctly. + + size = inner.computeSize(25, SWT.DEFAULT); + + assertEquals(25, size.x); + assertEquals(2500, size.y); + } + + @Test + public void testControlsFlushedCorrectly() + { + Composite composite = ControlFactory.create(inner, 200, 200, 200); + TestLayout layout = (TestLayout) composite.getLayout(); + + // Currently there is one unnecessary recursive flush on startup. Ignore it for + // now. + inner.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + layout.wasChanged = false; + + // Verify that there is no redundant cache flush. + inner.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + assertEquals(false, layout.wasChanged); + + inner.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); + assertEquals(true, layout.wasChanged); + + layout.wasChanged = false; + inner.layout(false); + assertEquals(false, layout.wasChanged); + + inner.layout(true); + assertEquals(true, layout.wasChanged); } + private void assertAllChildrenHaveWidth(int desiredWidth) { + Control[] children = inner.getChildren(); + + for (int idx = 0; idx < children.length; idx++) { + Control next = children[idx]; + + Rectangle bounds = next.getBounds(); + assertEquals("Child " + idx + " should have the correct width", desiredWidth, bounds.width); + } + } } diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestTableWrapLayout.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestTableWrapLayout.java index b8edcd1019b..2a1e2c2a296 100755 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestTableWrapLayout.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/layout/TestTableWrapLayout.java @@ -16,102 +16,123 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; 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.widgets.TableWrapData; import org.eclipse.ui.forms.widgets.TableWrapLayout; +import org.junit.After; +import org.junit.Before; import org.junit.Test; public class TestTableWrapLayout { - private final String A1 = "A"; - private final String A10 = "A A A A A A A A A A"; - private final String A20 = A10 + " " + A10; - private final String A40 = A20 + " " + A20; - private final String A80 = A40 + " " + A40; + private Display display; + private Shell shell; + private Composite inner; + private TableWrapLayout layout; // Returns the width + left - private int rightEdge(Label lab) { + private int rightEdge(Control lab) { Rectangle r = lab.getBounds(); return r.x + r.width; } + @Before + public void setUp() { + display = PlatformUI.getWorkbench().getDisplay(); + shell = new Shell(display); + shell.setLayout(new FillLayout()); + inner = new Composite(shell, SWT.NONE); + inner.setSize(100, 300); + layout = new TableWrapLayout(); + layout.leftMargin = 0; + layout.rightMargin = 0; + layout.topMargin = 0; + layout.bottomMargin = 0; + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + inner.setLayout(layout); + } + + @After + public void tearDown() { + shell.dispose(); + } + /** - * Test that labels with the WRAP property set do indeed wrap. + * Test a simple two-cell layout. */ @Test public void testTableWrapLayoutNonWrappingLabels() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(100, 300); - shell.setLayout(new FillLayout()); - Composite inner = new Composite(shell, SWT.V_SCROLL); - inner.setLayout(new TableWrapLayout()); - Label l1 = new Label(inner, SWT.NULL); - l1.setText(A10); - Label l2 = new Label(inner, SWT.NULL); - l2.setText(A80); - shell.layout(); - assertEquals(l1.getSize().y, l2.getSize().y); - assertTrue(l2.getSize().x > 100); - shell.dispose(); + Control l1 = ControlFactory.create(inner, 10, 100, 15); + Control l2 = ControlFactory.create(inner, 80, 800, 15); + + Point preferredSize = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + int minimumWidth = layout.computeMinimumWidth(inner, false); + Point wrappedSize = inner.computeSize(400, SWT.DEFAULT); + + inner.pack(); + assertEquals(new Rectangle(0, 0, 100, 15), l1.getBounds()); + assertEquals(new Rectangle(0, 15, 800, 15), l2.getBounds()); + assertEquals(new Point(800, 30), preferredSize); + assertEquals(80, minimumWidth); + assertEquals(new Point(400, 45), wrappedSize); } /** * Test that labels with the WRAP property set do indeed wrap. */ - // Test suppressed for now - does not pass but not sure if this is a bug - public void suppressed_testWrappingPoint() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(300, 300); - shell.setLayout(new FillLayout()); - Composite inner = new Composite(shell, SWT.V_SCROLL); - TableWrapLayout tableWrapLayout = new TableWrapLayout(); - tableWrapLayout.leftMargin = 0; - tableWrapLayout.rightMargin = 0; - inner.setLayout(tableWrapLayout); - Label l1 = new Label(inner, SWT.WRAP); - l1.setText(A10); - shell.layout(); - int originalWidth = l1.getSize().x; - int originalHeight = l1.getSize().y; - shell.setSize(originalWidth, 300); - shell.layout(); - assertEquals(l1.getSize().y, originalHeight); - shell.setSize(originalWidth / 2, 300); - shell.layout(); + @Test + public void testWrappingPoint() { + Control l1 = ControlFactory.create(inner, 30, 100, 15); + + // Validate the behavior of computeSize() + Point preferredSize = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + int preferredHeightWheneThereIsExtraHorizontalSpace = inner.computeSize(200, SWT.DEFAULT).y; + int preferredHeightWhenControlFillsSpace = inner.computeSize(100, SWT.DEFAULT).y; + int preferredHeightWhenControlCompressed = inner.computeSize(50, SWT.DEFAULT).y; + assertEquals(15, preferredHeightWheneThereIsExtraHorizontalSpace); + assertEquals(15, preferredHeightWhenControlFillsSpace); + assertEquals(30, preferredHeightWhenControlCompressed); + assertEquals(new Point(100, 15), preferredSize); + + // Validate the behavior of layout() + inner.setSize(100, 15); inner.layout(); - assertTrue(l1.getSize().y > originalHeight); - shell.dispose(); + assertEquals(15, l1.getSize().y); + + inner.setSize(100, 300); + inner.layout(); + assertEquals(15, l1.getSize().y); + + inner.setSize(50, 300); + inner.layout(); + assertEquals(30, l1.getSize().y); + + // Validate the behavior of computeMinimumWidth + assertEquals(30, layout.computeMinimumWidth(inner, false)); + assertEquals(100, layout.computeMaximumWidth(inner, false)); } /** * Test that labels with the WRAP property set do indeed wrap. */ - // Test suppressed for now, see Bug 196686 - public void suppressed_testTableWrapLayoutWrappingLabels() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(100, 300); - shell.setLayout(new FillLayout()); - Composite inner = new Composite(shell, SWT.V_SCROLL); - inner.setLayout(new TableWrapLayout()); - Label l1 = new Label(inner, SWT.WRAP); - l1.setText(A10); - Label l2 = new Label(inner, SWT.WRAP); - l2.setText(A80); - shell.layout(); - assertTrue(l1.getSize().y < l2.getSize().y); - assertTrue("Label is too wide for layout ", l1.getSize().x <= 100); - assertTrue("Label is too wide for layout ", l2.getSize().x <= 100); - assertTrue("Labels overlap", l2.getBounds().y >= l1.getBounds().y + l1.getBounds().height); - shell.dispose(); + @Test + public void testTableWrapLayoutWrappingLabels() { + Control l1 = ControlFactory.create(inner, 30, 100, 15); + Control l2 = ControlFactory.create(inner, 50, 800, 15); + + inner.setSize(300, 1000); + inner.layout(false); + + assertEquals("l1 had the wrong bounds", new Rectangle(0, 0, 100, 15), l1.getBounds()); + assertEquals("l2 had the wrong bounds", new Rectangle(0, 15, 300, 40), l2.getBounds()); } /** @@ -119,97 +140,121 @@ public class TestTableWrapLayout { */ @Test public void testTableWrapLayoutTwoColumnsWrappingLabels() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(100, 300); - shell.setLayout(new FillLayout()); - Composite inner = new Composite(shell, SWT.V_SCROLL); - TableWrapLayout tableWrapLayout = new TableWrapLayout(); - tableWrapLayout.numColumns = 2; - inner.setLayout(tableWrapLayout); - Label l1 = new Label(inner, SWT.WRAP); - l1.setText(A10); - Label l2 = new Label(inner, SWT.WRAP); - l2.setText(A20); - Label l3 = new Label(inner, SWT.WRAP); - l3.setText(A40); - Label l4 = new Label(inner, SWT.WRAP); - l4.setText(A80); - shell.layout(); - assertTrue(l1.getSize().x < l2.getSize().x); - assertTrue(l1.getSize().y < l3.getSize().y); - assertTrue(l1.getSize().x < l4.getSize().x); - assertTrue(l2.getSize().y < l3.getSize().y); - assertTrue("Label is too wide for layout ", l1.getSize().x + l2.getSize().x <= 100); - assertTrue("Labels overlap", l2.getBounds().x >= l1.getBounds().x + l1.getBounds().width); - assertTrue("Labels overlap", l3.getBounds().y >= l1.getBounds().y + l1.getBounds().height); - assertTrue("Labels overlap", l4.getBounds().x >= l3.getBounds().x + l3.getBounds().width); - assertTrue("Labels overlap", l4.getBounds().y >= l2.getBounds().y + l2.getBounds().height); - shell.dispose(); + layout.numColumns = 2; + Control l1 = ControlFactory.create(inner, 31, 100, 15); + Control l2 = ControlFactory.create(inner, 32, 200, 15); + Control l3 = ControlFactory.create(inner, 33, 400, 15); + Control l4 = ControlFactory.create(inner, 34, 800, 15); + + inner.setSize(300, 1000); + inner.layout(false); + + assertEquals(300, l3.getBounds().width + l4.getBounds().width); + assertTrue(rightEdge(l1) <= l2.getBounds().x); + assertEquals(rightEdge(l3), l4.getBounds().x); + assertTrue(bottomEdge(l1) <= l3.getBounds().y); + assertTrue(bottomEdge(l1) <= l4.getBounds().y); + + Point preferredSize = inner.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + assertEquals(new Point(1200, 30), preferredSize); + + int minWidth = layout.computeMinimumWidth(inner, false); + assertEquals(67, minWidth); + } + + /** + * Test what happens when the grid is compressed below its minimum size. It + * should remove pixels from the column that creates the least amount of + * truncation. + * <p> + * Test is currently suppressed because this layout cannot handle this case + * properly yet. + */ + public void suppressed_testCompressedBelowMinimumSize() { + layout.numColumns = 2; + Control l1 = ControlFactory.create(inner, 50, 200, 50); + l1.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL)); + Control l2 = ControlFactory.create(inner, 200, 200, 50); + l2.setLayoutData(new TableWrapData(TableWrapData.FILL, TableWrapData.FILL)); + Control l3 = ControlFactory.create(inner, 400, 400, 50); + TableWrapData data = new TableWrapData(TableWrapData.FILL, TableWrapData.FILL); + data.colspan = 2; + l3.setLayoutData(data); + + inner.setSize(300, 1000); + inner.layout(false); + + assertEquals(new Rectangle(0, 0, 100, 50), l1.getBounds()); + assertEquals(new Rectangle(100, 0, 200, 50), l1.getBounds()); + assertEquals(new Rectangle(0, 50, 300, 50), l1.getBounds()); } /** * Test alignments and margins */ - // Suppressed for now - see Bug 196686 - public void suppressed_testTableWrapLayoutAlignment() { - Display display = PlatformUI.getWorkbench().getDisplay(); - Shell shell = new Shell(display); - shell.setSize(100, 300); - shell.setLayout(new FillLayout()); - Composite inner = new Composite(shell, SWT.V_SCROLL); - TableWrapLayout tableWrapLayout = new TableWrapLayout(); + @Test + public void testTableWrapLayoutAlignment() { final int LEFT_MARGIN = 1; final int RIGHT_MARGIN = 2; final int TOP_MARGIN = 3; final int BOTTOM_MARGIN = 4; - tableWrapLayout.leftMargin = LEFT_MARGIN; - tableWrapLayout.rightMargin = RIGHT_MARGIN; - tableWrapLayout.topMargin = TOP_MARGIN; - tableWrapLayout.bottomMargin = BOTTOM_MARGIN; - inner.setLayout(tableWrapLayout); - Label lab0 = new Label(inner, SWT.WRAP); - lab0.setText(A80); - Label labLeft = new Label(inner, SWT.NULL); - labLeft.setText(A1); + layout.leftMargin = LEFT_MARGIN; + layout.rightMargin = RIGHT_MARGIN; + layout.topMargin = TOP_MARGIN; + layout.bottomMargin = BOTTOM_MARGIN; + Control lab0 = ControlFactory.create(inner, 50, 800, 15); + + Control labLeft = ControlFactory.create(inner, 50, 100, 15); TableWrapData dataLeft = new TableWrapData(); dataLeft.align = TableWrapData.LEFT; labLeft.setLayoutData(dataLeft); - Label labRight = new Label(inner, SWT.NULL); - labRight.setText(A1); + + Control labRight = ControlFactory.create(inner, 50, 100, 15); TableWrapData dataRight = new TableWrapData(); dataRight.align = TableWrapData.RIGHT; labRight.setLayoutData(dataRight); - Label labCenter = new Label(inner, SWT.NULL); - labCenter.setText(A1); + + Control labCenter = ControlFactory.create(inner, 50, 100, 15); TableWrapData dataCenter = new TableWrapData(); dataCenter.align = TableWrapData.CENTER; labCenter.setLayoutData(dataCenter); - Label labFill = new Label(inner, SWT.NULL); - labFill.setText(A1); + + Control labFill = ControlFactory.create(inner, 50, 100, 15); TableWrapData dataFill = new TableWrapData(); dataFill.align = TableWrapData.FILL; labFill.setLayoutData(dataFill); - shell.layout(); + + inner.setSize(300 + LEFT_MARGIN + RIGHT_MARGIN, 1000); + inner.layout(false); + // Check layout - assertEquals(LEFT_MARGIN , labLeft.getBounds().x); - assertTrue(rightEdge(lab0) > rightEdge(labLeft)); - assertTrue(rightEdge(labLeft) + tableWrapLayout.rightMargin < 100); + assertEquals(new Rectangle(LEFT_MARGIN, TOP_MARGIN, 300, 40), lab0.getBounds()); + assertEquals(new Rectangle(LEFT_MARGIN, bottomEdge(lab0), 100, 15), labLeft.getBounds()); + assertEquals(new Rectangle(rightEdge(lab0) - 100, bottomEdge(labLeft), 100, 15), labRight.getBounds()); - assertEquals(rightEdge(labRight), rightEdge(lab0)); - assertTrue(labRight.getBounds().x > LEFT_MARGIN); + int centerPoint = (leftEdge(labCenter) + rightEdge(labCenter)) / 2; + assertEquals(150, centerPoint - LEFT_MARGIN); - assertTrue(labCenter.getBounds().x > LEFT_MARGIN); - assertTrue(rightEdge(lab0) > rightEdge(labCenter)); + assertEquals(new Rectangle(LEFT_MARGIN, bottomEdge(labCenter), 300, 5), labFill.getBounds()); + } - int offCenter = rightEdge(labCenter) + labCenter.getBounds().x - - rightEdge(lab0) + lab0.getBounds().x; - assertTrue(offCenter >= -2); - assertTrue(offCenter <= 2); + private static void makeFilled(Control control) { + TableWrapData data = new TableWrapData(); + data.align = TableWrapData.FILL; + data.valign = TableWrapData.FILL; + control.setLayoutData(data); + } - assertEquals(LEFT_MARGIN , labFill.getBounds().x); - assertEquals(rightEdge(labFill), rightEdge(lab0)); - shell.dispose(); + private int leftEdge(Control control) { + Rectangle bounds = control.getBounds(); + + return bounds.x; + } + + private int bottomEdge(Control control) { + Rectangle bounds = control.getBounds(); + + return bounds.y + bounds.height; } } diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/ExpandableCompositeTest.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/ExpandableCompositeTest.java index 55b4e0dcf4b..37ed74327fa 100644 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/ExpandableCompositeTest.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/ExpandableCompositeTest.java @@ -31,6 +31,7 @@ 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; @@ -140,12 +141,7 @@ public class ExpandableCompositeTest { } private Composite rectangleComposite(final Composite parent, final int x, final int y) { - return new Composite(parent, SWT.NONE) { - @Override - public Point computeSize(int wHint, int hHint, boolean changed) { - return new Point(x, y); - } - }; + return ControlFactory.create(parent, x, y); } private Composite createClient() { diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/HintAdjustmentTest.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/HintAdjustmentTest.java new file mode 100644 index 00000000000..73595aebd32 --- /dev/null +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/HintAdjustmentTest.java @@ -0,0 +1,247 @@ +package org.eclipse.ui.tests.forms.widgets; + +import static org.junit.Assert.assertEquals; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Scrollable; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.forms.widgets.Form; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.eclipse.ui.forms.widgets.ImageHyperlink; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.ScrolledFormText; +import org.eclipse.ui.forms.widgets.ScrolledPageBook; +import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.forms.widgets.TreeNode; +import org.eclipse.ui.forms.widgets.Twistie; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * SWT controls have a complicated contract on + * {@link Control#computeSize(int, int)}. Specifically, if a non-SWT.DEFAULT + * width (or height) hint is passed as an argument, that argument is supposed to + * be a few pixels off from the constrained width being queried. That means you + * need to subtract a small adjustment to the hints prior to invoking + * computeSize. The implementation of computeSize is required to add these + * adjustments back on when it receives the arguments. The exact adjustment to + * use is well-defined (see the implementation of {@link #verifyComputeSize} for + * details). + * <p> + * This API contract is poorly documented and may seem a bit crazy, but since + * every layout depends on it and every control implements it, there's no good + * way to fix it without API breakage -- so libraries like forms need to + * implement it correctly. + * <p> + * The purpose of this test is to ensure that all the controls in the forms + * library add the correct adjustments to the hints they receive. One way to + * verify this is to invoke computeSize with constant hints and assert that the + * result differs by the hint by exactly the amount expected for the adjustment + * value. + */ +public class HintAdjustmentTest { + private static Display display; + + static { + try { + display = PlatformUI.getWorkbench().getDisplay(); + } catch (Throwable e) { + // this is to run without eclipse + display = new Display(); + } + } + + private Shell shell; + + @Before + public void setUp() throws Exception { + shell = new Shell(display); + } + + @After + public void tearDown() throws Exception { + shell.dispose(); + } + + void verifyComputeSize(Control control) { + int widthAdjustment; + int heightAdjustment; + if (control instanceof Scrollable) { + // For scrollables, subtract off the trim size + Scrollable scrollable = (Scrollable) control; + Rectangle trim = scrollable.computeTrim(0, 0, 0, 0); + + widthAdjustment = trim.width; + heightAdjustment = trim.height; + } else { + // For non-composites, subtract off 2 * the border size + widthAdjustment = control.getBorderWidth() * 2; + heightAdjustment = widthAdjustment; + } + + final int TEST_VALUE = 100; + + Point computedSize = control.computeSize(TEST_VALUE, TEST_VALUE); + + assertEquals("control is not applying the width adjustment correctly", TEST_VALUE + widthAdjustment, + computedSize.x); + assertEquals("control is not applying the height adjustment correctly", TEST_VALUE + heightAdjustment, + computedSize.y); + } + + @Test + public void testScrollingHyperlink() { + Hyperlink link = new Hyperlink(shell, SWT.H_SCROLL | SWT.V_SCROLL); + link.setText("This is some sample text"); + verifyComputeSize(link); + } + + @Test + public void testHyperlink() { + Hyperlink link = new Hyperlink(shell, SWT.NONE); + link.setText("This is some sample text"); + verifyComputeSize(link); + } + + @Test + public void testScrollingExpandableComposite() { + ExpandableComposite ec = new ExpandableComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL); + ec.setText("Foo bar baz zipp"); + verifyComputeSize(ec); + } + + @Test + public void testExpandableComposite() { + ExpandableComposite ec = new ExpandableComposite(shell, SWT.NONE); + ec.setText("Foo bar baz zipp"); + verifyComputeSize(ec); + } + + @Test + public void testScrollingForm() { + Form form = new Form(shell, SWT.H_SCROLL | SWT.V_SCROLL); + form.setMessage("Hello world"); + verifyComputeSize(form); + } + + @Test + public void testForm() { + Form form = new Form(shell, SWT.NONE); + form.setMessage("Hello world"); + verifyComputeSize(form); + } + + @Test + public void testScrollingFormText() { + FormText formText = new FormText(shell, SWT.H_SCROLL | SWT.V_SCROLL); + formText.setText("This izza test", false, false); + verifyComputeSize(formText); + } + + @Test + public void testFormText() { + FormText formText = new FormText(shell, SWT.NONE); + formText.setText("This izza test", false, false); + verifyComputeSize(formText); + } + + @Test + public void testScrollingImageHyperlink() { + ImageHyperlink hyperlink = new ImageHyperlink(shell, SWT.H_SCROLL | SWT.V_SCROLL); + hyperlink.setText("Foo, bar, baz"); + verifyComputeSize(hyperlink); + } + + @Test + public void testImageHyperlink() { + ImageHyperlink hyperlink = new ImageHyperlink(shell, SWT.NONE); + hyperlink.setText("Foo, bar, baz"); + verifyComputeSize(hyperlink); + } + + @Test + public void testScrolledForm() { + ScrolledForm scrolledForm = new ScrolledForm(shell, SWT.NONE); + scrolledForm.setText("Foo, bar, baz"); + verifyComputeSize(scrolledForm); + } + + @Test + public void testScrollingScrolledForm() { + ScrolledForm scrolledForm = new ScrolledForm(shell, SWT.H_SCROLL | SWT.V_SCROLL); + scrolledForm.setText("Foo, bar, baz"); + verifyComputeSize(scrolledForm); + } + + @Test + public void testScrolledFormText() { + ScrolledFormText scrolledForm = new ScrolledFormText(shell, SWT.NONE, true); + scrolledForm.setText("Foo, bar, baz"); + verifyComputeSize(scrolledForm); + } + + @Test + public void testScrollingScrolledFormText() { + ScrolledFormText scrolledForm = new ScrolledFormText(shell, SWT.H_SCROLL | SWT.V_SCROLL, true); + scrolledForm.setText("Foo, bar, baz"); + verifyComputeSize(scrolledForm); + } + + @Test + public void testScrolledPageBook() { + ScrolledPageBook scrolledPageBook = new ScrolledPageBook(shell, SWT.NONE); + verifyComputeSize(scrolledPageBook); + } + + @Test + public void testScrollingScrolledPageBook() { + ScrolledPageBook scrolledPageBook = new ScrolledPageBook(shell, SWT.H_SCROLL | SWT.V_SCROLL); + verifyComputeSize(scrolledPageBook); + } + + @Test + public void testSection() { + Section section = new Section(shell, SWT.NONE); + section.setText("Hi ho he hum de da doo dum"); + verifyComputeSize(section); + } + + @Test + public void testScrollingSection() { + Section section = new Section(shell, SWT.H_SCROLL | SWT.V_SCROLL); + section.setText("Hi ho he hum de da doo dum"); + verifyComputeSize(section); + } + + @Test + public void testTreeNode() { + TreeNode treeNode = new TreeNode(shell, SWT.NONE); + verifyComputeSize(treeNode); + } + + @Test + public void testScrollingTreeNode() { + TreeNode treeNode = new TreeNode(shell, SWT.H_SCROLL | SWT.V_SCROLL); + verifyComputeSize(treeNode); + } + + @Test + public void testTwistie() { + Twistie twistie = new Twistie(shell, SWT.NONE); + verifyComputeSize(twistie); + } + + @Test + public void testScrollingTwistie() { + Twistie twistie = new Twistie(shell, SWT.H_SCROLL | SWT.V_SCROLL); + verifyComputeSize(twistie); + } +} diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/SizeCacheTest.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/SizeCacheTest.java index d259d29dedf..044d61d8782 100644 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/SizeCacheTest.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/widgets/SizeCacheTest.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.ui.tests.forms.widgets; +import static org.junit.Assert.assertEquals; + import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; @@ -27,11 +29,11 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.SizeCache; import org.eclipse.ui.forms.widgets.TableWrapLayout; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import junit.framework.TestCase; - -public class SizeCacheTest extends TestCase { +public class SizeCacheTest { private static Display display; private Shell shell; private static final String SHORT_TEXT = "Hedgehog"; @@ -54,7 +56,7 @@ public class SizeCacheTest extends TestCase { } } - @Override + @Before public void setUp() throws Exception { font = new Font(display, "Arial", 12, SWT.NORMAL); shell = new Shell(display); @@ -64,7 +66,7 @@ public class SizeCacheTest extends TestCase { shell.open(); } - @Override + @After public void tearDown() throws Exception { if (humanWatching) dispatch(1000); @@ -120,15 +122,41 @@ public class SizeCacheTest extends TestCase { control.setSize(expectedSize); dispatch(); - expectedSize = getAdjustedExpected(expectedSize, whint, hhint); - checkDoubleCall(whint, hhint); checkPreferedThenOther(whint, hhint); return expectedSize; } - private Point controlComputeSize(int whint, int hhint) { - return control.computeSize(whint, hhint, true); + private Point controlComputeSize(int wHint, int hHint) { + Point adjusted = computeHintOffset(); + + int adjustedWidthHint = wHint; + if (adjustedWidthHint != SWT.DEFAULT) { + adjustedWidthHint = Math.max(0, wHint - adjusted.x); + } + + int adjustedHeightHint = hHint; + if (adjustedHeightHint != SWT.DEFAULT) { + adjustedHeightHint = Math.max(0, hHint - adjusted.y); + } + + Point result = control.computeSize(adjustedWidthHint, adjustedHeightHint, true); + + // Ignore any component if the hint was something other than SWT.DEFAULT. + // There's no way to measure hints smaller than the adjustment value and + // somecontrols have buggy computeSize methods that don't return non-default + // hints verbatim. The purpose of this test is to verify SizeCache, not the + // controls, and we don't want such quirks to create failures, so we correct the + // result here if necessary. + if (wHint != SWT.DEFAULT) { + result.x = wHint; + } + + if (hHint != SWT.DEFAULT) { + result.y = hHint; + } + + return result; } private Point checkAlterate(int whint, int hhint) { @@ -143,13 +171,6 @@ public class SizeCacheTest extends TestCase { return expectedSize1; } - private Point getAdjustedExpected(Point calcSize, int whint, int hhint) { - Point adjusted = computeHintOffset(); - int expectedHeight = hhint == SWT.DEFAULT ? calcSize.y : hhint + adjusted.y; - int expectedWidth = whint == SWT.DEFAULT ? calcSize.x : whint + adjusted.x; - return new Point(expectedWidth, expectedHeight); - } - private Point computeHintOffset() { Point size = new Point(0, 0); if (control instanceof Scrollable) { |
