Understanding Layouts in SWT

http://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm

Summary
When writing applications in SWT, you may need to use layouts to give your windows a specific look. A layout controls theposition and size of children in a Composite.Layout classes are subclasses of the abstract class Layout. This article shows you how to work with standard layouts,and write your own custom layout class.

 

By Carolyn MacLeod, OTI

March 22,2001

 



Layouts

Overview

When writing applications in SWT, you may need to use layoutsto give your windows a specific look. A layout controls the position and sizeof children in a Composite. Layoutclasses are subclasses of the abstract class Layout. SWT provides several standard layout classes, and you canwrite custom layout classes.

 

In SWT, positioning and sizing does nothappen automatically. Applications can decide to size and place a Composite’s children initially or in aresize listener – or they can specify a layout class to position and size thechildren. If children are not given a size, they will have zero size and theycannot be seen.

 

The diagram below illustrates a few generalterms that are used when discussing layouts. The Composite (in thiscase, a TabFolder) has a location, clientArea and trim.The size of the Composite is the size of the clientArea plus thesize of the trim. The Composite has two children that are laidout side by side. A Layout is managing the size and position of thechildren. This Layout allows spacing between the children, and a marginbetween the children and the edges of the Layout. The size of the Layoutis the same as the size of the Composite’s clientArea.

 


Thepreferred size of a widget is the minimum size needed to show itscontent. In the case of a Composite, the preferred size is the smallestrectangle that contains all of its children. If children have been positionedby the application, the Compositecomputes its own preferred size based on the size and position of the children.If a Composite is using a layoutclass to position its children, it asks the Layoutto compute the size of its clientArea, and then it adds in the trimto determine its preferred size.


Standard Layouts

The standard layoutclasses in the SWT library are:

·       FillLayout –lays out equal-sized widgets in a single row or column

·       RowLayout –lays out widgets in a row or rows, with fill, wrap, and spacing options

·       GridLayout –lays out widgets in a grid

 

To use the standardlayouts, you need to import the SWT layout package:

 

    import org.eclipse.swt.layout.*;

 

Layouts arepluggable. To set a layout into a Compositewidget, you use the widget’s setLayout(Layout)method. In the following code, a Shell (asubclass of Composite) is told toposition its children using a RowLayout:

 

    Shellshell = new Shell();

    shell.setLayout(new RowLayout());

 

A layout class mayhave a corresponding layout data class – a subclass of Object that contains layout data for a specific child. Byconvention, layout data classes are identified by substituting Data for Layout in the class name. For example, the standard layout class RowLayout has a layout data class calledRowData, and the layout class GridLayout uses a layout data classcalled GridData. Layout data classesare set into a widget as follows:

 

    Buttonbutton = new Button(shell, SWT.PUSH);

    button.setLayoutData(new RowData(50, 40));

 


Examples in thisDocument

Most of the snapshots in thisdocument were taken by running variations on the following example code. We maychange the type of layout, the options used, or the type or number of children.

 

importorg.eclipse.swt.*;

importorg.eclipse.swt.widgets.*;

importorg.eclipse.swt.layout.*;

 

publicclass LayoutExample {

 

    public static void main(String[] args) {

       Displaydisplay = new Display();

       Shellshell = new Shell(display);

       //Create the layout.

       RowLayout layout = new RowLayout();

       //Optionally set layout fields.

       layout.wrap = true;

       // Setthe layout into the composite.

       shell.setLayout(layout);

       //Create the children of the composite.

       newButton(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();

       }

    }

}

 

Running the above code results inthe following:

 

 

If the user resizes the shell sothat there is no longer room for Button 3 on the right, the RowLayout wraps Button 3 to the next row, as follows:

 

 

Using layouts is closely tied withresize, as we shall see. Consequently, most of the examples in this documentshow what would happen if the Compositebecomes smaller or larger, in order to illustrate how the Layout works.


FillLayout

