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 }