SWT Layout for absolute positioning with minimal-spanning composites

Posted by pure.equal on Stack Overflow See other posts from Stack Overflow or by pure.equal
Published on 2010-06-08T09:18:19Z Indexed on 2010/06/08 9:22 UTC
Read the original article Hit count: 511

Filed under:
|
|
|

Hi, I'm writing a DND-editor where I can position elemtents (like buttons, images ...) freely via absolute positioning. Every element has a parent composite. These composites should span/grasp/embrace every element they contain. There can be two or more elements in the same composite and a composite can contain another composite.

This image shows how it should look like.

To achive this I wrote a custom layoutmanager:

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;

public class SpanLayout extends Layout {

Point[] sizes;
int calcedHeight, calcedWidth, calcedX, calcedY;
Point[] positions;

/*
 * (non-Javadoc)
 * 
 * @see
 * org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite
 * , int, int, boolean)
 * 
 * A composite calls computeSize() on its associated layout to determine the
 * minimum size it should occupy, while still holding all its child controls
 * at their minimum sizes.
 */
@Override
protected Point computeSize(Composite composite, int wHint, int hHint,
        boolean flushCache) {

    int width = wHint, height = hHint;
    if (wHint == SWT.DEFAULT)
        width = composite.getBounds().width;
    if (hHint == SWT.DEFAULT)
        height = composite.getBounds().height;
    return new Point(width, height);

}

/*
 * (non-Javadoc)
 * 
 * @see
 * org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
 * boolean)
 * 
 * Calculates the positions and sizes for the children of the passed
 * Composite, then places them accordingly by calling setBounds() on each
 * one.
 */

@Override
protected void layout(Composite composite, boolean flushCache) {
    Control children[] = composite.getChildren();

    for (int i = 0; i < children.length; i++) {
        calcedX = calcX(children[i]);
        calcedY = calcY(children[i]);
        calcedHeight = calcHeight(children[i]) - calcedY;
        calcedWidth = calcWidth(children[i]) - calcedX;

        if (composite instanceof Composite) {
            calcedX = calcedX - composite.getLocation().x;
            calcedY = calcedY - composite.getLocation().y;
        }

        children[i].setBounds(calcedX, calcedY, calcedWidth, calcedHeight);
    }
}

private int calcHeight(Control control) {
    int maximum = 0;

    if (control instanceof Composite) {
        if (((Composite) control).getChildren().length > 0) {
            for (Control child : ((Composite) control).getChildren()) {
                int calculatedHeight = calcHeight(child);
                if (calculatedHeight > maximum) {
                    maximum = calculatedHeight;
                }
            }
            return maximum;
        }
    }

    return control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y
            + control.getLocation().y;
}

private int calcWidth(Control control) {
    int maximum = 0;

    if (control instanceof Composite) {
        if (((Composite) control).getChildren().length > 0) {
            for (Control child : ((Composite) control).getChildren()) {
                int calculatedWidth = calcWidth(child);
                if (calculatedWidth > maximum) {
                    maximum = calculatedWidth;
                }
            }
            return maximum;
        }
    }

    return control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x
            + control.getLocation().x;
}

private int calcX(Control control) {
    int minimum = Integer.MAX_VALUE;

    if (control instanceof Composite) {
        if (((Composite) control).getChildren().length > 0) {
            for (Control child : ((Composite) control).getChildren()) {
                int calculatedX = calcX(child);
                if (calculatedX < minimum) {
                    minimum = calculatedX;
                }
            }
            return minimum;
        }
    }

    return control.getLocation().x;
}

private int calcY(Control control) {
    int minimum = Integer.MAX_VALUE;

    if (control instanceof Composite) {
        if (((Composite) control).getChildren().length > 0) {
            for (Control child : ((Composite) control).getChildren()) {
                int calculatedY = calcY(child);
                if (calculatedY < minimum) {
                    minimum = calculatedY;
                }
            }
            return minimum;
        }
    }

    return control.getLocation().y;
}
}

The problem with it is that it always positions the composite at the position (0,0). This is because it tries to change the absolute positioning into a relative one. Lets say I position a image at position (100,100) and one at (200,200). Then it has to calculate the location of the composite to be at (100,100) and spanning the one at (200,200). But as all child positions are relative to their parents I have to change the positions of the children to remove the 100px offset of the parent. When the layout gets updated it moves everything to the top-left corner (as seen in the image) because the position of the image is not (100,100) but (0,0) since I tried to remove the 100px offset of the partent.

Where is my error in reasoning? Is this maybe a totally wrong approach?
Is there maybe an other way to achive the desired behavior?

Thanks in advance!
Best regards, Ed

© Stack Overflow or respective owner

Related posts about java

Related posts about layout