FillLayout is the simplest layout class.It lays out widgets in a single row or column, forcing them to be the samesize. Initially, the widgets will all be as tall as the tallest widget, and aswide as the widest. FillLayout doesnot wrap, and you cannot specify margins or spacing. You might use it to layout buttons in a task bar or tool bar, or to stack checkboxes in a Group. FillLayout can also be used when a Composite only has one child. For example, if a Shell has a single Group child, FillLayoutwill cause the Group to completelyfill the Shell.

 

Here is therelevant portion of the example code. First we create a FillLayout, then (if we want vertical) we set its type field, and then we set it into theComposite (a Shell). The Shell hasthree pushbutton children, B1, B2, and Button 3. Note that in a FillLayout, children are always the same size, and they fill allavailable space.

 

       FillLayoutfillLayout = new FillLayout();

       fillLayout.type= SWT.VERTICAL;

       shell.setLayout(fillLayout);

 

The following tableshows the differences between a horizontal and vertical FillLayout, initially and after the parent has grown.

 

 

Initial
After resize

 

fillLayout.type = SWT.HORIZONTAL

(default)

 

 

 

 

 

fillLayout.type = SWT.VERTICAL

 

 

 

 


RowLayout

RowLayout is more commonly used than FillLayout because of its ability towrap, and because it provides configurable margins and spacing. RowLayout has a number of configurationfields. In addition, the height and width of each widget in a RowLayout can be specified by setting a RowData object into the widget using setLayoutData.

RowLayout ConfigurationFields

Wrap

The wrap field controls whether or not the RowLayout will wrap widgets into the next row if there isn’t enoughspace in the current row. RowLayoutswrap by default.

Pack

If the pack field is true, widgets in a RowLayout will take their natural size, and they will be aligned asfar to the left as possible. If packing is false, widgets will fill theavailable space, similar to the widgets in a FillLayout. RowLayoutspack by default.

Justify

If the justify field is true, widgets in a RowLayout are spread across the available space from left to right.If the parent Composite grows wider, theextra space is distributed evenly among the widgets. If both pack and justify are true, widgets take their natural size, and the extraspace is placed between the widgets in order to keep them fully justified. Bydefault, RowLayouts do not justify.

MarginLeft, MarginTop,MarginRight, MarginBottom and Spacing

Thesefields control the number of pixels between widgets (spacing) and the number of pixels between a widget and the side ofthe parent Composite (margin). By default, RowLayouts leave 3 pixels for margin andspacing. The margin and spacing fields are shown in the following diagram.

 


RowLayout Examples

The followingexample code creates a RowLayout,sets all of its fields to non-default values, and then sets it into a Shell.

 

    RowLayoutrowLayout = new RowLayout();

    rowLayout.wrap= false;

    rowLayout.pack= false;

    rowLayout.justify= true;

    rowLayout.marginLeft= 5;

    rowLayout.marginTop= 5;

    rowLayout.marginRight= 5;

    rowLayout.marginBottom= 5;

    rowLayout.spacing= 0;

    shell.setLayout(rowLayout);

 

If you are usingthe default field values, you only need one line of code:

 

    shell.setLayout(new RowLayout());

 

In the table below,the result of setting specific fields is shown.

 

 

Initial
After resize

 

wrap = true

pack = true

justify = false

 

(defaults)

 

 

 

 

 

wrap = false

 

(clips if not enough space)

 

 

 

 

 

pack = false

 

(all widgets are the same size)

 

 

 

 

 

justify = true

 

(widgets are spread across the available space)

 

 

 

 

 


Using RowData Objectswith RowLayout

Each widget controlled by a RowLayout can have its initial width andheight specified by setting a RowDataobject into the widget. The following code uses RowData objects to change the initial size of the Buttons in a Shell.

 

importorg.eclipse.swt.*;

importorg.eclipse.swt.widgets.*;

importorg.eclipse.swt.layout.*;

 

publicclass RowDataExample {

 

    public static void main(String[] args) {

       Displaydisplay = new Display();

       Shellshell = new Shell(display);

       shell.setLayout(new RowLayout());

       Buttonbutton1 = new Button(shell, SWT.PUSH);

       button1.setText("Button 1");

       button1.setLayoutData(new RowData(50, 40));

       Buttonbutton2 = new Button(shell, SWT.PUSH);

       button2.setText("Button 2");

       button2.setLayoutData(new RowData(50, 30));

       Buttonbutton3 = 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();

       }

    }

}

 

