Mercurial > projects > dwt-addons
comparison 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 |
comparison
equal
deleted
inserted
replaced
96:b492ba44e44d | 98:95307ad235d9 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2005 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.FlowLayout; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.dwtxhelper.Collection; | |
17 | |
18 import dwtx.draw2d.geometry.Dimension; | |
19 import dwtx.draw2d.geometry.Rectangle; | |
20 import dwtx.draw2d.geometry.Transposer; | |
21 import dwtx.draw2d.AbstractHintLayout; | |
22 import dwtx.draw2d.IFigure; | |
23 | |
24 /** | |
25 * Lays out children in rows or columns, wrapping when the current row/column is filled. | |
26 * The aligment and spacing of rows in the parent can be configured. The aligment and | |
27 * spacing of children within a row can be configured. | |
28 */ | |
29 public class FlowLayout | |
30 : AbstractHintLayout | |
31 { | |
32 | |
33 /** Constant to specify components to be aligned in the center */ | |
34 public static const int ALIGN_CENTER = 0; | |
35 /** Constant to specify components to be aligned on the left/top */ | |
36 public static const int ALIGN_LEFTTOP = 1; | |
37 /** Constant to specify components to be aligned on the right/bottom */ | |
38 public static const int ALIGN_RIGHTBOTTOM = 2; | |
39 | |
40 /** Constant to specify components should be layed out horizontally */ | |
41 public static const bool HORIZONTAL = true; | |
42 /** Constant to specify components should be layed out vertically */ | |
43 public static const bool VERTICAL = false; | |
44 | |
45 /** The horizontal property. */ | |
46 protected bool horizontal = true; | |
47 /** | |
48 * The property that determines whether leftover space at the end of a row/column should | |
49 * be filled by the last item in that row/column. | |
50 */ | |
51 protected bool fill = false; | |
52 | |
53 /** The transposer used in converting horizontal layout to vertical. */ | |
54 protected Transposer transposer; | |
55 private void instanceInit(){ | |
56 transposer = new Transposer(); | |
57 transposer.setEnabled(!horizontal); | |
58 } | |
59 | |
60 /** The alignment along the major axis. */ | |
61 protected int majorAlignment = ALIGN_LEFTTOP; | |
62 /** The alignment along the minor axis. */ | |
63 protected int minorAlignment = ALIGN_LEFTTOP; | |
64 /** The spacing along the minor axis. */ | |
65 protected int minorSpacing = 5; | |
66 /** The spacing along the major axis. */ | |
67 protected int majorSpacing = 5; | |
68 protected WorkingData data = null; | |
69 | |
70 /** | |
71 * Holds the necessary information for layout calculations. | |
72 */ | |
73 protected class WorkingData { | |
74 public int rowHeight, rowWidth, rowCount, rowX, rowY, maxWidth; | |
75 public Rectangle[] bounds; | |
76 public Rectangle area; | |
77 public IFigure row[]; | |
78 } | |
79 | |
80 /** | |
81 * Constructs a FlowLayout with horizontal orientation. | |
82 * @since 2.0 | |
83 */ | |
84 public this() { | |
85 instanceInit(); | |
86 } | |
87 | |
88 /** | |
89 * Constructs a FlowLayout whose orientation is given in the input. | |
90 * @param isHorizontal <code>true</code> if the layout should be horizontal | |
91 * @since 2.0 | |
92 */ | |
93 public this(bool isHorizontal) { | |
94 instanceInit(); | |
95 setHorizontal(isHorizontal); | |
96 } | |
97 | |
98 /** | |
99 * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(IFigure, int, int) | |
100 */ | |
101 protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { | |
102 // Subtract out the insets from the hints | |
103 if (wHint > -1) | |
104 wHint = Math.max(0, wHint - container.getInsets().getWidth()); | |
105 if (hHint > -1) | |
106 hHint = Math.max(0, hHint - container.getInsets().getHeight()); | |
107 | |
108 // Figure out the new hint that we are interested in based on the orientation | |
109 // Ignore the other hint (by setting it to -1). NOTE: The children of the | |
110 // parent figure will then be asked to ignore that hint as well. | |
111 int maxWidth; | |
112 if (isHorizontal()) { | |
113 maxWidth = wHint; | |
114 hHint = -1; | |
115 } else { | |
116 maxWidth = hHint; | |
117 wHint = -1; | |
118 } | |
119 if (maxWidth < 0) { | |
120 maxWidth = Integer.MAX_VALUE; | |
121 } | |
122 | |
123 // The preferred dimension that is to be calculated and returned | |
124 Dimension prefSize = new Dimension(); | |
125 | |
126 List children = container.getChildren(); | |
127 int width = 0; | |
128 int height = 0; | |
129 IFigure child; | |
130 Dimension childSize; | |
131 | |
132 //Build the sizes for each row, and update prefSize accordingly | |
133 for (int i = 0; i < children.size(); i++) { | |
134 child = cast(IFigure)children.get(i); | |
135 childSize = transposer.t(getChildSize(child, wHint, hHint)); | |
136 if (i is 0) { | |
137 width = childSize.width; | |
138 height = childSize.height; | |
139 } else if (width + childSize.width + getMinorSpacing() > maxWidth) { | |
140 // The current row is full, start a new row. | |
141 prefSize.height += height + getMajorSpacing(); | |
142 prefSize.width = Math.max(prefSize.width, width); | |
143 width = childSize.width; | |
144 height = childSize.height; | |
145 } else { | |
146 // The current row can fit another child. | |
147 width += childSize.width + getMinorSpacing(); | |
148 height = Math.max(height, childSize.height); | |
149 } | |
150 } | |
151 | |
152 // Flush out the last row's data | |
153 prefSize.height += height; | |
154 prefSize.width = Math.max(prefSize.width, width); | |
155 | |
156 // Transpose the dimension back, and compensate for the border. | |
157 prefSize = transposer.t(prefSize); | |
158 prefSize.width += container.getInsets().getWidth(); | |
159 prefSize.height += container.getInsets().getHeight(); | |
160 prefSize.union_(getBorderPreferredSize(container)); | |
161 | |
162 return prefSize; | |
163 } | |
164 | |
165 /** | |
166 * Provides the given child's preferred size. | |
167 * | |
168 * @param child the Figure whose preferred size needs to be calculated | |
169 * @param wHint the width hint | |
170 * @param hHint the height hint | |
171 * @return the child's preferred size | |
172 */ | |
173 protected Dimension getChildSize(IFigure child, int wHint, int hHint) { | |
174 return child.getPreferredSize(wHint, hHint); | |
175 } | |
176 | |
177 | |
178 /** | |
179 * Returns the alignment used for an entire row/column. | |
180 * <P> | |
181 * Possible values are : | |
182 * <ul> | |
183 * <li>{@link #ALIGN_CENTER} | |
184 * <li>{@link #ALIGN_LEFTTOP} | |
185 * <li>{@link #ALIGN_RIGHTBOTTOM} | |
186 * </ul> | |
187 * | |
188 * @return the major alignment | |
189 * @since 2.0 | |
190 */ | |
191 public int getMajorAlignment() { | |
192 return majorAlignment; | |
193 } | |
194 | |
195 /** | |
196 * Returns the spacing in pixels to be used between children in the direction parallel to | |
197 * the layout's orientation. | |
198 * @return the major spacing | |
199 */ | |
200 public int getMajorSpacing() { | |
201 return majorSpacing; | |
202 } | |
203 | |
204 /** | |
205 * Returns the alignment used for children within a row/column. | |
206 * <P> | |
207 * Possible values are : | |
208 * <ul> | |
209 * <li>{@link #ALIGN_CENTER} | |
210 * <li>{@link #ALIGN_LEFTTOP} | |
211 * <li>{@link #ALIGN_RIGHTBOTTOM} | |
212 * </ul> | |
213 * | |
214 * @return the minor alignment | |
215 * @since 2.0 | |
216 */ | |
217 public int getMinorAlignment() { | |
218 return minorAlignment; | |
219 } | |
220 | |
221 /** | |
222 * Returns the spacing to be used between children within a row/column. | |
223 * @return the minor spacing | |
224 */ | |
225 public int getMinorSpacing() { | |
226 return minorSpacing; | |
227 } | |
228 | |
229 /** | |
230 * Initializes the state of row data, which is internal to the layout process. | |
231 */ | |
232 protected void initRow() { | |
233 data.rowX = 0; | |
234 data.rowHeight = 0; | |
235 data.rowWidth = 0; | |
236 data.rowCount = 0; | |
237 } | |
238 | |
239 /** | |
240 * Initializes state data for laying out children, based on the Figure given as input. | |
241 * | |
242 * @param parent the parent figure | |
243 * @since 2.0 | |
244 */ | |
245 protected void initVariables(IFigure parent) { | |
246 data.row = new IFigure[parent.getChildren().size()]; | |
247 data.bounds = new Rectangle[data.row.length]; | |
248 data.maxWidth = data.area.width; | |
249 } | |
250 | |
251 /** | |
252 * Returns <code>true</code> if the orientation of the layout is horizontal. | |
253 * | |
254 * @return <code>true</code> if the orientation of the layout is horizontal | |
255 * @since 2.0 | |
256 */ | |
257 public bool isHorizontal() { | |
258 return horizontal; | |
259 } | |
260 | |
261 /** | |
262 * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure) | |
263 */ | |
264 protected bool isSensitiveHorizontally(IFigure parent) { | |
265 return isHorizontal(); | |
266 } | |
267 | |
268 /** | |
269 * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure) | |
270 */ | |
271 protected bool isSensitiveVertically(IFigure parent) { | |
272 return !isHorizontal(); | |
273 } | |
274 | |
275 /** | |
276 * @see dwtx.draw2d.LayoutManager#layout(IFigure) | |
277 */ | |
278 public void layout(IFigure parent) { | |
279 data = new WorkingData(); | |
280 Rectangle relativeArea = parent.getClientArea(); | |
281 data.area = transposer.t(relativeArea); | |
282 | |
283 Iterator iterator = parent.getChildren().iterator(); | |
284 int dx; | |
285 | |
286 //Calculate the hints to be passed to children | |
287 int wHint = -1; | |
288 int hHint = -1; | |
289 if (isHorizontal()) | |
290 wHint = parent.getClientArea().width; | |
291 else | |
292 hHint = parent.getClientArea().height; | |
293 | |
294 initVariables(parent); | |
295 initRow(); | |
296 int i = 0; | |
297 while (iterator.hasNext()) { | |
298 IFigure f = cast(IFigure)iterator.next(); | |
299 Dimension pref = transposer.t(getChildSize(f, wHint, hHint)); | |
300 Rectangle r = new Rectangle(0, 0, pref.width, pref.height); | |
301 | |
302 if (data.rowCount > 0) { | |
303 if (data.rowWidth + pref.width > data.maxWidth) | |
304 layoutRow(parent); | |
305 } | |
306 r.x = data.rowX; | |
307 r.y = data.rowY; | |
308 dx = r.width + getMinorSpacing(); | |
309 data.rowX += dx; | |
310 data.rowWidth += dx; | |
311 data.rowHeight = Math.max(data.rowHeight, r.height); | |
312 data.row [data.rowCount] = f; | |
313 data.bounds[data.rowCount] = r; | |
314 data.rowCount++; | |
315 i++; | |
316 } | |
317 if (data.rowCount !is 0) | |
318 layoutRow(parent); | |
319 data = null; | |
320 } | |
321 | |
322 /** | |
323 * Layouts one row of components. This is done based on the layout's orientation, minor | |
324 * alignment and major alignment. | |
325 * | |
326 * @param parent the parent figure | |
327 * @since 2.0 | |
328 */ | |
329 protected void layoutRow(IFigure parent) { | |
330 int majorAdjustment = 0; | |
331 int minorAdjustment = 0; | |
332 int correctMajorAlignment = majorAlignment; | |
333 int correctMinorAlignment = minorAlignment; | |
334 | |
335 majorAdjustment = data.area.width - data.rowWidth + getMinorSpacing(); | |
336 | |
337 switch (correctMajorAlignment) { | |
338 case ALIGN_LEFTTOP: | |
339 majorAdjustment = 0; | |
340 break; | |
341 case ALIGN_CENTER: | |
342 majorAdjustment /= 2; | |
343 break; | |
344 case ALIGN_RIGHTBOTTOM: | |
345 break; | |
346 } | |
347 | |
348 for (int j = 0; j < data.rowCount; j++) { | |
349 if (fill) { | |
350 data.bounds[j].height = data.rowHeight; | |
351 } else { | |
352 minorAdjustment = data.rowHeight - data.bounds[j].height; | |
353 switch (correctMinorAlignment) { | |
354 case ALIGN_LEFTTOP: | |
355 minorAdjustment = 0; | |
356 break; | |
357 case ALIGN_CENTER: | |
358 minorAdjustment /= 2; | |
359 break; | |
360 case ALIGN_RIGHTBOTTOM: | |
361 break; | |
362 } | |
363 data.bounds[j].y += minorAdjustment; | |
364 } | |
365 data.bounds[j].x += majorAdjustment; | |
366 | |
367 setBoundsOfChild(parent, data.row[j], transposer.t(data.bounds[j])); | |
368 } | |
369 data.rowY += getMajorSpacing() + data.rowHeight; | |
370 initRow(); | |
371 } | |
372 | |
373 /** | |
374 * Sets the given bounds for the child figure input. | |
375 * | |
376 * @param parent the parent figure | |
377 * @param child the child figure | |
378 * @param bounds the size of the child to be set | |
379 * @since 2.0 | |
380 */ | |
381 protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) { | |
382 parent.getClientArea(Rectangle.SINGLETON); | |
383 bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y); | |
384 child.setBounds(bounds); | |
385 } | |
386 | |
387 /** | |
388 * Sets flag based on layout orientation. If in horizontal orientation, all figures will | |
389 * have the same height. If in vertical orientation, all figures will have the same width. | |
390 * | |
391 * @param value fill state desired | |
392 * @since 2.0 | |
393 */ | |
394 public void setStretchMinorAxis(bool value) { | |
395 fill = value; | |
396 } | |
397 | |
398 /** | |
399 * Sets the orientation of the layout. | |
400 * | |
401 * @param flag <code>true</code> if this layout should be horizontal | |
402 * @since 2.0 | |
403 */ | |
404 public void setHorizontal(bool flag) { | |
405 if (horizontal is flag) return; | |
406 invalidate(); | |
407 horizontal = flag; | |
408 transposer.setEnabled(!horizontal); | |
409 } | |
410 | |
411 /** | |
412 * Sets the alignment for an entire row/column within the parent figure. | |
413 * <P> | |
414 * Possible values are : | |
415 * <ul> | |
416 * <li>{@link #ALIGN_CENTER} | |
417 * <li>{@link #ALIGN_LEFTTOP} | |
418 * <li>{@link #ALIGN_RIGHTBOTTOM} | |
419 * </ul> | |
420 * | |
421 * @param align the major alignment | |
422 * @since 2.0 | |
423 */ | |
424 public void setMajorAlignment(int align_) { | |
425 majorAlignment = align_; | |
426 } | |
427 | |
428 /** | |
429 * Sets the spacing in pixels to be used between children in the direction parallel to the | |
430 * layout's orientation. | |
431 * | |
432 * @param n the major spacing | |
433 * @since 2.0 | |
434 */ | |
435 public void setMajorSpacing(int n) { | |
436 majorSpacing = n; | |
437 } | |
438 | |
439 /** | |
440 * Sets the alignment to be used within a row/column. | |
441 * <P> | |
442 * Possible values are : | |
443 * <ul> | |
444 * <li>{@link #ALIGN_CENTER} | |
445 * <li>{@link #ALIGN_LEFTTOP} | |
446 * <li>{@link #ALIGN_RIGHTBOTTOM} | |
447 * </ul> | |
448 * | |
449 * @param align the minor alignment | |
450 * @since 2.0 | |
451 */ | |
452 public void setMinorAlignment(int align_) { | |
453 minorAlignment = align_; | |
454 } | |
455 | |
456 /** | |
457 * Sets the spacing to be used between children within a row/column. | |
458 * | |
459 * @param n the minor spacing | |
460 * @since 2.0 | |
461 */ | |
462 public void setMinorSpacing(int n) { | |
463 minorSpacing = n; | |
464 } | |
465 | |
466 } |