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