Here is what you see when you runthe above code.

 


GridLayout

GridLayout is the most useful andpowerful of the standard layouts, but it is also the most complicated. With a GridLayout, the widget children of a Composite are laid out in a grid. GridLayout has a number of configurationfields, and, like RowLayout, thewidgets it lays out can have an associated layout data object, called GridData. The power of GridLayout lies in the ability toconfigure GridData for each widgetcontrolled by the GridLayout.

GridLayout ConfigurationFields

NumColumns

The numColumns field is the most important field in a GridLayout, and it is usually the firstfield an application will set. Widgets are laid out in columns from left toright, and a new row is created when numColumns+ 1 widgets are added to the Composite.The default is to have only 1 column. The following code creates a Shell with five Button children of various widths, managed by a GridLayout. The table below shows thegrid when numColumns is set to 1, 2,or 3.

 

       Displaydisplay = new Display();

       Shellshell = new Shell(display);

       GridLayoutgridLayout = 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();

       }

 

numColumns = 1

numColumns = 2

numColumns = 3

 

 

 

 

 

 

 


MakeColumnsEqualWidth

The makeColumnsEqualWidth field forces the columns to be the same width.The default is false. If we change the example above to have 3 columns of equalwidth, this is what we would get (note that in the absence of furtherinstruction, widgets are left-justified in their columns):

 
 

 
MarginWidth,MarginHeight, HorizontalSpacing, and VerticalSpacing

The margin and spacingfields in a GridLayout are similar tothose in a RowLayout. The differenceis that the left and right margins are grouped into marginWidth, and the top and bottom margins are grouped into marginHeight. Also, in a GridLayout you can specify verticalSpacing, whereas in a RowLayout, only horizontalSpacing applied.

GridData Object Fields

GridData is the layout data objectassociated with GridLayout. To set a GridData object into a widget, you usethe setLayoutData method. Forexample, to set the GridData for a Button, we could do the following:

 

       Buttonbutton1 = new Button(shell, SWT.PUSH);

       button1.setText("B1");

       button1.setLayoutData(new GridData());

 

Of course, thiscode just creates a GridData objectwith all of its fields set to their default values, which is the same as notsetting the layout data at all. There are two ways to create a GridData object with certain fields set.The first is to set the fields directly, like this:

 

       GridDatagridData = new GridData();

       gridData.horizontalAlignment= GridData.FILL;

       gridData.grabExcessHorizontalSpace= true;

       button1.setLayoutData(gridData);

 

The second is totake advantage of convenience API – style bits defined by GridData:

 

       button1.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL |GridData.GRAB_HORIZONTAL));

 

In fact, certaincommon style bit combinations are provided for further convenience:

 

       button1.setLayoutData(newGridData(GridData.FILL_HORIZONTAL));

 

Note that FILL_ conveniencestyles set both fill alignment andgrab. GridData style bits can only beused for boolean and enumeration fields. Numeric fields must be set directly.

 

One final note about GridDataobjects before we get into their fields: do not reuse GridData objects. Everywidget in a Composite that is managed by a GridLayout must have aunique GridData object. If the layout data for a widget in a GridLayoutis null at layout time, a unique GridData object is created for it.

HorizontalAlignment andVerticalAlignment

The alignment fields specify where to place a widget horizontallyand/or vertically within its grid cell. Each alignment field can have any ofthe following values:

·       BEGINNING

·       CENTER

·       END

·       FILL

The default horizontalAlignmentis BEGINNING (or left-aligned). The default verticalAlignment is CENTER.

 

Let’s go back to our five-buttonexample with three columns, and we will vary the horizontalAlignment of Button 5.

 

 

horizontalAlignment = GridData.BEGINNING

 

 

 

horizontalAlignment = GridData.CENTER

 

 

 

horizontalAlignment = GridData.END

 

 

 

