Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[platform-swt-dev] Question about layouts and resizing....

Hello....

I'm having a problem with layouts (particularly with regards to laying out 
child controls).  At the bottom of this email I have included some sample code 
that demonstrates the problem.

If you run the code, you'll get a shell that contains an add column and remove 
column button at the top.  Below that is a column (a composite really) which 
has 3 child controls (2 labels with a list control in the middle).

If you click on the add column button, another one of these "columns" is 
created.  The goal of all this is to get the list controls to be the same 
height. 

While clicking on the addColumn button does indeed result in these list 
controls being the same height, if you look closely, they are actually too 
big.  In fact, they are too big by the exactly the height of a horizontal 
scroll bar. 

If you click the remove column button, the shell goes back to one column.

To see the problem, watch the first list control. Click add column and then 
remove column buttons over and over.  Watch how the first list control grows 
and shrinks.


I think the source of my problem is in the computeSize method in the List 
control itself.  It looks like computeSize is always adding the height of the 
horizontal scroll bar to the returned height (it's doing the same for vert. 
scroll bars too).

I can work around this kind of problem by determining the height of the tallest 
list control in my columns, substracting the height of the horizontal scrollbar 
from this "max" height.  Then setting the height of all the list controls to 
that height.  But that really seems like a hack.

Thanks in advance!

Frank Pavelski
pavelski@xxxxxxxxx




import java.util.ArrayList;

import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

public class ResizeProblem {
    static Composite container;
    static ArrayList columns = new ArrayList(2);

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Test a resize issue");

		// We use a form layout on the shell.
        FormLayout layout = new FormLayout();
        shell.setLayout(layout);

        // Make a container to hold "columns"
        container = new Composite(shell, SWT.NONE);

		// Make a button that will add a column
        Button addColumn = new Button(shell, SWT.NONE);
        addColumn.setText("Add Column");

		// Make a button that will remove a column
        Button remColumn = new Button(shell, SWT.NONE);
        remColumn.setText("Remove Column");

		// When the add button is clicked, we do this code
		addColumn.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				Composite column = makeColumn
(ResizeProblem.container, SWT.NONE, false);
				columns.add(column);
				((GridLayout)container.getLayout()).numColumns 
= 2;
				container.layout();
				resizeColumns();
			}
		});

		// When the remove button is clicked, we do this code
		remColumn.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				if (columns.size() == 1) {
					return;
				}

				((Composite)columns.get(1)).dispose();
				columns.remove(1);
				((GridLayout)container.getLayout()).numColumns 
= 1;
				container.layout();
				resizeColumns();
			}
		});


		// place the addcolumn at the top of the shell, on the left
        FormData data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(remColumn, 0, SWT.LEFT);
        data.height = 20;
        addColumn.setLayoutData(data);

		// placed the remove button at the top of the shell on the right
        data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        data.height = 20;
        remColumn.setLayoutData(data);

		// Place our container of "columns" under the add button.
		// attach it the left, right, and bottom of the shell
        data = new FormData();
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        data.top = new FormAttachment(addColumn, 0, SWT.BOTTOM);
        data.bottom = new FormAttachment(100, 0);
        container.setLayoutData(data);

		
        // Set a gridlayout on our container (one column for now);
        // We use a grid layout to make the columns equal in width
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 1;
        gridLayout.makeColumnsEqualWidth = true;
        gridLayout.horizontalSpacing = 1;
        gridLayout.marginHeight = 0;
        gridLayout.marginWidth = 0;
        container.setLayout(gridLayout);

        // create our first column
        Composite column1 = makeColumn(container, SWT.NONE, true);
        columns.add(column1);


        shell.open();
        shell.layout();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();

    }

	// Herein lies the problem.....
	// Attempt to resize all of the columns such all the list viewers are 
equal in height
    public static void resizeColumns() {
        int numCols = columns.size();
        int maxHeight = 0;

		// First, go though each column's....
        for (int i = 0; i < numCols; i++) {
            Composite column = (Composite)columns.get(i);
            Object[] children = column.getChildren();
			
			// For each column, go through it's children (there 
will be 3 of them)
			// We want the List control....
            for (int j = 0; j < children.length; j++) {
                if (children[j] instanceof List) {
                	// OK, we've got the list.
                	// Let's see if its the tallest list and snarf off its 
height if it is.
                    Point p = ((Control)children[j]).computeSize(SWT.DEFAULT, 
SWT.DEFAULT, true);
                    if (p.y > maxHeight) {
                        maxHeight = p.y;
                    }
                }
            }
        }

		// Now go back through again, this time setting the height of 
