view org.eclipse.swt.gtk.linux.x86/src/org/eclipse/swt/layout/FormLayout.d @ 120:536e43f63c81

Comprehensive update for Win32/Linux32 dmd-2.053/dmd-1.068+Tango-r5661 ===D2=== * added [Try]Immutable/Const/Shared templates to work with differenses in D1/D2 instead of version statements used these templates to work with strict type storage rules of dmd-2.053 * com.ibm.icu now also compilable with D2, but not tested yet * small fixes Snippet288 - shared data is in TLS ===Phobos=== * fixed critical bugs in Phobos implemention completely incorrect segfault prone fromStringz (Linux's port ruthless killer) terrible, incorrect StringBuffer realization (StyledText killer) * fixed small bugs as well Snippet72 - misprint in the snippet * implemented missed functionality for Phobos ByteArrayOutputStream implemented (image loading available) formatting correctly works for all DWT's cases As a result, folowing snippets now works with Phobos (Snippet### - what is fixed): Snippet24, 42, 111, 115, 130, 235, 276 - bad string formatting Snippet48, 282 - crash on image loading Snippet163, 189, 211, 213, 217, 218, 222 - crash on copy/cut in StyledText Snippet244 - hang-up ===Tango=== * few changes for the latest Tango trunc-r5661 * few small performance improvments ===General=== * implMissing-s for only one version changed to implMissingInTango/InPhobos * incorrect calls to Format in toString-s fixed * fixed loading \uXXXX characters in ResourceBundle * added good UTF-8 support for StyledText, TextLayout (Win32) and friends UTF functions revised and tested. It is now in java.nonstandard.*Utf modules StyledText and TextLayout (Win32) modules revised for UTF-8 support * removed small diferences in most identical files in *.swt.* folders *.swt.internal.image, *.swt.events and *.swt.custom are identical in Win32/Linux32 now 179 of 576 (~31%) files in *.swt.* folders are fully identical * Win32: snippets now have right subsystem, pretty icons and native system style controls * small fixes in snippets Snippet44 - it's not Snippet44 Snippet212 - functions work with different images and offsets arrays Win32: Snippet282 - crash on close if the button has an image Snippet293 - setGrayed is commented and others Win32: As a result, folowing snippets now works Snippet68 - color doesn't change Snippet163, 189, 211, 213, 217, 218, 222 - UTF-8 issues (see above) Snippet193 - no tabel headers
author Denis Shelomovskij <verylonglogin.reg@gmail.com>
date Sat, 09 Jul 2011 15:50:20 +0300
parents c01d033c633a
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2008 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
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module org.eclipse.swt.layout.FormLayout;

import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
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.Layout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Scrollable;

import java.lang.all;

/**
 * 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
 * @see <a href="http://www.eclipse.org/swt/snippets/#formlayout">FormLayout snippets</a>
 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: LayoutExample</a>
 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 
 *
 * @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));
}

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

override protected bool flushCache (Control control) {
    Object data = control.getLayoutData ();
    if (data !is null) (cast(FormData) data).flushCache ();
    return true;
}

String getName () {
    String string = this.classinfo.name;
    int index = string.lastIndexOf('.');
    if (index is -1 ) 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));
}

override 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 = cast(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 = cast(FormData) child.getLayoutData ();
        if (width !is SWT.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 SWT.DEFAULT && !data.needed) {
                int trim = 0;
                //TEMPORARY CODE
                if ( auto sa = cast(Scrollable)child) {
                    Rectangle rect = sa.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 = cast(FormData) child.getLayoutData ();
        if (height !is SWT.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 = cast(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
 */
override public String toString () {
    String string =  getName ()~" {";
    if (marginWidth !is 0) string ~= "marginWidth="~String_valueOf(marginWidth)~" ";
    if (marginHeight !is 0) string ~= "marginHeight="~String_valueOf(marginHeight)~" ";
    if (marginLeft !is 0) string ~= "marginLeft="~String_valueOf(marginLeft)~" ";
    if (marginRight !is 0) string ~= "marginRight="~String_valueOf(marginRight)~" ";
    if (marginTop !is 0) string ~= "marginTop="~String_valueOf(marginTop)~" ";
    if (marginBottom !is 0) string ~= "marginBottom="~String_valueOf(marginBottom)~" ";
    if (spacing !is 0) string ~= "spacing="~String_valueOf(spacing)~" ";
    string = string.trim();
    string ~= "}";
    return string;
}
}