1 /*******************************************************************************
2 * Copyright (c) 2000, 2018 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.layout;
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.graphics.*;
18 import org.eclipse.swt.widgets.*;
21 * Instances of this class determine the size and position of the
22 * children of a <code>Composite</code> by placing them either in
23 * horizontal rows or vertical columns within the parent <code>Composite</code>.
25 * <code>RowLayout</code> aligns all controls in one row if the
26 * <code>type</code> is set to horizontal, and one column if it is
27 * set to vertical. It has the ability to wrap, and provides configurable
28 * margins and spacing. <code>RowLayout</code> has a number of configuration
29 * fields. In addition, the height and width of each control in a
30 * <code>RowLayout</code> can be specified by setting a <code>RowData</code>
31 * object into the control using <code>setLayoutData ()</code>.
34 * The following example code creates a <code>RowLayout</code>, sets all
35 * of its fields to non-default values, and then sets it into a
36 * <code>Shell</code>.</p>
38 * RowLayout rowLayout = new RowLayout();
39 * rowLayout.wrap = false;
40 * rowLayout.pack = false;
41 * rowLayout.justify = true;
42 * rowLayout.type = SWT.VERTICAL;
43 * rowLayout.marginLeft = 5;
44 * rowLayout.marginTop = 5;
45 * rowLayout.marginRight = 5;
46 * rowLayout.marginBottom = 5;
47 * rowLayout.spacing = 0;
48 * shell.setLayout(rowLayout);
50 * If you are using the default field values, you only need one line of code:
52 * shell.setLayout(new RowLayout());
56 * @see <a href="http://www.eclipse.org/swt/snippets/#rowlayout">RowLayout snippets</a>
57 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: LayoutExample</a>
58 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
60 public final class RowLayout extends Layout {
63 * type specifies whether the layout places controls in rows or
66 * The default value is HORIZONTAL.
68 * Possible values are: <ul>
69 * <li>HORIZONTAL: Position the controls horizontally from left to right</li>
70 * <li>VERTICAL: Position the controls vertically from top to bottom</li>
75 public int type = SWT.HORIZONTAL;
78 * marginWidth specifies the number of points of horizontal margin
79 * that will be placed along the left and right edges of the layout.
81 * The default value is 0.
85 public int marginWidth = 0;
88 * marginHeight specifies the number of points of vertical margin
89 * that will be placed along the top and bottom edges of the layout.
91 * The default value is 0.
95 public int marginHeight = 0;
98 * spacing specifies the number of points between the edge of one cell
99 * and the edge of its neighbouring cell.
101 * The default value is 3.
103 public int spacing = 3;
106 * wrap specifies whether a control will be wrapped to the next
107 * row if there is insufficient space on the current row.
109 * The default value is true.
111 public boolean wrap = true;
114 * pack specifies whether all controls in the layout take
115 * their preferred size. If pack is false, all controls will
116 * have the same size which is the size required to accommodate the
117 * largest preferred height and the largest preferred width of all
118 * the controls in the layout.
120 * The default value is true.
122 public boolean pack = true;
125 * fill specifies whether the controls in a row should be
126 * all the same height for horizontal layouts, or the same
127 * width for vertical layouts.
129 * The default value is false.
133 public boolean fill = false;
136 * center specifies whether the controls in a row should be
137 * centered vertically in each cell for horizontal layouts,
138 * or centered horizontally in each cell for vertical layouts.
140 * The default value is false.
144 public boolean center = false;
147 * justify specifies whether the controls in a row should be
148 * fully justified, with any extra space placed between the controls.
150 * The default value is false.
152 public boolean justify = false;
155 * marginLeft specifies the number of points of horizontal margin
156 * that will be placed along the left edge of the layout.
158 * The default value is 3.
160 public int marginLeft = 3;
163 * marginTop specifies the number of points of vertical margin
164 * that will be placed along the top edge of the layout.
166 * The default value is 3.
168 public int marginTop = 3;
171 * marginRight specifies the number of points of horizontal margin
172 * that will be placed along the right edge of the layout.
174 * The default value is 3.
176 public int marginRight = 3;
179 * marginBottom specifies the number of points of vertical margin
180 * that will be placed along the bottom edge of the layout.
182 * The default value is 3.
184 public int marginBottom = 3;
187 * Constructs a new instance of this class with type HORIZONTAL.
189 public RowLayout () {
193 * Constructs a new instance of this class given the type.
195 * @param type the type of row layout
199 public RowLayout (int type) {
204 protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) {
206 if (type == SWT.HORIZONTAL) {
207 extent = layoutHorizontal (composite, false, (wHint != SWT.DEFAULT) && wrap, wHint, flushCache);
209 extent = layoutVertical (composite, false, (hHint != SWT.DEFAULT) && wrap, hHint, flushCache);
211 if (wHint != SWT.DEFAULT) extent.x = wHint;
212 if (hHint != SWT.DEFAULT) extent.y = hHint;
216 Point computeSize (Control control, boolean flushCache) {
217 int wHint = SWT.DEFAULT, hHint = SWT.DEFAULT;
218 RowData data = (RowData) control.getLayoutData ();
223 return control.computeSize (wHint, hHint, flushCache);
227 protected boolean flushCache (Control control) {
232 String string = getClass ().getName ();
233 int index = string.lastIndexOf ('.');
234 if (index == -1) return string;
235 return string.substring (index + 1, string.length ());
239 protected void layout (Composite composite, boolean flushCache) {
240 Rectangle clientArea = composite.getClientArea ();
241 if (type == SWT.HORIZONTAL) {
242 layoutHorizontal (composite, true, wrap, clientArea.width, flushCache);
244 layoutVertical (composite, true, wrap, clientArea.height, flushCache);
248 Point layoutHorizontal (Composite composite, boolean move, boolean wrap, int width, boolean flushCache) {
249 Control [] children = composite.getChildren ();
251 for (int i=0; i<children.length; i++) {
252 Control control = children [i];
253 RowData data = (RowData) control.getLayoutData ();
254 if (data == null || !data.exclude) {
255 children [count++] = children [i];
259 return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
261 int childWidth = 0, childHeight = 0, maxHeight = 0;
263 for (int i=0; i<count; i++) {
264 Control child = children [i];
265 Point size = computeSize (child, flushCache);
266 if (width > SWT.DEFAULT && width < size.x && wrap) {
267 size = child.computeSize (width, child.getLayoutData() == null ? SWT.DEFAULT : ((RowData) child.getLayoutData()).height, flushCache);
269 childWidth = Math.max (childWidth, size.x);
270 childHeight = Math.max (childHeight, size.y);
272 maxHeight = childHeight;
274 int clientX = 0, clientY = 0;
276 Rectangle rect = composite.getClientArea ();
281 boolean wrapped = false;
282 Rectangle [] bounds = null;
283 if (move && (justify || fill || center)) {
284 bounds = new Rectangle [count];
285 wraps = new int [count];
287 int maxX = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
288 for (int i=0; i<count; i++) {
289 Control child = children [i];
291 Point size = computeSize (child, flushCache);
292 if (width > SWT.DEFAULT && width < size.x && wrap) {
293 size = child.computeSize (width, child.getLayoutData() == null ? SWT.DEFAULT : ((RowData) child.getLayoutData()).height, flushCache);
296 childHeight = size.y;
298 if (wrap && (i != 0) && (x + childWidth > width)) {
300 if (move && (justify || fill || center)) wraps [i - 1] = maxHeight;
301 x = marginLeft + marginWidth;
302 y += spacing + maxHeight;
303 if (pack) maxHeight = 0;
305 if (pack || fill || center) {
306 maxHeight = Math.max (maxHeight, childHeight);
309 int childX = x + clientX, childY = y + clientY;
310 if (justify || fill || center) {
311 bounds [i] = new Rectangle (childX, childY, childWidth, childHeight);
313 child.setBounds (childX, childY, childWidth, childHeight);
316 x += spacing + childWidth;
317 maxX = Math.max (maxX, x);
319 maxX = Math.max (clientX + marginLeft + marginWidth, maxX - spacing);
320 if (!wrapped) maxX += marginRight + marginWidth;
321 if (move && (justify || fill || center)) {
322 int space = 0, margin = 0;
324 space = Math.max (0, (width - maxX) / (count + 1));
325 margin = Math.max (0, ((width - maxX) % (count + 1)) / 2);
327 if (fill || justify || center) {
329 if (count > 0) wraps [count - 1] = maxHeight;
330 for (int i=0; i<count; i++) {
331 if (wraps [i] != 0) {
332 int wrapCount = i - last + 1;
335 for (int j=last; j<=i; j++) {
336 wrapX += bounds [j].width + spacing;
338 space = Math.max (0, (width - wrapX) / (wrapCount + 1));
339 margin = Math.max (0, ((width - wrapX) % (wrapCount + 1)) / 2);
341 for (int j=last; j<=i; j++) {
342 if (justify) bounds [j].x += (space * (j - last + 1)) + margin;
344 bounds [j].height = wraps [i];
347 bounds [j].y += Math.max (0, (wraps [i] - bounds [j].height) / 2);
356 for (int i=0; i<count; i++) {
358 if (justify) bounds [i].x += (space * (i + 1)) + margin;
360 bounds [i].height = maxHeight;
363 bounds [i].y += Math.max (0, (maxHeight - bounds [i].height) / 2);
367 children [i].setBounds (bounds [i]);
370 return new Point (maxX, y + maxHeight + marginBottom + marginHeight);
373 Point layoutVertical (Composite composite, boolean move, boolean wrap, int height, boolean flushCache) {
374 Control [] children = composite.getChildren ();
376 for (int i=0; i<children.length; i++) {
377 Control control = children [i];
378 RowData data = (RowData) control.getLayoutData ();
379 if (data == null || !data.exclude) {
380 children [count++] = children [i];
384 return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
386 int childWidth = 0, childHeight = 0, maxWidth = 0;
388 for (int i=0; i<count; i++) {
389 Control child = children [i];
390 Point size = computeSize (child, flushCache);
391 if(height>SWT.DEFAULT && height<size.y && wrap)
392 size=child.computeSize(child.getLayoutData()==null?SWT.DEFAULT:((RowData)child.getLayoutData()).width,height,flushCache);
393 childWidth = Math.max (childWidth, size.x);
394 childHeight = Math.max (childHeight, size.y);
396 maxWidth = childWidth;
398 int clientX = 0, clientY = 0;
400 Rectangle rect = composite.getClientArea ();
405 boolean wrapped = false;
406 Rectangle [] bounds = null;
407 if (move && (justify || fill || center)) {
408 bounds = new Rectangle [count];
409 wraps = new int [count];
411 int maxY = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
412 for (int i=0; i<count; i++) {
413 Control child = children [i];
415 Point size = computeSize (child, flushCache);
416 if(height>SWT.DEFAULT && height<size.y && wrap)
417 size=child.computeSize(child.getLayoutData()==null?SWT.DEFAULT:((RowData)child.getLayoutData()).width,height,flushCache);
419 childHeight = size.y;
421 if (wrap && (i != 0) && (y + childHeight > height)) {
423 if (move && (justify || fill || center)) wraps [i - 1] = maxWidth;
424 x += spacing + maxWidth;
425 y = marginTop + marginHeight;
426 if (pack) maxWidth = 0;
428 if (pack || fill || center) {
429 maxWidth = Math.max (maxWidth, childWidth);
432 int childX = x + clientX, childY = y + clientY;
433 if (justify || fill || center) {
434 bounds [i] = new Rectangle (childX, childY, childWidth, childHeight);
436 child.setBounds (childX, childY, childWidth, childHeight);
439 y += spacing + childHeight;
440 maxY = Math.max (maxY, y);
442 maxY = Math.max (clientY + marginTop + marginHeight, maxY - spacing);
443 if (!wrapped) maxY += marginBottom + marginHeight;
444 if (move && (justify || fill || center)) {
445 int space = 0, margin = 0;
447 space = Math.max (0, (height - maxY) / (count + 1));
448 margin = Math.max (0, ((height - maxY) % (count + 1)) / 2);
450 if (fill || justify || center) {
452 if (count > 0) wraps [count - 1] = maxWidth;
453 for (int i=0; i<count; i++) {
454 if (wraps [i] != 0) {
455 int wrapCount = i - last + 1;
458 for (int j=last; j<=i; j++) {
459 wrapY += bounds [j].height + spacing;
461 space = Math.max (0, (height - wrapY) / (wrapCount + 1));
462 margin = Math.max (0, ((height - wrapY) % (wrapCount + 1)) / 2);
464 for (int j=last; j<=i; j++) {
465 if (justify) bounds [j].y += (space * (j - last + 1)) + margin;
467 bounds [j].width = wraps [i];
470 bounds [j].x += Math.max (0, (wraps [i] - bounds [j].width) / 2);
479 for (int i=0; i<count; i++) {
481 if (justify) bounds [i].y += (space * (i + 1)) + margin;
483 bounds [i].width = maxWidth;
486 bounds [i].x += Math.max (0, (maxWidth - bounds [i].width) / 2);
491 children [i].setBounds (bounds [i]);
494 return new Point (x + maxWidth + marginRight + marginWidth, maxY);
498 * Returns a string containing a concise, human-readable
499 * description of the receiver.
501 * @return a string representation of the layout
504 public String toString () {
505 String string = getName ()+" {";
506 string += "type="+((type != SWT.HORIZONTAL) ? "SWT.VERTICAL" : "SWT.HORIZONTAL")+" ";
507 if (marginWidth != 0) string += "marginWidth="+marginWidth+" ";
508 if (marginHeight != 0) string += "marginHeight="+marginHeight+" ";
509 if (marginLeft != 0) string += "marginLeft="+marginLeft+" ";
510 if (marginTop != 0) string += "marginTop="+marginTop+" ";
511 if (marginRight != 0) string += "marginRight="+marginRight+" ";
512 if (marginBottom != 0) string += "marginBottom="+marginBottom+" ";
513 if (spacing != 0) string += "spacing="+spacing+" ";
514 string += "wrap="+wrap+" ";
515 string += "pack="+pack+" ";
516 string += "fill="+fill+" ";
517 string += "justify="+justify+" ";
518 string = string.trim();