comparison 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
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.draw2d.ToolbarLayout;
14
15 import dwt.dwthelper.utils;
16 import dwtx.dwtxhelper.Collection;
17
18 import dwtx.draw2d.geometry.Dimension;
19 import dwtx.draw2d.geometry.Insets;
20 import dwtx.draw2d.geometry.Rectangle;
21 import dwtx.draw2d.geometry.Transposer;
22 import dwtx.draw2d.AbstractHintLayout;
23 import dwtx.draw2d.IFigure;
24
25 /**
26 * Arranges figures in a single row or column. Orientation can be set to produce either
27 * a row or column layout. This layout tries to fit all children within the parent's
28 * client area. To do this, it compresses the children by some amount, but will not
29 * compress them smaller than their minimum size. If a child's preferred size is smaller
30 * than the row's or column's minor dimension, the layout can be configured to stretch the
31 * child.
32 */
33 public class ToolbarLayout
34 : AbstractHintLayout
35 {
36
37 /** Space in pixels between Figures **/
38 protected int spacing;
39 /** Sets whether children should "stretch" with their container **/
40 protected bool matchWidth;
41 /** Orientation of layout **/
42 protected bool horizontal = false;
43 /** Alignment of layout **/
44 protected int minorAlignment;
45
46 /** Constant for center alignment **/
47 public static const int ALIGN_CENTER = 0;
48 /** Constant for top-left alignment **/
49 public static const int ALIGN_TOPLEFT = 1;
50 /** Constant for bottom-right alignment **/
51 public static const int ALIGN_BOTTOMRIGHT = 2;
52
53 /** Constant for horizontal alignment **/
54 public static const bool HORIZONTAL = true;
55 /** Constant for vertical alignment **/
56 public static const bool VERTICAL = false;
57
58 /** Transposer object used in layout calculations **/
59 protected Transposer transposer;
60 private void instanceInit() {
61 transposer = new Transposer();
62 transposer.setEnabled(horizontal);
63 }
64
65 /**
66 * Constructs a vertically oriented ToolbarLayout with child spacing of 0 pixels,
67 * matchWidth <code>true</code>, and {@link #ALIGN_TOPLEFT} alignment.
68 *
69 * @since 2.0
70 */
71 public this() {
72 instanceInit();
73 spacing = 0;
74 matchWidth = true;
75 minorAlignment = ALIGN_TOPLEFT;
76 horizontal = false;
77 }
78
79 /**
80 * Constructs a ToolbarLayout with a specified orientation. Default values are: child
81 * spacing 0 pixels, matchWidth <code>false</code>, and {@link #ALIGN_TOPLEFT}
82 * alignment.
83 *
84 * @param isHorizontal whether the children are oriented horizontally
85 * @since 2.0
86 */
87 public this(bool isHorizontal) {
88 instanceInit();
89 horizontal = isHorizontal;
90 transposer.setEnabled(horizontal);
91 spacing = 0;
92 matchWidth = false;
93 minorAlignment = ALIGN_TOPLEFT;
94 }
95
96 private Dimension calculateChildrenSize(List children, int wHint, int hHint,
97 bool preferred) {
98 Dimension childSize;
99 IFigure child;
100 int height = 0, width = 0;
101 for (int i = 0; i < children.size(); i++) {
102 child = cast(IFigure)children.get(i);
103 childSize = transposer.t(preferred ? getChildPreferredSize(child, wHint, hHint)
104 : getChildMinimumSize(child, wHint, hHint));
105 height += childSize.height;
106 width = Math.max(width, childSize.width);
107 }
108 return new Dimension(width, height);
109 }
110
111 /**
112 * Calculates the minimum size of the container based on the given hints. If this is a
113 * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means
114 * that the children can be as tall as they desire). In this case, the minimum width
115 * is that of the widest child, and the minimum height is the sum of the minimum
116 * heights of all children, plus the spacing between them. The border and insets of the
117 * container figure are also accounted for.
118 *
119 * @param container the figure whose minimum size has to be calculated
120 * @param wHint the width hint (the desired width of the container)
121 * @param hHint the height hint (the desired height of the container)
122 * @return the minimum size of the container
123 * @see #getMinimumSize(IFigure, int, int)
124 * @since 2.1
125 */
126 protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) {
127 Insets insets = container.getInsets();
128 if (isHorizontal()) {
129 wHint = -1;
130 if (hHint >= 0)
131 hHint = Math.max(0, hHint - insets.getHeight());
132 } else {
133 hHint = -1;
134 if (wHint >= 0)
135 wHint = Math.max(0, wHint - insets.getWidth());
136 }
137
138 List children = container.getChildren();
139 Dimension minSize = calculateChildrenSize(children, wHint, hHint, false);
140 // Do a second pass, if necessary
141 if (wHint >= 0 && minSize.width > wHint) {
142 minSize = calculateChildrenSize(children, minSize.width, hHint, false);
143 } else if (hHint >= 0 && minSize.width > hHint) {
144 minSize = calculateChildrenSize(children, wHint, minSize.width, false);
145 }
146
147 minSize.height += Math.max(0, children.size() - 1) * spacing;
148 return transposer.t(minSize)
149 .expand(insets.getWidth(), insets.getHeight())
150 .union_(getBorderPreferredSize(container));
151 }
152
153 /**
154 * Calculates the preferred size of the container based on the given hints. If this is a
155 * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means
156 * that the children can be as tall as they desire). In this case, the preferred width
157 * is that of the widest child, and the preferred height is the sum of the preferred
158 * heights of all children, plus the spacing between them. The border and insets of the
159 * container figure are also accounted for.
160 *
161 * @param container the figure whose preferred size has to be calculated
162 * @param wHint the width hint (the desired width of the container)
163 * @param hHint the height hint (the desired height of the container)
164 * @return the preferred size of the container
165 * @see #getPreferredSize(IFigure, int, int)
166 * @since 2.0
167 */
168 protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
169 Insets insets = container.getInsets();
170 if (isHorizontal()) {
171 wHint = -1;
172 if (hHint >= 0)
173 hHint = Math.max(0, hHint - insets.getHeight());
174 } else {
175 hHint = -1;
176 if (wHint >= 0)
177 wHint = Math.max(0, wHint - insets.getWidth());
178 }
179
180 List children = container.getChildren();
181 Dimension prefSize = calculateChildrenSize(children, wHint, hHint, true);
182 // Do a second pass, if necessary
183 if (wHint >= 0 && prefSize.width > wHint) {
184 prefSize = calculateChildrenSize(children, prefSize.width, hHint, true);
185 } else if (hHint >= 0 && prefSize.width > hHint) {
186 prefSize = calculateChildrenSize(children, wHint, prefSize.width, true);
187 }
188
189 prefSize.height += Math.max(0, children.size() - 1) * spacing;
190 return transposer.t(prefSize)
191 .expand(insets.getWidth(), insets.getHeight())
192 .union_(getBorderPreferredSize(container));
193 }
194
195 /**
196 * @param child the figure whose minimum size is to be determined
197 * @param wHint the width hint
198 * @param hHint the height hint
199 * @return the given figure's minimum size
200 * @since 3.3
201 */
202 protected Dimension getChildMinimumSize(IFigure child, int wHint, int hHint) {
203 return child.getMinimumSize(wHint, hHint);
204 }
205
206 /**
207 * @param child the figure whose preferred size is to be determined
208 * @param wHint the width hint
209 * @param hHint the height hint
210 * @return given figure's preferred size
211 * @since 3.3
212 */
213 protected Dimension getChildPreferredSize(IFigure child, int wHint, int hHint) {
214 return child.getPreferredSize(wHint, hHint);
215 }
216
217 /**
218 * Returns the minor aligment of the layout. Minor minor axis is the axis perpindicular
219 * to the overall orientation set in the contructor.
220 * @return the minor aligment
221 */
222 public int getMinorAlignment() {
223 return minorAlignment;
224 }
225
226 /**
227 * @return the spacing between children
228 */
229 public int getSpacing() {
230 return spacing;
231 }
232
233 /**
234 * Returns <code>true</code> if stretch minor axis has been enabled. The default value is
235 * false.
236 * @return <code>true</code> if stretch minor axis is enabled
237 */
238 public bool getStretchMinorAxis() {
239 return matchWidth;
240 }
241
242 /**
243 * @return whether the orientation of the layout is horizontal
244 * @since 2.0
245 */
246 public bool isHorizontal() {
247 return horizontal;
248 }
249
250 /**
251 * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
252 */
253 protected bool isSensitiveHorizontally(IFigure parent) {
254 return !isHorizontal();
255 }
256
257 /**
258 * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
259 */
260 protected bool isSensitiveVertically(IFigure parent) {
261 return isHorizontal();
262 }
263
264 /**
265 * @see dwtx.draw2d.LayoutManager#layout(IFigure)
266 */
267 public void layout(IFigure parent) {
268 List children = parent.getChildren();
269 int numChildren = children.size();
270 Rectangle clientArea = transposer.t(parent.getClientArea());
271 int x = clientArea.x;
272 int y = clientArea.y;
273 int availableHeight = clientArea.height;
274
275 Dimension prefSizes [] = new Dimension[numChildren];
276 Dimension minSizes [] = new Dimension[numChildren];
277
278 // Calculate the width and height hints. If it's a vertical ToolBarLayout,
279 // then ignore the height hint (set it to -1); otherwise, ignore the
280 // width hint. These hints will be passed to the children of the parent
281 // figure when getting their preferred size.
282 int wHint = -1;
283 int hHint = -1;
284 if (isHorizontal()) {
285 hHint = parent.getClientArea(Rectangle.SINGLETON).height;
286 } else {
287 wHint = parent.getClientArea(Rectangle.SINGLETON).width;
288 }
289
290 /*
291 * Calculate sum of preferred heights of all children(totalHeight).
292 * Calculate sum of minimum heights of all children(minHeight).
293 * Cache Preferred Sizes and Minimum Sizes of all children.
294 *
295 * totalHeight is the sum of the preferred heights of all children
296 * totalMinHeight is the sum of the minimum heights of all children
297 * prefMinSumHeight is the sum of the difference between all children's
298 * preferred heights and minimum heights. (This is used as a ratio to
299 * calculate how much each child will shrink).
300 */
301 IFigure child;
302 int totalHeight = 0;
303 int totalMinHeight = 0;
304 int prefMinSumHeight = 0;
305
306 for (int i = 0; i < numChildren; i++) {
307 child = cast(IFigure)children.get(i);
308
309 prefSizes[i] = transposer.t(getChildPreferredSize(child, wHint, hHint));
310 minSizes[i] = transposer.t(getChildMinimumSize(child, wHint, hHint));
311
312 totalHeight += prefSizes[i].height;
313 totalMinHeight += minSizes[i].height;
314 }
315 totalHeight += (numChildren - 1) * spacing;
316 totalMinHeight += (numChildren - 1) * spacing;
317 prefMinSumHeight = totalHeight - totalMinHeight;
318 /*
319 * The total amount that the children must be shrunk is the
320 * sum of the preferred Heights of the children minus
321 * Max(the available area and the sum of the minimum heights of the children).
322 *
323 * amntShrinkHeight is the combined amount that the children must shrink
324 * amntShrinkCurrentHeight is the amount each child will shrink respectively
325 */
326 int amntShrinkHeight = totalHeight - Math.max(availableHeight, totalMinHeight);
327
328 if (amntShrinkHeight < 0) {
329 amntShrinkHeight = 0;
330 }
331
332 for (int i = 0; i < numChildren; i++) {
333 int amntShrinkCurrentHeight = 0;
334 int prefHeight = prefSizes[i].height;
335 int minHeight = minSizes[i].height;
336 int prefWidth = prefSizes[i].width;
337 int minWidth = minSizes[i].width;
338 Rectangle newBounds = new Rectangle(x, y, prefWidth, prefHeight);
339
340 child = cast(IFigure)children.get(i);
341 if (prefMinSumHeight !is 0)
342 amntShrinkCurrentHeight =
343 (prefHeight - minHeight) * amntShrinkHeight / (prefMinSumHeight);
344
345 int width = Math.min(prefWidth, transposer.t(child.getMaximumSize()).width);
346 if (matchWidth)
347 width = transposer.t(child.getMaximumSize()).width;
348 width = Math.max(minWidth, Math.min(clientArea.width, width));
349 newBounds.width = width;
350
351 int adjust = clientArea.width - width;
352 switch (minorAlignment) {
353 case ALIGN_TOPLEFT:
354 adjust = 0;
355 break;
356 case ALIGN_CENTER:
357 adjust /= 2;
358 break;
359 case ALIGN_BOTTOMRIGHT:
360 break;
361 }
362 newBounds.x += adjust;
363 newBounds.height -= amntShrinkCurrentHeight;
364 child.setBounds(transposer.t(newBounds));
365
366 amntShrinkHeight -= amntShrinkCurrentHeight;
367 prefMinSumHeight -= (prefHeight - minHeight);
368 y += newBounds.height + spacing;
369 }
370 }
371
372 /**
373 * Sets the alignment of the children contained in the layout. Possible values are
374 * {@link #ALIGN_CENTER}, {@link #ALIGN_BOTTOMRIGHT} and {@link #ALIGN_TOPLEFT}.
375 *
376 * @param align the minor alignment
377 * @since 2.0
378 */
379 public void setMinorAlignment(int align_) {
380 minorAlignment = align_;
381 }
382
383 /**
384 * Sets the amount of space between children.
385 *
386 * @param space the amount of space between children
387 * @since 2.0
388 */
389 public void setSpacing(int space) {
390 spacing = space;
391 }
392
393 /**
394 * Sets children's width (if vertically oriented) or height (if horizontally oriented) to
395 * stretch with their container.
396 *
397 * @deprecated use {@link #setStretchMinorAxis(bool)}
398 * @param match whether to stretch children
399 * @since 2.0
400 */
401 public void setMatchWidth(bool match) {
402 matchWidth = match;
403 }
404
405 /**
406 * Causes children that are smaller in the dimension of the minor axis to be stretched to
407 * fill the minor axis. The minor axis is the opposite of the orientation.
408 * @param stretch whether to stretch children
409 * @since 2.0
410 */
411 public void setStretchMinorAxis(bool stretch) {
412 matchWidth = stretch;
413 }
414
415 /**
416 * Sets the orientation of the children in the ToolbarLayout.
417 *
418 * @param flag whether the orientation should be vertical
419 * @since 2.0
420 */
421 public void setVertical(bool flag) {
422 if (horizontal !is flag) return;
423 invalidate();
424 horizontal = !flag;
425 transposer.setEnabled(horizontal);
426 }
427
428 }