root/dwt/layout/GridLayout.d

Revision 246:fd9c62a2998e, 32.5 kB (checked in by Frank Benoit <benoit@tionex.de>, 6 months ago)

Updater SWT 3.4M7 to 3.4

Line 
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 dwt.layout.GridLayout;
14
15 import dwt.layout.GridData;
16 import dwt.DWT;
17 import dwt.graphics.Point;
18 import dwt.graphics.Rectangle;
19 import dwt.widgets.Control;
20 import dwt.widgets.Composite;
21 import dwt.widgets.Layout;
22 import dwt.widgets.Scrollable;
23
24 import dwt.dwthelper.System;
25
26 import tango.util.Convert;
27 import Math = tango.math.Math;
28 import dwt.dwthelper.utils;
29
30
31 /**
32  * Instances of this class lay out the control children of a
33  * <code>Composite</code> in a grid.
34  * <p>
35  * <code>GridLayout</code> has a number of configuration fields, and the
36  * controls it lays out can have an associated layout data object, called
37  * <code>GridData</code>. The power of <code>GridLayout</code> lies in the
38  * ability to configure <code>GridData</code> for each control in the layout.
39  * </p>
40  * <p>
41  * The following code creates a shell managed by a <code>GridLayout</code>
42  * with 3 columns:
43  * <pre>
44  *      Display display = new Display();
45  *      Shell shell = new Shell(display);
46  *      GridLayout gridLayout = new GridLayout();
47  *      gridLayout.numColumns = 3;
48  *      shell.setLayout(gridLayout);
49  * </pre>
50  * The <code>numColumns</code> field is the most important field in a
51  * <code>GridLayout</code>. Widgets are laid out in columns from left
52  * to right, and a new row is created when <code>numColumns</code> + 1
53  * controls are added to the <code>Composite<code>.
54  * </p>
55  *
56  * @see GridData
57  * @see <a href="http://www.eclipse.org/swt/snippets/#gridlayout">GridLayout snippets</a>
58  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: LayoutExample</a>
59  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
60  */
61 public final class GridLayout : Layout {
62
63     /**
64      * numColumns specifies the number of cell columns in the layout.
65      * If numColumns has a value less than 1, the layout will not
66      * set the size and position of any controls.
67      *
68      * The default value is 1.
69      */
70     public int numColumns = 1;
71
72     /**
73      * makeColumnsEqualWidth specifies whether all columns in the layout
74      * will be forced to have the same width.
75      *
76      * The default value is false.
77      */
78     public bool makeColumnsEqualWidth = false;
79
80     /**
81      * marginWidth specifies the number of pixels of horizontal margin
82      * that will be placed along the left and right edges of the layout.
83      *
84      * The default value is 5.
85      */
86     public int marginWidth = 5;
87
88     /**
89      * marginHeight specifies the number of pixels of vertical margin
90      * that will be placed along the top and bottom edges of the layout.
91      *
92      * The default value is 5.
93      */
94     public int marginHeight = 5;
95
96     /**
97      * marginLeft specifies the number of pixels of horizontal margin
98      * that will be placed along the left edge of the layout.
99      *
100      * The default value is 0.
101      *
102      * @since 3.1
103      */
104     public int marginLeft = 0;
105
106     /**
107      * marginTop specifies the number of pixels of vertical margin
108      * that will be placed along the top edge of the layout.
109      *
110      * The default value is 0.
111      *
112      * @since 3.1
113      */
114     public int marginTop = 0;
115
116     /**
117      * marginRight specifies the number of pixels of horizontal margin
118      * that will be placed along the right edge of the layout.
119      *
120      * The default value is 0.
121      *
122      * @since 3.1
123      */
124     public int marginRight = 0;
125
126     /**
127      * marginBottom specifies the number of pixels of vertical margin
128      * that will be placed along the bottom edge of the layout.
129      *
130      * The default value is 0.
131      *
132      * @since 3.1
133      */
134     public int marginBottom = 0;
135
136     /**
137      * horizontalSpacing specifies the number of pixels between the right
138      * edge of one cell and the left edge of its neighbouring cell to
139      * the right.
140      *
141      * The default value is 5.
142      */
143     public int horizontalSpacing = 5;
144
145     /**
146      * verticalSpacing specifies the number of pixels between the bottom
147      * edge of one cell and the top edge of its neighbouring cell underneath.
148      *
149      * The default value is 5.
150      */
151     public int verticalSpacing = 5;
152
153 /**
154  * Constructs a new instance of this class.
155  */
156 public this () {}
157
158 /**
159  * Constructs a new instance of this class given the
160  * number of columns, and whether or not the columns
161  * should be forced to have the same width.
162  * If numColumns has a value less than 1, the layout will not
163  * set the size and position of any controls.
164  *
165  * @param numColumns the number of columns in the grid
166  * @param makeColumnsEqualWidth whether or not the columns will have equal width
167  *
168  * @since 2.0
169  */
170 public this (int numColumns, bool makeColumnsEqualWidth) {
171     this.numColumns = numColumns;
172     this.makeColumnsEqualWidth = makeColumnsEqualWidth;
173 }
174
175 override protected Point computeSize (Composite composite, int wHint, int hHint, bool flushCache_) {
176     Point size = layout (composite, false, 0, 0, wHint, hHint, flushCache_);
177     if (wHint !is DWT.DEFAULT) size.x = wHint;
178     if (hHint !is DWT.DEFAULT) size.y = hHint;
179     return size;
180 }
181
182 override protected bool flushCache (Control control) {
183     Object data = control.getLayoutData ();
184     if (data !is null) (cast(GridData) data).flushCache ();
185     return true;
186 }
187
188 GridData getData (Control [][] grid, int row, int column, int rowCount, int columnCount, bool first) {
189     Control control = grid [row] [column];
190     if (control !is null) {
191         GridData data = cast(GridData) control.getLayoutData ();
192         int hSpan = Math.max (1, Math.min (data.horizontalSpan, columnCount));
193         int vSpan = Math.max (1, data.verticalSpan);
194         int i = first ? row + vSpan - 1 : row - vSpan + 1;
195         int j = first ? column + hSpan - 1 : column - hSpan + 1;
196         if (0 <= i && i < rowCount) {
197             if (0 <= j && j < columnCount) {
198                 if (control is grid [i][j]) return data;
199             }
200         }
201     }
202     return null;
203 }
204
205 override protected void layout (Composite composite, bool flushCache_) {
206     Rectangle rect = composite.getClientArea ();
207     layout (composite, true, rect.x, rect.y, rect.width, rect.height, flushCache_);
208 }
209
210 Point layout (Composite composite, bool move, int x, int y, int width, int height, bool flushCache_) {
211     if (numColumns < 1) {
212         return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
213     }
214     Control [] children = composite.getChildren ();
215     int count = 0;
216     for (int i=0; i<children.length; i++) {
217         Control control = children [i];
218         GridData data = cast(GridData) control.getLayoutData ();
219         if (data is null || !data.exclude) {
220             children [count++] = children [i];
221         }
222     }
223     if (count is 0) {
224         return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom);
225     }
226     for (int i=0; i<count; i++) {
227         Control child = children [i];
228         GridData data = cast(GridData) child.getLayoutData ();
229         if (data is null) child.setLayoutData (data = new GridData ());
230         if (flushCache_) data.flushCache ();
231         data.computeSize (child, data.widthHint, data.heightHint, flushCache_);
232         if (data.grabExcessHorizontalSpace && data.minimumWidth > 0) {
233             if (data.cacheWidth < data.minimumWidth) {
234                 int trim = 0;
235                 //TEMPORARY CODE
236                 if ( auto sa = cast(Scrollable)child ) {
237                     Rectangle rect = sa.computeTrim (0, 0, 0, 0);
238                     trim = rect.width;
239                 } else {
240                     trim = child.getBorderWidth () * 2;
241                 }
242                 data.cacheWidth = data.cacheHeight = DWT.DEFAULT;
243                 data.computeSize (child, Math.max (0, data.minimumWidth - trim), data.heightHint, false);
244             }
245         }
246         if (data.grabExcessVerticalSpace && data.minimumHeight > 0) {
247             data.cacheHeight = Math.max (data.cacheHeight, data.minimumHeight);
248         }
249     }
250
251     /* Build the grid */
252     int row = 0, column = 0, rowCount = 0, columnCount = numColumns;
253     Control [][] grid = new Control [][]( 4, columnCount );
254     for (int i=0; i<count; i++) {
255         Control child = children [i];
256         GridData data = cast(GridData) child.getLayoutData ();
257         int hSpan = Math.max (1, Math.min (data.horizontalSpan, columnCount));
258         int vSpan = Math.max (1, data.verticalSpan);
259         while (true) {
260             int lastRow = row + vSpan;
261             if (lastRow >= grid.length) {
262                 Control [][] newGrid = new Control[][]( lastRow + 4, columnCount );
263                 SimpleType!(Control[]).arraycopy (grid, 0, newGrid, 0, grid.length);
264                 grid = newGrid;
265             }
266             if (grid [row] is null) {
267                 grid [row] = new Control [columnCount];
268             }
269             while (column < columnCount && grid [row] [column] !is null) {
270                 column++;
271             }
272             int endCount = column + hSpan;
273             if (endCount <= columnCount) {
274                 int index = column;
275                 while (index < endCount && grid [row] [index] is null) {
276                     index++;
277                 }
278                 if (index is endCount) break;
279                 column = index;
280             }
281             if (column + hSpan >= columnCount) {
282                 column = 0;
283                 row++;
284             }
285         }
286         for (int j=0; j<vSpan; j++) {
287             if (grid [row + j] is null) {
288                 grid [row + j] = new Control [columnCount];
289             }
290             for (int k=0; k<hSpan; k++) {
291                 grid [row + j] [column + k] = child;
292             }
293         }
294         rowCount = Math.max (rowCount, row + vSpan);
295         column += hSpan;
296     }
297
298     /* Column widths */
299     int availableWidth = width - horizontalSpacing * (columnCount - 1) - (marginLeft + marginWidth * 2 + marginRight);
300     int expandCount = 0;
301     int [] widths = new int [columnCount];
302     int [] minWidths = new int [columnCount];
303     bool [] expandColumn = new bool [columnCount];
304     for (int j=0; j<columnCount; j++) {
305         for (int i=0; i<rowCount; i++) {
306             GridData data = getData (grid, i, j, rowCount, columnCount, true);
307             if (data !is null) {
308                 int hSpan = Math.max (1, Math.min (data.horizontalSpan, columnCount));
309                 if (hSpan is 1) {
310                     int w = data.cacheWidth + data.horizontalIndent;
311                     widths [j] = Math.max (widths [j], w);
312                     if (data.grabExcessHorizontalSpace) {
313                         if (!expandColumn [j]) expandCount++;
314                         expandColumn [j] = true;
315                     }
316                     if (!data.grabExcessHorizontalSpace || data.minimumWidth !is 0) {
317                         w = !data.grabExcessHorizontalSpace || data.minimumWidth is DWT.DEFAULT ? data.cacheWidth : data.minimumWidth;
318                         w += data.horizontalIndent;
319                         minWidths [j] = Math.max (minWidths [j], w);
320                     }
321                 }
322             }
323         }
324         for (int i=0; i<rowCount; i++) {
325             GridData data = getData (grid, i, j, rowCount, columnCount, false);
326             if (data !is null) {
327                 int hSpan = Math.max (1, Math.min (data.horizontalSpan, columnCount));
328                 if (hSpan > 1) {
329                     int spanWidth = 0, spanMinWidth = 0, spanExpandCount = 0;
330                     for (int k=0; k<hSpan; k++) {
331                         spanWidth += widths [j-k];
332                         spanMinWidth += minWidths [j-k];
333                         if (expandColumn [j-k]) spanExpandCount++;
334                     }
335                     if (data.grabExcessHorizontalSpace && spanExpandCount is 0) {
336                         expandCount++;
337                         expandColumn [j] = true;
338                     }
339                     int w = data.cacheWidth + data.horizontalIndent - spanWidth - (hSpan - 1) * horizontalSpacing;
340                     if (w > 0) {
341                         if (makeColumnsEqualWidth) {
342                             int equalWidth = (w + spanWidth) / hSpan;
343                             int remainder = (w + spanWidth) % hSpan, last = -1;
344                             for (int k = 0; k < hSpan; k++) {
345                                 widths [last=j-k] = Math.max (equalWidth, widths [j-k]);
346                             }
347                             if (last > -1) widths [last] += remainder;
348                         } else {
349                             if (spanExpandCount is 0) {
350                                 widths [j] += w;
351                             } else {
352                                 int delta = w / spanExpandCount;
353                                 int remainder = w % spanExpandCount, last = -1;
354                                 for (int k = 0; k < hSpan; k++) {
355                                     if (expandColumn [j-k]) {
356                                         widths [last=j-k] += delta;
357                                     }
358                                 }
359                                 if (last > -1) widths [last] += remainder;
360                             }
361                         }
362                     }
363                     if (!data.grabExcessHorizontalSpace || data.minimumWidth !is 0) {
364                         w = !data.grabExcessHorizontalSpace || data.minimumWidth is DWT.DEFAULT ? data.cacheWidth : data.minimumWidth;
365                         w += data.horizontalIndent - spanMinWidth - (hSpan - 1) * horizontalSpacing;
366                         if (w > 0) {
367                             if (spanExpandCount is 0) {
368                                 minWidths [j] += w;
369                             } else {
370                                 int delta = w / spanExpandCount;
371                                 int remainder = w % spanExpandCount, last = -1;
372                                 for (int k = 0; k < hSpan; k++) {
373                                     if (expandColumn [j-k]) {
374                                         minWidths [last=j-k] += delta;
375                                     }
376                                 }
377                                 if (last > -1) minWidths [last] += remainder;
378                             }
379                         }
380                     }
381                 }
382             }
383         }
384     }
385     if (makeColumnsEqualWidth) {
386         int minColumnWidth = 0;
387         int columnWidth = 0;
388         for (int i=0; i<columnCount; i++) {
389             minColumnWidth = Math.max (minColumnWidth, minWidths [i]);
390             columnWidth = Math.max (columnWidth, widths [i]);
391         }
392         columnWidth = width is DWT.DEFAULT || expandCount is 0 ? columnWidth : Math.max (minColumnWidth, availableWidth / columnCount);
393         for (int i=0; i<columnCount; i++) {
394             expandColumn [i] = expandCount > 0;
395             widths [i] = columnWidth;
396         }
397     } else {
398         if (width !is DWT.DEFAULT && expandCount > 0) {
399             int totalWidth = 0;
400             for (int i=0; i<columnCount; i++) {
401                 totalWidth += widths [i];
402             }
403             int c = expandCount;
404             int delta = (availableWidth - totalWidth) / c;
405             int remainder = (availableWidth - totalWidth) % c;
406             int last = -1;
407             while (totalWidth !is availableWidth) {
408                 for (int j=0; j<columnCount; j++) {
409                     if (expandColumn [j]) {
410                         if (widths [j] + delta > minWidths [j]) {
411                             widths [last = j] = widths [j] + delta;
412                         } else {
413                             widths [j] = minWidths [j];
414                             expandColumn [j] = false;
415                             c--;
416                         }
417                     }
418                 }
419                 if (last > -1) widths [last] += remainder;
420
421                 for (int j=0; j<columnCount; j++) {
422                     for (int i=0; i<rowCount; i++) {
423                         GridData data = getData (grid, i, j, rowCount, columnCount, false);
424                         if (data !is null) {
425                             int hSpan = Math.max (1, Math.min (data.horizontalSpan, columnCount));
426                             if (hSpan > 1) {
427                                 if (!data.grabExcessHorizontalSpace || data.minimumWidth !is 0) {
428                                     int spanWidth = 0, spanExpandCount = 0;
429                                     for (int k=0; k<hSpan; k++) {
430                                         spanWidth += widths [j-k];
431                                         if (expandColumn [j-k]) spanExpandCount++;
432                                     }
433                                     int w = !data.grabExcessHorizontalSpace || data.minimumWidth is DWT.DEFAULT ? data.cacheWidth : data.minimumWidth;
434                                     w += data.horizontalIndent - spanWidth - (hSpan - 1) * horizontalSpacing;
435                                     if (w > 0) {
436                                         if (spanExpandCount is 0) {
437                                             widths [j] += w;
438                                         } else {
439                                             int delta2 = w / spanExpandCount;
440                                             int remainder2 = w % spanExpandCount, last2 = -1;
441                                             for (int k = 0; k < hSpan; k++) {
442                                                 if (expandColumn [j-k]) {
443                                                     widths [last2=j-k] += delta2;
444                                                 }
445                                             }
446                                             if (last2 > -1) widths [last2] += remainder2;
447                                         }
448                                     }
449                                 }
450                             }
451                         }
452                     }
453                 }
454                 if (c is 0) break;
455                 totalWidth = 0;
456                 for (int i=0; i<columnCount; i++) {
457                     totalWidth += widths [i];
458                 }
459                 delta = (availableWidth - totalWidth) / c;
460                 remainder = (availableWidth - totalWidth) % c;
461                 last = -1;
462             }
463         }
464     }
465
466     /* Wrapping */
467     GridData [] flush = null;
468     int flushLength = 0;
469     if (width !is DWT.DEFAULT) {
470         for (int j=0; j<columnCount; j++) {
471             for (int i=0; i<rowCount; i++) {
472                 GridData data =