comparison dwt/layout/RowLayout.d @ 0:5406a8f6526d

Add initial files
author John Reimer <terminal.node@gmail.com
date Sun, 20 Jan 2008 21:50:55 -0800
parents
children 9a64a7781bab
comparison
equal deleted inserted replaced
-1:000000000000 0:5406a8f6526d
1 /*******************************************************************************
2 * Copyright (c) 2000, 2006 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 dwt.layout.RowLayout;
14
15 import dwt.DWT;
16 import dwt.graphics.Point;
17 import dwt.graphics.Rectangle;
18 import dwt.widgets.Control;
19 import dwt.widgets.Layout;
20 import dwt.widgets.Composite;
21 import dwt.layout.RowData;
22 import tango.text.Util;
23 import tango.util.Convert;
24 import Math = tango.math.Math;
25
26
27 /**
28 * Instances of this class determine the size and position of the
29 * children of a <code>Composite</code> by placing them either in
30 * horizontal rows or vertical columns within the parent <code>Composite</code>.
31 * <p>
32 * <code>RowLayout</code> aligns all controls in one row if the
33 * <code>type</code> is set to horizontal, and one column if it is
34 * set to vertical. It has the ability to wrap, and provides configurable
35 * margins and spacing. <code>RowLayout</code> has a number of configuration
36 * fields. In addition, the height and width of each control in a
37 * <code>RowLayout</code> can be specified by setting a <code>RowData</code>
38 * object into the control using <code>setLayoutData ()</code>.
39 * </p>
40 * <p>
41 * The following example code creates a <code>RowLayout</code>, sets all
42 * of its fields to non-default values, and then sets it into a
43 * <code>Shell</code>.
44 * <pre>
45 * RowLayout rowLayout = new RowLayout();
46 * rowLayout.wrap = false;
47 * rowLayout.pack = false;
48 * rowLayout.justify = true;
49 * rowLayout.type = DWT.VERTICAL;
50 * rowLayout.marginLeft = 5;
51 * rowLayout.marginTop = 5;
52 * rowLayout.marginRight = 5;
53 * rowLayout.marginBottom = 5;
54 * rowLayout.spacing = 0;
55 * shell.setLayout(rowLayout);
56 * </pre>
57 * If you are using the default field values, you only need one line of code:
58 * <pre>
59 * shell.setLayout(new RowLayout());
60 * </pre>
61 * </p>
62 *
63 * @see RowData
64 */
65 public final class RowLayout : Layout {
66
67 /**
68 * type specifies whether the layout places controls in rows or
69 * columns.
70 *
71 * The default value is HORIZONTAL.
72 *
73 * Possible values are: <ul>
74 * <li>HORIZONTAL: Position the controls horizontally from left to right</li>
75 * <li>VERTICAL: Position the controls vertically from top to bottom</li>
76 * </ul>
77 *
78 * @since 2.0
79 */
80 public int type = DWT.HORIZONTAL;
81
82 /**
83 * marginWidth specifies the number of pixels of horizontal margin
84 * that will be placed along the left and right edges of the layout.
85 *
86 * The default value is 0.
87 *
88 * @since 3.0
89 */
90 public int marginWidth = 0;
91
92 /**
93 * marginHeight specifies the number of pixels of vertical margin
94 * that will be placed along the top and bottom edges of the layout.
95 *
96 * The default value is 0.
97 *
98 * @since 3.0
99 */
100 public int marginHeight = 0;
101
102 /**
103 * spacing specifies the number of pixels between the edge of one cell
104 * and the edge of its neighbouring cell.
105 *
106 * The default value is 3.
107 */
108 public int spacing = 3;
109
110 /**
111 * wrap specifies whether a control will be wrapped to the next
112 * row if there is insufficient space on the current row.
113 *
114 * The default value is true.
115 */
116 public bool wrap = true;
117
118 /**
119 * pack specifies whether all controls in the layout take
120 * their preferred size. If pack is false, all controls will
121 * have the same size which is the size required to accommodate the
122 * largest preferred height and the largest preferred width of all
123 * the controls in the layout.
124 *
125 * The default value is true.
126 */
127 public bool pack = true;
128
129 /**
130 * fill specifies whether the controls in a row should be
131 * all the same height for horizontal layouts, or the same
132 * width for vertical layouts.
133 *
134 * The default value is false.
135 *
136 * @since 3.0
137 */
138 public bool fill = false;
139
140 /**
141 * justify specifies whether the controls in a row should be
142 * fully justified, with any extra space placed between the controls.
143 *
144 * The default value is false.
145 */
146 public bool justify = false;
147
148 /**
149 * marginLeft specifies the number of pixels of horizontal margin
150 * that will be placed along the left edge of the layout.
151 *
152 * The default value is 3.
153 */
154 public int marginLeft = 3;
155
156 /**
157 * marginTop specifies the number of pixels of vertical margin
158 * that will be placed along the top edge of the layout.
159 *
160 * The default value is 3.
161 */
162 public int marginTop = 3;
163
164 /**
165 * marginRight specifies the number of pixels of horizontal margin
166 * that will be placed along the right edge of the layout.
167 *
168 * The default value is 3.
169 */
170 public int marginRight = 3;
171
172 /**
173 * marginBottom specifies the number of pixels of vertical margin
174 * that will be placed along the bottom edge of the layout.
175 *
176 * The default value is 3.
177 */
178 public int marginBottom = 3;
179
180 /**
181 * Constructs a new instance of this class.
182 */
183 public this () {
184 }
185
186 /**
187 * Constructs a new instance of this class given the type.
188 *
189 * @param type the type of row layout
190 *
191 * @since 2.0
192 */
193 public this (int type) {
194 this.type = type;
195 }
196
197 protected Point computeSize (Composite composite, int wHint, int hHint, bool flushCache_) {
198 Point extent;
199 if (type is DWT.HORIZONTAL) {
200 extent = layoutHorizontal (composite, false, (wHint !is DWT.DEFAULT) && wrap, wHint, flushCache_);
201 } else {
202 extent = layoutVertical (composite, false, (hHint !is DWT.DEFAULT) && wrap, hHint, flushCache_);
203 }
204 if (wHint !is DWT.DEFAULT) extent.x = wHint;
205 if (hHint !is DWT.DEFAULT) extent.y = hHint;
206 return extent;
207 }
208
209 Point computeSize (Control control, bool flushCache_) {
210 int wHint = DWT.DEFAULT, hHint = DWT.DEFAULT;
211 RowData data = cast(RowData) control.getLayoutData ();
212 if (data !is null) {
213 wHint = data.width;
214 hHint = data.height;
215 }
216 return control.computeSize (wHint, hHint, flushCache_);
217 }
218
219 protected bool flushCache (Control control) {
220 return true;
221 }
222
223 char[] getName () {
224 char[] string = this.classinfo.name;
225 int index = locatePrior( string, '.');
226 if (index is string.length ) return string;
227 return string[ index + 1 .. string.length ];
228 }
229
230 protected void layout (Composite composite, bool flushCache_) {
231 Rectangle clientArea = composite.getClientArea ();
232 if (type is DWT.HORIZONTAL) {
233 layoutHorizontal (composite, true, wrap, clientArea.width, flushCache_);
234 } else {
235 layoutVertical (composite, true, wrap, clientArea.height, flushCache_);
236 }
237 }
238
239 Point layoutHorizontal (Composite composite, bool move, bool wrap, int width, bool flushCache_) {
240 Control [] children = composite.getChildren ();
241 int count = 0;
242 for (int i=0; i<children.length; i++) {
243 Control control = children [i];
244 RowData data = cast(RowData) control.getLayoutData ();
245 if (data is null || !data.exclude) {
246 children [count++] = children [i];
247 }
248 }
249 if (count is 0) {
250 return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
251 }
252 int childWidth = 0, childHeight = 0, maxHeight = 0;
253 if (!pack) {
254 for (int i=0; i<count; i++) {
255 Control child = children [i];
256 Point size = computeSize (child, flushCache_);
257 childWidth = Math.max (childWidth, size.x);
258 childHeight = Math.max (childHeight, size.y);
259 }
260 maxHeight = childHeight;
261 }
262 int clientX = 0, clientY = 0;
263 if (move) {
264 Rectangle rect = composite.getClientArea ();
265 clientX = rect.x;
266 clientY = rect.y;
267 }
268 int [] wraps = null;
269 bool wrapped = false;
270 Rectangle [] bounds = null;
271 if (move && (justify || fill)) {
272 bounds = new Rectangle [count];
273 wraps = new int [count];
274 }
275 int maxX = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
276 for (int i=0; i<count; i++) {
277 Control child = children [i];
278 if (pack) {
279 Point size = computeSize (child, flushCache_);
280 childWidth = size.x;
281 childHeight = size.y;
282 }
283 if (wrap && (i !is 0) && (x + childWidth > width)) {
284 wrapped = true;
285 if (move && (justify || fill)) wraps [i - 1] = maxHeight;
286 x = marginLeft + marginWidth;
287 y += spacing + maxHeight;
288 if (pack) maxHeight = 0;
289 }
290 if (pack || fill) {
291 maxHeight = Math.max (maxHeight, childHeight);
292 }
293 if (move) {
294 int childX = x + clientX, childY = y + clientY;
295 if (justify || fill) {
296 bounds [i] = new Rectangle (childX, childY, childWidth, childHeight);
297 } else {
298 child.setBounds (childX, childY, childWidth, childHeight);
299 }
300 }
301 x += spacing + childWidth;
302 maxX = Math.max (maxX, x);
303 }
304 maxX = Math.max (clientX + marginLeft + marginWidth, maxX - spacing);
305 if (!wrapped) maxX += marginRight + marginWidth;
306 if (move && (justify || fill)) {
307 int space = 0, margin = 0;
308 if (!wrapped) {
309 space = Math.max (0, (width - maxX) / (count + 1));
310 margin = Math.max (0, ((width - maxX) % (count + 1)) / 2);
311 } else {
312 if (fill || justify) {
313 int last = 0;
314 if (count > 0) wraps [count - 1] = maxHeight;
315 for (int i=0; i<count; i++) {
316 if (wraps [i] !is 0) {
317 int wrapCount = i - last + 1;
318 if (justify) {
319 int wrapX = 0;
320 for (int j=last; j<=i; j++) {
321 wrapX += bounds [j].width + spacing;
322 }
323 space = Math.max (0, (width - wrapX) / (wrapCount + 1));
324 margin = Math.max (0, ((width - wrapX) % (wrapCount + 1)) / 2);
325 }
326 for (int j=last; j<=i; j++) {
327 if (justify) bounds [j].x += (space * (j - last + 1)) + margin;
328 if (fill) bounds [j].height = wraps [i];
329 }
330 last = i + 1;
331 }
332 }
333 }
334 }
335 for (int i=0; i<count; i++) {
336 if (!wrapped) {
337 if (justify) bounds [i].x += (space * (i + 1)) + margin;
338 if (fill) bounds [i].height = maxHeight;
339 }
340 children [i].setBounds (bounds [i]);
341 }
342 }
343 return new Point (maxX, y + maxHeight + marginBottom + marginHeight);
344 }
345
346 Point layoutVertical (Composite composite, bool move, bool wrap, int height, bool flushCache_) {
347 Control [] children = composite.getChildren ();
348 int count = 0;
349 for (int i=0; i<children.length; i++) {
350 Control control = children [i];
351 RowData data = cast(RowData) control.getLayoutData ();
352 if (data is null || !data.exclude) {
353 children [count++] = children [i];
354 }
355 }
356 if (count is 0) {
357 return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
358 }
359 int childWidth = 0, childHeight = 0, maxWidth = 0;
360 if (!pack) {
361 for (int i=0; i<count; i++) {
362 Control child = children [i];
363 Point size = computeSize (child, flushCache_);
364 childWidth = Math.max (childWidth, size.x);
365 childHeight = Math.max (childHeight, size.y);
366 }
367 maxWidth = childWidth;
368 }
369 int clientX = 0, clientY = 0;
370 if (move) {
371 Rectangle rect = composite.getClientArea ();
372 clientX = rect.x;
373 clientY = rect.y;
374 }
375 int [] wraps = null;
376 bool wrapped = false;
377 Rectangle [] bounds = null;
378 if (move && (justify || fill)) {
379 bounds = new Rectangle [count];
380 wraps = new int [count];
381 }
382 int maxY = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
383 for (int i=0; i<count; i++) {
384 Control child = children [i];
385 if (pack) {
386 Point size = computeSize (child, flushCache_);
387 childWidth = size.x;
388 childHeight = size.y;
389 }
390 if (wrap && (i !is 0) && (y + childHeight > height)) {
391 wrapped = true;
392 if (move && (justify || fill)) wraps [i - 1] = maxWidth;
393 x += spacing + maxWidth;
394 y = marginTop + marginHeight;
395 if (pack) maxWidth = 0;
396 }
397 if (pack || fill) {
398 maxWidth = Math.max (maxWidth, childWidth);
399 }
400 if (move) {
401 int childX = x + clientX, childY = y + clientY;
402 if (justify || fill) {
403 bounds [i] = new Rectangle (childX, childY, childWidth, childHeight);
404 } else {
405 child.setBounds (childX, childY, childWidth, childHeight);
406 }
407 }
408 y += spacing + childHeight;
409 maxY = Math.max (maxY, y);
410 }
411 maxY = Math.max (clientY + marginTop + marginHeight, maxY - spacing);
412 if (!wrapped) maxY += marginBottom + marginHeight;
413 if (move && (justify || fill)) {
414 int space = 0, margin = 0;
415 if (!wrapped) {
416 space = Math.max (0, (height - maxY) / (count + 1));
417 margin = Math.max (0, ((height - maxY) % (count + 1)) / 2);
418 } else {
419 if (fill || justify) {
420 int last = 0;
421 if (count > 0) wraps [count - 1] = maxWidth;
422 for (int i=0; i<count; i++) {
423 if (wraps [i] !is 0) {
424 int wrapCount = i - last + 1;
425 if (justify) {
426 int wrapY = 0;
427 for (int j=last; j<=i; j++) {
428 wrapY += bounds [j].height + spacing;
429 }
430 space = Math.max (0, (height - wrapY) / (wrapCount + 1));
431 margin = Math.max (0, ((height - wrapY) % (wrapCount + 1)) / 2);
432 }
433 for (int j=last; j<=i; j++) {
434 if (justify) bounds [j].y += (space * (j - last + 1)) + margin;
435 if (fill) bounds [j].width = wraps [i];
436 }
437 last = i + 1;
438 }
439 }
440 }
441 }
442 for (int i=0; i<count; i++) {
443 if (!wrapped) {
444 if (justify) bounds [i].y += (space * (i + 1)) + margin;
445 if (fill) bounds [i].width = maxWidth;
446 }
447 children [i].setBounds (bounds [i]);
448 }
449 }
450 return new Point (x + maxWidth + marginRight + marginWidth, maxY);
451 }
452
453 /**
454 * Returns a string containing a concise, human-readable
455 * description of the receiver.
456 *
457 * @return a string representation of the layout
458 */
459 public char[] toString () {
460 char[] string = getName ()~" {";
461 string ~= "type="~((type !is DWT.HORIZONTAL) ? "DWT.VERTICAL" : "DWT.HORIZONTAL")~" ";
462 if (marginWidth !is 0) string ~= "marginWidth="~to!(char[])(marginWidth)~" ";
463 if (marginHeight !is 0) string ~= "marginHeight="~to!(char[])(marginHeight)~" ";
464 if (marginLeft !is 0) string ~= "marginLeft="~to!(char[])(marginLeft)~" ";
465 if (marginTop !is 0) string ~= "marginTop="~to!(char[])(marginTop)~" ";
466 if (marginRight !is 0) string ~= "marginRight="~to!(char[])(marginRight)~" ";
467 if (marginBottom !is 0) string ~= "marginBottom="~to!(char[])(marginBottom)~" ";
468 if (spacing !is 0) string ~= "spacing="~to!(char[])(spacing)~" ";
469 string ~= "wrap="~to!(char[])(wrap)~" ";
470 string ~= "pack="~to!(char[])(pack)~" ";
471 string ~= "fill="~to!(char[])(fill)~" ";
472 string ~= "justify="~to!(char[])(justify)~" ";
473 string = trim( string );
474 string ~= "}";
475 return string;
476 }
477 }