horizontalAlignment = GridData.FILL

 

 

 

HorizontalIndent

The horizontalIndent field allows you to move a widget to the right by aspecified number of pixels. This field is typically only useful when the horizontalAlignment is BEGINNING. Wecannot use a style bit to set the indent, so we will indent Button 5 inour example by 4 pixels as follows:

 


 


GridData gridData = new GridData();

gridData.horizontalIndent = 4;

button5.setLayoutData(gridData);

 

HorizontalSpan andVerticalSpan

The span fields let widgets occupy more than one grid cell. They areoften used in conjunction with FILL alignment. We can make Button 5 inour example span the last two cells as follows:

 


 


GridData gridData = new GridData();

gridData.horizontalAlignment =GridData.FILL;

gridData.horizontalSpan = 2;

button5.setLayoutData(gridData);

 

 

 

If we decide to make Wide Button 2span two cells instead, we would end up with this:

 

 
 

 

GridData gridData = new GridData();

gridData.horizontalAlignment =GridData.FILL;

gridData.horizontalSpan = 2;

button2.setLayoutData(gridData);

 

 

 

Or we could make Button 3span two cells vertically:

 


 


GridData gridData = new GridData();

gridData.verticalAlignment =GridData.FILL;

gridData.verticalSpan = 2;

button3.setLayoutData(gridData);

 

 

GrabExcessHorizontalSpaceand GrabExcessVerticalSpace

The grabExcessHorizontalSpace and grabExcessVerticalSpacefields are typically used for larger widgets such as Texts or Lists to allowthem to grow if their containing Compositegrows. If a Text is grabbing excesshorizontal space and the user resizes the Shellwider, then the Text will get all of thenew horizontal space and other widgets in the same row will stay their originalwidth. Of course, the widget that is grabbing excess space is also the firstone to shrink when the Shell getssmaller. It is easiest to always think of the grabExcessSpace fields in the context of resizing. For a simpleexample, let’s reuse the previous example where Button 3 spanned two cells vertically. Here it isagain:

 

 

If we resize this window, theonly thing that happens is the window gets bigger:

 

 

Now we will tell Button 3 tograb excess horizontal and vertical space, and B1 and B4 to fill vertically (without grabbing), and weresize the window again:

 

 

This time, Button 3 grewin both directions, and B4 grew vertically. The other buttons stayed their original sizes.Because Button3 was grabbing vertically and it spans two rows, the last row that it spans grew taller. Notethat B1 did notgrow – although it is filling vertically – because its row did not grow. Since Button 3was grabbing horizontally, its column grew wider, and since it was fillinghorizontally, it grew wider to fill the column. Here is the code for all fivebuttons:

 

       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");

 

In a typical application window,you often want to have at least one widget that is grabbing. If more than onewidget is trying to grab the same space, then the excess space is shared evenlyamong the grabbing widgets:

 

 

 

One final point to note aboutgrabbing. If a widget is grabbing excess horizontal space and its parent Composite grows wider, then the entire column containing that widget growswider. If a widget is grabbing excess vertical space and its parent Composite grows taller, then the entire row containing that widget grows taller.The implication of this is that if any other widget in the affected column orrow has fill alignment, then it willstretch also. Widgets that have beginning, center, or end alignment will notstretch – they will stay at the beginning, center or end of the wider column ortaller row.

WidthHint and HeightHint

The widthHint and heightHintfields indicate the number of pixels wide or tall that you would like a widgetto be, if it does not conflict with other requirements in the GridLayout’s constraint system. Lookingback at the five-button, three-column example, say we want Button 5to be 70 pixels wide and 40 pixels tall. We code it as follows:

 

       GridDatagridData = new GridData();

       gridData.widthHint= 70;

       gridData.heightHint= 40;

       button5.setLayoutData(gridData);

 

The natural size of Button 5is shown in the window on the left, below, and the 70-pixel wide, 40-pixel tallButton 5is on the right.

 

                     

 

Note, however, thatif the horizontalAlignment of Button 5was FILL, then the GridLayout wouldnot have been able to honor the request for a width of 70 pixels.

 

