# HG changeset patch # User Frank Benoit # Date 1200044652 -3600 # Node ID d48f7334742c9b94668199a41b6fb52a2e6af4a8 # Parent a67796650ad4fb0ffe028514db42ca3e2507079e FormLayout, FormData, FormAttachment diff -r a67796650ad4 -r d48f7334742c dwt/layout/FormAttachment.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/layout/FormAttachment.d Fri Jan 11 10:44:12 2008 +0100 @@ -0,0 +1,304 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.FormAttachment; + +import dwt.SWT; +import dwt.widgets.Control; +import dwt.layout.FormLayout; +import dwt.layout.FormData; + +import tango.text.convert.Format; + +/** + * Instances of this class are used to define the edges of a control + * within a FormLayout. + *

+ * FormAttachments are set into the top, bottom, left, + * and right fields of the FormData for a control. + * For example: + *

+ * 		FormData data = new FormData();
+ * 		data.top = new FormAttachment(0,5);
+ * 		data.bottom = new FormAttachment(100,-5);
+ * 		data.left = new FormAttachment(0,5);
+ * 		data.right = new FormAttachment(100,-5);
+ * 		button.setLayoutData(data);
+ * 
+ *

+ *

+ * A FormAttachment defines where to attach the side of + * a control by using the equation, y = ax + b. The "a" term represents + * a fraction of the parent composite's width (from the left) or height + * (from the top). It can be defined using a numerator and denominator, + * or just a percentage value. If a percentage is used, the denominator + * is set to 100. The "b" term in the equation represents an offset, in + * pixels, from the attachment position. For example: + *

+ * 		FormAttachment attach = new FormAttachment (20, -5);
+ * 
+ * specifies that the side to which the FormAttachment + * object belongs will lie at 20% of the parent composite, minus 5 pixels. + *

+ *

+ * Control sides can also be attached to another control. + * For example: + *

+ * 		FormAttachment attach = new FormAttachment (button, 10);
+ * 
+ * specifies that the side to which the FormAttachment + * object belongs will lie in the same position as the adjacent side of + * the button control, plus 10 pixels. The control side can + * also be attached to the opposite side of the specified control. + * For example: + *
+ * 		FormData data = new FormData ();
+ * 		data.left = new FormAttachment (button, 0, SWT.LEFT);
+ * 
+ * specifies that the left side of the control will lie in the same position + * as the left side of the button control. The control can also + * be attached in a position that will center the control on the specified + * control. For example: + *
+ * 		data.left = new FormAttachment (button, 0, SWT.CENTER);
+ * 
+ * specifies that the left side of the control will be positioned so that it is + * centered between the left and right sides of the button control. + * If the alignment is not specified, the default is to attach to the adjacent side. + *

