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;
+}   
+}