One final commentabout using width and height hints. Something that looks good on one platformmay not look good on another. The variation between font sizes and natural widgetsizes across platforms means that hard-coding pixel values is not usually thebest way to lay out windows. So, keep the use of size hints to a minimum, ifyou use them at all.


A Complex GridLayoutExample

So far, the GridLayout examples have been fairly simple, in order to show howeach field works. Now, we will put them all together to create a morecomplicated example. We start by hand-drawing a rough sketch of the window wewant to create, to determine things like how many columns the grid should contain,and whether or not any widgets need to span.

 

  

 

Then we start coding the examplefrom the diagram. The code is below. Note that we have added a bit of logic tomake the code more interesting, for example, Browse… opens a FileDialog to read an Imagefile which the Canvas displays in apaint listener, Delete deletes the Image,and Enterprints the current dog and owner info. The example has been coded in a single main method to keep it as simple aspossible.

 

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layout.*;

import org.eclipse.swt.events.*;

import org.eclipse.swt.graphics.*;

 

publicclass ComplexGridLayoutExample {

    static Display display;

    static Shell shell;

    static Text dogName;

    static Combo dogBreed;

    static Canvas dogPhoto;

    static Image dogImage;

    static List categories;

    static Text ownerName;

    static Text ownerPhone;

 

    publicstaticvoid main(String[] args) {

       display = new Display();

       shell = new Shell(display);

       shell.setText("Dog Show Entry");

       GridLayout gridLayout = new GridLayout();

       gridLayout.numColumns = 3;

       shell.setLayout(gridLayout);

      

       new Label(shell, SWT.NULL).setText("Dog's Name:");

       dogName = new Text(shell, SWT.SINGLE | SWT.BORDER);

       GridData gridData = newGridData(GridData.HORIZONTAL_ALIGN_FILL);

       gridData.horizontalSpan = 2;

       dogName.setLayoutData(gridData);

 

       new Label(shell, SWT.NULL).setText("Breed:");

       dogBreed = new Combo(shell, SWT.NULL);

       dogBreed.setItems(new String [] {"Collie", "Pitbull", "Poodle", "Scottie"});

       dogBreed.setLayoutData(newGridData(GridData.HORIZONTAL_ALIGN_FILL));

 

       Label label = new Label(shell, SWT.NULL);

       label.setText("Categories");

       label.setLayoutData(newGridData(GridData.HORIZONTAL_ALIGN_CENTER));

      

       new Label(shell, SWT.NULL).setText("Photo:");

       dogPhoto = new Canvas(shell, SWT.BORDER);

       gridData = new GridData(GridData.FILL_BOTH);

       gridData.widthHint = 80;

       gridData.heightHint = 80;

       gridData.verticalSpan = 3;

       dogPhoto.setLayoutData(gridData);

       dogPhoto.addPaintListener(new PaintListener() {

          publicvoid 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 [] {

          "Bestof Breed", "Prettiest Female", "Handsomest Male",

          "BestDressed", "FluffiestEars", "MostColors",

          "BestPerformer", "Loudest Bark", "Best Behaved",

          "PrettiestEyes", "MostHair", "LongestTail",

          "CutestTrick"});

       gridData = newGridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);

       gridData.verticalSpan = 4;

       int listHeight = categories.getItemHeight() * 12;

       Rectangletrim = 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.HORIZONTAL_ALIGN_FILL);

       gridData.horizontalIndent = 5;

       browse.setLayoutData(gridData);

       browse.addSelectionListener(new SelectionAdapter() {

          publicvoid widgetSelected(SelectionEvent event) {

              String fileName = newFileDialog(shell).open();

              if (fileName != null) {

                 dogImage = new Image(display,fileName);

              }

          }

       });

      

       Button delete = new Button(shell,SWT.PUSH);

       delete.setText("Delete");

       gridData = newGridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_BEGINNING);

       gridData.horizontalIndent = 5;

       delete.setLayoutData(gridData);

       delete.addSelectionListener(new SelectionAdapter() {

          publicvoid widgetSelected(SelectionEvent event) {

              if (dogImage != null) {

                 dogImage.dispose();

                 dogImage = null;

                 dogPhoto.redraw();

              }

          }

       });

      

       Group ownerInfo = new Group(shell, SWT.NULL);

       ownerInfo.setText("Owner Info");

       gridLayout = new GridLayout();

       gridLayout.numColumns = 2;

       ownerInfo.setLayout(gridLayout);

       gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);

       gridData.horizontalSpan = 2;

       ownerInfo.setLayoutData(gridData);

      

       new Label(ownerInfo, SWT.NULL).setText("Name:");

       ownerName = new Text(ownerInfo,SWT.SINGLE | SWT.BORDER);

       ownerName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

      

       new Label(ownerInfo, SWT.NULL).setText("Phone:");

       ownerPhone = new Text(ownerInfo,SWT.SINGLE | SWT.BORDER);

       ownerPhone.setLayoutData(newGridData(GridData.FILL_HORIZONTAL));

      

       Button enter = new Button(shell,SWT.PUSH);

       enter.setText("Enter");

       gridData = newGridData(GridData.HORIZONTAL_ALIGN_END);

       gridData.horizontalSpan = 3;

       enter.setLayoutData(gridData);

       enter.addSelectionListener(new SelectionAdapter() {

          publicvoid 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.pack();

       shell.open();

       while (!shell.isDisposed()) {

          if (!display.readAndDispatch()) display.sleep();

       }

       if (dogImage != null) {

          dogImage.dispose();

       }

    }

}


