/******************************************************************************* * Copyright (c) 2005, 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 * Stefan Xenos, IBM - initial implementation, bug 178888 * Karsten Stoeckmann - bug 156982 *******************************************************************************/ package org.eclipse.jface.layout; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; /** * GridLayoutFactory creates and initializes grid layouts. There are two ways to use GridLayoutFactory. * Normally, it is used as a shorthand for writing "new GridLayout()" and initializing a bunch * of fields. In this case the main benefit is a more concise syntax and the ability to create more * than one identical GridLayout from the same factory. Changing a property of the factory will affect * future layouts created by the factory, but has no effect on layouts that have already been created. * * <p> * GridLayoutFactory can also generate grid data for all the controls in a layout. This is done with * the generateLayout method. To use this feature: * </p> * * <ol> * <li>Create the composite</li> * <li>Create all the controls in the composite</li> * <li>Call generateLayout</li> * </ol> * * <p> * The order here is important. generateLayout must be called after all the child controls have * been created. generateLayout will not change any layout data that has already been attached * to a child control and it will not recurse into nested composites. * </p> * * @since 3.2 */ public final class GridLayoutFactory { /** * Template layout. The factory will create copies of this layout. */ private GridLayout l; /** * Creates a new GridLayoutFactory that will create copies of the given layout. * * @param l layout to copy */ private GridLayoutFactory(GridLayout l) { this.l = l; } /** * Creates a factory that creates copies of the given layout. * * @param l layout to copy * @return a new GridLayoutFactory instance that creates copies of the given layout */ public static GridLayoutFactory createFrom(GridLayout l) { return new GridLayoutFactory(copyLayout(l)); } /** * Creates a copy of the reciever. * * @return a copy of the reciever */ public GridLayoutFactory copy() { return new GridLayoutFactory(create()); } /** * Creates a GridLayoutFactory that creates GridLayouts with the default SWT * values. * * <p> * Initial values are: * </p> * * <ul> * <li>numColumns(1)</li> * <li>margins(5,5)</li> * <li>extendedMargins(0,0,0,0)</li> * <li>spacing(5,5)</li> * <li>equalWidth(false)</li> * </ul> * * @return a GridLayoutFactory that creates GridLayouts as though created with * their default constructor * @see #fillDefaults */ public static GridLayoutFactory swtDefaults() { return new GridLayoutFactory(new GridLayout()); } /** * Creates a GridLayoutFactory that creates GridLayouts with no margins and * default dialog spacing. * * <p> * Initial values are: * </p> * * <ul> * <li>numColumns(1)</li> * <li>margins(0,0)</li> * <li>extendedMargins(0,0,0,0)</li> * <li>spacing(LayoutConstants.getSpacing())</li> * <li>equalWidth(false)</li> * </ul> * * @return a GridLayoutFactory that creates GridLayouts as though created with * their default constructor * @see #swtDefaults */ public static GridLayoutFactory fillDefaults() { GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; Point defaultSpacing = LayoutConstants.getSpacing(); layout.horizontalSpacing = defaultSpacing.x; layout.verticalSpacing = defaultSpacing.y; return new GridLayoutFactory(layout); } /** * Sets whether the columns should be forced to be equal width * * @param equal true iff the columns should be forced to be equal width * @return this */ public GridLayoutFactory equalWidth(boolean equal) { l.makeColumnsEqualWidth = equal; return this; } /** * Sets the spacing for layouts created with this factory. The spacing * is the distance between cells within the layout. * * @param hSpacing horizontal spacing (pixels) * @param vSpacing vertical spacing (pixels) * @return this * @see #margins(Point) * @see #margins(int, int) */ public GridLayoutFactory spacing(int hSpacing, int vSpacing) { l.horizontalSpacing = hSpacing; l.verticalSpacing = vSpacing; return this; } /** * Sets the spacing for layouts created with this factory. The spacing * is the distance between cells within the layout. * * @param spacing space between controls in the layout (pixels) * @return this * @see #margins(Point) * @see #margins(int, int) */ public GridLayoutFactory spacing(Point spacing) { l.horizontalSpacing = spacing.x; l.verticalSpacing = spacing.y; return this; } /** * Sets the margins for layouts created with this factory. The margins * are the distance between the outer cells and the edge of the layout. * * @param margins margin size (pixels) * @return this * @see #spacing(Point) * @see #spacing(int, int) */ public GridLayoutFactory margins(Point margins) { l.marginWidth = margins.x; l.marginHeight = margins.y; return this; } /** * Sets the margins for layouts created with this factory. The margins * specify the number of pixels of horizontal and vertical margin that will * be placed along the left/right and top/bottom edges of the layout. Note * that thes margins will be added to the ones specified by * {@link #extendedMargins(int, int, int, int)}. * * @param width * margin width (pixels) * @param height * margin height (pixels) * @return this * @see #spacing(Point) * * @see #spacing(int, int) */ public GridLayoutFactory margins(int width, int height) { l.marginWidth = width; l.marginHeight = height; return this; } /** * Sets the margins for layouts created with this factory. The margins * specify the number of pixels of horizontal and vertical margin that will * be placed along the left, right, top, and bottom edges of the layout. * Note that thes margins will be added to the ones specified by * {@link #margins(int, int)}. * * @param left * left margin size (pixels) * @param right * right margin size (pixels) * @param top * top margin size (pixels) * @param bottom * bottom margin size (pixels) * @return this * @see #spacing(Point) * @see #spacing(int, int) * * @since 3.3 */ public GridLayoutFactory extendedMargins(int left, int right, int top, int bottom) { l.marginLeft = left; l.marginRight = right; l.marginTop = top; l.marginBottom = bottom; return this; } /** * Sets the margins for layouts created with this factory. The margins * specify the number of pixels of horizontal and vertical margin that will * be placed along the left, right, top, and bottom edges of the layout. * Note that thes margins will be added to the ones specified by * {@link #margins(int, int)}. * * <code><pre> * // Construct a GridLayout whose left, right, top, and bottom * // margin sizes are 10, 5, 0, and 15 respectively * * Rectangle margins = Geometry.createDiffRectangle(10,5,0,15); * GridLayoutFactory.fillDefaults().extendedMargins(margins).applyTo(composite1); * </pre></code> * * @param differenceRect rectangle which, when added to the client area of the * layout, returns the outer area of the layout. The x and y values of * the rectangle correspond to the position of the bounds of the * layout with respect to the client area. They should be negative. * The width and height correspond to the relative size of the bounds * of the layout with respect to the client area, and should be positive. * @return this * @see #spacing(Point) * @see #spacing(int, int) * * @since 3.3 */ public GridLayoutFactory extendedMargins(Rectangle differenceRect) { l.marginLeft = -differenceRect.x; l.marginTop = -differenceRect.y; l.marginBottom = differenceRect.y + differenceRect.height; l.marginRight = differenceRect.x + differenceRect.width; return this; } /** * Sets the number of columns in the layout * * @param numColumns number of columns in the layout * @return this */ public GridLayoutFactory numColumns(int numColumns) { l.numColumns = numColumns; return this; } /** * Creates a new GridLayout, and initializes it with values from the factory. * * @return a new initialized GridLayout. * @see #applyTo */ public GridLayout create() { return copyLayout(l); } /** * Creates a new GridLayout and attaches it to the given composite. * Does not create the GridData of any of the controls in the composite. * * @param c composite whose layout will be set * @see #generateLayout * @see #create * @see GridLayoutFactory */ public void applyTo(Composite c) { c.setLayout(copyLayout(l)); } /** * Copies the given GridLayout instance * * @param l layout to copy * @return a new GridLayout */ public static GridLayout copyLayout(GridLayout l) { GridLayout result = new GridLayout(l.numColumns, l.makeColumnsEqualWidth); result.horizontalSpacing = l.horizontalSpacing; result.marginBottom = l.marginBottom; result.marginHeight = l.marginHeight; result.marginLeft = l.marginLeft; result.marginRight = l.marginRight; result.marginTop = l.marginTop; result.marginWidth = l.marginWidth; result.verticalSpacing = l.verticalSpacing; return result; } /** * Applies this layout to the given composite, and attaches default GridData * to all immediate children that don't have one. The layout is generated using * heuristics based on the widget types. In most cases, it will create exactly the same * layout that would have been hardcoded by the programmer. In any situation * where it does not produce the desired layout, the GridData for any child can be * overridden by attaching the layout data before calling this method. In these cases, * the special-case layout data can be hardcoded and the algorithm can supply defaults * to the rest. * * <p> * This must be called <b>AFTER</b> all of the child controls have been created and their * layouts attached. This method will attach a layout to the given composite. If any new * children are created after calling this method, their GridData must be created manually. * The algorithm does not recurse into child composites. To generate all the layouts in * a widget hierarchy, the method must be called bottom-up for each Composite. * </p> * * <p> * All controls are made to span a single cell. The algorithm tries to classify controls into one * of the following categories: * </p> * * <ul> * <li>Pushbuttons: Set to a constant size large enough to fit their text and no smaller * than the default button size.</li> * <li>Wrapping with text (labels, read-only text boxes, etc.): override the preferred horizontal * size with the default wrapping point, fill horizontally, grab horizontal space, keep the * preferred vertical size</li> * <li>Wrapping without text (toolbars, coolbars, etc.): fill align, don't grab, use the preferred size</li> * <li>Horizontally scrolling controls (anything with horizontal scrollbars or where the user edits * text and can cursor through it from left-to-right): override the preferred horizontal size with * a constant, grab horizontal, fill horizontal.</li> * <li>Vertically scrolling controls (anything with vertical scrollbars or where the user edits * text and can cursor through it up and down): override the preferred vertical size with a constant, * grab vertical, fill vertical</li> * <li>Nested layouts: fill align both directions, grab along any dimension if the layout would * be able to expand along that dimension.</li> * <li>Non-wrapping non-scrollable read-only text: fill horizontally, center vertically, default size, don't grab </li> * <li>Non-wrapping non-scrollable non-text: fill both, default size, don't grab</li> * </ul> * * @param c composite whose layout will be generated */ public void generateLayout(Composite c) { applyTo(c); LayoutGenerator.generateLayout(c); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("GridLayoutFactory.fillDefaults()\n"); //$NON-NLS-1$ if (l.numColumns > 1) { builder.append(" .numColumns("); //$NON-NLS-1$ builder.append(l.numColumns); builder.append(")\n"); //$NON-NLS-1$ } if (l.makeColumnsEqualWidth) { builder.append(" .equalWidth(true)\n"); //$NON-NLS-1$ } if (l.marginBottom != 0 || l.marginTop != 0 || l.marginLeft != 0 || l.marginRight != 0) { builder.append(" .extendedMargins("); //$NON-NLS-1$ builder.append(l.marginLeft); builder.append(", "); //$NON-NLS-1$ builder.append(l.marginRight); builder.append(", "); //$NON-NLS-1$ builder.append(l.marginTop); builder.append(", "); //$NON-NLS-1$ builder.append(l.marginBottom); builder.append(")\n"); //$NON-NLS-1$ } if (l.marginWidth != 0 || l.marginHeight != 0) { builder.append(" .margins("); //$NON-NLS-1$ builder.append(l.marginWidth); builder.append(", "); //$NON-NLS-1$ builder.append(l.marginHeight); builder.append(")\n"); //$NON-NLS-1$ } Point defaultSpacing = LayoutConstants.getSpacing(); if (defaultSpacing.x != l.horizontalSpacing || defaultSpacing.y != l.verticalSpacing) { builder.append(" .spacing("); //$NON-NLS-1$ builder.append(l.horizontalSpacing); builder.append(", "); //$NON-NLS-1$ builder.append(l.verticalSpacing); builder.append(")\n"); //$NON-NLS-1$ } return builder.toString(); } }