each list 
		// control to be maxHeight we determined above
        for (int i = 0; i < numCols; i++) {
            Composite column = (Composite)columns.get(i);
            Object[] children = column.getChildren();
            for (int j = 0; j < children.length; j++) {
                if (children[j] instanceof List) {
                    FormData formData = (FormData) ((Control)children
[j]).getLayoutData();
	
					// Just to show the point here.  When 
we have only one column, try to set the height
					// of the list control in that column 
to SWT.DEFAULT.  This get's it back to its
					// initial state.
                    if (numCols == 1) {
                        formData.height = SWT.DEFAULT;
                    }
                    else {
                    	// We have more than one column here.  Try to set the 
list control's height
                    	// to the max height.  If you debug/use Spyxx, you will 
see that the end result
                    	// is that the list control get's set to maxHeight + 
ScrollBar's height.
                    	// THIS IS THE LITTLE NIT I'm trying to overcome.

                    	// I could fix this by subtracting the height of the 
horizontal scroll bar from maxHeight
                    	// but this is really a hack I think.  There must be a 
better way.
                        formData.height = maxHeight;
                    }

                }
            }
            column.layout();
        }

    }

	// A Column is just a composite that contains 3 children,  
	// 	a label at the top
	// 	a list viewer in the middle
	// 	and another label at the top.
 
    public static Composite makeColumn(Composite parent, int style, boolean 
bFirst) {
		
		// We use a FormLayout on the Column
        FormData formData;

        Composite column = new Composite(parent, style);
        column.setLayout(new FormLayout());

		// Create the ListViewer
        ListViewer _listViewer = new ListViewer(column, SWT.H_SCROLL | 
SWT.V_SCROLL);

        column.setLayout(new FormLayout());

		// Create the Top label
        Label topButton = new Label(column, SWT.NONE);
        topButton.setText("Top Label");

        // Create the bottom label
        Label bottomButton = new Label(column, SWT.NONE);
        bottomButton.setText("Bottom Label");

		// Place top label at the top of the column
		// attach it's left and right sides to the column
		// make it 20 pixels in height.
        formData = new FormData();
        formData.left = new FormAttachment(0, 0);
        formData.right = new FormAttachment(100, 0);
        formData.top = new FormAttachment(0, 0);
        formData.height = 20;
        topButton.setLayoutData(formData);

		// Place the ListViewer's list control under the top label and 
above the bottom label
		// attach it's left and right sides to the column
		// Make it's height SWT.DEFAULT
		formData = new FormData();
		formData.left = new FormAttachment(0, 0);
		formData.right = new FormAttachment(100, 0);
		formData.top = new FormAttachment(topButton, 0, SWT.BOTTOM);
		formData.height = SWT.DEFAULT;
		_listViewer.getControl().setLayoutData(formData);


		// Place the bottom label at the bottom.
		// attach it's left and right sides to the column
		// attach it's bottom to the column's bottom.
		// attach it's top to the bottom of the listviewer's list 
control
        formData = new FormData();
        formData.left = new FormAttachment(0, 0);
        formData.right = new FormAttachment(100, 0);
        formData.bottom = new FormAttachment(100, 0);
        formData.top = new FormAttachment(_listViewer.getControl(), 0, 
SWT.BOTTOM);
        bottomButton.setLayoutData(formData);


		// Add a content provider to the listviewer.  Lots of ugly code,
		// but the point is to have one listviewer have more items than 
		// the other one.  We will try to keep them all the same height
        if (bFirst) {
            _listViewer.setContentProvider(new IStructuredContentProvider() {
                private String[] elements =
                    
{ "GGGGGGGGGGGGG", "HHHHHHHHHHHHHHH", "IIIIIIIIIIIII", "JJJJJJJJJJJ", "KKKKKKKKK
KKK", "LLLLLLLLLLLLLL" };

                public Object[] getElements(Object inputElement) {
                    return elements;
                }

                public void dispose() {
                }

                public void inputChanged(Viewer viewer, Object oldInput, Object 
newInput) {
                }
            });
        }
        else {
            _listViewer.setContentProvider(new IStructuredContentProvider() {
                private String[] elements2 = 
{ "AAAAAAAAAA", "BBBBBBBBBB", "CCCCCCCCCCCCC" };

                public Object[] getElements(Object inputElement) {
                    return elements2;
                }

                public void dispose() {
                }

                public void inputChanged(Viewer viewer, Object oldInput, Object 
newInput) {
                }
            });
        }

        _listViewer.setInput(parent);

        // set grid data on the containing column.  
        // This way each column is the same Width.
        GridData gridData = new GridData();
        gridData.horizontalAlignment = GridData.FILL;
        gridData.verticalAlignment = GridData.FILL;
        gridData.grabExcessVerticalSpace = true;
        column.setLayoutData(gridData);

        return column;
    }

}


Back to the top