+ * + * @see FormLayout + * @see FormData + * + * @since 2.0 + */ +public final class FormAttachment { + /** + * numerator specifies the numerator of the "a" term in the + * equation, y = ax + b, which defines the attachment. + */ + public int numerator; + + /** + * denominator specifies the denominator of the "a" term in the + * equation, y = ax + b, which defines the attachment. + * + * The default value is 100. + */ + public int denominator = 100; + + /** + * offset specifies the offset, in pixels, of the control side + * from the attachment position. + * If the offset is positive, then the control side is offset + * to the right of or below the attachment position. If it is + * negative, then the control side is offset to the left of or + * above the attachment position. + * + * This is equivalent to the "b" term in the equation y = ax + b. + * The default value is 0. + */ + public int offset; + + /** + * control specifies the control to which the control side is + * attached. + */ + public Control control; + + /** + * alignment specifies the alignment of the control side that is + * attached to a control. + *

+ * For top and bottom attachments, TOP, BOTTOM and CENTER are used. For left + * and right attachments, LEFT, RIGHT and CENTER are used. If any other case + * occurs, the default will be used instead. + *

+ * + *
Possible values are: + */ + public int alignment; + +/** + * Constructs a new instance of this class. + * Since no numerator, denominator or offset is specified, + * the attachment is treated as a percentage of the form. + * The numerator is zero, the denominator is 100 and the + * offset is zero. + * + * @since 3.2 + */ +public this () { +} + +/** + * Constructs a new instance of this class given a numerator + * Since no denominator or offset is specified, the default + * is to treat the numerator as a percentage of the form, with a + * denominator of 100. The offset is zero. + * + * @param numerator the percentage of the position + * + * @since 3.0 + */ +public this (int numerator) { + this (numerator, 100, 0); +} + +/** + * Constructs a new instance of this class given a numerator + * and an offset. Since no denominator is specified, the default + * is to treat the numerator as a percentage of the form, with a + * denominator of 100. + * + * @param numerator the percentage of the position + * @param offset the offset of the side from the position + */ +public this (int numerator, int offset) { + this (numerator, 100, offset); +} + +/** + * Constructs a new instance of this class given a numerator + * and denominator and an offset. The position of the side is + * given by the fraction of the form defined by the numerator + * and denominator. + * + * @param numerator the numerator of the position + * @param denominator the denominator of the position + * @param offset the offset of the side from the position + */ +public this (int numerator, int denominator, int offset) { + if (denominator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO); + this.numerator = numerator; + this.denominator = denominator; + this.offset = offset; +} + +/** + * Constructs a new instance of this class given a control. + * Since no alignment is specified, the default alignment is + * to attach the side to the adjacent side of the specified + * control. Since no offset is specified, an offset of 0 is + * used. + * + * @param control the control the side is attached to + */ +public this (Control control) { + this (control, 0, SWT.DEFAULT); +} + +/** + * Constructs a new instance of this class given a control + * and an offset. Since no alignment is specified, the default + * alignment is to attach the side to the adjacent side of the + * specified control. + * + * @param control the control the side is attached to + * @param offset the offset of the side from the control + */ +public this (Control control, int offset) { + this (control, offset, SWT.DEFAULT); +} + +/** + * Constructs a new instance of this class given a control, + * an offset and an alignment. + * + * @param control the control the side is attached to + * @param offset the offset of the side from the control + * @param alignment the alignment of the side to the control it is attached to + */ +public this (Control control, int offset, int alignment) { + this.control = control; + this.offset = offset; + this.alignment = alignment; +} + +FormAttachment divide (int value) { + return new FormAttachment (numerator, denominator * value, offset / value); +} + +int gcd (int m, int n) { + int temp; + m = Math.abs (m); + n = Math.abs (n); + if (m < n) { + temp = m; + m = n; + n = temp; + } + while (n != 0){ + temp = m; + m = n; + n = temp % n; + } + return m; +} + +FormAttachment minus (FormAttachment attachment) { + FormAttachment solution = new FormAttachment (); + solution.numerator = numerator * attachment.denominator - denominator * attachment.numerator; + solution.denominator = denominator * attachment.denominator; + int gcd = gcd (solution.denominator, solution.numerator); + solution.numerator = solution.numerator / gcd; + solution.denominator = solution.denominator / gcd; + solution.offset = offset - attachment.offset; + return solution; +} + +FormAttachment minus (int value) { + return new FormAttachment (numerator, denominator, offset - value); +} + +FormAttachment plus (FormAttachment attachment) { + FormAttachment solution = new FormAttachment (); + solution.numerator = numerator * attachment.denominator + denominator * attachment.numerator; + solution.denominator = denominator * attachment.denominator; + int gcd = gcd (solution.denominator, solution.numerator); + solution.numerator = solution.numerator / gcd; + solution.denominator = solution.denominator / gcd; + solution.offset = offset + attachment.offset; + return solution; +} + +FormAttachment plus (int value) { + return new FormAttachment (numerator, denominator, offset + value); +} + +int solveX (int value) { + if (denominator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO); + return ((numerator * value) / denominator) + offset; +} + +int solveY (int value) { + if (numerator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO); + return (value - offset) * denominator / numerator; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the FormAttachment + */ +public char[] toString () { + char[] string = control != null ? control.toString () : Format( "{}/{}", numerator, denominator ); + return Format("{{y = ({})x + {}}", string, ( offset >= 0 ? Format(")x + {}", offset ) : Format( ")x - {}", -offset))); +} + +} diff -r a67796650ad4 -r d48f7334742c dwt/layout/FormData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/layout/FormData.d Fri Jan 11 10:44:12 2008 +0100 @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.FormData; + + +import dwt.SWT; +import dwt.graphics.Point; +import dwt.widgets.Control; +import dwt.layout.FormAttachment; +import tango.text.Util; + +import tango.text.Util; +import tango.util.Convert; + +/** + * Instances of this class are used to define the attachments + * of a control in a FormLayout. + *

+ * To set a FormData object into a control, you use the + * setLayoutData () method. To define attachments for the + * FormData, set the fields directly, like this: + *

+ * 		FormData data = new FormData();
+ * 		data.left = new FormAttachment(0,5);
+ * 		data.right = new FormAttachment(100,-5);
+ * 		button.setLayoutData(formData);
+ * 
+ *

+ *

+ * FormData contains the FormAttachments for + * each edge of the control that the FormLayout uses to + * determine the size and position of the control. FormData + * objects also allow you to set the width and height of controls within + * a FormLayout. + *

+ * + * @see FormLayout + * @see FormAttachment + * + * @since 2.0 + */ +public final class FormData { + /** + * width specifies the preferred width in pixels. This value + * is the wHint passed into Control.computeSize(int, int, bool) + * to determine the preferred size of the control. + * + * The default value is SWT.DEFAULT. + * + * @see Control#computeSize(int, int, bool) + */ + public int width = SWT.DEFAULT; + /** + * height specifies the preferred height in pixels. This value + * is the hHint passed into Control.computeSize(int, int, bool) + * to determine the preferred size of the control. + * + * The default value is SWT.DEFAULT. + * + * @see Control#computeSize(int, int, bool) + */ + public int height = SWT.DEFAULT; + /** + * left specifies the attachment of the left side of + * the control. + */ + public FormAttachment left; + /** + * right specifies the attachment of the right side of + * the control. + */ + public FormAttachment right; + /** + * top specifies the attachment of the top of the control. + */ + public FormAttachment top; + /** + * bottom specifies the attachment of the bottom of the + * control. + */ + public FormAttachment bottom; + + int cacheWidth = -1, cacheHeight = -1; + int defaultWhint, defaultHhint, defaultWidth = -1, defaultHeight = -1; + int currentWhint, currentHhint, currentWidth = -1, currentHeight = -1; + FormAttachment cacheLeft, cacheRight, cacheTop, cacheBottom; + bool isVisited, needed; + +/** + * Constructs a new instance of FormData using + * default values. + */ +public this () { +} + +/** + * Constructs a new instance of FormData according to the parameters. + * A value of SWT.DEFAULT indicates that no minimum width or + * no minimum height is specified. + * + * @param width a minimum width for the control + * @param height a minimum height for the control + */ +public this (int width, int height) { + this.width = width; + this.height = height; +} + +void computeSize (Control control, int wHint, int hHint, bool flushCache_) { + if (cacheWidth !is -1 && cacheHeight !is -1) return; + if (wHint is this.width && hHint is this.height) { + if (defaultWidth is -1 || defaultHeight is -1 || wHint !is defaultWhint || hHint !is defaultHhint) { + Point size = control.computeSize (wHint, hHint, flushCache_); + defaultWhint = wHint; + defaultHhint = hHint; + defaultWidth = size.x; + defaultHeight = size.y; + } + cacheWidth = defaultWidth; + cacheHeight = defaultHeight; + return; + } + if (currentWidth is -1 || currentHeight is -1 || wHint !is currentWhint || hHint !is currentHhint) { + Point size = control.computeSize (wHint, hHint, flushCache_); + currentWhint = wHint; + currentHhint = hHint; + currentWidth = size.x; + currentHeight = size.y; + } + cacheWidth = currentWidth; + cacheHeight = currentHeight; +} + +void flushCache () { + cacheWidth = cacheHeight = -1; + defaultHeight = defaultWidth = -1; + currentHeight = currentWidth = -1; +} + +int getWidth (Control control, bool flushCache) { + needed = true; + computeSize (control, width, height, flushCache); + return cacheWidth; +} + +int getHeight (Control control, bool flushCache) { + computeSize (control, width, height, flushCache); + return cacheHeight; +} + +FormAttachment getBottomAttachment (Control control, int spacing, bool flushCache) { + if (cacheBottom !is null) return cacheBottom; + if (isVisited) return cacheBottom = new FormAttachment (0, getHeight (control, flushCache)); + if (bottom is null) { + if (top is null) return cacheBottom = new FormAttachment (0, getHeight (control, flushCache)); + return cacheBottom = getTopAttachment (control, spacing, flushCache).plus (getHeight (control, flushCache)); + } + Control bottomControl = bottom.control; + if (bottomControl !is null) { + if (bottomControl.isDisposed ()) { + bottom.control = bottomControl = null; + } else { + if (bottomControl.getParent () !is control.getParent ()) { + bottomControl = null; + } + } + } + if (bottomControl is null) return cacheBottom = bottom; + isVisited = true; + FormData bottomData = cast(FormData) bottomControl.getLayoutData (); + FormAttachment bottomAttachment = bottomData.getBottomAttachment (bottomControl, spacing, flushCache); + switch (bottom.alignment) { + case SWT.BOTTOM: + cacheBottom = bottomAttachment.plus (bottom.offset); + break; + case SWT.CENTER: { + FormAttachment topAttachment = bottomData.getTopAttachment (bottomControl, spacing, flushCache); + FormAttachment bottomHeight = bottomAttachment.minus (topAttachment); + cacheBottom = bottomAttachment.minus (bottomHeight.minus (getHeight (control, flushCache)).divide (2)); + break; + } + default: { + FormAttachment topAttachment = bottomData.getTopAttachment (bottomControl, spacing, flushCache); + cacheBottom = topAttachment.plus (bottom.offset - spacing); + break; + } + } + isVisited = false; + return cacheBottom; +} + +FormAttachment getLeftAttachment (Control control, int spacing, bool flushCache) { + if (cacheLeft !is null) return cacheLeft; + if (isVisited) return cacheLeft = new FormAttachment (0, 0); + if (left is null) { + if (right is null) return cacheLeft = new FormAttachment (0, 0); + return cacheLeft = getRightAttachment (control, spacing, flushCache).minus (getWidth (control, flushCache)); + } + Control leftControl = left.control; + if (leftControl !is null) { + if (leftControl.isDisposed ()) { + left.control = leftControl = null; + } else { + if (leftControl.getParent () !is control.getParent ()) { + leftControl = null; + } + } + } + if (leftControl is null) return cacheLeft = left; + isVisited = true; + FormData leftData = cast(FormData) leftControl.getLayoutData (); + FormAttachment leftAttachment = leftData.getLeftAttachment (leftControl, spacing, flushCache); + switch (left.alignment) { + case SWT.LEFT: + cacheLeft = leftAttachment.plus (left.offset); + break; + case SWT.CENTER: { + FormAttachment rightAttachment = leftData.getRightAttachment (leftControl, spacing, flushCache); + FormAttachment leftWidth = rightAttachment.minus (leftAttachment); + cacheLeft = leftAttachment.plus (leftWidth.minus (getWidth (control, flushCache)).divide (2)); + break; + } + default: { + FormAttachment rightAttachment = leftData.getRightAttachment (leftControl, spacing, flushCache); + cacheLeft = rightAttachment.plus (left.offset + spacing); + } + } + isVisited = false; + return cacheLeft; +} + +char[] getName () { + char[] string = this.classinfo.name; + int index = locatePrior( string, '.'); + if (index is string.length ) return string; + return string[ index + 1 .. string.length ]; +} + +FormAttachment getRightAttachment (Control control, int spacing, bool flushCache) { + if (cacheRight !is null) return cacheRight; + if (isVisited) return cacheRight = new FormAttachment (0, getWidth (control, flushCache)); + if (right is null) { + if (left is null) return cacheRight = new FormAttachment (0, getWidth (control, flushCache)); + return cacheRight = getLeftAttachment (control, spacing, flushCache).plus (getWidth (control, flushCache)); + } + Control rightControl = right.control; + if (rightControl !is null) { + if (rightControl.isDisposed ()) { + right.control = rightControl = null; + } else { + if (rightControl.getParent () !is control.getParent ()) { + rightControl = null; + } + } + } + if (rightControl is null) return cacheRight = right; + isVisited = true; + FormData rightData = cast(FormData) rightControl.getLayoutData (); + FormAttachment rightAttachment = rightData.getRightAttachment (rightControl, spacing, flushCache); + switch (right.alignment) { + case SWT.RIGHT: + cacheRight = rightAttachment.plus (right.offset); + break; + case SWT.CENTER: { + FormAttachment leftAttachment = rightData.getLeftAttachment (rightControl, spacing, flushCache); + FormAttachment rightWidth = rightAttachment.minus (leftAttachment); + cacheRight = rightAttachment.minus (rightWidth.minus (getWidth (control, flushCache)).divide (2)); + break; + } + default: { + FormAttachment leftAttachment = rightData.getLeftAttachment (rightControl, spacing, flushCache); + cacheRight = leftAttachment.plus (right.offset - spacing); + break; + } + } + isVisited = false; + return cacheRight; +} + +FormAttachment getTopAttachment (Control control, int spacing, bool flushCache) { + if (cacheTop !is null) return cacheTop; + if (isVisited) return cacheTop = new FormAttachment (0, 0); + if (top is null) { + if (bottom is null) return cacheTop = new FormAttachment (0, 0); + return cacheTop = getBottomAttachment (control, spacing, flushCache).minus (getHeight (control, flushCache)); + } + Control topControl = top.control; + if (topControl !is null) { + if (topControl.isDisposed ()) { + top.control = topControl = null; + } else { + if (topControl.getParent () !is control.getParent ()) { + topControl = null; + } + } + } + if (topControl is null) return cacheTop = top; + isVisited = true; + FormData topData = cast(FormData) topControl.getLayoutData (); + FormAttachment topAttachment = topData.getTopAttachment (topControl, spacing, flushCache); + switch (top.alignment) { + case SWT.TOP: + cacheTop = topAttachment.plus (top.offset); + break; + case SWT.CENTER: { + FormAttachment bottomAttachment = topData.getBottomAttachment (topControl, spacing, flushCache); + FormAttachment topHeight = bottomAttachment.minus (topAttachment); + cacheTop = topAttachment.plus (topHeight.minus (getHeight (control, flushCache)).divide (2)); + break; + } + default: { + FormAttachment bottomAttachment = topData.getBottomAttachment (topControl, spacing, flushCache); + cacheTop = bottomAttachment.plus (top.offset + spacing); + break; + } + } + isVisited = false; + return cacheTop; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the FormData object + */ +public char[] toString () { + char[] string = getName()~" {"; + if (width !is SWT.DEFAULT) string ~= "width="~to!(char[])(width)~" "; + if (height !is SWT.DEFAULT) string ~= "height="~to!(char[])(height)~" "; + if (left !is null) string ~= "left="~to!(char[])(left)~" "; + if (right !is null) string ~= "right="~to!(char[])(right)~" "; + if (top !is null) string ~= "top="~to!(char[])(top)~" "; + if (bottom !is null) string ~= "bottom="~to!(char[])(bottom)~" "; + string = trim( string ); + string ~= "}"; + return string; +} + +} diff -r a67796650ad4 -r d48f7334742c dwt/layout/FormLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/layout/FormLayout.d Fri Jan 11 10:44:12 2008 +0100 @@ -0,0 +1,393 @@ +/******************************************************************************* + * 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.layout.FormAttachment; +import dwt.layout.FormData; +import dwt.SWT; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Layout; +import dwt.widgets.Composite; +import dwt.widgets.Scrollable; + +import tango.text.Util; +import tango.util.Convert; +import Math = tango.math.Math; + +/** + * Instances of this class control the position and size of the + * children of a composite control by using FormAttachments + * to optionally configure the left, top, right and bottom edges of + * each child. + *

+ * The following example code creates a FormLayout and then sets + * it into a Shell: + *

+ * 		Display display = new Display ();
+ *		Shell shell = new Shell(display);
+ *		FormLayout layout = new FormLayout();
+ *		layout.marginWidth = 3;
+ *		layout.marginHeight = 3;
+ *		shell.setLayout(layout);
+ * 
+ *

+ *

+ * To use a FormLayout, create a FormData with + * FormAttachment for each child of Composite. + * The following example code attaches button1 to the top + * and left edge of the composite and button2 to the right + * edge of button1 and the top and right edges of the + * composite: + *

+ *		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);
+ * 
+ *

+ *

+ * Each side of a child control can be attached to a position in the parent + * composite, or to other controls within the Composite by + * creating instances of FormAttachment and setting them into + * the top, bottom, left, and right fields of the child's FormData. + *

+ *

+ * 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 button1 and button2 + * but relies on default attachments: + *

+ *		FormData data2 = new FormData();
+ *		data2.left = new FormAttachment(button1);
+ *		data2.right = new FormAttachment(100, 0);
+ *		button2.setLayoutData(data2);
+ * 
+ *

+ *

+ * IMPORTANT: Do not define circular attachments. For example, do not attach + * the right edge of button1 to the left edge of button2 + * and then attach the left edge of button2 to the right edge of + * button1. This will over constrain the layout, causing undefined + * behavior. The algorithm will terminate, but the results are undefined. + *

+ * + * @see FormData + * @see FormAttachment + * + * @since 2.0 + * + */ +public final class FormLayout : 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 this () { +} + +/* + * 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 SWT.DEFAULT) size.x = wHint; + if (hHint !is SWT.DEFAULT) size.y = hHint; + return size; +} + +protected bool flushCache (Control control) { + Object data = control.getLayoutData (); + if (data !is null) (cast(FormData) data).flushCache (); + return true; +} + +char[] getName () { + char[] string = this.classinfo.name; + int index = locatePrior( string, '.'); + if (index is string.length ) return string; + return string[ 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