| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
| <html> | |
| <head> | |
| <meta http-equiv="Content-Type" | |
| content="text/html; charset=windows-1252"> | |
| <title>Understanding Layouts in SWT</title> | |
| <link rel="stylesheet" href="../article.css" type="text/css" /> | |
| </head> | |
| <body> | |
| <h1>Understanding Layouts in SWT</h1> | |
| <div class="summary"> | |
| <h2>Summary</h2> | |
| <p>When writing applications in SWT, you may need to use <i>layouts</i> | |
| to give your windows a specific look. A layout controls the position and | |
| size of children in a <code>Composite</code>. Layout classes are | |
| subclasses of the abstract class <code>Layout</code>. This article shows | |
| you how to work with standard layouts, and write your own custom layout | |
| class.</p> | |
| <div class="author">By Carolyn MacLeod, OTI</div> | |
| <div class="copyright">Copyright © 2001, 2002 Object | |
| Technology International, Inc.</div> | |
| <div class="date">March 22, 2001</div> | |
| <div class="author">Revised by Shantha Ramachandran, OTI</div> | |
| <div class="date">May 02, 2002</div> | |
| <div class="author">Revised by Wayne Beaton, The Eclipse Foundation</div> | |
| <div class="copyright">Copyright © 2008 The Eclipse Foundation, Inc.</div> | |
| <div class="date">May 30, 2008</div> | |
| <div class="author">Revised by Wayne Beaton, The Eclipse Foundation</div> | |
| <div class="copyright">Copyright © 2008 The Eclipse Foundation, Inc.</div> | |
| <div class="date">May 13, 2009</div> | |
| </div> | |
| <div class="content"> | |
| <h2>Overview</h2> | |
| <p>When writing applications in the <a | |
| href="http://www.eclipse.org/swt">Standard Widget Toolkit</a> (SWT), | |
| you may need to use <i>layouts</i> to give your windows a specific look. | |
| A layout controls the position and size of children in a <code>Composite</code>. | |
| Layout classes are subclasses of the abstract class <code>Layout</code>. | |
| SWT provides several standard layout classes, and you can write custom | |
| layout classes.</p> | |
| <p>In SWT, positioning and sizing does not happen automatically. | |
| Applications can decide to size and place a <code>Composite</code>'s | |
| children initially, or in a resize listener. Another option is to | |
| specify a layout class to position and size the children. If children | |
| are not given a size, they will have zero size and they cannot be seen.</p> | |
| <p>The diagram below illustrates a few general terms that are used | |
| when discussing layouts. The <code>Composite</code> (in this case, a <code>TabFolder</code>) | |
| has a <i>location</i>, <i>clientArea</i> and <i>trim</i>. The size of | |
| the <code>Composite</code> is the size of the <i>clientArea</i> plus the | |
| size of the <code>trim</code>. This <code>Composite</code> has two | |
| children that are laid out side by side. A <code>Layout</code> is | |
| managing the size and position of the children. This <code>Layout</code> | |
| allows <code>spacing</code> between the children, and a <i>margin</i> | |
| between the children and the edges of the <code>Layout</code>. The size | |
| of the <code>Layout</code> is the same as the size of the <code>Composite</code>'s | |
| <i>clientArea</i>.</p> | |
| <img src="images/GeneralTerms.jpg" /> | |
| <p>The <i>preferred size</i> of a widget is the minimum size needed | |
| to show its content. In the case of a <code>Composite</code>, the | |
| preferred size is the smallest rectangle that contains all of its | |
| children. If children have been positioned by the application, the <code>Composite</code> | |
| computes its own preferred size based on the size and position of the | |
| children. If a <code>Composite</code> is using a layout class to | |
| position its children, it asks the <code>Layout</code> to compute the | |
| size of its <code>clientArea</code>, and then it adds in the <code>trim</code> | |
| to determine its preferred size.</p> | |
| <h3>Standard Layouts</h3> | |
| <p>The standard layout classes in the SWT library are:</p> | |
| <ul> | |
| <li><code>FillLayout</code> lays out equal-sized widgets in a | |
| single row or column</li> | |
| <li><code>RowLayout</code> lays out widgets in a row or rows, with | |
| fill, wrap, and spacing options</li> | |
| <li><code>GridLayout</code> lays out widgets in a grid</li> | |
| <li><code>FormLayout</code> lays out widgets by creating | |
| attachments for each of their sides</li> | |
| </ul> | |
| <p>To use the standard layouts, you need to import the SWT layout | |
| package:</p> | |
| <pre>import org.eclipse.swt.layout.*;</pre> | |
| <p>Layouts are pluggable. To set a <code>Composite</code> widget's | |
| layout, you use the widget's <code>setLayout(Layout)</code> method. In | |
| the following code, a <code>Shell</code> (a subclass of <code>Composite</code>) | |
| is told to position its children using a <code>RowLayout</code>:</p> | |
| <pre>Shell shell = new Shell(); | |
| shell.setLayout(new RowLayout());</pre> | |
| <p>A layout class may have a corresponding layout data class: a | |
| subclass of <code>Object</code> that contains layout data for a specific | |
| child. By convention, layout data classes are identified by substituting | |
| "Data" for "Layout" in the class name. For example, | |
| the standard layout class <code>RowLayout</code> has a layout data class | |
| called <code>RowData</code>, the layout class <code>GridLayout</code> | |
| uses a layout data class called <code>GridData</code>, and the layout | |
| class <code>FormLayout</code> has a layout data class called <code>FormData</code>. | |
| A widget's layout data class is set as follows:</p> | |
| <pre>Button button = new Button(shell, SWT.PUSH); | |
| button.setLayoutData(new RowData(50, 40));</pre> | |
| <h3>Examples in this Document</h3> | |
| <p>Most of the snapshots in this document were taken by running | |
| variations on the following example code. We may change the type of | |
| layout, the options used, or the type or number of children.</p> | |
| <pre>import org.eclipse.swt.SWT; | |
| import org.eclipse.swt.layout.RowLayout; | |
| import org.eclipse.swt.widgets.Button; | |
| import org.eclipse.swt.widgets.Display; | |
| import org.eclipse.swt.widgets.Shell; | |
| public class LayoutExample { | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| Shell shell = new Shell(display); | |
| // Create the layout. | |
| RowLayout layout = new RowLayout(); | |
| // Optionally set layout fields. | |
| layout.wrap = true; | |
| // Set the layout into the composite. | |
| shell.setLayout(layout); | |
| // Create the children of the composite. | |
| new Button(shell, SWT.PUSH).setText("B1"); | |
| new Button(shell, SWT.PUSH).setText("Wide Button 2"); | |
| new Button(shell, SWT.PUSH).setText("Button 3"); | |
| shell.pack(); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) display.sleep(); | |
| } | |
| } | |
| }</pre> | |
| <p>Running the above code results in the following:</p> | |
| <img src="images/LayoutExample.png" /> | |
| <p>If the user resizes the shell so that there is no longer room for | |
| Button 3 on the right, the <code>RowLayout</code> wraps Button 3 to the | |
| next row, as follows:</p> | |
| <img src="images/LayoutExampleResized.png" /> | |
| <p>Using layouts is closely tied with resize, as we shall see. | |
| Consequently, most of the examples in this document show what would | |
| happen if the <code>Composite</code> becomes smaller or larger, in order | |
| to illustrate how the <code>Layout</code> works.</p> | |
| <h2>FillLayout</h2> | |
| <p><code>FillLayout</code> is the simplest layout class. It lays out | |
| widgets in a single row or column, forcing them to be the same size. | |
| Initially, the widgets will all be as tall as the tallest widget, and as | |
| wide as the widest. <code>FillLayout</code> does not wrap, and you | |
| cannot specify margins or spacing. You might use it to lay out buttons | |
| in a task bar or tool bar, or to stack checkboxes in a <code>Group</code>. | |
| <code>FillLayout</code> can also be used when a <code>Composite</code> | |
| only has one child. For example, if a <code>Shell</code> has a single <code>Group</code> | |
| child, <code>FillLayout</code> will cause the <code>Group</code> to | |
| completely fill the <code>Shell</code>.</p> | |
| <p>Here is the relevant portion of the example code. First we create | |
| a <code>FillLayout</code>, then (if we want vertical) we set its <code>type</code> | |
| field to <code>SWT.VERTICAL</code>, and set it into the <code>Composite</code> | |
| (a <code>Shell</code>). The <code>Shell</code> has three push button | |
| children, "B1", "Wide Button 2", and "Button | |
| 3". Note that in a <code>FillLayout</code>, children are always the | |
| same size, and they fill all available space.</p> | |
| <pre>FillLayout fillLayout = new FillLayout(); | |
| fillLayout.type = SWT.VERTICAL; | |
| shell.setLayout(fillLayout); | |
| new Button(shell, SWT.PUSH).setText("B1"); | |
| new Button(shell, SWT.PUSH).setText("Wide Button 2"); | |
| new Button(shell, SWT.PUSH).setText("Button 3");</pre> | |
| <p>The following table shows the differences between a horizontal | |
| and vertical <code>FillLayout</code>, initially and after the parent has | |
| grown.</p> | |
| <table> | |
| <tr> | |
| <td> </td> | |
| <td> | |
| <p>Initial</p> | |
| </td> | |
| <td> | |
| <p>After resize</p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><code>fillLayout.type = SWT.HORIZONTAL</code> | |
| <p>(default)</p> | |
| </td> | |
| <td><img src="images/FillLayoutSampleHorizontal.png" /></td> | |
| <td><img src="images/FillLayoutSampleHorizontalResized.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><code>fillLayout.type = SWT.VERTICAL</code></td> | |
| <td><img src="images/FillLayoutSampleVertical.png" /></td> | |
| <td><img src="images/FillLayoutSampleVerticalResized.png" /></td> | |
| </tr> | |
| </table> | |
| <h2>RowLayout</h2> | |
| <p><code>RowLayout</code> is more commonly used than <code>FillLayout</code> | |
| because of its ability to wrap, and because it provides configurable | |
| margins and spacing. <code>RowLayout</code> has a number of | |
| configuration fields. In addition, the height and width of each widget | |
| in a <code>RowLayout</code> can be specified by setting the widget's <code>RowData</code> | |
| object using <code>setLayoutData</code>.</p> | |
| <h3>RowLayoutConfiguration Fields</h3> | |
| <p>The <code>type</code> field controls whether the <code>RowLayout</code> | |
| lays out widgets in horizontal rows, or vertical columns. <code>RowLayouts</code> | |
| are horizontal by default.</p> | |
| <p>The <code>wrap</code> field controls whether or not the <code>RowLayout</code> | |
| will wrap widgets into the next row if there isn't enough space in the | |
| current row. <code>RowLayouts</code> wrap by default.</p> | |
| <p>If the <code>pack</code> field is true, widgets in a <code>RowLayout</code> | |
| will take their natural size ("natural size" varies by widget; | |
| the natural size for a label or push button, for example, is large enough to | |
| display its textual contents), and they will be aligned as far to the | |
| left as possible. If pack is false, widgets will fill the available | |
| space, similar to the widgets in a <code>FillLayout</code>. <code>RowLayouts</code> | |
| pack by default.</p> | |
| <p>If the <code>justify</code> field is true, widgets in a <code>RowLayout</code> | |
| are spread across the available space from left to right. If the parent | |
| <code>Composite</code> grows wider, the extra space is distributed | |
| evenly among the widgets. If both <code>pack</code> and <code>justify</code> | |
| are true, widgets take their natural size, and the extra space is placed | |
| between the widgets in order to keep them fully justified. By default, <code>RowLayouts</code> | |
| do not justify.</p> | |
| <p>The <code>marginLeft</code>, <code>marginTop</code>, <code>marginRight</code>, | |
| <code>marginBottom</code> and <code>spacing</code> fields control the | |
| number of pixels between widgets (<code>spacing</code>) and the number | |
| of pixels between a widget and the side of the parent <code>Composite</code> | |
| (margin). By default, <code>RowLayouts</code> leave 3 pixels for margins | |
| and spacing. The margin and spacing fields are shown in the following | |
| diagram.</p> | |
| <img src="images/RowLayoutMargins.png" /> | |
| <h3>RowLayout Examples</h3> | |
| <p>The following example code creates a <code>RowLayout</code>, sets | |
| all of its fields to non-default values, and then sets it into a <code>Shell</code>.</p> | |
| <pre>RowLayout rowLayout = new RowLayout(); | |
| rowLayout.wrap = false; | |
| rowLayout.pack = false; | |
| rowLayout.justify = true; | |
| rowLayout.type = SWT.VERTICAL; | |
| rowLayout.marginLeft = 5; | |
| rowLayout.marginTop = 5; | |
| rowLayout.marginRight = 5; | |
| rowLayout.marginBottom = 5; | |
| rowLayout.spacing = 0; | |
| shell.setLayout(rowLayout);</pre> | |
| <p>If you are using the default field values, you only need one line | |
| of code:</p> | |
| <pre>shell.setLayout(new RowLayout());</pre> | |
| <p>The results of setting specific fields is shown below:</p> | |
| <table> | |
| <tr> | |
| <td width="150"> </td> | |
| <td> | |
| <p>Initial</p> | |
| </td> | |
| <td> | |
| <p>After resize</p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><pre>rowLayout.wrap = true; | |
| rowLayout.pack = true; | |
| rowLayout.justify = false; | |
| rowLayout.type = SWT.HORIZONTAL;</pre> | |
| <p>(defaults)</p> | |
| </td> | |
| <td><img src="images/RowLayoutSample01.png" /></td> | |
| <td><img src="images/RowLayoutSample01Resized.png" /><p>and</p><img src="images/RowLayoutSample01ResizedNarrower.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>wrap = false</pre> | |
| <p>(clips if not enough space)</p> | |
| </td> | |
| <td><img src="images/RowLayoutSample02.png" /></td> | |
| <td><img src="images/RowLayoutSample02Resized.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>pack = false</pre> | |
| <p>(all widgets are the same size)</p> | |
| </td> | |
| <td><img src="images/RowLayoutSample03.png" /></td> | |
| <td><img src="images/RowLayoutSample03Resized.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>justify = true</pre> | |
| <p>(widgets are spread across the available space)</p> | |
| </td> | |
| <td><img src="images/RowLayoutSample04.png" /></td> | |
| <td><img src="images/RowLayoutSample04Resized.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>type = SWT.VERTICAL</pre> | |
| <p>(widgets are arranged vertically in columns)</p> | |
| </td> | |
| <td><img src="images/RowLayoutSample05.png" /></td> | |
| <td><img src="images/RowLayoutSample05Resized.png" /></td> | |
| </tr> | |
| </table> | |
| <h3>Using RowData Objects with RowLayout</h3> | |
| <p>Each widget controlled by a <code>RowLayout</code> can have its | |
| initial width and height specified by setting its <code>RowData</code> | |
| object. The following code uses <code>RowData</code> objects to change | |
| the initial size of the <code>Buttons</code> in a <code>Shell</code>.</p> | |
| <pre>package org.eclipse.articles.layouts.samples; | |
| import org.eclipse.swt.SWT; | |
| import org.eclipse.swt.layout.RowData; | |
| import org.eclipse.swt.layout.RowLayout; | |
| import org.eclipse.swt.widgets.Button; | |
| import org.eclipse.swt.widgets.Display; | |
| import org.eclipse.swt.widgets.Shell; | |
| public class RowDataExample { | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| Shell shell = new Shell(display); | |
| shell.setLayout(new RowLayout()); | |
| Button button1 = new Button(shell, SWT.PUSH); | |
| button1.setText("Button 1"); | |
| button1.setLayoutData(new RowData(50, 40)); | |
| Button button2 = new Button(shell, SWT.PUSH); | |
| button2.setText("Button 2"); | |
| button2.setLayoutData(new RowData(50, 30)); | |
| Button button3 = new Button(shell, SWT.PUSH); | |
| button3.setText("Button 3"); | |
| button3.setLayoutData(new RowData(50, 20)); | |
| shell.pack(); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) | |
| display.sleep(); | |
| } | |
| } | |
| }</pre> | |
| <p>Here is what you see when you run this code.</p> | |
| <img src="images/RowDataExample.png" /> | |
| <h2>GridLayout</h2> | |
| <p>With a <code>GridLayout</code>, | |
| the widget children of a <code>Composite</code> are laid out in a grid. | |
| <code>GridLayout</code> has a number of configuration fields, and—like | |
| <code>RowLayout</code>—the widgets it lays out can have an associated | |
| layout data object, called <code>GridData</code>. The power of <code>GridLayout</code> | |
| lies in the ability to configure <code>GridData</code> for each widget | |
| controlled by the <code>GridLayout</code>.</p> | |
| <h3>GridLayout Configuration Fields</h3> | |
| <p>The <code>numColumns</code> field is the most important field in | |
| a <code>GridLayout</code>, and it is usually the first field an | |
| application will set. Widgets are laid out in columns from left to | |
| right, and a new row is created when <code>numColumns</code> + 1 widgets | |
| are added to the <code>Composite</code>. The default is to have only 1 | |
| column. The following code creates a <code>Shell</code> with five <code>Button</code> | |
| children of various widths, managed by a <code>GridLayout</code>. The | |
| table below shows the grid when <code>numColumns</code> is set to one, two, | |
| or three.</p> | |
| <pre>Display display = new Display(); | |
| Shell shell = new Shell(display); | |
| GridLayout gridLayout = new GridLayout(); | |
| gridLayout.numColumns = 3; | |
| shell.setLayout(gridLayout); | |
| new Button(shell, SWT.PUSH).setText("B1"); | |
| new Button(shell, SWT.PUSH).setText("Wide Button 2"); | |
| new Button(shell, SWT.PUSH).setText("Button 3"); | |
| new Button(shell, SWT.PUSH).setText("B4"); | |
| new Button(shell, SWT.PUSH).setText("Button 5"); | |
| shell.pack(); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) display.sleep(); | |
| }</pre> | |
| <table> | |
| <tr> | |
| <td><pre>numColumns = 1</pre></td> | |
| <td><pre>numColumns = 2</pre></td> | |
| <td><pre>numColumns = 3</pre></td> | |
| </tr> | |
| <tr> | |
| <td><img src="images/GridLayoutSampleNumColumns1.png" /></td> | |
| <td><img src="images/GridLayoutSampleNumColumns2.png" /></td> | |
| <td><img src="images/GridLayoutSampleNumColumns3.png" /></td> | |
| </tr> | |
| </table> | |
| <p>The <code>makeColumnsEqualWidth</code> field forces the columns | |
| to be the same width. The default is <code>false</code>. If we change the example | |
| above to have three columns of equal width, this is what we would get (note | |
| that in the absence of further instruction, widgets are left-justified | |
| in their columns).</p> | |
| <img src="images/GridLayoutSampleEqualWidth.png" /> | |
| <p>The <code>marginWidth</code>, <code>marginHeight</code>, <code>horizontalSpacing</code>, | |
| and <code>verticalSpacing</code> fields in a <code>GridLayout</code> are | |
| similar to those in a <code>RowLayout</code>. The difference is that the | |
| left and right margins are grouped into <code>marginWidth</code>, and | |
| the top and bottom margins are grouped into <code>marginHeight</code>. | |
| Also, in a <code>GridLayout</code> you can specify <code>horizontalSpacing</code> | |
| and <code>verticalSpacing</code> independently, whereas in a <code>RowLayout</code>, | |
| <code>spacing</code> applies to horizontal or vertical depending on the | |
| type of the <code>RowLayout</code>.</p> | |
| <h3>GridData Object Fields</h3> | |
| <p><code>GridData</code> is the layout data object associated with <code>GridLayout</code>. | |
| To set a widget's <code>GridData</code> object, you use the <code>setLayoutData</code> | |
| method. For example, to set the <code>GridData</code> for a <code>Button</code>, | |
| we could do the following:</p> | |
| <pre>Button button1 = new Button(shell, SWT.PUSH); | |
| button1.setText("B1"); | |
| button1.setLayoutData(new GridData());</pre> | |
| <p>Of course, this code just creates a <code>GridData</code> object | |
| with all of its fields set to their default values, which is the same as | |
| not setting the layout data at all. There are two ways to create a <code>GridData</code> | |
| object with certain fields set. The first is to set the fields directly:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.horizontalAlignment = GridData.FILL; | |
| gridData.grabExcessHorizontalSpace = true; | |
| button1.setLayoutData(gridData);</pre> <img | |
| src="images/GridDataSampleHorizontalFill.png" /> | |
| <p>One final note about <code>GridData</code> objects before we get | |
| into their fields: do not reuse <code>GridData</code> objects. Every | |
| widget in a <code>Composite</code> that is managed by a <code>GridLayout</code> | |
| must have a unique <code>GridData</code> object. If the layout data for | |
| a widget in a <code>GridLayout</code> is null at layout time, a unique <code>GridData</code> | |
| object is created for it.</p> | |
| <p>The <code>horizontalAlignment</code> and <code>verticalAlignment</code> | |
| fields specify where to place a widget horizontally and/or vertically | |
| within its grid cell. Each alignment field can have one of the following | |
| values:</p> | |
| <ul> | |
| <li><code>BEGINNING</code></li> | |
| <li><code>CENTER</code></li> | |
| <li><code>END</code></li> | |
| <li><code>FILL</code></li> | |
| </ul> | |
| <p>The default horizontalAlignment is BEGINNING (or left-aligned). | |
| The default verticalAlignment is CENTER.</p> | |
| <p>Let's go back to our five-button example with three columns, and | |
| we will vary the <code>horizontalAlignment</code> of Button 5.</p> | |
| <table> | |
| <tr> | |
| <td><pre>horizontalAlignment = GridData.BEGINNING</pre> | |
| <p>(default)</p> | |
| </td> | |
| <td><img src="images/GridDataSampleBeginning.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>horizontalAlignment = GridData.CENTER</pre></td> | |
| <td><img src="images/GridDataSampleCenter.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>horizontalAlignment = GridData.END</pre></td> | |
| <td><img src="images/GridDataSampleEnd.png" /></td> | |
| </tr> | |
| <tr> | |
| <td><pre>horizontalAlignment = GridData.FILL</pre></td> | |
| <td><img src="images/GridDataSampleFill.png" /></td> | |
| </tr> | |
| </table> | |
| <p>The <code>horizontalIndent</code> field allows you to move a | |
| widget to the right by a specified number of pixels. This field is | |
| typically only useful when the <code>horizontalAlignment</code> is <code>BEGINNING</code>. | |
| We cannot use a style bit to set the indent, so we will indent "Button 5" | |
| in our example by four pixels as follows:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.horizontalIndent = 4; | |
| button5.setLayoutData(gridData);</pre> <img | |
| src="images/GridDataSampleIndent.png" /> | |
| <p>The <code>horizontalSpan</code> and <code>verticalSpan</code> | |
| fields let widgets occupy more than one grid cell. They are often used | |
| in conjunction with <code>FILL</code> alignment. We can make "Button 5" in our example | |
| span the last two cells as follows:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.horizontalAlignment = GridData.FILL; | |
| gridData.horizontalSpan = 2; | |
| button5.setLayoutData(gridData);</pre> <img | |
| src="images/GridDataSampleSpan01.png" /> | |
| <p>If we decide to make "Wide Button 2" span two cells instead, we | |
| would end up with this:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.horizontalAlignment = GridData.FILL; | |
| gridData.horizontalSpan = 2; | |
| button2.setLayoutData(gridData);</pre> <img | |
| src="images/GridDataSampleSpan02.png" /> | |
| <p>Or we could make "Button 3" span two cells vertically:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.verticalAlignment = GridData.FILL; | |
| gridData.verticalSpan = 2; | |
| button3.setLayoutData(gridData);</pre> <img | |
| src="images/GridDataSampleSpan03.png" /> | |
| <p>The <code>grabExcessHorizontalSpace</code> and <code>grabExcessVerticalSpace</code> | |
| fields are typically used for larger widgets such as <code>Text,</code> | |
| <code>List</code> or <code>Canvas</code> to allow them to grow if their | |
| containing <code>Composite</code> grows. If a <code>Text</code> is | |
| grabbing excess horizontal space and the user resizes the <code>Shell</code> | |
| wider, then the <code>Text</code> will get all of the new horizontal | |
| space and other widgets in the same row will stay their original width. | |
| Of course, the widget that is grabbing excess space is also the first | |
| one to shrink when the <code>Shell</code> gets smaller. It is easiest to | |
| always think of the <b>grabExcessSpace</b> fields in the context of | |
| resizing. For a simple example, let's reuse the previous example where | |
| "Button 3" spanned two cells vertically. Here it is again:</p> | |
| <img src="images/GridDataSampleSpan03.png" /> | |
| <p>If we resize this window, the only thing that happens is that the | |
| window gets bigger:</p> | |
| <img src="images/GridDataSampleSpan03Resized.png" /> | |
| <p>Now we will tell "Button 3" to grab excess horizontal and vertical | |
| space, and "B1" and "B4" to fill vertically (without grabbing), and we | |
| resize the window again:</p> | |
| <pre>Button button1 = new Button(shell, SWT.PUSH); | |
| button1.setText("B1"); | |
| GridData gridData = new GridData(); | |
| gridData.verticalAlignment = GridData.FILL; | |
| button1.setLayoutData(gridData); | |
| new Button(shell, SWT.PUSH).setText("Wide Button 2"); | |
| Button button3 = new Button(shell, SWT.PUSH); | |
| button3.setText("Button 3"); | |
| gridData = new GridData(); | |
| gridData.verticalAlignment = GridData.FILL; | |
| gridData.verticalSpan = 2; | |
| gridData.grabExcessVerticalSpace = true; | |
| gridData.horizontalAlignment = GridData.FILL; | |
| gridData.grabExcessHorizontalSpace = true; | |
| button3.setLayoutData(gridData); | |
| Button button4 = new Button(shell, SWT.PUSH); | |
| button4.setText("B4"); | |
| gridData = new GridData(); | |
| gridData.verticalAlignment = GridData.FILL; | |
| button4.setLayoutData(gridData); | |
| new Button(shell, SWT.PUSH).setText("Button 5");</pre> <img | |
| src="images/GridDataSampleGrab01.png" /> | |
| <p>This time, "Button 3" grew in both directions, and "B4" grew | |
| vertically. The other buttons stayed their original sizes. Because | |
| "Button 3" was grabbing vertically and it spans two rows, the <i>last</i> | |
| row that it spans grew taller. Note that "B1" did not | |
| grow—although it is filling vertically—because its row did | |
| not grow. Since "Button 3" was grabbing horizontally, its column grew | |
| wider, and since it was filling horizontally, it grew wider to fill the | |
| column.</p> | |
| <p>In a typical application window, you often want to have at least | |
| one widget that is grabbing. If more than one widget is trying to grab | |
| the same space, then the excess space is shared evenly among the | |
| grabbing widgets:</p> | |
| <pre>import org.eclipse.swt.SWT; | |
| import org.eclipse.swt.layout.GridData; | |
| import org.eclipse.swt.layout.GridLayout; | |
| import org.eclipse.swt.widgets.Display; | |
| import org.eclipse.swt.widgets.Label; | |
| import org.eclipse.swt.widgets.List; | |
| import org.eclipse.swt.widgets.Shell; | |
| import org.eclipse.swt.widgets.Text; | |
| public class SampleGrabExcess { | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| Shell shell = new Shell(display); | |
| shell.setLayout(new GridLayout(2, false)); | |
| Label nameLabel = new Label(shell, SWT.NONE); | |
| nameLabel.setText("Name:"); | |
| Text nameText = new Text(shell, SWT.BORDER); | |
| GridData gridData = new GridData(); | |
| gridData.horizontalAlignment = SWT.FILL; | |
| gridData.grabExcessHorizontalSpace = true; | |
| nameText.setLayoutData(gridData); | |
| nameText.setText("Text grows horizontally"); | |
| Label addressLabel = new Label(shell, SWT.NONE); | |
| addressLabel.setText("Address:"); | |
| gridData = new GridData(); | |
| gridData.verticalAlignment = SWT.TOP; | |
| addressLabel.setLayoutData(gridData); | |
| Text addressText = new Text(shell, SWT.BORDER | SWT.WRAP | SWT.MULTI); | |
| gridData = new GridData(); | |
| gridData.horizontalAlignment = SWT.FILL; | |
| gridData.grabExcessHorizontalSpace = true; | |
| gridData.verticalAlignment = SWT.FILL; | |
| gridData.grabExcessVerticalSpace = true; | |
| addressText.setLayoutData(gridData); | |
| addressText.setText("This text field and the List\nbelow share any excess space."); | |
| Label sportsLabel = new Label(shell, SWT.NONE); | |
| sportsLabel.setText("Sports played:"); | |
| gridData = new GridData(); | |
| gridData.horizontalSpan = 2; | |
| sportsLabel.setLayoutData(gridData); | |
| List sportsList = new List(shell, SWT.BORDER | SWT.MULTI); | |
| gridData = new GridData(); | |
| gridData.horizontalSpan = 2; | |
| gridData.horizontalAlignment = SWT.FILL; | |
| gridData.grabExcessHorizontalSpace = true; | |
| gridData.verticalAlignment = SWT.FILL; | |
| gridData.grabExcessVerticalSpace = true; | |
| sportsList.setLayoutData(gridData); | |
| sportsList.add("Hockey"); | |
| sportsList.add("Street Hockey"); | |
| shell.pack(); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) | |
| display.sleep(); | |
| } | |
| } | |
| }</pre> | |
| <p><img src="images/GridLayoutGrabExcess.png" /></p> | |
| <p>When resized, the single line (top-most) <code>Text</code> grows | |
| to consume all available horizontal space, and the second <code>Text</code> | |
| and the <code>List</code> grow to consume all remaining space (both | |
| vertically and horizontally):</p> | |
| <p><img src="images/GridLayoutGrabExcessResized.png" /></p> | |
| <p>One final point to note about grabbing: if a widget is set to | |
| grab excess horizontal space and its parent <code>Composite</code> grows | |
| wider, then the entire <i>column</i> containing that widget grows wider. | |
| If a widget is grabbing excess vertical space and its parent <code>Composite</code> | |
| grows taller, then the entire <i>row</i> containing that widget grows | |
| taller. The implication of this is that if any other widget in the | |
| affected column or row has <i>fill</i> alignment, then it will stretch | |
| also. Widgets that have beginning, center, or end alignment will not | |
| stretch: they will stay at the beginning, center or end of the wider | |
| column or taller row.</p> | |
| <p>The <code>widthHint</code> and <code>heightHint</code> fields | |
| indicate the number of pixels wide or tall that you would like a widget | |
| to be, assuming that it does not conflict with other requirements in the | |
| <code>GridLayout</code>'s constraint system. Looking back at the | |
| five-button, three-column example, say we want "Button 5" to be 70 pixels | |
| wide and 40 pixels tall. We code it as follows:</p> | |
| <pre>GridData gridData = new GridData(); | |
| gridData.widthHint = 70; | |
| gridData.heightHint = 40; | |
| button5.setLayoutData(gridData);</pre> | |
| <p>The natural size of "Button 5" is shown in the window on the left, | |
| below, and the 70-pixel wide, 40-pixel tall "Button 5" is on the right.</p> | |
| <img src="images/GridLayoutSampleNumColumns3.png" /> <img | |
| src="images/GridDataSampleSpan04.png" /> | |
| <p>Note, however, that if the <code>horizontalAlignment</code> of | |
| "Button 5" was <code>FILL</code>, then the <code>GridLayout</code> would not have been | |
| able to honor the request for a width of 70 pixels.</p> | |
| <p>One final comment about using width and height hints: something | |
| that looks good on one platform may not look good on another. The | |
| variation between font sizes and natural widget sizes across platforms | |
| means that hard-coding pixel values is not usually the best way to lay | |
| out windows. So, keep the use of size hints to a minimum, if you use | |
| them at all.</p> | |
| <h3>A Complex GridLayout Example</h3> | |
| <p>So far, the <code>GridLayout</code> examples have been fairly | |
| simple, in order to show how each field works. Now, we will put them all | |
| together to create a more complicated example. We start by hand-drawing | |
| a rough sketch of the window we want to create, to determine things like | |
| how many columns the grid should contain, and whether or not any widgets | |
| need to span.</p> | |
| <img src="images/DogShowHandDrawn.gif" /> | |
| <p>Then we start coding the example from the diagram. The code is | |
| below. Note that we have added a bit of logic to make the code more | |
| interesting, for example, "Browse..." opens a <code>FileDialog</code> | |
| to read an <code>Image</code> file which the <code>Canvas</code> | |
| displays in a paint listener, "Delete" deletes the <code>Image</code>, and | |
| Enter prints the current dog and owner info.</p> | |
| <pre>package org.eclipse.articles.layouts.samples; | |
| import org.eclipse.swt.SWT; | |
| import org.eclipse.swt.events.DisposeEvent; | |
| import org.eclipse.swt.events.DisposeListener; | |
| import org.eclipse.swt.events.PaintEvent; | |
| import org.eclipse.swt.events.PaintListener; | |
| import org.eclipse.swt.events.SelectionAdapter; | |
| import org.eclipse.swt.events.SelectionEvent; | |
| import org.eclipse.swt.graphics.Image; | |
| import org.eclipse.swt.graphics.Rectangle; | |
| import org.eclipse.swt.layout.GridData; | |
| import org.eclipse.swt.layout.GridLayout; | |
| import org.eclipse.swt.widgets.Button; | |
| import org.eclipse.swt.widgets.Canvas; | |
| import org.eclipse.swt.widgets.Combo; | |
| import org.eclipse.swt.widgets.Display; | |
| import org.eclipse.swt.widgets.FileDialog; | |
| import org.eclipse.swt.widgets.Group; | |
| import org.eclipse.swt.widgets.Label; | |
| import org.eclipse.swt.widgets.List; | |
| import org.eclipse.swt.widgets.Shell; | |
| import org.eclipse.swt.widgets.Text; | |
| public class DogShowRegistrationWindow { | |
| Text dogName; | |
| Combo dogBreed; | |
| Canvas dogPhoto; | |
| Image dogImage; | |
| List categories; | |
| Text ownerName; | |
| Text ownerPhone; | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| Shell shell = new DogShowRegistrationWindow().createShell(display); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) | |
| display.sleep(); | |
| } | |
| } | |
| public Shell createShell(final Display display) { | |
| final Shell shell = new Shell(display); | |
| shell.setText("Dog Show Entry"); | |
| GridLayout gridLayout = new GridLayout(); | |
| gridLayout.numColumns = 3; | |
| shell.setLayout(gridLayout); | |
| new Label(shell, SWT.NONE).setText("Dog's Name:"); | |
| dogName = new Text(shell, SWT.SINGLE | SWT.BORDER); | |
| GridData gridData = new GridData(GridData.FILL, GridData.CENTER, true, false); | |
| gridData.horizontalSpan = 2; | |
| dogName.setLayoutData(gridData); | |
| new Label(shell, SWT.NONE).setText("Breed:"); | |
| dogBreed = new Combo(shell, SWT.NONE); | |
| dogBreed.setItems(new String[] { "Collie", "Pitbull", "Poodle", | |
| "Scottie", "Black Lab" }); | |
| dogBreed.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); | |
| Label label = new Label(shell, SWT.NONE); | |
| label.setText("Categories"); | |
| label.setLayoutData(new GridData(GridData.CENTER, GridData.CENTER, true, false)); | |
| new Label(shell, SWT.NONE).setText("Photo:"); | |
| dogPhoto = new Canvas(shell, SWT.BORDER); | |
| gridData = new GridData(GridData.FILL, GridData.FILL, true, true); | |
| gridData.widthHint = 80; | |
| gridData.heightHint = 80; | |
| gridData.verticalSpan = 3; | |
| dogPhoto.setLayoutData(gridData); | |
| dogPhoto.addPaintListener(new PaintListener() { | |
| public void paintControl(final PaintEvent event) { | |
| if (dogImage != null) { | |
| event.gc.drawImage(dogImage, 0, 0); | |
| } | |
| } | |
| }); | |
| categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL); | |
| categories.setItems(new String[] { "Best of Breed", "Prettiest Female", | |
| "Handsomest Male", "Best Dressed", "Fluffiest Ears", | |
| "Most Colors", "Best Performer", "Loudest Bark", | |
| "Best Behaved", "Prettiest Eyes", "Most Hair", "Longest Tail", | |
| "Cutest Trick" }); | |
| gridData = new GridData(GridData.FILL, GridData.FILL, true, true); | |
| gridData.verticalSpan = 4; | |
| int listHeight = categories.getItemHeight() * 12; | |
| Rectangle trim = categories.computeTrim(0, 0, 0, listHeight); | |
| gridData.heightHint = trim.height; | |
| categories.setLayoutData(gridData); | |
| Button browse = new Button(shell, SWT.PUSH); | |
| browse.setText("Browse..."); | |
| gridData = new GridData(GridData.FILL, GridData.CENTER, true, false); | |
| gridData.horizontalIndent = 5; | |
| browse.setLayoutData(gridData); | |
| browse.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| String fileName = new FileDialog(shell).open(); | |
| if (fileName != null) { | |
| dogImage = new Image(display, fileName); | |
| } | |
| } | |
| }); | |
| Button delete = new Button(shell, SWT.PUSH); | |
| delete.setText("Delete"); | |
| gridData = new GridData(GridData.FILL, GridData.BEGINNING, true, false); | |
| gridData.horizontalIndent = 5; | |
| delete.setLayoutData(gridData); | |
| delete.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| if (dogImage != null) { | |
| dogImage.dispose(); | |
| dogImage = null; | |
| dogPhoto.redraw(); | |
| } | |
| } | |
| }); | |
| Group ownerInfo = new Group(shell, SWT.NONE); | |
| ownerInfo.setText("Owner Info"); | |
| gridLayout = new GridLayout(); | |
| gridLayout.numColumns = 2; | |
| ownerInfo.setLayout(gridLayout); | |
| gridData = new GridData(GridData.FILL, GridData.CENTER, true, false); | |
| gridData.horizontalSpan = 2; | |
| ownerInfo.setLayoutData(gridData); | |
| new Label(ownerInfo, SWT.NONE).setText("Name:"); | |
| ownerName = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); | |
| ownerName.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); | |
| new Label(ownerInfo, SWT.NONE).setText("Phone:"); | |
| ownerPhone = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); | |
| ownerPhone.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); | |
| Button enter = new Button(shell, SWT.PUSH); | |
| enter.setText("Enter"); | |
| gridData = new GridData(GridData.END, GridData.CENTER, false, false); | |
| gridData.horizontalSpan = 3; | |
| enter.setLayoutData(gridData); | |
| enter.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| System.out.println("\nDog Name: " + dogName.getText()); | |
| System.out.println("Dog Breed: " + dogBreed.getText()); | |
| System.out.println("Owner Name: " + ownerName.getText()); | |
| System.out.println("Owner Phone: " + ownerPhone.getText()); | |
| System.out.println("Categories:"); | |
| String cats[] = categories.getSelection(); | |
| for (int i = 0; i > cats.length; i++) { | |
| System.out.println("\t" + cats[i]); | |
| } | |
| } | |
| }); | |
| shell.addDisposeListener(new DisposeListener() { | |
| public void widgetDisposed(DisposeEvent arg0) { | |
| if (dogImage != null) { | |
| dogImage.dispose(); | |
| dogImage = null; | |
| } | |
| } | |
| }); | |
| shell.pack(); | |
| return shell; | |
| } | |
| }</pre> | |
| <p>Here is what the window looks like after Mary Smith enters | |
| Bifford in the dog show:</p> | |
| <img src="images/DogShowBiff.png" /> | |
| <p>If this window is resized larger, the layout adjusts as follows:</p> | |
| <img src="images/DogShowBiffResized.png" /> | |
| <p>Notice the following:</p> | |
| <ul> | |
| <li>There are three columns and seven rows.</li> | |
| <li>The <code>dogPhoto</code> <code>Canvas</code> grew wider and | |
| taller because it is filling and grabbing horizontally and vertically | |
| (we did not resize the <code>Image</code>, but we could have).</li> | |
| <li>The <code>dogBreed</code> <code>Combo</code> grew wider | |
| because it is filling horizontally, and it is in the same column as the | |
| <code>Canvas</code>.</li> | |
| <li>The <code>dogName</code> <code>Text</code> grew wider because | |
| it is filling horizontally, and one of the columns it spans is the | |
| column containing the <code>Canvas</code>.</li> | |
| <li>The <code>categories</code> <code>List</code> grew taller | |
| because it is filling vertically, and it spans the same rows that the <code>Canvas</code> | |
| does.</li> | |
| <li>Because the <code>categories</code> <code>List</code> grew | |
| taller, its vertical scrollbar disappeared (it did not grow wider).</li> | |
| <li>The <code>ownerInfo</code> <code>Group</code> grew wider | |
| because it is filling horizontally, and one of the columns it spans is | |
| the column containing the <code>Canvas</code>.</li> | |
| <li>The <code>ownerInfo</code> <code>Group</code>, as a subclass | |
| of <code>Composite</code>, has its own <code>GridLayout</code> with 2 | |
| columns and 2 rows.</li> | |
| <li>The <code>ownerName</code> and <code>ownerPhone</code> <code>Texts</code> | |
| grew wider because the <code>Group</code> grew wider, and they are | |
| filling and grabbing horizontally in the <code>Group</code>'s <code>GridLayout</code>.</li> | |
| <li>The <code>browse</code> and <code>delete</code> <code>Buttons</code> | |
| are indented slightly, and because they both fill horizontally, they | |
| are the same width.</li> | |
| <li>The <code>delete</code> <code>Button</code> is vertically | |
| aligned at the top of its row.</li> | |
| <li>The "Categories" <code>Label</code> is centered over | |
| the <code>categories</code> <code>List</code>. | |
| <li>The <code>enter</code> <code>Button</code> is horizontally | |
| aligned to the right of the 3 columns it spans.</li> | |
| <li>The <code>dogPhoto</code> <code>Canvas</code> was created with | |
| width and height hints because we want the <code>Image</code> to be 80 | |
| pixels x 80 pixels, if possible.</li> | |
| <li>The <code>categories</code> <code>List</code> was created with | |
| a height hint that was based on the <code>List</code>'s font times 12, | |
| because we want try to get the <code>List</code> to show 12 items | |
| initially.</li> | |
| </ul> | |
| <h2>FormLayout</h2> | |
| <p><code>FormLayout</code> works by creating <code>FormAttachment</code>s | |
| for each side of the widget, and storing them in the layout data. An | |
| attachment 'attaches' a specific side of the widget either to a position | |
| in the parent <code>Composite</code> or to another widget within the | |
| layout. This provides tremendous flexibility when laying out, as it | |
| allows you to specify the placement of individual widgets within the | |
| layout.</p> | |
| <h3>FormLayout Configuration Fields</h3> | |
| <p>The <code>marginWidth</code>, and <code>MarginHeight</code> | |
| fields in <code>FormLayout</code> are similar to those in <code>GridLayout</code>. | |
| Left and right margins are defined by <code>marginWidth</code>, and top | |
| and bottom margins are defined by <code>marginHeight</code>. Margins can | |
| also be defined on a per-widget basis in the attachments. <code>FormLayout</code> | |
| margins are zero by default.</p> | |
| <p>To set the margins, we create a <code>FormLayout</code>, and set | |
| the margin fields. The following code will set a margin of five pixels | |
| around all four sides of the parent <code>Composite</code>:</p> | |
| <pre>Display display = new Display (); | |
| Shell shell = new Shell (display); | |
| FormLayout layout= new FormLayout (); | |
| layout.marginHeight = 5; | |
| layout.marginWidth = 5; | |
| shell.setLayout(layout);</pre> | |
| <h3>FormData Object Fields</h3> | |
| <p><code>FormData</code> objects specify how each widget in a <code>FormLayout</code> | |
| will be laid out. Each <code>FormData</code> object defines the | |
| attachments for all four sides of the widget. These attachments tell | |
| where to position each side of the widget. To set a widget's <code>FormData</code> | |
| object, you use the <code>setLayoutData(Object)</code> method, for example:</p> | |
| <pre>Button button1 = new Button(shell, SWT.PUSH); | |
| button1.setText("B1"); | |
| button1.setLayoutData(new FormData());</pre> | |
| <p>This code creates a <code>FormData</code> object with no | |
| attachments. In this case, default attachments are defined, which | |
| defeats the whole purpose and utility of <code>FormLayout</code>. The | |
| default attachments attach the widget to the top and left edges of the | |
| parent <code>Composite</code>. If every widget in a <code>FormLayout</code> | |
| used the default attachments, they would all be laid out one on top of | |
| another in the top left corner of the parent <code>Composite</code>.</p> | |
| <p>The <code>left</code>, <code>right</code>, <code>top</code>, and | |
| <code>bottom</code> fields of <code>FormData</code> specify the <code>FormAttachment</code> | |
| objects that are associated with the left, right, top and bottom sides | |
| of the widget, respectively. These fields are set in the following | |
| example:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.top = new FormAttachment(0,60); | |
| formData.bottom = new FormAttachment(100,-5); | |
| formData.left = new FormAttachment(20,0); | |
| formData.right = new FormAttachment(100,-3); | |
| button1.setLayoutData(formData);</pre> <img | |
| src="images/FormLayoutSample01.png" /> | |
| <p>A <code>FormAttachment</code> object defines the attachment of a | |
| specific side of a widget. There are many ways that a side can be | |
| attached: to a position in the parent <code>Composite</code>, to an edge | |
| of the <code>Composite</code>, to the adjacent side of another widget, | |
| to the opposite side of another widget, or centered on another widget. | |
| Attaching to a position places the side of the widget so that it is | |
| always at a percentage of the <code>Composite</code>. To attach to an | |
| edge of the <code>Composite</code>, the percentage is either 0% or 100%. | |
| Attaching to the adjacent side of another widget ensures that the | |
| specified side of the widget is always next to the closest side of the | |
| other widget. Attaching to the opposite side of another widget ensures | |
| that the specific side of the widget is aligned with the furthest side | |
| of the other widget. Finally, attaching to the center of another widget | |
| centers the widget on the other widget. Any of these ways can be done | |
| with or without an offset.</p> | |
| <p>The <code>width</code> and <code>height</code> fields of <code>FormData</code> | |
| specify the requested width and the height of the widget. If a requested | |
| width or height conflicts with constraints set by the attachments, then | |
| that width or height will not be honored. Although setting attachments can | |
| also determine width and height, there are some cases when you do not | |
| want to define attachments for all sides of the widget. In this case, it | |
| may be useful to set the width and height of the widget as follows:</p> | |
| <pre>FormData formData = new FormData(20,30); | |
| formData.top = new FormAttachment(0,60); | |
| formData.left = new FormAttachment(20,0); | |
| button1.setLayoutData(formData);</pre> | |
| <p>If you wish to set only the width or the height, you can directly | |
| set the width or height field in the <code>FormData</code> object:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.width = 30; | |
| formData.top = new FormAttachment(0,60); | |
| formData.bottom = new FormAttachment(100,-5); | |
| formData.left = new FormAttachment(20,0); | |
| button1.setLayoutData(formData);</pre> | |
| <p>Note that if a button is attached to the parent <code>Composite</code> | |
| on both sides, when the <code>Composite</code> is resized, the button | |
| will grow or shrink along with it.</p> | |
| <h3>FormAttachment Objects</h3> | |
| <p>A <code>FormAttachment</code> is an object that defines the | |
| attachment for a specific side of a widget. It is not always necessary | |
| to set an attachment for all four sides of a widget. Often, specifying | |
| one or more sides of a widget can fully specify its placement in the | |
| layout. In order to properly place your widgets, you should define an | |
| attachment for at least one of <code>left</code> or <code>right</code> | |
| in the <code>FormData</code>, and at least one of <code>top</code> or <code>bottom</code>. | |
| If you only wish to attach the left side of a widget and not the right, | |
| then the widget will be positioned based on its left side, and the | |
| widget will take its natural size (or its requested size, if one was set | |
| for it). If you do not attach the left or the right, default positioning | |
| will attach your widget to the left side of the form. The same logic | |
| applies for the top and bottom sides.</p> | |
| <h3>Attaching to a Position</h3> | |
| <p>There are many types of attachment. The first is to attach the | |
| widget to a position in the parent <code>Composite</code>. This can be | |
| done by defining a percentage value out of 100, for example:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.top = new FormAttachment(50,0); | |
| button1.setLayoutData(formData);</pre> <img | |
| src="images/FormLayoutSamplePosition50.png" /> | |
| <p>This sets the top of the <code>Button</code> to a position that | |
| represents 50% of the height of the parent <code>Composite</code> (a <code>Shell</code>), | |
| with an offset of 0. When the shell is resized, the top side of the <code>Button</code> | |
| will still be at 50%, like so:</p> | |
| <img src="images/FormLayoutSamplePosition50Resized.png" /> | |
| <p>If we chose to set an offset value, the top side of the <code>Button</code> | |
| would have been set to 50% of the <code>Composite</code> plus or minus | |
| the number of pixels set for the offset.</p> | |
| <p>We can also define the position of the button using an arbitrary scale, for example:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.top = new FormAttachment(30,70,10); | |
| button1.setLayoutData(formData);</pre> | |
| <p>If the height of the <code>Composite</code> is defined as being | |
| 70 units, this sets the top of the <code>Button</code> to a position | |
| representing 30 units down from the top of the <code>Composite</code>, | |
| plus 10 pixels (i.e. 3/7ths of the height of the composite plus 10 pixels).</p> | |
| <p>To attach a side of a widget to an edge of | |
| the parent <code>Composite</code>, set the position to either 0% or 100%. The 0 | |
| position is defined as the top of the <code>Composite</code> when going | |
| vertically, and the left when going horizontally. The right and bottom | |
| edges of the <code>Composite</code> are defined as the 100 position. | |
| Therefore, if we want to attach a widget to the right edge of the <code>Composite</code>, | |
| we simply have to create an attachment that sets the position to 100:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.right = new FormAttachment(100,-5); | |
| button1.setLayoutData(formData);</pre> <img | |
| src="images/FormLayoutSampleParent100.png" /> | |
| <p>This attaches the right side of the <code>Button</code> to the | |
| right edge of the parent (a <code>Shell</code>), with an offset of five | |
| pixels. Note that the offsets go in one direction only. If you want a | |
| widget offset down or to the right, the offset should be positive. For | |
| offsets that shift the widget up or to the left, the offset should be | |
| negative. When the <code>Shell</code> is resized, the <code>Button</code> | |
| will always be five pixels away from the right edge:</p> | |
| <img src="images/FormLayoutSampleParent100Resized.png" /> | |
| <h3>Attaching to Another Widget</h3> | |
| <p>The third type of attachment is to attach the side of the widget | |
| to another control within the parent <code>Composite</code>. The side | |
| can be attached to the adjacent side of the other control (the default), | |
| to the opposite side of the other control, or the widget can be centered | |
| on the other control, all with or without and offset.</p> | |
| <p>The most common way to attach to another control is to attach to | |
| its adjacent side. For example, the following code:</p> | |
| <pre>FormData formData = new FormData(); | |
| formData.top = new FormAttachment(20,0); | |
| button1.setLayoutData(formData); | |
| FormData formData2 = new FormData(); | |
| formData2.top = new FormAttachment(button1,10); | |
| button2.setLayoutData(formData2);</pre> <img | |
| src="images/FormLayoutSampleAttachWidget01.png" /> | |
| <p>This example attaches the top of button2 to the bottom of | |
| button1. Note that when the window is resized, button1 will move so that | |
| its top side is always positioned at 20% of the <code>Shell</code>, and | |
| button2 will move so that its top side is always 10 pixels below the | |
| adjacent (bottom) side of button1.</p> | |
| <img src="images/FormLayoutSampleAttachWidget01Resized.png" /> | |
| <p>While the default is to attach the side of a widget to the | |
| adjacent side of a control, <code>FormAttachment</code>s can also be | |
| created to attach to the opposite side of a control. This is useful when | |
| lining up widgets. In this case, you create the attachment to the other | |
| control using <code>TOP</code>, <code>BOTTOM</code>, <code>LEFT</code> | |
| or <code>RIGHT</code> alignment, for example:</p> | |
| <pre>formData2.top = new FormAttachment(button1,0,SWT.TOP);</pre> | |
| <p>In the following example, the top side of <code>button1</code> is positioned | |
| at 20% of the <code>Shell</code>. <code>button2</code>'s top side is aligned with | |
| <code>button1</code>'s top side, using <code>TOP</code> alignment. This means that the top side of | |
| <code>button2</code> is also positioned at 20% of the <code>Shell</code>. Note that | |
| when specifying the top attachment, only the vertical placement of the | |
| widget is being defined. It is still necessary to set the left | |
| attachment for <code>button2</code> so that the <code>Button</code>s are not stacked | |
| on top of each other.</p> | |
| <pre>FormData formData = new FormData(50,50); | |
| formData.top = new FormAttachment(20,0); | |
| button1.setLayoutData(formData); | |
| FormData formData2 = new FormData(); | |
| formData2.left = new FormAttachment(button1,5); | |
| formData2.top = new FormAttachment(button1,0,SWT.TOP); | |
| button2.setLayoutData(formData2);</pre> <img | |
| src="images/FormLayoutSampleAttachWidget02.png" /> | |
| <p>The final way to attach a widget to another control is to center | |
| it on the other control. This is useful when the widgets are different | |
| sizes. In this case, you create the attachment to the other control with | |
| <code>CENTER</code> alignment, for example:</p> | |
| <pre>formData.top = new FormAttachment(button1,0,SWT.CENTER);</pre> | |
| <p>This will place the top of the widget in a position that will | |
| allow the widget to be centered on the other control, with an offset of | |
| 0. Setting only the top, or the bottom, or both as a center attachment | |
| will produce the same result. The top side of the widget is not | |
| centered, but the entire widget is centered, so this only needs to be | |
| specified once. Here is an example:</p> | |
| <pre>FormData formData1 = new FormData (50,50); | |
| button1.setLayoutData(formData1); | |
| FormData formData2 = new FormData (); | |
| formData2.left = new FormAttachment (button1,5); | |
| formData2.top = new FormAttachment (button1,0,SWT.CENTER); | |
| button2.setLayoutData(formData2);</pre> <img | |
| src="images/FormLayoutSampleAttachWidget03.png" /> | |
| <p>Using the different types of <code>FormAttachment</code> allows | |
| layouts to be defined in many different ways. <code>FormLayout</code> | |
| covers certain cases that cannot be solved using <code>FillLayout</code>, | |
| <code>RowLayout</code> or <code>GridLayout</code>, making it a very | |
| useful class for defining layouts.</p> | |
| <p><b>Important: </b>Do <b>not</b> define circular | |
| attachments. For example, do not attach the right edge of button1 | |
| to the left edge of button2 and then attach the left edge button2 to the | |
| right edge of button1. This will over-constrain the layout, causing | |
| undefined behavior. The algorithm will terminate, but the results | |
| are undefined. Therefore, make sure that you do not over-constrain your | |
| widgets. Only provide the attachments necessary to properly lay out the | |
| widgets.</p> | |
| <h3>A FormLayout Example</h3> | |
| <p>So far, all the examples using <code>FormLayout</code> have | |
| involved one or two <code>Button</code>s, to show how <code>FormAttachment</code>s | |
| work. Next, we will do a simple example using more <code>Button</code>s | |
| to show how a layout can be arranged using the attachments. We'll start | |
| by drawing a basic diagram outlining the attachments that we wish to | |
| create.</p> | |
| <p><img src="images/FormExampleHandDrawn.jpg" /></p> | |
| <pre>FormData data1 = new FormData(); | |
| data1.left = new FormAttachment(0,5); | |
| data1.right = new FormAttachment(25,0); | |
| button1.setLayoutData(data1); | |
| FormData data2 = new FormData(); | |
| data2.left = new FormAttachment(button1,5); | |
| data2.right = new FormAttachment(100,-5); | |
| button2.setLayoutData(data2); | |
| FormData data3 = new FormData(60,60); | |
| data3.top = new FormAttachment(button1,5); | |
| data3.left = new FormAttachment(50,-30); | |
| data3.right = new FormAttachment(50,30); | |
| button3.setLayoutData(data3); | |
| FormData data4 = new FormData(); | |
| data4.top = new FormAttachment(button3,5); | |
| data4.bottom = new FormAttachment(100,-5); | |
| data4.left = new FormAttachment(25,0); | |
| button4.setLayoutData(data4); | |
| FormData data5 = new FormData(); | |
| data5.bottom = new FormAttachment(100,-5); | |
| data5.left = new FormAttachment(button4,5); | |
| button5.setLayoutData(data5);</pre> | |
| <p>In this case, since no top attachment was defined for <code>button1</code> or | |
| <code>button2</code>, they are attached to the top of the layout. <code>button3</code> is centred | |
| in the layout using percentages and offsets on the left and right sides. | |
| <code>button4</code> and <code>button5</code> are attached to the bottom of the layout with a five | |
| pixel offset.</p> | |
| <img src="images/FormLayoutSample5Buttons01.png" /> | |
| <p>When we resize, the attachments become more visible. <code>button1</code> is | |
| attached on the left and the right side, so when the window is resized, | |
| it grows. Note that the right side will always be at 25% of the window. | |
| The same resize results apply for <code>button2</code>, as both sides are attached. | |
| The left side is attached to <code>button1</code>, so it will always be at 25% plus five | |
| pixels. <code>button3</code> stays in the center of the window, horizontally. <code>button4</code> | |
| is attached at the top and the bottom, so it grows vertically when the | |
| window is resized, but it is only attached on the left and not the | |
| right, so it does not grow horizontally. <code>button5</code> will not grow or | |
| shrink, but it will always stay five pixels away from <code>button4</code> on the left, | |
| and five pixels away from the bottom of the window.</p> | |
| <img src="images/FormLayoutSample5Buttons01Resized.png" /> | |
| <h3>A Complex FormLayout Example</h3> | |
| <p>To illustrate how <code>FormLayout</code> can be used for more | |
| complicated arrangements, the Dog Show Entry example done previously for | |
| <code>GridLayout</code> is redone using <code>FormLayout</code>. This | |
| code produces an identical layout, but uses different concepts to | |
| achieve it.</p> | |
| <pre>import org.eclipse.swt.SWT; | |
| import org.eclipse.swt.events.PaintEvent; | |
| import org.eclipse.swt.events.PaintListener; | |
| import org.eclipse.swt.events.SelectionAdapter; | |
| import org.eclipse.swt.events.SelectionEvent; | |
| import org.eclipse.swt.graphics.Image; | |
| import org.eclipse.swt.layout.FormAttachment; | |
| import org.eclipse.swt.layout.FormData; | |
| import org.eclipse.swt.layout.FormLayout; | |
| import org.eclipse.swt.widgets.Button; | |
| import org.eclipse.swt.widgets.Canvas; | |
| import org.eclipse.swt.widgets.Combo; | |
| import org.eclipse.swt.widgets.Display; | |
| import org.eclipse.swt.widgets.FileDialog; | |
| import org.eclipse.swt.widgets.Group; | |
| import org.eclipse.swt.widgets.Label; | |
| import org.eclipse.swt.widgets.List; | |
| import org.eclipse.swt.widgets.Shell; | |
| import org.eclipse.swt.widgets.Text; | |
| public class DogShowRegistrationWindowWithFormLayout { | |
| Image dogImage; | |
| Text dogNameText; | |
| Combo dogBreedCombo; | |
| Canvas dogPhoto; | |
| List categories; | |
| Text nameText; | |
| Text phoneText; | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| Shell shell = new DogShowRegistrationWindow().createShell(display); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) | |
| display.sleep(); | |
| } | |
| } | |
| public Shell createShell(final Display display) { | |
| final Shell shell = new Shell(display); | |
| FormLayout layout = new FormLayout(); | |
| layout.marginWidth = 5; | |
| layout.marginHeight = 5; | |
| shell.setLayout(layout); | |
| shell.setText("Dog Show Entry"); | |
| Group ownerInfo = new Group(shell, SWT.NONE); | |
| ownerInfo.setText("Owner Info"); | |
| FormLayout ownerLayout = new FormLayout(); | |
| ownerLayout.marginWidth = 5; | |
| ownerLayout.marginHeight = 5; | |
| ownerInfo.setLayout(ownerLayout); | |
| Label dogName = new Label(shell, SWT.NONE); | |
| dogName.setText("Dog's Name:"); | |
| dogNameText = new Text(shell, SWT.SINGLE | SWT.BORDER); | |
| Label dogBreed = new Label(shell, SWT.NONE); | |
| dogBreed.setText("Breed:"); | |
| dogBreedCombo = new Combo(shell, SWT.NONE); | |
| dogBreedCombo.setItems(new String[] { "Collie", "Pitbull", "Poodle", | |
| "Scottie", "Black Lab" }); | |
| Label photo = new Label(shell, SWT.NONE); | |
| photo.setText("Photo:"); | |
| dogPhoto = new Canvas(shell, SWT.BORDER); | |
| Button browse = new Button(shell, SWT.PUSH); | |
| browse.setText("Browse..."); | |
| Button delete = new Button(shell, SWT.PUSH); | |
| delete.setText("Delete"); | |
| Label cats = new Label(shell, SWT.NONE); | |
| cats.setText("Categories"); | |
| categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | |
| | SWT.H_SCROLL); | |
| categories.setItems(new String[] { "Best of Breed", "Prettiest Female", | |
| "Handsomest Male", "Best Dressed", "Fluffiest Ears", | |
| "Most Colors", "Best Performer", "Loudest Bark", | |
| "Best Behaved", "Prettiest Eyes", "Most Hair", "Longest Tail", | |
| "Cutest Trick" }); | |
| Button enter = new Button(shell, SWT.PUSH); | |
| enter.setText("Enter"); | |
| FormData data = new FormData(); | |
| data.top = new FormAttachment(dogNameText, 0, SWT.CENTER); | |
| dogName.setLayoutData(data); | |
| data = new FormData(); | |
| data.left = new FormAttachment(dogName, 5); | |
| data.right = new FormAttachment(100, 0); | |
| dogNameText.setLayoutData(data); | |
| data = new FormData(); | |
| data.top = new FormAttachment(dogBreedCombo, 0, SWT.CENTER); | |
| dogBreed.setLayoutData(data); | |
| data = new FormData(); | |
| data.top = new FormAttachment(dogNameText, 5); | |
| data.left = new FormAttachment(dogNameText, 0, SWT.LEFT); | |
| data.right = new FormAttachment(categories, -5); | |
| dogBreedCombo.setLayoutData(data); | |
| data = new FormData(80, 80); | |
| data.top = new FormAttachment(dogBreedCombo, 5); | |
| data.left = new FormAttachment(dogNameText, 0, SWT.LEFT); | |
| data.right = new FormAttachment(categories, -5); | |
| data.bottom = new FormAttachment(ownerInfo, -5); | |
| dogPhoto.setLayoutData(data); | |
| dogPhoto.addPaintListener(new PaintListener() { | |
| public void paintControl(final PaintEvent event) { | |
| if (dogImage != null) { | |
| event.gc.drawImage(dogImage, 0, 0); | |
| } | |
| } | |
| }); | |
| data = new FormData(); | |
| data.top = new FormAttachment(dogPhoto, 0, SWT.TOP); | |
| photo.setLayoutData(data); | |
| data = new FormData(); | |
| data.top = new FormAttachment(photo, 5); | |
| data.right = new FormAttachment(dogPhoto, -5); | |
| browse.setLayoutData(data); | |
| browse.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| String fileName = new FileDialog(shell).open(); | |
| if (fileName != null) { | |
| dogImage = new Image(display, fileName); | |
| } | |
| } | |
| }); | |
| data = new FormData(); | |
| data.left = new FormAttachment(browse, 0, SWT.LEFT); | |
| data.top = new FormAttachment(browse, 5); | |
| data.right = new FormAttachment(dogPhoto, -5); | |
| delete.setLayoutData(data); | |
| delete.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| if (dogImage != null) { | |
| dogImage.dispose(); | |
| dogImage = null; | |
| dogPhoto.redraw(); | |
| } | |
| } | |
| }); | |
| data = new FormData(90, 140); | |
| data.top = new FormAttachment(dogPhoto, 0, SWT.TOP); | |
| data.right = new FormAttachment(100, 0); | |
| data.bottom = new FormAttachment(enter, -5); | |
| categories.setLayoutData(data); | |
| data = new FormData(); | |
| data.bottom = new FormAttachment(categories, -5); | |
| data.left = new FormAttachment(categories, 0, SWT.CENTER); | |
| cats.setLayoutData(data); | |
| data = new FormData(); | |
| data.right = new FormAttachment(100, 0); | |
| data.bottom = new FormAttachment(100, 0); | |
| enter.setLayoutData(data); | |
| enter.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent event) { | |
| System.out.println("\nDog Name: " + dogNameText.getText()); | |
| System.out.println("Dog Breed: " + dogBreedCombo.getText()); | |
| System.out.println("Owner Name: " + nameText.getText()); | |
| System.out.println("Owner Phone: " + phoneText.getText()); | |
| System.out.println("Categories:"); | |
| String cats[] = categories.getSelection(); | |
| for (int i = 0; i < cats.length; i++) { | |
| System.out.println("\t" + cats[i]); | |
| } | |
| } | |
| }); | |
| data = new FormData(); | |
| data.bottom = new FormAttachment(enter, -5); | |
| data.left = new FormAttachment(0, 0); | |
| data.right = new FormAttachment(categories, -5); | |
| ownerInfo.setLayoutData(data); | |
| Label name = new Label(ownerInfo, SWT.NULL); | |
| name.setText("Name:"); | |
| Label phone = new Label(ownerInfo, SWT.PUSH); | |
| phone.setText("Phone:"); | |
| nameText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); | |
| phoneText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER); | |
| data = new FormData(); | |
| data.top = new FormAttachment(nameText, 0, SWT.CENTER); | |
| name.setLayoutData(data); | |
| data = new FormData(); | |
| data.top = new FormAttachment(phoneText, 0, SWT.CENTER); | |
| phone.setLayoutData(data); | |
| data = new FormData(); | |
| data.left = new FormAttachment(phone, 5); | |
| data.right = new FormAttachment(100, 0); | |
| nameText.setLayoutData(data); | |
| data = new FormData(); | |
| data.left = new FormAttachment(nameText, 0, SWT.LEFT); | |
| data.right = new FormAttachment(100, 0); | |
| data.top = new FormAttachment(55, 0); | |
| phoneText.setLayoutData(data); | |
| shell.pack(); | |
| return shell; | |
| } | |
| }</pre> | |
| <p>This is what the layout looks like after Mary Smith enters | |
| Bifford in the dog show:</p> | |
| <img src="images/DogShowBiffWithFormLayout.png" /> | |
| <p>When the window is resized, the same controls are resized as in | |
| the <code>GridLayout</code> example.</p> | |
| <img src="images/DogShowBiffWithFormLayoutResized.png" /> | |
| <h2>Writing Your Own Layout Class</h2> | |
| <p>Occasionally, you may want to write your own <code>Layout</code> | |
| class. Perhaps your layout needs are very complex. Maybe you have the | |
| same look in many places, and you want to take advantage of code reuse. | |
| Or you want to leverage domain knowledge to create a very efficient | |
| layout class. Whatever the reason, there are things to consider before | |
| writing a new class:</p> | |
| <ul> | |
| <li>Can the layout be done using a <code>GridLayout</code> or <code>FormLayout</code>, | |
| with maybe a few nested layouts?</li> | |
| <li>Can the desired effect be more easily achieved with a resize | |
| listener?</li> | |
| <li>Are you defining a general layout algorithm or just | |
| positioning widgets?</li> | |
| </ul> | |
| <p>Unless you are writing a very generic <code>Layout</code> type | |
| that will be used by several <code>Composite</code> widgets, it is often | |
| better and easier to simply calculate sizes and position children in a | |
| resize listener. Many of the SWT custom widgets were written this way. | |
| Although a new widget can be implemented as a <code>Composite</code>/<code>Layout</code> | |
| pair, implementing it as a <code>Composite</code> that does its layout | |
| in a resize listener and computes its preferred size in <code>computeSize</code> | |
| is clearer, and does not involve writing an extra class.</p> | |
| <p>First, we will look at how layouts work, and then we will create | |
| a new <code>Layout</code> class. Another example of writing your own <code>Layout</code> | |
| can be found in the <i>Compound Widget Example</i> section of <a | |
| href="http://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm">Creating | |
| Your Own Widgets Using SWT</a>, which shows how to achieve the same look | |
| using either a resize listener or a new <code>Layout</code> class.</p> | |
| <h3>How Layouts Work</h3> | |
| <p><code>Layout</code> is the abstract superclass of all layouts. It | |
| only has two methods: <code>computeSize</code> and <code>layout</code>. | |
| The class is defined as follows:</p> | |
| <pre>public abstract class Layout { | |
| protected abstract Point computeSize(Composite composite, int widthHint, int heightHint, boolean flushCache); | |
| protected abstract void layout(Composite composite, boolean flushCache); | |
| }</pre> | |
| <p>The <code>computeSize</code> method calculates the width and | |
| height of a rectangle that encloses all of the <code>Composite</code>'s | |
| children once they have been sized and placed according to the layout | |
| algorithm encoded in the <code>Layout</code> class. The hint parameters | |
| allow the width and/or height to be constrained. For example, a layout | |
| may choose to grow in one dimension if constrained in another. A hint of | |
| SWT.DEFAULT means to use the preferred size.</p> | |
| <p>The <code>layout</code> method positions and sizes the <code>Composite</code>'s | |
| children. A <code>Layout</code> can choose to cache layout-related | |
| information, such as the preferred extent of each of the children. The <code>flushCache</code> | |
| parameter tells the <code>Layout</code> to flush cached data.</p> | |
| <p>Since a <code>Layout</code> controls the size and placement of | |
| widgets in a <code>Composite</code>, there are several methods in <code>Composite</code> | |
| that are used with <code>Layout</code>s.</p> | |
| <p>The first two methods allow setting and getting a <code>Layout</code> | |
| object in a <code>Composite</code>.</p> | |
| <pre>public void setLayout(Layout layout); | |
| public Layout getLayout();</pre> | |
| <p>An application can force a <code>Layout</code> to recalculate the | |
| sizes of and reposition children by sending <code>layout()</code> to the | |
| parent <code>Composite</code>.</p> | |
| <pre>public void layout(boolean changed); | |
| public void layout(); | |
| // calls layout(true);</pre> | |
| <p>You would do this after changing anything about the children that | |
| might affect their size or position, such as changing the font of a | |
| child, changing the text or image of a child, adding a new child, or | |
| adding children to a child (If the child can accommodate the change, | |
| then layout may not be necessary; for example, changing the font or | |
| text of a scrollable multi-line <code>Text</code>). Since these changes | |
| are done programmatically, they do not cause events to happen. | |
| Consequently, the parent doesn't know about the changes, and has to be | |
| told through the <code>layout</code> method. This strategy reduces flash | |
| because the application can make several changes and then tell the | |
| parent to layout, and the children are only redrawn once instead of once | |
| per change. If <code>layout()</code> is not called and changes are made | |
| after the shell is opened, then the children may not be correctly laid | |
| out until the shell is somehow resized. Note that <code>shell.open()</code> | |
| causes a layout to occur.</p> | |
| <p>The <code>computeSize</code> methods of a <code>Composite</code> | |
| calculate the Composite's preferred size, which is the size of its | |
| client area as determined by the <code>Layout</code>, plus its trim.</p> | |
| <pre>public Point computeSize(int widthHint, int heightHint, boolean changed); | |
| public Point computeSize(int widthHint, int heightHint); | |
| // calls computeSize(widthHint, heightHint, true);</pre> | |
| <p>The <b>clientArea</b> of a <code>Composite</code> is the | |
| rectangle that will contain all of the children. A <code>Layout</code> | |
| positions the children inside the client area.</p> | |
| <pre>public Rectangle getClientArea ();</pre> | |
| <p>The <b>trim</b> of a <code>Composite</code> is the area outside | |
| the client area. For some composites, the size of the trim is zero. The | |
| trim can be computed by passing the dimensions of the client area into | |
| the method <code>computeTrim</code>.</p> | |
| <pre>public Rectangle computeTrim (int x, int y, int width, int height);</pre> | |
| <p>Sending <code>pack</code> to a <code>Composite</code> resizes it | |
| to its preferred size.</p> | |
| <pre>public void pack(boolean changed); | |
| // calls setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT, changed)); | |
| public void pack(); | |
| // calls pack(true);</pre> | |
| <p>The boolean parameter to the <code>layout</code>, <code>computeSize</code>, | |
| and <code>pack</code> methods is the <code>changed</code> flag. If <code>true</code>, | |
| it indicates that the <code>Composite</code>'s contents have changed in | |
| some way that affects its preferred size, therefore any caches that the | |
| <code>Layout</code> may have been keeping need to be flushed. When a <code>Composite</code> | |
| is resized, it asks its <code>Layout</code> to lay out its children by | |
| calling layout(false); therefore widget content caches are <i>not</i> | |
| flushed. This lets the <code>Layout</code> perform any expensive | |
| calculations only when necessary.</p> | |
| <p>Caching can increase performance, but it can also be tricky. You | |
| can choose not to cache at all: in fact, it is best not to try caching | |
| until your code is stable. When considering what to cache, be certain | |
| not to store any widget state, such as the text of a label, or the | |
| number of items in a list.</p> | |
| <h3>Custom Layout Example</h3> | |
| <p>If you have several vertically oriented <code>Composite</code> | |
| widgets in your application, you might choose to write <code>ColumnLayout</code>. | |
| We will show a simple version of a <code>Layout</code> class that lays | |
| out <code>Composite</code> children into a single column. The class has | |
| fixed margins and spacing. Children are given the same width, but they | |
| take their natural height. (Note that <code>RowLayout</code> will have <code>ColumnLayout</code> | |
| behaviour if its type is set to <code>SWT.VERTICAL</code>. This example is, | |
| therefore, just an example. In practice, if you need to lay widgets out | |
| in a column, you would use <code>RowLayout.</code>)</p> | |
| <p>The code for the <code>ColumnLayout</code> class is below. Note | |
| that we cache the width of the widest child, and the sum of the child | |
| heights (plus spacing), and these values are used to compute the size | |
| and lie out the children. They are recalculated if <code>flushCache</code> | |
| is <code>true</code>.</p> | |
| <pre>import org.eclipse.swt.*; | |
| import org.eclipse.swt.graphics.*; | |
| import org.eclipse.swt.widgets.*; | |
| import org.eclipse.swt.layout.*; | |
| public class ColumnLayout extends Layout { | |
| // fixed margin and spacing | |
| public static final int MARGIN = 4; | |
| public static final int SPACING = 2; | |
| // cache | |
| Point[] sizes; | |
| int maxWidth, totalHeight; | |
| protected Point computeSize(Composite composite, int wHint, int hHint, | |
| boolean flushCache) { | |
| Control children[] = composite.getChildren(); | |
| if (flushCache || sizes == null || sizes.length != children.length) { | |
| initialize(children); | |
| } | |
| int width = wHint, height = hHint; | |
| if (wHint == SWT.DEFAULT) | |
| width = maxWidth; | |
| if (hHint == SWT.DEFAULT) | |
| height = totalHeight; | |
| return new Point(width + 2 * MARGIN, height + 2 * MARGIN); | |
| } | |
| protected void layout(Composite composite, boolean flushCache) { | |
| Control children[] = composite.getChildren(); | |
| if (flushCache || sizes == null || sizes.length != children.length) { | |
| initialize(children); | |
| } | |
| Rectangle rect = composite.getClientArea(); | |
| int x = MARGIN, y = MARGIN; | |
| int width = Math.max(rect.width - 2 * MARGIN, maxWidth); | |
| for (int i = 0; i < children.length; i++) { | |
| int height = sizes[i].y; | |
| children[i].setBounds(x, y, width, height); | |
| y += height + SPACING; | |
| } | |
| } | |
| void initialize(Control children[]) { | |
| maxWidth = 0; | |
| totalHeight = 0; | |
| sizes = new Point[children.length]; | |
| for (int i = 0; i < children.length; i++) { | |
| sizes[i] = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, true); | |
| maxWidth = Math.max(maxWidth, sizes[i].x); | |
| totalHeight += sizes[i].y; | |
| } | |
| totalHeight += (children.length - 1) * SPACING; | |
| } | |
| }</pre> | |
| <p>Here is some simple test code to test the <code>ColumnLayout</code>. | |
| The <b>grow</b> and <b>shrink</b> <code>Buttons</code> show a call to | |
| the <code>Shell</code>'s <code>layout()</code> method to force a | |
| re-layout after changing the width of one of the children. Calling <code>layout()</code> | |
| is the same as calling <code>layout(true</code>) which tells the <code>ColumnLayout</code> | |
| to flush its caches before setting the bounds of the children. The <code>Shell</code> | |
| is also told to <code>pack()</code> after laying out the children. This | |
| forces the <code>Shell</code> to take the new size.</p> | |
| <pre>import org.eclipse.swt.*; | |
| import org.eclipse.swt.widgets.*; | |
| import org.eclipse.swt.layout.*; | |
| import org.eclipse.swt.events.*; | |
| public class ColumnLayoutTest { | |
| static Shell shell; | |
| static Button button3; | |
| public static void main(String[] args) { | |
| Display display = new Display(); | |
| shell = new Shell(display); | |
| shell.setLayout(new ColumnLayout()); | |
| new Button(shell, SWT.PUSH).setText("B1"); | |
| new Button(shell, SWT.PUSH).setText("Very Wide Button 2"); | |
| (button3 = new Button(shell, SWT.PUSH)).setText("Button 3"); | |
| Button grow = new Button(shell, SWT.PUSH); | |
| grow.setText("Grow Button 3"); | |
| grow.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent e) { | |
| button3.setText("Extreemely Wide Button 3"); | |
| shell.layout(); | |
| shell.pack(); | |
| } | |
| }); | |
| Button shrink = new Button(shell, SWT.PUSH); | |
| shrink.setText("Shrink Button 3"); | |
| shrink.addSelectionListener(new SelectionAdapter() { | |
| public void widgetSelected(SelectionEvent e) { | |
| button3.setText("Button 3"); | |
| shell.layout(); | |
| shell.pack(); | |
| } | |
| }); | |
| shell.pack(); | |
| shell.open(); | |
| while (!shell.isDisposed()) { | |
| if (!display.readAndDispatch()) | |
| display.sleep(); | |
| } | |
| } | |
| }</pre> | |
| <p>If we run the test code, the window on the left appears. Pressing | |
| the Grow Button 3 button results in the window on the right. Resizing | |
| the window with the mouse will also make the buttons wider (or narrower) | |
| but they do not grow taller.</p> | |
| <img src="images/CustomLayout.png" /> <img | |
| src="images/CustomLayoutResized.png" /> | |
| <h3>Overriding Composite</h3> | |
| <p>If you are writing your own widget, as outlined in <a | |
| href="http://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm">Creating | |
| Your Own Widgets Using SWT</a>, and you subclass Composite, then here are a | |
| few points to consider for your implementation:</p> | |
| <ul> | |
| <li>If you are providing trimmings in your new <code>Composite</code>, | |
| make sure to override both <code>computeTrim()</code> and <code>getClientArea()</code>.</li> | |
| <li>Never override <code>layout()</code>, but you may override <code>layout(boolean)</code>.</li> | |
| </ul> | |
| <p>Sometimes you want your new <code>Composite</code> to have a | |
| specific look, and you don't want the application to be able to specify | |
| a layout. Your new <code>Composite</code> would either do its layout in | |
| a resize handler or using a private custom layout. In either case, you | |
| will probably want to do the following:</p> | |
| <ul> | |
| <li>Override <code>setLayout()</code> to do nothing.</li> | |
| <li>Override <code>layout(boolean)</code> to call your layout | |
| code.</li> | |
| <li>Override <code>computeSize()</code> to correctly compute the | |
| size of your <code>Composite</code>.</li> | |
| </ul> | |
| <h2>Summary</h2> | |
| <p>SWT provides several different ways to lay out widgets. The | |
| simplest method, and the one you will typically use, is to use one of | |
| the standard <code>Layout</code> classes: <code>FillLayout</code>, <code>RowLayout</code>, | |
| <code>GridLayout</code> or <code>FormLayout</code>.</p> | |
| <p>In certain cases you may want to write your own <code>Layout</code> | |
| class to provide a very specific look or to reuse very similar layout | |
| code, but often a resize listener on the parent widget will suffice.</p> | |
| <p>For further assistance in understanding the standard SWT <code>Layout</code> | |
| classes, see the <a href="http://www.eclipse.org/swt/snippets">SWT Snippets</a>.</p> | |
| </div> | |
| </body> | |
| </html> |