diff dwtx/draw2d/FlowLayout.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children c3583c6ec027
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/FlowLayout.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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 dwtx.draw2d.FlowLayout;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Dimension;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.geometry.Transposer;
+import dwtx.draw2d.AbstractHintLayout;
+import dwtx.draw2d.IFigure;
+
+/**
+ * Lays out children in rows or columns, wrapping when the current row/column is filled.
+ * The aligment and spacing of rows in the parent can be configured.  The aligment and
+ * spacing of children within a row can be configured.
+ */
+public class FlowLayout
+    : AbstractHintLayout
+{
+
+/** Constant to specify components to be aligned in the center */
+public static const int ALIGN_CENTER = 0;
+/** Constant to specify components to be aligned on the left/top */
+public static const int ALIGN_LEFTTOP = 1;
+/** Constant to specify components to be aligned on the right/bottom */
+public static const int ALIGN_RIGHTBOTTOM = 2;
+
+/** Constant to specify components should be layed out horizontally */
+public static const bool HORIZONTAL = true;
+/** Constant to specify components should be layed out vertically */
+public static const bool VERTICAL = false;
+
+/** The horizontal property. */
+protected bool horizontal = true;
+/**
+ * The property that determines whether leftover space at the end of a row/column should
+ * be filled by the last item in that row/column.
+ */
+protected bool fill = false;
+
+/** The transposer used in converting horizontal layout to vertical. */
+protected Transposer transposer;
+private void instanceInit(){
+    transposer = new Transposer();
+    transposer.setEnabled(!horizontal);
+}
+
+/** The alignment along the major axis. */
+protected int majorAlignment = ALIGN_LEFTTOP;
+/** The alignment along the minor axis. */
+protected int minorAlignment = ALIGN_LEFTTOP;
+/** The spacing along the minor axis. */
+protected int minorSpacing = 5;
+/** The spacing along the major axis. */
+protected int majorSpacing = 5;
+protected WorkingData data = null;
+
+/**
+ * Holds the necessary information for layout calculations.
+ */
+protected class WorkingData {
+    public int rowHeight, rowWidth, rowCount, rowX, rowY, maxWidth;
+    public Rectangle[] bounds;
+    public Rectangle area;
+    public IFigure row[];
+}
+
+/**
+ * Constructs a FlowLayout with horizontal orientation.
+ * @since 2.0
+ */
+public this() {
+    instanceInit();
+}
+
+/**
+ * Constructs a FlowLayout whose orientation is given in the input.
+ * @param isHorizontal <code>true</code> if the layout should be horizontal
+ * @since 2.0
+ */
+public this(bool isHorizontal) {
+    instanceInit();
+    setHorizontal(isHorizontal);
+}
+
+/**
+ * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(IFigure, int, int)
+ */
+protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
+    // Subtract out the insets from the hints
+    if (wHint > -1)
+        wHint = Math.max(0, wHint - container.getInsets().getWidth());
+    if (hHint > -1)
+        hHint = Math.max(0, hHint - container.getInsets().getHeight());
+
+    // Figure out the new hint that we are interested in based on the orientation
+    // Ignore the other hint (by setting it to -1).  NOTE: The children of the
+    // parent figure will then be asked to ignore that hint as well.
+    int maxWidth;
+    if (isHorizontal()) {
+        maxWidth = wHint;
+        hHint = -1;
+    } else {
+        maxWidth = hHint;
+        wHint = -1;
+    }
+    if (maxWidth < 0) {
+        maxWidth = Integer.MAX_VALUE;
+    }
+
+    // The preferred dimension that is to be calculated and returned
+    Dimension prefSize = new Dimension();
+
+    List children = container.getChildren();
+    int width = 0;
+    int height = 0;
+    IFigure child;
+    Dimension childSize;
+
+    //Build the sizes for each row, and update prefSize accordingly
+    for (int i = 0; i < children.size(); i++) {
+        child = cast(IFigure)children.get(i);
+        childSize = transposer.t(getChildSize(child, wHint, hHint));
+        if (i is 0) {
+            width = childSize.width;
+            height = childSize.height;
+        } else if (width + childSize.width + getMinorSpacing() > maxWidth) {
+            // The current row is full, start a new row.
+            prefSize.height += height + getMajorSpacing();
+            prefSize.width = Math.max(prefSize.width, width);
+            width = childSize.width;
+            height = childSize.height;
+        } else {
+            // The current row can fit another child.
+            width += childSize.width + getMinorSpacing();
+            height = Math.max(height, childSize.height);
+        }
+    }
+
+    // Flush out the last row's data
+    prefSize.height += height;
+    prefSize.width = Math.max(prefSize.width, width);
+
+    // Transpose the dimension back, and compensate for the border.
+    prefSize = transposer.t(prefSize);
+    prefSize.width += container.getInsets().getWidth();
+    prefSize.height += container.getInsets().getHeight();
+    prefSize.union_(getBorderPreferredSize(container));
+
+    return prefSize;
+}
+
+/**
+ * Provides the given child's preferred size.
+ *
+ * @param child the Figure whose preferred size needs to be calculated
+ * @param wHint the width hint
+ * @param hHint the height hint
+ * @return the child's preferred size
+ */
+protected Dimension getChildSize(IFigure child, int wHint, int hHint) {
+    return child.getPreferredSize(wHint, hHint);
+}
+
+
+/**
+ * Returns the alignment used for an entire row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @return the major alignment
+ * @since 2.0
+ */
+public int getMajorAlignment() {
+    return majorAlignment;
+}
+
+/**
+ * Returns the spacing in pixels to be used between children in the direction parallel to
+ * the layout's orientation.
+ * @return the major spacing
+ */
+public int getMajorSpacing() {
+    return majorSpacing;
+}
+
+/**
+ * Returns the alignment used for children within a row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @return the minor alignment
+ * @since 2.0
+ */
+public int getMinorAlignment() {
+    return minorAlignment;
+}
+
+/**
+ * Returns the spacing to be used between children within a row/column.
+ * @return the minor spacing
+ */
+public int getMinorSpacing() {
+    return minorSpacing;
+}
+
+/**
+ * Initializes the state of row data, which is internal to the layout process.
+ */
+protected void initRow() {
+    data.rowX = 0;
+    data.rowHeight = 0;
+    data.rowWidth = 0;
+    data.rowCount = 0;
+}
+
+/**
+ * Initializes state data for laying out children, based on the Figure given as input.
+ *
+ * @param parent the parent figure
+ * @since 2.0
+ */
+protected void initVariables(IFigure parent) {
+    data.row = new IFigure[parent.getChildren().size()];
+    data.bounds = new Rectangle[data.row.length];
+    data.maxWidth = data.area.width;
+}
+
+/**
+ * Returns <code>true</code> if the orientation of the layout is horizontal.
+ *
+ * @return <code>true</code> if the orientation of the layout is horizontal
+ * @since 2.0
+ */
+public bool isHorizontal() {
+    return horizontal;
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return !isHorizontal();
+}
+
+/**
+ * @see dwtx.draw2d.LayoutManager#layout(IFigure)
+ */
+public void layout(IFigure parent) {
+    data = new WorkingData();
+    Rectangle relativeArea = parent.getClientArea();
+    data.area = transposer.t(relativeArea);
+
+    Iterator iterator = parent.getChildren().iterator();
+    int dx;
+
+    //Calculate the hints to be passed to children
+    int wHint = -1;
+    int hHint = -1;
+    if (isHorizontal())
+        wHint = parent.getClientArea().width;
+    else
+        hHint = parent.getClientArea().height;
+
+    initVariables(parent);
+    initRow();
+    int i = 0;
+    while (iterator.hasNext()) {
+        IFigure f = cast(IFigure)iterator.next();
+        Dimension pref = transposer.t(getChildSize(f, wHint, hHint));
+        Rectangle r = new Rectangle(0, 0, pref.width, pref.height);
+
+        if (data.rowCount > 0) {
+            if (data.rowWidth + pref.width > data.maxWidth)
+                layoutRow(parent);
+        }
+        r.x = data.rowX;
+        r.y = data.rowY;
+        dx = r.width + getMinorSpacing();
+        data.rowX += dx;
+        data.rowWidth += dx;
+        data.rowHeight = Math.max(data.rowHeight, r.height);
+        data.row [data.rowCount] = f;
+        data.bounds[data.rowCount] = r;
+        data.rowCount++;
+        i++;
+    }
+    if (data.rowCount !is 0)
+        layoutRow(parent);
+    data = null;
+}
+
+/**
+ * Layouts one row of components. This is done based on the layout's orientation, minor
+ * alignment and major alignment.
+ *
+ * @param parent the parent figure
+ * @since 2.0
+ */
+protected void layoutRow(IFigure parent) {
+    int majorAdjustment = 0;
+    int minorAdjustment = 0;
+    int correctMajorAlignment = majorAlignment;
+    int correctMinorAlignment = minorAlignment;
+
+    majorAdjustment = data.area.width - data.rowWidth + getMinorSpacing();
+
+    switch (correctMajorAlignment) {
+        case ALIGN_LEFTTOP:
+            majorAdjustment = 0;
+            break;
+        case ALIGN_CENTER:
+            majorAdjustment /= 2;
+            break;
+        case ALIGN_RIGHTBOTTOM:
+            break;
+    }
+
+    for (int j = 0; j < data.rowCount; j++) {
+        if (fill) {
+            data.bounds[j].height = data.rowHeight;
+        } else {
+            minorAdjustment = data.rowHeight - data.bounds[j].height;
+            switch (correctMinorAlignment) {
+                case ALIGN_LEFTTOP:
+                    minorAdjustment = 0;
+                    break;
+                case ALIGN_CENTER:
+                    minorAdjustment /= 2;
+                break;
+                case ALIGN_RIGHTBOTTOM:
+                    break;
+            }
+            data.bounds[j].y += minorAdjustment;
+        }
+        data.bounds[j].x += majorAdjustment;
+
+        setBoundsOfChild(parent, data.row[j], transposer.t(data.bounds[j]));
+    }
+    data.rowY += getMajorSpacing() + data.rowHeight;
+    initRow();
+}
+
+/**
+ * Sets the given bounds for the child figure input.
+ *
+ * @param parent the parent figure
+ * @param child the child figure
+ * @param bounds the size of the child to be set
+ * @since 2.0
+ */
+protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) {
+    parent.getClientArea(Rectangle.SINGLETON);
+    bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y);
+    child.setBounds(bounds);
+}
+
+/**
+ * Sets flag based on layout orientation. If in horizontal orientation, all figures will
+ * have the same height. If in vertical orientation, all figures will have the same width.
+ *
+ * @param value fill state desired
+ * @since 2.0
+ */
+public void setStretchMinorAxis(bool value) {
+    fill = value;
+}
+
+/**
+ * Sets the orientation of the layout.
+ *
+ * @param flag <code>true</code> if this layout should be horizontal
+ * @since 2.0
+ */
+public void setHorizontal(bool flag) {
+    if (horizontal is flag) return;
+    invalidate();
+    horizontal = flag;
+    transposer.setEnabled(!horizontal);
+}
+
+/**
+ * Sets the alignment for an entire row/column within the parent figure.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @param align the major alignment
+ * @since 2.0
+ */
+public void setMajorAlignment(int align_) {
+    majorAlignment = align_;
+}
+
+/**
+ * Sets the spacing in pixels to be used between children in the direction parallel to the
+ * layout's orientation.
+ *
+ * @param n the major spacing
+ * @since 2.0
+ */
+public void setMajorSpacing(int n) {
+    majorSpacing = n;
+}
+
+/**
+ * Sets the alignment to be used within a row/column.
+ * <P>
+ * Possible values are :
+ * <ul>
+ *   <li>{@link #ALIGN_CENTER}
+ *   <li>{@link #ALIGN_LEFTTOP}
+ *   <li>{@link #ALIGN_RIGHTBOTTOM}
+ * </ul>
+ *
+ * @param align the minor alignment
+ * @since 2.0
+ */
+public void setMinorAlignment(int align_) {
+    minorAlignment = align_;
+}
+
+/**
+ * Sets the spacing to be used between children within a row/column.
+ *
+ * @param n the minor spacing
+ * @since 2.0
+ */
+public void setMinorSpacing(int n) {
+    minorSpacing = n;
+}
+
+}