Understanding Layouts in SWT

来源:互联网 发布:自动供料机的plc编程 编辑:程序博客网 时间:2024/05/01 11:58
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 aComposite.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 aComposite. 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 aComposite’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. TheComposite (in thiscase, a TabFolder) has a location,clientArea and trim.The size of the Composite is the size of theclientArea plus thesize of the trim. The Composite has two children that are laidout side by side. ALayout is managing the size and position of thechildren. This Layout allowsspacing between the children, and a marginbetween the children and the edges of theLayout. 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 aComposite, the preferred size is the smallestrectangle that contains all of its children. If children have been positionedby the application, theCompositecomputes its own preferred size based on the size and position of the children.If aComposite is using a layoutclass to position its children, it asks the Layoutto compute the size of its clientArea, and then it adds in thetrimto 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’ssetLayout(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 substitutingData for Layout in the class name. For example, the standard layout classRowLayout has a layout data class calledRowData, and the layout classGridLayout 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 staticvoid 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, theRowLayout 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 theCompositebecomes 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 aGroup. FillLayout can also be used when a Composite only has one child. For example, if aShell has a single Group child, FillLayoutwill cause theGroup to completelyfill the Shell.

 

Here is therelevant portion of the example code. First we create a FillLayout, then (if we want vertical) we set itstype field, and then we set it into theComposite (a Shell). The Shell hasthree pushbutton children, B1, B2, and Button 3. Note that in aFillLayout, 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 aRowLayout can be specified by setting a RowData object into the widget usingsetLayoutData.

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 aFillLayout. 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 parentComposite grows wider, theextra space is distributed evenly among the widgets. If bothpack 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 parentComposite (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 aShell.

 

    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 aRowDataobject into the widget. The following code uses RowData objects to change the initial size of theButtons in a Shell.

 

importorg.eclipse.swt.*;

importorg.eclipse.swt.widgets.*;

importorg.eclipse.swt.layout.*;

 

publicclass RowDataExample {

 

    public staticvoid 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 aGridLayout, 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, calledGridData. The power of GridLayout lies in the ability toconfigureGridData 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 whennumColumns+ 1 widgets are added to the Composite.The default is to have only 1 column. The following code creates aShell with five Button children of various widths, managed by aGridLayout. 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 aRowLayout. The differenceis that the left and right margins are grouped intomarginWidth, and the top and bottom margins are grouped into marginHeight. Also, in a GridLayout you can specify verticalSpacing, whereas in aRowLayout, only horizontalSpacing applied.

GridData Object Fields

GridData is the layout data objectassociated with GridLayout. To set aGridData object into a widget, you usethe setLayoutData method. Forexample, to set theGridData 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 aGridData 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 reuseGridData objects. Everywidget in a Composite that is managed by aGridLayout must have aunique GridData object. If the layout data for a widget in aGridLayoutis 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 thehorizontalAlignment 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 thehorizontalAlignment 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 asTexts or Lists to allowthem to grow if their containing Compositegrows. If aText is grabbing excesshorizontal space and the user resizes the Shellwider, then theText 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 theShell 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, thelast 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 parentComposite grows wider, then the entire column containing that widget growswider. If a widget is grabbing excess vertical space and its parentComposite 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 hasfill 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 theGridLayout’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 theGridLayout 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 aFileDialog to read an Imagefile which the Canvas displays in apaint listener, Delete deletes theImage,and Enterprints the current dog and owner info. The example has been coded in a singlemain 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.

·                                                                                                                    ThedogPhotoCanvas grew wider and taller becauseit is filling and grabbing horizontally and vertically (we did not resize theImage, but we could have).

·                                                                                                                    ThedogBreedCombo grew wider because it isfilling horizontally, and it is in the same column as theCanvas.

·                                                                                                                    ThedogName Text grew wider because it is fillinghorizontally, and one of the columns it spans is the column containing theCanvas.

·                                                                                                                    ThecategoriesList grew taller because it isfilling vertically, and it spans the same rows that theCanvas does.

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

·                                                                                                                    TheownerInfoGroup grew wider because it isfilling horizontally, and one of the columns it spans is the column containingtheCanvas.

·                                                                                                                    TheownerInfoGroup, as a subclass of Composite, has its ownGridLayout with 2 columns and 2 rows.

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

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

·                                                                                                                    Thedelete Button is vertically aligned at the topof its row.

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

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

·                                                                                                                    ThedogPhotoCanvas was created with width andheight hints because we want theImageto be 80 pixels x 80 pixels, if possible.

·                                                                                                                    ThecategoriesList was created with a height hintthat was based on theList’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 ownLayoutclass. 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 aComposite that does its layout in a resize listener and computesits preferred size incomputeSize 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 ownLayoutcan 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 newLayoutclass.

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, intheightHint, boolean flushCache);

    protected abstract voidlayout(Composite composite,boolean flushCache);

}

 

The computeSize method calculates thewidth and height of a rectangle that encloses all of theComposite’schildren once they have been sized and placed according to the layout algorithmencoded in theLayout 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. ALayout can choose to cache layout-relatedinformation, such as the preferred extent of each of the children. TheflushCacheparameter tells the Layout to flush cached data.

 

Since a Layoutcontrols the size and placement of widgets in a Composite, there are several methods inComposite 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 sendinglayout() 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-lineText). 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 thelayoutmethod. 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. Iflayout() 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 thatshell.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 theLayout, 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. ALayoutpositions 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 themethodcomputeTrim.

 

   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, andpack 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 theLayoutmay have been keeping need to be flushed. When a Composite is resized, it asks itsLayout to lay out its children by calling layout(false); therefore widget content caches arenot 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 writeColumnLayout.We will show a simple version of a Layoutclass that lays outCompositechildren 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 ColumnLayoutextendsLayout {

    // fixed margin and spacing

    public staticfinal int MARGIN = 4;

    public staticfinal 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 andshrink Buttons show a call to the Shell’s layout() method to force a re-layout after changing the width ofone of the children. Callinglayout()is the same as calling layout(true)which tells theColumnLayout to flushits caches before setting the bounds of the children. TheShell is also told to pack()after laying out the children. This forces theShell 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 staticvoid 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 bothcomputeTrim 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 newComposite 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 standardLayoutclasses: 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