Here is what the window lookslike after Mary Smith enters Fifi in the dog show:

 

 

Ifthis window is resized larger, the layout adjusts as follows:

 

 

Noticethe following:

·                                                                                                                    There are 3 columns and 7 rows.

·                                                                                                                    The dogPhotoCanvas grew wider and taller becauseit is filling and grabbing horizontally and vertically (we did not resize the Image, but we could have).

·                                                                                                                    The dogBreedCombo grew wider because it isfilling horizontally, and it is in the same column as the Canvas.

·                                                                                                                    The dogName Text grew wider because it is fillinghorizontally, and one of the columns it spans is the column containing the Canvas.

·                                                                                                                    The categoriesList grew taller because it isfilling vertically, and it spans the same rows that the Canvas does.

·                                                                                                                    Because the categoriesList grew taller, its verticalscrollbar disappeared (it did not grow wider).

·                                                                                                                    The ownerInfoGroup grew wider because it isfilling horizontally, and one of the columns it spans is the column containingthe Canvas.

·                                                                                                                    The ownerInfoGroup, as a subclass of Composite, has its own GridLayout with 2 columns and 2 rows.

·                                                                                                                    The ownerNameand ownerPhone Texts grew wider because the Groupgrew wider, and they are filling and grabbing horizontally in the Group’s GridLayout.

·                                                                                                                    The browseand delete Buttons are indented slightly, and because they both fillhorizontally, they are the same width.

·                                                                                                                    The delete Button is vertically aligned at the topof its row.

·                                                                                                                    The “Categories” Labelis centered over the categories List.

·                                                                                                                    The enter Button is horizontally aligned to theright of the 3 columns it spans.

·                                                                                                                    The dogPhotoCanvas was created with width andheight hints because we want the Imageto be 80 pixels x 80 pixels, if possible.

·                                                                                                                    The categoriesList was created with a height hintthat was based on the List’s fonttimes 12, because we want try to get the Listto show 12 items initially.


Writing Your Own LayoutClass

Occasionally, youmay want to write your own Layoutclass. Perhaps your layout needs are very complex. Maybe you have the same lookin many places, and you want to take advantage of code reuse. Or you want toleverage domain knowledge to create a very efficient layout class. Whatever thereason, there are things to consider before writing a new class:

·       Can the layout be done using a GridLayout, with maybe a few nested layouts?

·       Can the desired effect be more easily achieved with aresize listener?

·       Are you defining a general layout algorithm or justpositioning widgets?

 

