Mercurial > projects > dwt-mac
diff dwt/layout/FormLayout.d @ 0:380af2bdd8e5
Upload of whole dwt tree
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sat, 09 Aug 2008 17:00:02 +0200 |
parents | |
children | 649b8e223d5a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/layout/FormLayout.d Sat Aug 09 17:00:02 2008 +0200 @@ -0,0 +1,389 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + *******************************************************************************/ +module dwt.layout.FormLayout; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Layout; +import dwt.widgets.Scrollable; + +/** + * Instances of this class control the position and size of the + * children of a composite control by using <code>FormAttachments</code> + * to optionally configure the left, top, right and bottom edges of + * each child. + * <p> + * The following example code creates a <code>FormLayout</code> and then sets + * it into a <code>Shell</code>: + * <pre> + * Display display = new Display (); + * Shell shell = new Shell(display); + * FormLayout layout = new FormLayout(); + * layout.marginWidth = 3; + * layout.marginHeight = 3; + * shell.setLayout(layout); + * </pre> + * </p> + * <p> + * To use a <code>FormLayout</code>, create a <code>FormData</code> with + * <code>FormAttachment</code> for each child of <code>Composite</code>. + * The following example code attaches <code>button1</code> to the top + * and left edge of the composite and <code>button2</code> to the right + * edge of <code>button1</code> and the top and right edges of the + * composite: + * <pre> + * FormData data1 = new FormData(); + * data1.left = new FormAttachment(0, 0); + * data1.top = new FormAttachment(0, 0); + * button1.setLayoutData(data1); + * FormData data2 = new FormData(); + * data2.left = new FormAttachment(button1); + * data2.top = new FormAttachment(0, 0); + * data2.right = new FormAttachment(100, 0); + * button2.setLayoutData(data2); + * </pre> + * </p> + * <p> + * Each side of a child control can be attached to a position in the parent + * composite, or to other controls within the <code>Composite</code> by + * creating instances of <code>FormAttachment</code> and setting them into + * the top, bottom, left, and right fields of the child's <code>FormData</code>. + * </p> + * <p> + * If a side is not given an attachment, it is defined as not being attached + * to anything, causing the child to remain at its preferred size. If a child + * is given no attachment on either the left or the right or top or bottom, it is + * automatically attached to the left and top of the composite respectively. + * The following code positions <code>button1</code> and <code>button2</code> + * but relies on default attachments: + * <pre> + * FormData data2 = new FormData(); + * data2.left = new FormAttachment(button1); + * data2.right = new FormAttachment(100, 0); + * button2.setLayoutData(data2); + * </pre> + * </p> + * <p> + * IMPORTANT: Do not define circular attachments. For example, do not attach + * the right edge of <code>button1</code> to the left edge of <code>button2</code> + * and then attach the left edge of <code>button2</code> to the right edge of + * <code>button1</code>. This will over constrain the layout, causing undefined + * behavior. The algorithm will terminate, but the results are undefined. + * </p> + * + * @see FormData + * @see FormAttachment + * + * @since 2.0 + * + */ +public final class FormLayout extends Layout { + + /** + * marginWidth specifies the number of pixels of horizontal margin + * that will be placed along the left and right edges of the layout. + * + * The default value is 0. + */ + public int marginWidth = 0; + + /** + * marginHeight specifies the number of pixels of vertical margin + * that will be placed along the top and bottom edges of the layout. + * + * The default value is 0. + */ + public int marginHeight = 0; + + + /** + * marginLeft specifies the number of pixels of horizontal margin + * that will be placed along the left edge of the layout. + * + * The default value is 0. + * + * @since 3.1 + */ + public int marginLeft = 0; + + /** + * marginTop specifies the number of pixels of vertical margin + * that will be placed along the top edge of the layout. + * + * The default value is 0. + * + * @since 3.1 + */ + public int marginTop = 0; + + /** + * marginRight specifies the number of pixels of horizontal margin + * that will be placed along the right edge of the layout. + * + * The default value is 0. + * + * @since 3.1 + */ + public int marginRight = 0; + + /** + * marginBottom specifies the number of pixels of vertical margin + * that will be placed along the bottom edge of the layout. + * + * The default value is 0. + * + * @since 3.1 + */ + public int marginBottom = 0; + + /** + * spacing specifies the number of pixels between the edge of one control + * and the edge of its neighbouring control. + * + * The default value is 0. + * + * @since 3.0 + */ + public int spacing = 0; + +/** + * Constructs a new instance of this class. + */ +public FormLayout () { +} + +/* + * Computes the preferred height of the form with + * respect to the preferred height of the control. + * + * Given that the equations for top (T) and bottom (B) + * of the control in terms of the height of the form (X) + * are: + * T = AX + B + * B = CX + D + * + * The equation for the height of the control (H) + * is bottom (B) minus top (T) or (H = B - T) or: + * + * H = (CX + D) - (AX + B) + * + * Solving for (X), the height of the form, we get: + * + * X = (H + B - D) / (C - A) + * + * When (A = C), (C - A = 0) and the equation has no + * solution for X. This is a special case meaning that + * the control does not constrain the height of the + * form. In this case, we need to arbitrarily define + * the height of the form (X): + * + * Case 1: A = C, A = 0, C = 0 + * + * Let X = D, the distance from the top of the form + * to the bottom edge of the control. In this case, + * the control was attached to the top of the form + * and the form needs to be large enough to show the + * bottom edge of the control. + * + * Case 2: A = C, A = 1, C = 1 + * + * Let X = -B, the distance from the bottom of the + * form to the top edge of the control. In this case, + * the control was attached to the bottom of the form + * and the only way that the control would be visible + * is if the offset is negative. If the offset is + * positive, there is no possible height for the form + * that will show the control as it will always be + * below the bottom edge of the form. + * + * Case 3: A = C, A !is 0, C !is 0 and A !is 1, C !is 0 + * + * Let X = D / (1 - C), the distance from the top of the + * form to the bottom edge of the control. In this case, + * since C is not 0 or 1, it must be a fraction, U / V. + * The offset D is the distance from CX to the bottom edge + * of the control. This represents a fraction of the form + * (1 - C)X. Since the height of a fraction of the form is + * known, the height of the entire form can be found by setting + * (1 - C)X = D. We solve this equation for X in terms of U + * and V, giving us X = (U * D) / (U - V). Similarly, if the + * offset D is negative, the control is positioned above CX. + * The offset -B is the distance from the top edge of the control + * to CX. We can find the height of the entire form by setting + * CX = -B. Solving in terms of U and V gives us X = (-B * V) / U. + */ +int computeHeight (Control control, FormData data, bool flushCache) { + FormAttachment top = data.getTopAttachment (control, spacing, flushCache); + FormAttachment bottom = data.getBottomAttachment (control, spacing, flushCache); + FormAttachment height = bottom.minus (top); + if (height.numerator is 0) { + if (bottom.numerator is 0) return bottom.offset; + if (bottom.numerator is bottom.denominator) return -top.offset; + if (bottom.offset <= 0) { + return -top.offset * top.denominator / bottom.numerator; + } + int divider = bottom.denominator - bottom.numerator; + return bottom.denominator * bottom.offset / divider; + } + return height.solveY (data.getHeight (control, flushCache)); +} + +protected Point computeSize (Composite composite, int wHint, int hHint, bool flushCache) { + Point size = layout (composite, false, 0, 0, wHint, hHint, flushCache); + if (wHint !is DWT.DEFAULT) size.x = wHint; + if (hHint !is DWT.DEFAULT) size.y = hHint; + return size; +} + +protected bool flushCache (Control control) { + Object data = control.getLayoutData (); + if (data !is null) ((FormData) data).flushCache (); + return true; +} + +String getName () { + String string = getClass ().getName (); + int index = string.lastIndexOf ('.'); + if (index is -1) return string; + return string.substring (index + 1, string.length ()); +} + +/* + * Computes the preferred height of the form with + * respect to the preferred height of the control. + */ +int computeWidth (Control control, FormData data, bool flushCache) { + FormAttachment left = data.getLeftAttachment (control, spacing, flushCache); + FormAttachment right = data.getRightAttachment (control, spacing, flushCache); + FormAttachment width = right.minus (left); + if (width.numerator is 0) { + if (right.numerator is 0) return right.offset; + if (right.numerator is right.denominator) return -left.offset; + if (right.offset <= 0) { + return -left.offset * left.denominator / left.numerator; + } + int divider = right.denominator - right.numerator; + return right.denominator * right.offset / divider; + } + return width.solveY (data.getWidth (control, flushCache)); +} + +protected void layout (Composite composite, bool flushCache) { + Rectangle rect = composite.getClientArea (); + int x = rect.x + marginLeft + marginWidth; + int y = rect.y + marginTop + marginHeight; + int width = Math.max (0, rect.width - marginLeft - 2 * marginWidth - marginRight); + int height = Math.max (0, rect.height - marginTop - 2 * marginHeight - marginBottom); + layout (composite, true, x, y, width, height, flushCache); +} + +Point layout (Composite composite, bool move, int x, int y, int width, int height, bool flushCache) { + Control [] children = composite.getChildren (); + for (int i=0; i<children.length; i++) { + Control child = children [i]; + FormData data = (FormData) child.getLayoutData (); + if (data is null) child.setLayoutData (data = new FormData ()); + if (flushCache) data.flushCache (); + data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null; + } + bool [] flush = null; + Rectangle [] bounds = null; + int w = 0, h = 0; + for (int i=0; i<children.length; i++) { + Control child = children [i]; + FormData data = (FormData) child.getLayoutData (); + if (width !is DWT.DEFAULT) { + data.needed = false; + FormAttachment left = data.getLeftAttachment (child, spacing, flushCache); + FormAttachment right = data.getRightAttachment (child, spacing, flushCache); + int x1 = left.solveX (width), x2 = right.solveX (width); + if (data.height is DWT.DEFAULT && !data.needed) { + int trim = 0; + //TEMPORARY CODE + if (child instanceof Scrollable) { + Rectangle rect = ((Scrollable) child).computeTrim (0, 0, 0, 0); + trim = rect.width; + } else { + trim = child.getBorderWidth () * 2; + } + data.cacheWidth = data.cacheHeight = -1; + int currentWidth = Math.max (0, x2 - x1 - trim); + data.computeSize (child, currentWidth, data.height, flushCache); + if (flush is null) flush = new bool [children.length]; + flush [i] = true; + } + w = Math.max (x2, w); + if (move) { + if (bounds is null) bounds = new Rectangle [children.length]; + bounds [i] = new Rectangle (0, 0, 0, 0); + bounds [i].x = x + x1; + bounds [i].width = x2 - x1; + } + } else { + w = Math.max (computeWidth (child, data, flushCache), w); + } + } + for (int i=0; i<children.length; i++) { + Control child = children [i]; + FormData data = (FormData) child.getLayoutData (); + if (height !is DWT.DEFAULT) { + int y1 = data.getTopAttachment (child, spacing, flushCache).solveX (height); + int y2 = data.getBottomAttachment (child, spacing, flushCache).solveX (height); + h = Math.max (y2, h); + if (move) { + bounds [i].y = y + y1; + bounds [i].height = y2 - y1; + } + } else { + h = Math.max (computeHeight (child, data, flushCache), h); + } + } + for (int i=0; i<children.length; i++) { + Control child = children [i]; + FormData data = (FormData) child.getLayoutData (); + if (flush !is null && flush [i]) data.cacheWidth = data.cacheHeight = -1; + data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null; + } + if (move) { + for (int i=0; i<children.length; i++) { + children [i].setBounds (bounds [i]); + } + } + w += marginLeft + marginWidth * 2 + marginRight; + h += marginTop + marginHeight * 2 + marginBottom; + return new Point (w, h); +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the layout + */ +public String toString () { + String string = getName ()+" {"; + if (marginWidth !is 0) string += "marginWidth="+marginWidth+" "; + if (marginHeight !is 0) string += "marginHeight="+marginHeight+" "; + if (marginLeft !is 0) string += "marginLeft="+marginLeft+" "; + if (marginRight !is 0) string += "marginRight="+marginRight+" "; + if (marginTop !is 0) string += "marginTop="+marginTop+" "; + if (marginBottom !is 0) string += "marginBottom="+marginBottom+" "; + if (spacing !is 0) string += "spacing="+spacing+" "; + string = string.trim(); + string += "}"; + return string; +} +}