Mercurial > projects > dwt-addons
diff dwtx/draw2d/ToolbarLayout.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/ToolbarLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,428 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.ToolbarLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; + +/** + * Arranges figures in a single row or column. Orientation can be set to produce either + * a row or column layout. This layout tries to fit all children within the parent's + * client area. To do this, it compresses the children by some amount, but will not + * compress them smaller than their minimum size. If a child's preferred size is smaller + * than the row's or column's minor dimension, the layout can be configured to stretch the + * child. + */ +public class ToolbarLayout + : AbstractHintLayout +{ + +/** Space in pixels between Figures **/ +protected int spacing; +/** Sets whether children should "stretch" with their container **/ +protected bool matchWidth; +/** Orientation of layout **/ +protected bool horizontal = false; +/** Alignment of layout **/ +protected int minorAlignment; + +/** Constant for center alignment **/ +public static const int ALIGN_CENTER = 0; +/** Constant for top-left alignment **/ +public static const int ALIGN_TOPLEFT = 1; +/** Constant for bottom-right alignment **/ +public static const int ALIGN_BOTTOMRIGHT = 2; + +/** Constant for horizontal alignment **/ +public static const bool HORIZONTAL = true; +/** Constant for vertical alignment **/ +public static const bool VERTICAL = false; + +/** Transposer object used in layout calculations **/ +protected Transposer transposer; +private void instanceInit() { + transposer = new Transposer(); + transposer.setEnabled(horizontal); +} + +/** + * Constructs a vertically oriented ToolbarLayout with child spacing of 0 pixels, + * matchWidth <code>true</code>, and {@link #ALIGN_TOPLEFT} alignment. + * + * @since 2.0 + */ +public this() { + instanceInit(); + spacing = 0; + matchWidth = true; + minorAlignment = ALIGN_TOPLEFT; + horizontal = false; +} + +/** + * Constructs a ToolbarLayout with a specified orientation. Default values are: child + * spacing 0 pixels, matchWidth <code>false</code>, and {@link #ALIGN_TOPLEFT} + * alignment. + * + * @param isHorizontal whether the children are oriented horizontally + * @since 2.0 + */ +public this(bool isHorizontal) { + instanceInit(); + horizontal = isHorizontal; + transposer.setEnabled(horizontal); + spacing = 0; + matchWidth = false; + minorAlignment = ALIGN_TOPLEFT; +} + +private Dimension calculateChildrenSize(List children, int wHint, int hHint, + bool preferred) { + Dimension childSize; + IFigure child; + int height = 0, width = 0; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + childSize = transposer.t(preferred ? getChildPreferredSize(child, wHint, hHint) + : getChildMinimumSize(child, wHint, hHint)); + height += childSize.height; + width = Math.max(width, childSize.width); + } + return new Dimension(width, height); +} + +/** + * Calculates the minimum size of the container based on the given hints. If this is a + * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means + * that the children can be as tall as they desire). In this case, the minimum width + * is that of the widest child, and the minimum height is the sum of the minimum + * heights of all children, plus the spacing between them. The border and insets of the + * container figure are also accounted for. + * + * @param container the figure whose minimum size has to be calculated + * @param wHint the width hint (the desired width of the container) + * @param hHint the height hint (the desired height of the container) + * @return the minimum size of the container + * @see #getMinimumSize(IFigure, int, int) + * @since 2.1 + */ +protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) { + Insets insets = container.getInsets(); + if (isHorizontal()) { + wHint = -1; + if (hHint >= 0) + hHint = Math.max(0, hHint - insets.getHeight()); + } else { + hHint = -1; + if (wHint >= 0) + wHint = Math.max(0, wHint - insets.getWidth()); + } + + List children = container.getChildren(); + Dimension minSize = calculateChildrenSize(children, wHint, hHint, false); + // Do a second pass, if necessary + if (wHint >= 0 && minSize.width > wHint) { + minSize = calculateChildrenSize(children, minSize.width, hHint, false); + } else if (hHint >= 0 && minSize.width > hHint) { + minSize = calculateChildrenSize(children, wHint, minSize.width, false); + } + + minSize.height += Math.max(0, children.size() - 1) * spacing; + return transposer.t(minSize) + .expand(insets.getWidth(), insets.getHeight()) + .union_(getBorderPreferredSize(container)); +} + +/** + * Calculates the preferred size of the container based on the given hints. If this is a + * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means + * that the children can be as tall as they desire). In this case, the preferred width + * is that of the widest child, and the preferred height is the sum of the preferred + * heights of all children, plus the spacing between them. The border and insets of the + * container figure are also accounted for. + * + * @param container the figure whose preferred size has to be calculated + * @param wHint the width hint (the desired width of the container) + * @param hHint the height hint (the desired height of the container) + * @return the preferred size of the container + * @see #getPreferredSize(IFigure, int, int) + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { + Insets insets = container.getInsets(); + if (isHorizontal()) { + wHint = -1; + if (hHint >= 0) + hHint = Math.max(0, hHint - insets.getHeight()); + } else { + hHint = -1; + if (wHint >= 0) + wHint = Math.max(0, wHint - insets.getWidth()); + } + + List children = container.getChildren(); + Dimension prefSize = calculateChildrenSize(children, wHint, hHint, true); + // Do a second pass, if necessary + if (wHint >= 0 && prefSize.width > wHint) { + prefSize = calculateChildrenSize(children, prefSize.width, hHint, true); + } else if (hHint >= 0 && prefSize.width > hHint) { + prefSize = calculateChildrenSize(children, wHint, prefSize.width, true); + } + + prefSize.height += Math.max(0, children.size() - 1) * spacing; + return transposer.t(prefSize) + .expand(insets.getWidth(), insets.getHeight()) + .union_(getBorderPreferredSize(container)); +} + +/** + * @param child the figure whose minimum size is to be determined + * @param wHint the width hint + * @param hHint the height hint + * @return the given figure's minimum size + * @since 3.3 + */ +protected Dimension getChildMinimumSize(IFigure child, int wHint, int hHint) { + return child.getMinimumSize(wHint, hHint); +} + +/** + * @param child the figure whose preferred size is to be determined + * @param wHint the width hint + * @param hHint the height hint + * @return given figure's preferred size + * @since 3.3 + */ +protected Dimension getChildPreferredSize(IFigure child, int wHint, int hHint) { + return child.getPreferredSize(wHint, hHint); +} + +/** + * Returns the minor aligment of the layout. Minor minor axis is the axis perpindicular + * to the overall orientation set in the contructor. + * @return the minor aligment + */ +public int getMinorAlignment() { + return minorAlignment; +} + +/** + * @return the spacing between children + */ +public int getSpacing() { + return spacing; +} + +/** + * Returns <code>true</code> if stretch minor axis has been enabled. The default value is + * false. + * @return <code>true</code> if stretch minor axis is enabled + */ +public bool getStretchMinorAxis() { + return matchWidth; +} + +/** + * @return whether 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) { + List children = parent.getChildren(); + int numChildren = children.size(); + Rectangle clientArea = transposer.t(parent.getClientArea()); + int x = clientArea.x; + int y = clientArea.y; + int availableHeight = clientArea.height; + + Dimension prefSizes [] = new Dimension[numChildren]; + Dimension minSizes [] = new Dimension[numChildren]; + + // Calculate the width and height hints. If it's a vertical ToolBarLayout, + // then ignore the height hint (set it to -1); otherwise, ignore the + // width hint. These hints will be passed to the children of the parent + // figure when getting their preferred size. + int wHint = -1; + int hHint = -1; + if (isHorizontal()) { + hHint = parent.getClientArea(Rectangle.SINGLETON).height; + } else { + wHint = parent.getClientArea(Rectangle.SINGLETON).width; + } + + /* + * Calculate sum of preferred heights of all children(totalHeight). + * Calculate sum of minimum heights of all children(minHeight). + * Cache Preferred Sizes and Minimum Sizes of all children. + * + * totalHeight is the sum of the preferred heights of all children + * totalMinHeight is the sum of the minimum heights of all children + * prefMinSumHeight is the sum of the difference between all children's + * preferred heights and minimum heights. (This is used as a ratio to + * calculate how much each child will shrink). + */ + IFigure child; + int totalHeight = 0; + int totalMinHeight = 0; + int prefMinSumHeight = 0; + + for (int i = 0; i < numChildren; i++) { + child = cast(IFigure)children.get(i); + + prefSizes[i] = transposer.t(getChildPreferredSize(child, wHint, hHint)); + minSizes[i] = transposer.t(getChildMinimumSize(child, wHint, hHint)); + + totalHeight += prefSizes[i].height; + totalMinHeight += minSizes[i].height; + } + totalHeight += (numChildren - 1) * spacing; + totalMinHeight += (numChildren - 1) * spacing; + prefMinSumHeight = totalHeight - totalMinHeight; + /* + * The total amount that the children must be shrunk is the + * sum of the preferred Heights of the children minus + * Max(the available area and the sum of the minimum heights of the children). + * + * amntShrinkHeight is the combined amount that the children must shrink + * amntShrinkCurrentHeight is the amount each child will shrink respectively + */ + int amntShrinkHeight = totalHeight - Math.max(availableHeight, totalMinHeight); + + if (amntShrinkHeight < 0) { + amntShrinkHeight = 0; + } + + for (int i = 0; i < numChildren; i++) { + int amntShrinkCurrentHeight = 0; + int prefHeight = prefSizes[i].height; + int minHeight = minSizes[i].height; + int prefWidth = prefSizes[i].width; + int minWidth = minSizes[i].width; + Rectangle newBounds = new Rectangle(x, y, prefWidth, prefHeight); + + child = cast(IFigure)children.get(i); + if (prefMinSumHeight !is 0) + amntShrinkCurrentHeight = + (prefHeight - minHeight) * amntShrinkHeight / (prefMinSumHeight); + + int width = Math.min(prefWidth, transposer.t(child.getMaximumSize()).width); + if (matchWidth) + width = transposer.t(child.getMaximumSize()).width; + width = Math.max(minWidth, Math.min(clientArea.width, width)); + newBounds.width = width; + + int adjust = clientArea.width - width; + switch (minorAlignment) { + case ALIGN_TOPLEFT: + adjust = 0; + break; + case ALIGN_CENTER: + adjust /= 2; + break; + case ALIGN_BOTTOMRIGHT: + break; + } + newBounds.x += adjust; + newBounds.height -= amntShrinkCurrentHeight; + child.setBounds(transposer.t(newBounds)); + + amntShrinkHeight -= amntShrinkCurrentHeight; + prefMinSumHeight -= (prefHeight - minHeight); + y += newBounds.height + spacing; + } +} + +/** + * Sets the alignment of the children contained in the layout. Possible values are + * {@link #ALIGN_CENTER}, {@link #ALIGN_BOTTOMRIGHT} and {@link #ALIGN_TOPLEFT}. + * + * @param align the minor alignment + * @since 2.0 + */ +public void setMinorAlignment(int align_) { + minorAlignment = align_; +} + +/** + * Sets the amount of space between children. + * + * @param space the amount of space between children + * @since 2.0 + */ +public void setSpacing(int space) { + spacing = space; +} + +/** + * Sets children's width (if vertically oriented) or height (if horizontally oriented) to + * stretch with their container. + * + * @deprecated use {@link #setStretchMinorAxis(bool)} + * @param match whether to stretch children + * @since 2.0 + */ +public void setMatchWidth(bool match) { + matchWidth = match; +} + +/** + * Causes children that are smaller in the dimension of the minor axis to be stretched to + * fill the minor axis. The minor axis is the opposite of the orientation. + * @param stretch whether to stretch children + * @since 2.0 + */ +public void setStretchMinorAxis(bool stretch) { + matchWidth = stretch; +} + +/** + * Sets the orientation of the children in the ToolbarLayout. + * + * @param flag whether the orientation should be vertical + * @since 2.0 + */ +public void setVertical(bool flag) { + if (horizontal !is flag) return; + invalidate(); + horizontal = !flag; + transposer.setEnabled(horizontal); +} + +}