Mercurial > projects > dwt-addons
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 } |