root/dwt/layout/FormLayout.d

Revision 246:fd9c62a2998e, 14.9 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.FormLayout;
14
15 import dwt.layout.FormAttachment;
16 import dwt.layout.FormData;
17 import dwt.DWT;
18 import dwt.graphics.Point;
19 import dwt.graphics.Rectangle;
20 import dwt.widgets.Control;
21 import dwt.widgets.Layout;
22 import dwt.widgets.Composite;
23 import dwt.widgets.Scrollable;
24
25 import tango.util.Convert;
26 import Math = tango.math.Math;
27 import dwt.dwthelper.utils;
28
29 /**
30  * Instances of this class control the position and size of the
31  * children of a composite control by using <code>FormAttachments</code>
32  * to optionally configure the left, top, right and bottom edges of
33  * each child.
34  * <p>
35  * The following example code creates a <code>FormLayout</code> and then sets
36  * it into a <code>Shell</code>:
37  * <pre>
38  *      Display display = new Display ();
39  *      Shell shell = new Shell(display);
40  *      FormLayout layout = new FormLayout();
41  *      layout.marginWidth = 3;
42  *      layout.marginHeight = 3;
43  *      shell.setLayout(layout);
44  * </pre>
45  * </p>
46  * <p>
47  * To use a <code>FormLayout</code>, create a <code>FormData</code> with
48  * <code>FormAttachment</code> for each child of <code>Composite</code>.
49  * The following example code attaches <code>button1</code> to the top
50  * and left edge of the composite and <code>button2</code> to the right
51  * edge of <code>button1</code> and the top and right edges of the
52  * composite:
53  * <pre>
54  *      FormData data1 = new FormData();
55  *      data1.left = new FormAttachment(0, 0);
56  *      data1.top = new FormAttachment(0, 0);
57  *      button1.setLayoutData(data1);
58  *      FormData data2 = new FormData();
59  *      data2.left = new FormAttachment(button1);
60  *      data2.top = new FormAttachment(0, 0);
61  *      data2.right = new FormAttachment(100, 0);
62  *      button2.setLayoutData(data2);
63  * </pre>
64  * </p>
65  * <p>
66  * Each side of a child control can be attached to a position in the parent
67  * composite, or to other controls within the <code>Composite</code> by
68  * creating instances of <code>FormAttachment</code> and setting them into
69  * the top, bottom, left, and right fields of the child's <code>FormData</code>.
70  * </p>
71  * <p>
72  * If a side is not given an attachment, it is defined as not being attached
73  * to anything, causing the child to remain at its preferred size.  If a child
74  * is given no attachment on either the left or the right or top or bottom, it is
75  * automatically attached to the left and top of the composite respectively.
76  * The following code positions <code>button1</code> and <code>button2</code>
77  * but relies on default attachments:
78  * <pre>
79  *      FormData data2 = new FormData();
80  *      data2.left = new FormAttachment(button1);
81  *      data2.right = new FormAttachment(100, 0);
82  *      button2.setLayoutData(data2);
83  * </pre>
84  * </p>
85  * <p>
86  * IMPORTANT: Do not define circular attachments.  For example, do not attach
87  * the right edge of <code>button1</code> to the left edge of <code>button2</code>
88  * and then attach the left edge of <code>button2</code> to the right edge of
89  * <code>button1</code>.  This will over constrain the layout, causing undefined
90  * behavior.  The algorithm will terminate, but the results are undefined.
91  * </p>
92  *
93  * @see FormData
94  * @see FormAttachment
95  * @see <a href="http://www.eclipse.org/swt/snippets/#formlayout">FormLayout snippets</a>
96  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: LayoutExample</a>
97  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
98  *
99  * @since 2.0
100  */
101 public final class FormLayout : Layout {
102
103     /**
104      * marginWidth specifies the number of pixels of horizontal margin
105      * that will be placed along the left and right edges of the layout.
106      *
107      * The default value is 0.
108      */
109     public int marginWidth = 0;
110
111     /**
112      * marginHeight specifies the number of pixels of vertical margin
113      * that will be placed along the top and bottom edges of the layout.
114      *
115      * The default value is 0.
116      */
117     public int marginHeight = 0;
118
119
120     /**
121      * marginLeft specifies the number of pixels of horizontal margin
122      * that will be placed along the left edge of the layout.
123      *
124      * The default value is 0.
125      *
126      * @since 3.1
127      */
128     public int marginLeft = 0;
129
130     /**
131      * marginTop specifies the number of pixels of vertical margin
132      * that will be placed along the top edge of the layout.
133      *
134      * The default value is 0.
135      *
136      * @since 3.1
137      */
138     public int marginTop = 0;
139
140     /**
141      * marginRight specifies the number of pixels of horizontal margin
142      * that will be placed along the right edge of the layout.
143      *
144      * The default value is 0.
145      *
146      * @since 3.1
147      */
148     public int marginRight = 0;
149
150     /**
151      * marginBottom specifies the number of pixels of vertical margin
152      * that will be placed along the bottom edge of the layout.
153      *
154      * The default value is 0.
155      *
156      * @since 3.1
157      */
158     public int marginBottom = 0;
159
160     /**
161      * spacing specifies the number of pixels between the edge of one control
162      * and the edge of its neighbouring control.
163      *
164      * The default value is 0.
165      *
166      * @since 3.0
167      */
168     public int spacing = 0;
169
170 /**
171  * Constructs a new instance of this class.
172  */
173 public this () {
174 }
175
176 /*
177  * Computes the preferred height of the form with
178  * respect to the preferred height of the control.
179  *
180  * Given that the equations for top (T) and bottom (B)
181  * of the control in terms of the height of the form (X)
182  * are:
183  *      T = AX + B
184  *      B = CX + D
185  *
186  * The equation for the height of the control (H)
187  * is bottom (B) minus top (T) or (H = B - T) or:
188  *
189  *      H = (CX + D) - (AX + B)
190  *
191  * Solving for (X), the height of the form, we get:
192  *
193  *      X = (H + B - D) / (C - A)
194  *
195  * When (A = C), (C - A = 0) and the equation has no
196  * solution for X.  This is a special case meaning that
197  * the control does not constrain the height of the
198  * form.  In this case, we need to arbitrarily define
199  * the height of the form (X):
200  *
201  * Case 1: A = C, A = 0, C = 0
202  *
203  *      Let X = D, the distance from the top of the form
204  *      to the bottom edge of the control.  In this case,
205  *      the control was attached to the top of the form
206  *      and the form needs to be large enough to show the
207  *      bottom edge of the control.
208  *
209  * Case 2: A = C, A = 1, C = 1
210  *
211  *      Let X = -B, the distance from the bottom of the
212  *      form to the top edge of the control.  In this case,
213  *      the control was attached to the bottom of the form
214  *      and the only way that the control would be visible
215  *      is if the offset is negative.  If the offset is
216  *      positive, there is no possible height for the form
217  *      that will show the control as it will always be
218  *      below the bottom edge of the form.
219  *
220  * Case 3: A = C, A !is 0, C !is 0 and A !is 1, C !is 0
221  *
222  *      Let X = D / (1 - C), the distance from the top of the
223  *      form to the bottom edge of the control.  In this case,
224  *      since C is not 0 or 1, it must be a fraction, U / V.
225  *      The offset D is the distance from CX to the bottom edge
226  *      of the control.  This represents a fraction of the form
227  *      (1 - C)X. Since the height of a fraction of the form is
228  *      known, the height of the entire form can be found by setting
229  *      (1 - C)X = D.  We solve this equation for X in terms of U
230  *      and V, giving us X = (U * D) / (U - V). Similarly, if the
231  *      offset D is negative, the control is positioned above CX.
232  *      The offset -B is the distance from the top edge of the control
233  *      to CX. We can find the height of the entire form by setting
234  *      CX = -B. Solving in terms of U and V gives us X = (-B * V) / U.
235  */
236 int computeHeight (Control control, FormData data, bool flushCache) {
237     FormAttachment top = data.getTopAttachment (control, spacing, flushCache);
238     FormAttachment bottom = data.getBottomAttachment (control, spacing, flushCache);
239     FormAttachment height = bottom.minus (top);
240     if (height.numerator is 0) {
241         if (bottom.numerator is 0) return bottom.offset;
242         if (bottom.numerator is bottom.denominator) return -top.offset;
243         if (bottom.offset <= 0) {
244             return -top.offset * top.denominator / bottom.numerator;
245         }
246         int divider = bottom.denominator - bottom.numerator;
247         return bottom.denominator * bottom.offset / divider;
248     }
249     return height.solveY (data.getHeight (control, flushCache));
250 }
251
252 override protected Point computeSize (Composite composite, int wHint, int hHint, bool flushCache) {
253     Point size = layout (composite, false, 0, 0, wHint, hHint, flushCache);
254     if (wHint !is DWT.DEFAULT) size.x = wHint;
255     if (hHint !is DWT.DEFAULT) size.y = hHint;
256     return size;
257 }
258
259 override protected bool flushCache (Control control) {
260     Object data = control.getLayoutData ();
261     if (data !is null) (cast(FormData) data).flushCache ();
262     return true;
263 }
264
265 String getName () {
266     String string = this.classinfo.name;
267     int index = string.lastIndexOf('.');
268     if (index is -1 ) return string;
269     return string[ index + 1 .. string.length ];
270 }
271
272 /*
273  * Computes the preferred height of the form with
274  * respect to the preferred height of the control.
275  */
276 int computeWidth (Control control, FormData data, bool flushCache) {
277     FormAttachment left = data.getLeftAttachment (control, spacing, flushCache);
278     FormAttachment right = data.getRightAttachment (control, spacing, flushCache);
279     FormAttachment width = right.minus (left);
280     if (width.numerator is 0) {
281         if (right.numerator is 0) return right.offset;
282         if (right.numerator is right.denominator) return -left.offset;
283         if (right.offset <= 0) {
284             return -left.offset * left.denominator / left.numerator;
285         }
286         int divider = right.denominator - right.numerator;
287         return right.denominator * right.offset / divider;
288     }
289     return width.solveY (data.getWidth (control, flushCache));
290 }
291
292 override protected void layout (Composite composite, bool flushCache) {
293     Rectangle rect = composite.getClientArea ();
294     int x = rect.x + marginLeft + marginWidth;
295     int y = rect.y + marginTop + marginHeight;
296     int width = Math.max (0, rect.width - marginLeft - 2 * marginWidth - marginRight);
297     int height = Math.max (0, rect.height - marginTop - 2 * marginHeight - marginBottom);
298     layout (composite, true, x, y, width, height, flushCache);
299 }
300
301 Point layout (Composite composite, bool move, int x, int y, int width, int height, bool flushCache) {
302     Control [] children = composite.getChildren ();
303     for (int i=0; i<children.length; i++) {
304         Control child = children [i];
305         FormData data = cast(FormData) child.getLayoutData ();
306         if (data is null) child.setLayoutData (data = new FormData ());
307         if (flushCache) data.flushCache ();
308         data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
309     }
310     bool [] flush = null;
311     Rectangle [] bounds = null;
312     int w = 0, h = 0;
313     for (int i=0; i<children.length; i++) {
314         Control child = children [i];
315         FormData data = cast(FormData) child.getLayoutData ();
316         if (width !is DWT.DEFAULT) {
317             data.needed = false;
318             FormAttachment left = data.getLeftAttachment (child, spacing, flushCache);
319             FormAttachment right = data.getRightAttachment (child, spacing, flushCache);
320             int x1 = left.solveX (width), x2 = right.solveX (width);
321             if (data.height is DWT.DEFAULT && !data.needed) {
322                 int trim = 0;
323                 //TEMPORARY CODE
324                 if ( auto sa = cast(Scrollable)child) {
325                     Rectangle rect = sa.computeTrim (0, 0, 0, 0);
326                     trim = rect.width;
327                 } else {
328                     trim = child.getBorderWidth () * 2;
329                 }
330                 data.cacheWidth = data.cacheHeight = -1;
331                 int currentWidth = Math.max (0, x2 - x1 - trim);
332                 data.computeSize (child, currentWidth, data.height, flushCache);
333                 if (flush is null) flush = new bool [children.length];
334                 flush [i] = true;
335             }
336             w = Math.max (x2, w);
337             if (move) {
338                 if (bounds is null) bounds = new Rectangle [children.length];
339                 bounds [i] = new Rectangle (0, 0, 0, 0);
340                 bounds [i].x = x + x1;
341                 bounds [i].width = x2 - x1;
342             }
343         } else {
344             w = Math.max (computeWidth (child, data, flushCache), w);
345         }
346     }
347     for (int i=0; i<children.length; i++) {
348         Control child = children [i];
349         FormData data = cast(FormData) child.getLayoutData ();
350         if (height !is DWT.DEFAULT) {
351             int y1 = data.getTopAttachment (child, spacing, flushCache).solveX (height);
352             int y2 = data.getBottomAttachment (child, spacing, flushCache).solveX (height);
353             h = Math.max (y2, h);
354             if (move) {
355                 bounds [i].y = y + y1;
356                 bounds [i].height = y2 - y1;
357             }
358         } else {
359             h = Math.max (computeHeight (child, data, flushCache), h);
360         }
361     }
362     for (int i=0; i<children.length; i++) {
363         Control child = children [i];
364         FormData data = cast(FormData) child.getLayoutData ();
365         if (flush !is null && flush [i]) data.cacheWidth = data.cacheHeight = -1;
366         data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
367     }
368     if (move) {
369         for (int i=0; i<children.length; i++) {
370             children [i].setBounds (bounds [i]);
371         }
372     }
373     w += marginLeft + marginWidth * 2 + marginRight;
374     h += marginTop + marginHeight * 2 + marginBottom;
375     return new Point (w, h);
376 }
377
378 /**
379  * Returns a string containing a concise, human-readable
380  * description of the receiver.
381  *
382  * @return a string representation of the layout
383  */
384 override public String toString () {
385     String string =  getName ()~" {";
386     if (marginWidth !is 0) string ~= "marginWidth="~to!(String)(marginWidth)~" ";
387     if (marginHeight !is 0) string ~= "marginHeight="~to!(String)(marginHeight)~" ";
388     if (marginLeft !is 0) string ~= "marginLeft="~to!(String)(marginLeft)~" ";
389     if (marginRight !is 0) string ~= "marginRight="~to!(String)(marginRight)~" ";
390     if (marginTop !is 0) string ~= "marginTop="~to!(String)(marginTop)~" ";
391     if (marginBottom !is 0) string ~= "marginBottom="~to!(String)(marginBottom)~" ";
392     if (spacing !is 0) string ~= "spacing="~to!(String)(spacing)~" ";
393     string = string.trim();
394     string ~= "}";
395     return string;
396 }
397 }
Note: See TracBrowser for help on using the browser.