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.custom.SashForm; 14 15 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.SWTException; 19 import org.eclipse.swt.graphics.Color; 20 import org.eclipse.swt.graphics.Rectangle; 21 import org.eclipse.swt.widgets.Composite; 22 import org.eclipse.swt.widgets.Control; 23 import org.eclipse.swt.widgets.Event; 24 import org.eclipse.swt.widgets.Layout; 25 import org.eclipse.swt.widgets.Listener; 26 import org.eclipse.swt.widgets.Sash; 27 import org.eclipse.swt.custom.SashFormLayout; 28 import org.eclipse.swt.custom.SashFormData; 29 import java.lang.all; 30 31 /** 32 * The SashForm is a composite control that lays out its children in a 33 * row or column arrangement (as specified by the orientation) and places 34 * a Sash between each child. One child may be maximized to occupy the 35 * entire size of the SashForm. The relative sizes of the children may 36 * be specified using weights. 37 * <p> 38 * <dl> 39 * <dt><b>Styles:</b></dt> 40 * <dd>HORIZONTAL, VERTICAL, SMOOTH</dd> 41 * </dl> 42 * </p> 43 * 44 * @see <a href="http://www.eclipse.org/swt/snippets/#sashform">SashForm snippets</a> 45 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample</a> 46 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 47 */ 48 public class SashForm : Composite { 49 50 /** 51 * The width of all sashes in the form. 52 */ 53 public int SASH_WIDTH = 3; 54 55 int sashStyle; 56 Sash[] sashes; 57 // Remember background and foreground 58 // colors to determine whether to set 59 // sashes to the default color (null) or 60 // a specific color 61 Color background = null; 62 Color foreground = null; 63 Control[] controls; 64 Control maxControl = null; 65 Listener sashListener; 66 static const int DRAG_MINIMUM = 20; 67 68 /** 69 * Constructs a new instance of this class given its parent 70 * and a style value describing its behavior and appearance. 71 * <p> 72 * The style value is either one of the style constants defined in 73 * class <code>SWT</code> which is applicable to instances of this 74 * class, or must be built by <em>bitwise OR</em>'ing together 75 * (that is, using the <code>int</code> "|" operator) two or more 76 * of those <code>SWT</code> style constants. The class description 77 * lists the style constants that are applicable to the class. 78 * Style bits are also inherited from superclasses. 79 * </p> 80 * 81 * @param parent a widget which will be the parent of the new instance (cannot be null) 82 * @param style the style of widget to construct 83 * 84 * @exception IllegalArgumentException <ul> 85 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 86 * </ul> 87 * @exception SWTException <ul> 88 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 89 * </ul> 90 * 91 * @see SWT#HORIZONTAL 92 * @see SWT#VERTICAL 93 * @see #getStyle() 94 */ 95 public this(Composite parent, int style) { 96 super(parent, checkStyle(style)); 97 super.setLayout(new SashFormLayout()); 98 sashStyle = ((style & SWT.VERTICAL) !is 0) ? SWT.HORIZONTAL : SWT.VERTICAL; 99 if ((style & SWT.BORDER) !is 0) sashStyle |= SWT.BORDER; 100 if ((style & SWT.SMOOTH) !is 0) sashStyle |= SWT.SMOOTH; 101 sashListener = new class() Listener { 102 public void handleEvent(Event e) { 103 onDragSash(e); 104 } 105 }; 106 } 107 static int checkStyle (int style) { 108 int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; 109 return style & mask; 110 } 111 /** 112 * Returns SWT.HORIZONTAL if the controls in the SashForm are laid out side by side 113 * or SWT.VERTICAL if the controls in the SashForm are laid out top to bottom. 114 * 115 * @return SWT.HORIZONTAL or SWT.VERTICAL 116 */ 117 public int getOrientation() { 118 //checkWidget(); 119 return (sashStyle & SWT.VERTICAL) !is 0 ? SWT.HORIZONTAL : SWT.VERTICAL; 120 } 121 /** 122 * Returns the width of the sashes when the controls in the SashForm are 123 * laid out. 124 * 125 * @return the width of the sashes 126 * 127 * @exception SWTException <ul> 128 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 129 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 130 * </ul> 131 * 132 * @since 3.4 133 */ 134 public int getSashWidth() { 135 checkWidget(); 136 return SASH_WIDTH; 137 } 138 public override int getStyle() { 139 int style = super.getStyle(); 140 style |= getOrientation() is SWT.VERTICAL ? SWT.VERTICAL : SWT.HORIZONTAL; 141 if ((sashStyle & SWT.SMOOTH) !is 0) style |= SWT.SMOOTH; 142 return style; 143 } 144 /** 145 * Answer the control that currently is maximized in the SashForm. 146 * This value may be null. 147 * 148 * @return the control that currently is maximized or null 149 */ 150 public Control getMaximizedControl(){ 151 //checkWidget(); 152 return this.maxControl; 153 } 154 /** 155 * Answer the relative weight of each child in the SashForm. The weight represents the 156 * percent of the total width (if SashForm has Horizontal orientation) or 157 * total height (if SashForm has Vertical orientation) each control occupies. 158 * The weights are returned in order of the creation of the widgets (weight[0] 159 * corresponds to the weight of the first child created). 160 * 161 * @return the relative weight of each child 162 * 163 * @exception SWTException <ul> 164 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 165 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 166 * </ul> 167 */ 168 169 public int[] getWeights() { 170 checkWidget(); 171 Control[] cArray = getControls(false); 172 int[] ratios = new int[cArray.length]; 173 for (int i = 0; i < cArray.length; i++) { 174 Object data = cArray[i].getLayoutData(); 175 if ( auto sfd = cast(SashFormData)data ) { 176 ratios[i] = cast(int)(sfd.weight * 1000 >> 16); 177 } else { 178 ratios[i] = 200; 179 } 180 } 181 return ratios; 182 } 183 Control[] getControls(bool onlyVisible) { 184 Control[] children = getChildren(); 185 Control[] result = new Control[0]; 186 for (int i = 0; i < children.length; i++) { 187 if ( null !is cast(Sash)children[i]) continue; 188 if (onlyVisible && !children[i].getVisible()) continue; 189 190 Control[] newResult = new Control[result.length + 1]; 191 System.arraycopy(result, 0, newResult, 0, result.length); 192 newResult[result.length] = children[i]; 193 result = newResult; 194 } 195 return result; 196 } 197 void onDragSash(Event event) { 198 Sash sash = cast(Sash)event.widget; 199 int sashIndex = -1; 200 for (int i= 0; i < sashes.length; i++) { 201 if (sashes[i] is sash) { 202 sashIndex = i; 203 break; 204 } 205 } 206 if (sashIndex is -1) return; 207 208 Control c1 = controls[sashIndex]; 209 Control c2 = controls[sashIndex + 1]; 210 Rectangle b1 = c1.getBounds(); 211 Rectangle b2 = c2.getBounds(); 212 213 Rectangle sashBounds = sash.getBounds(); 214 Rectangle area = getClientArea(); 215 bool correction = false; 216 if (getOrientation() is SWT.HORIZONTAL) { 217 correction = b1.width < DRAG_MINIMUM || b2.width < DRAG_MINIMUM; 218 int totalWidth = b2.x + b2.width - b1.x; 219 int shift = event.x - sashBounds.x; 220 b1.width += shift; 221 b2.x += shift; 222 b2.width -= shift; 223 if (b1.width < DRAG_MINIMUM) { 224 b1.width = DRAG_MINIMUM; 225 b2.x = b1.x + b1.width + sashBounds.width; 226 b2.width = totalWidth - b2.x; 227 event.x = b1.x + b1.width; 228 event.doit = false; 229 } 230 if (b2.width < DRAG_MINIMUM) { 231 b1.width = totalWidth - DRAG_MINIMUM - sashBounds.width; 232 b2.x = b1.x + b1.width + sashBounds.width; 233 b2.width = DRAG_MINIMUM; 234 event.x = b1.x + b1.width; 235 event.doit = false; 236 } 237 Object data1 = c1.getLayoutData(); 238 if (data1 is null || !( null !is cast(SashFormData)data1 )) { 239 data1 = new SashFormData(); 240 c1.setLayoutData(data1); 241 } 242 Object data2 = c2.getLayoutData(); 243 if (data2 is null || !( null !is cast(SashFormData)data2 )) { 244 data2 = new SashFormData(); 245 c2.setLayoutData(data2); 246 } 247 (cast(SashFormData)data1).weight = ((cast(long)b1.width << 16) + area.width - 1) / area.width; 248 (cast(SashFormData)data2).weight = ((cast(long)b2.width << 16) + area.width - 1) / area.width; 249 } else { 250 correction = b1.height < DRAG_MINIMUM || b2.height < DRAG_MINIMUM; 251 int totalHeight = b2.y + b2.height - b1.y; 252 int shift = event.y - sashBounds.y; 253 b1.height += shift; 254 b2.y += shift; 255 b2.height -= shift; 256 if (b1.height < DRAG_MINIMUM) { 257 b1.height = DRAG_MINIMUM; 258 b2.y = b1.y + b1.height + sashBounds.height; 259 b2.height = totalHeight - b2.y; 260 event.y = b1.y + b1.height; 261 event.doit = false; 262 } 263 if (b2.height < DRAG_MINIMUM) { 264 b1.height = totalHeight - DRAG_MINIMUM - sashBounds.height; 265 b2.y = b1.y + b1.height + sashBounds.height; 266 b2.height = DRAG_MINIMUM; 267 event.y = b1.y + b1.height; 268 event.doit = false; 269 } 270 Object data1 = c1.getLayoutData(); 271 if (data1 is null || !( null !is cast(SashFormData)data1 )) { 272 data1 = new SashFormData(); 273 c1.setLayoutData(data1); 274 } 275 Object data2 = c2.getLayoutData(); 276 if (data2 is null || !(null !is cast(SashFormData)data2 )) { 277 data2 = new SashFormData(); 278 c2.setLayoutData(data2); 279 } 280 (cast(SashFormData)data1).weight = ((cast(long)b1.height << 16) + area.height - 1) / area.height; 281 (cast(SashFormData)data2).weight = ((cast(long)b2.height << 16) + area.height - 1) / area.height; 282 } 283 if (correction || (event.doit && event.detail !is SWT.DRAG)) { 284 c1.setBounds(b1); 285 sash.setBounds(event.x, event.y, event.width, event.height); 286 c2.setBounds(b2); 287 } 288 } 289 /** 290 * If orientation is SWT.HORIZONTAL, lay the controls in the SashForm 291 * out side by side. If orientation is SWT.VERTICAL, lay the 292 * controls in the SashForm out top to bottom. 293 * 294 * @param orientation SWT.HORIZONTAL or SWT.VERTICAL 295 * 296 * @exception SWTException <ul> 297 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 298 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 299 * <li>ERROR_INVALID_ARGUMENT - if the value of orientation is not SWT.HORIZONTAL or SWT.VERTICAL 300 * </ul> 301 */ 302 public void setOrientation(int orientation) { 303 checkWidget(); 304 if (getOrientation() is orientation) return; 305 if (orientation !is SWT.HORIZONTAL && orientation !is SWT.VERTICAL) { 306 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 307 } 308 sashStyle &= ~(SWT.HORIZONTAL | SWT.VERTICAL); 309 sashStyle |= orientation is SWT.VERTICAL ? SWT.HORIZONTAL : SWT.VERTICAL; 310 for (int i = 0; i < sashes.length; i++) { 311 sashes[i].dispose(); 312 sashes[i] = new Sash(this, sashStyle); 313 sashes[i].setBackground(background); 314 sashes[i].setForeground(foreground); 315 sashes[i].addListener(SWT.Selection, sashListener); 316 } 317 layout(false); 318 } 319 public override void setBackground (Color color) { 320 super.setBackground(color); 321 background = color; 322 for (int i = 0; i < sashes.length; i++) { 323 sashes[i].setBackground(background); 324 } 325 } 326 public override void setForeground (Color color) { 327 super.setForeground(color); 328 foreground = color; 329 for (int i = 0; i < sashes.length; i++) { 330 sashes[i].setForeground(foreground); 331 } 332 } 333 /** 334 * Sets the layout which is associated with the receiver to be 335 * the argument which may be null. 336 * <p> 337 * Note: No Layout can be set on this Control because it already 338 * manages the size and position of its children. 339 * </p> 340 * 341 * @param layout the receiver's new layout or null 342 * 343 * @exception SWTException <ul> 344 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 345 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 346 * </ul> 347 */ 348 public override void setLayout (Layout layout) { 349 checkWidget(); 350 return; 351 } 352 /** 353 * Specify the control that should take up the entire client area of the SashForm. 354 * If one control has been maximized, and this method is called with a different control, 355 * the previous control will be minimized and the new control will be maximized. 356 * If the value of control is null, the SashForm will minimize all controls and return to 357 * the default layout where all controls are laid out separated by sashes. 358 * 359 * @param control the control to be maximized or null 360 * 361 * @exception SWTException <ul> 362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 364 * </ul> 365 */ 366 public void setMaximizedControl(Control control){ 367 checkWidget(); 368 if (control is null) { 369 if (maxControl !is null) { 370 this.maxControl = null; 371 layout(false); 372 for (int i= 0; i < sashes.length; i++){ 373 sashes[i].setVisible(true); 374 } 375 } 376 return; 377 } 378 379 for (int i= 0; i < sashes.length; i++){ 380 sashes[i].setVisible(false); 381 } 382 maxControl = control; 383 layout(false); 384 } 385 386 /** 387 * Specify the width of the sashes when the controls in the SashForm are 388 * laid out. 389 * 390 * @param width the width of the sashes 391 * 392 * @exception SWTException <ul> 393 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 394 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 395 * </ul> 396 * 397 * @since 3.4 398 */ 399 public void setSashWidth(int width) { 400 checkWidget(); 401 if (SASH_WIDTH is width) return; 402 SASH_WIDTH = width; 403 layout(false); 404 } 405 /** 406 * Specify the relative weight of each child in the SashForm. This will determine 407 * what percent of the total width (if SashForm has Horizontal orientation) or 408 * total height (if SashForm has Vertical orientation) each control will occupy. 409 * The weights must be positive values and there must be an entry for each 410 * non-sash child of the SashForm. 411 * 412 * @param weights the relative weight of each child 413 * 414 * @exception SWTException <ul> 415 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 416 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 417 * <li>ERROR_INVALID_ARGUMENT - if the weights value is null or of incorrect length (must match the number of children)</li> 418 * </ul> 419 */ 420 public void setWeights(int[] weights) { 421 checkWidget(); 422 Control[] cArray = getControls(false); 423 if (weights is null || weights.length !is cArray.length) { 424 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 425 } 426 427 int total = 0; 428 for (int i = 0; i < weights.length; i++) { 429 if (weights[i] < 0) { 430 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 431 } 432 total += weights[i]; 433 } 434 if (total is 0) { 435 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 436 } 437 for (int i = 0; i < cArray.length; i++) { 438 Object data = cArray[i].getLayoutData(); 439 if (data is null || !( null !is cast(SashFormData)data )) { 440 data = new SashFormData(); 441 cArray[i].setLayoutData(data); 442 } 443 (cast(SashFormData)data).weight = ((cast(long)weights[i] << 16) + total - 1) / total; 444 } 445 446 layout(false); 447 } 448 }