Unless you are writing a very generic Layout type that will be used by severalComposite widgets, it is often better and easier to simply calculatesizes and position children in a resize listener. Many of the SWT customwidgets were written this way. Although a new widget can be implemented as a Composite/Layout pair, implementing it as a Composite that does its layout in a resize listener and computesits preferred size in computeSize isclearer, and does not involve writing an extra class.

 

First, we will look at how layouts work, andthen we will create a new Layoutclass. Another example of writing your own Layoutcan be found in the Compound WidgetExample section of “Creating Your OwnWidgets Using SWT”, which shows how to achieve the same look using either aresize listener or a new Layoutclass.

How Layouts Work

Layoutis the abstract superclass of all layouts. It only has two methods: computeSize and layout. The class is defined as follows:

 

public abstractclass Layout {

    protected abstract PointcomputeSize(

       Composite composite, int widthHint, int heightHint, boolean flushCache);

    protected abstract voidlayout(Composite composite, boolean flushCache);

}

 

The computeSize method calculates thewidth and height of a rectangle that encloses all of the Composite’schildren once they have been sized and placed according to the layout algorithmencoded in the Layout class. The hint parameters allow the width and/orheight to be constrained. For example, a layout may choose to grow in onedimension if constrained in another. A hint of SWT.DEFAULTmeans to use the preferred size.

 

The layout method positions and sizesthe Composite’s children. A Layout can choose to cache layout-relatedinformation, such as the preferred extent of each of the children. The flushCacheparameter tells the Layout to flush cached data.

 

Since a Layoutcontrols the size and placement of widgets in a Composite, there are several methods in Composite that are used with Layouts.

 

The first two methods allow setting andgetting a Layout object in a Composite.

 

    public voidsetLayout(Layout layout);

    public Layout getLayout();

 

An application can force a Layout to recalculatethe sizes of and reposition children by sending layout() to the parent Composite.

 

    public voidlayout(boolean changed);

    public voidlayout();

       // calls layout(true);

 

You would do this after changing anythingabout the children that might affect their size or position, such as changingthe font of a child, changing the text or image of a child, adding a new child,adding children to a child, etc. (If the child can accommodate the change, thenlayout may not be necessary – for example, changing the font or text of ascrollable multi-line Text). Since these changes are doneprogrammatically, they do not cause events to happen. Consequently, the parentdoesn’t know about the changes, and has to be told through the layoutmethod. This strategy reduces flash because the application can make severalchanges and then tell the parent to layout, and the children are only redrawnonce instead of once per change. If layout() is not called and changesare made after the shell is opened, then the children may not be correctly laidout until the shell is somehow resized. Note that shell.open() causes alayout to occur.

 

The computeSize methods of a Compositecalculate the Composite’s preferred size, which is the size of its client areaas determined by the Layout, plus its trim.

 

    public PointcomputeSize(int widthHint, int heightHint, boolean changed);

    public PointcomputeSize(int widthHint, int heightHint);

       // calls computeSize(widthHint, heightHint, true);

 

The clientArea of a Compositeis the rectangle that will contain all of the children. A Layoutpositions the children inside the client area.

 

   publicRectangle getClientArea ();

 

The trim of a Composite is thearea 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 themethod computeTrim.

 

   publicRectangle computeTrim (int x, int y, int width,int height);

 

Sending pack to a Compositeresizes it to its preferred size.

 

    public voidpack(boolean changed);

       // calls setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT,changed));

    public voidpack();

       // calls pack(true);

 

Theboolean parameter to the layout, computeSize, and pack methods is the changedflag. If true, it indicates that theComposite’s contents have changed in some way that affects its preferredsize, therefore any caches that the Layoutmay have been keeping need to be flushed. When a Composite is resized, it asks its Layout to lay out its children by calling layout(false); therefore widget content caches are not flushed. This lets the Layout perform any expensivecalculations only when necessary.

 

Cachingcan increase performance, but it can also be tricky. You can choose not tocache at all – in fact, it is best not to try caching until your code isstable. When considering what to cache, be certain not to store any widgetstate, such as the text of a label, or the number of items in a list.


