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 }