Custom LayoutExample

If you have several vertically oriented Composite widgets in your application,you might choose to write ColumnLayout.We will show a simple version of a Layoutclass that lays out Compositechildren into a single column. The class has fixed margins and spacing.Children are given the same width, but they take their natural height.

 

The code for the ColumnLayout class is below. Note that we cache the width of thewidest child, and the sum of the child heights (plus spacing), and these valuesare used to compute the size and lie out the children. They are recalculated ifflushCache is true.

 

importorg.eclipse.swt.*;

importorg.eclipse.swt.graphics.*;

importorg.eclipse.swt.widgets.*;

importorg.eclipse.swt.layout.*;

 

public class ColumnLayout extendsLayout {

    // fixed margin and spacing

    public static final int MARGIN = 4;

    public static final int SPACING =2;

   

    // cache

    Point[] sizes;

    int maxWidth, totalHeight;

 

protectedPoint computeSize(Composite composite, intwHint, int hHint, booleanflushCache) {

    Controlchildren[] = 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) {

    Controlchildren[] = composite.getChildren();

    if (flushCache || sizes == null|| sizes.length != children.length) {

       initialize(children);

    }

    Rectanglerect = 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;

    }

}

 

voidinitialize(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;

}

}

 

Here is some simple test code to test the ColumnLayout. The grow and shrink Buttons show a call to the Shell’s layout() method to force a re-layout after changing the width ofone of the children. Calling layout()is the same as calling layout(true)which tells the ColumnLayout to flushits caches before setting the bounds of the children. The Shell is also told to pack()after laying out the children. This forces the Shell to take the new size.

 

importorg.eclipse.swt.*;

importorg.eclipse.swt.widgets.*;

importorg.eclipse.swt.layout.*;

importorg.eclipse.swt.events.*;

 

public class ColumnLayoutTest {

    static Shell shell;

    static Button button3;

 

    public static void main(String[] args) {

       Displaydisplay = 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");

       Buttongrow = new Button(shell, SWT.PUSH);

       grow.setText("Grow Button 3");

       grow.addSelectionListener(new SelectionAdapter() {

          public void widgetSelected(SelectionEvente) {

              button3.setText("Extreemely Wide Button 3");

              shell.layout();

              shell.pack();

          }

       });

       Buttonshrink = new Button(shell, SWT.PUSH);

       shrink.setText("Shrink Button 3");

       shrink.addSelectionListener(new SelectionAdapter() {

          public voidwidgetSelected(SelectionEvent e) {

              button3.setText("Button 3");

              shell.layout();

              shell.pack();

          }

       });

       shell.pack();

       shell.open();

       while (!shell.isDisposed()) {

          if (!display.readAndDispatch()) display.sleep();

       }

    }

}

 

If we run the test code, the window on theleft appears. Pressing the Grow Button 3 button results in the window on theright. Resizing the window with the mouse will also make the buttons wider (ornarrower) but they do not grow taller.

 

 

Overriding Composite

If you are writingyour own widget, as outlined in “CreatingYour Own Widgets Using SWT”, and you subclass Composite, then hereare a few points to consider for your implementation:

·  If you are providing trimmings in your new Composite,make sure to override both computeTrim and getClientArea.

·  Never override layout(), but you may override layout(boolean).

 

Sometimesyou want your new Composite to have a specific look, and you don’t wantthe application to be able to specify a layout. Your new Composite wouldeither do its layout in a resize handler or using a private custom layout. Ineither case, you will probably want to do the following:

·  Override setLayout to do nothing.

·  Override layout(boolean) to call your layoutcode.

·  Override computeSize to correctly compute thesize of your Composite.

 

Summary

SWT provides several differentways to lay out widgets. The simplest method, and the one you will typicallyuse, is to use one of the standard Layoutclasses: FillLayout, RowLayout, or GridLayout.

 

In certain cases you may want towrite your own Layout class toprovide a very specific look or to reuse very similar layout code, but often aresize listener on the parent widget will suffice.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值