eclipseswt

Is Control.requestLayout() really done asynchronously?


org.eclipse.swt.widgets.Control.requestLayout()

public void requestLayout () {
    getShell ().layout (new Control[] {this}, SWT.DEFER);
}

org.eclipse.swt.widgets.Composite.layout(Control[], int) code

public void layout (Control [] changed, int flags) {
    checkWidget ();
    if (changed != null) {
        for (Control control : changed) {
            if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
            if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
            boolean ancestor = false;
            Composite composite = control.parent;
            while (composite != null) {
                ancestor = composite == this;
                if (ancestor) break;
                composite = composite.parent;
            }
            if (!ancestor) error (SWT.ERROR_INVALID_PARENT);
        }
        int updateCount = 0;
        Composite [] update = new Composite [16];
        for (Control element : changed) {
            Control child = element;
            Composite composite = child.parent;
            // Update layout when the list of children has changed.
            // See bug 497812.
            child.markLayout(false, false);
            while (child != this) {
                if (composite.layout != null) {
                    composite.state |= LAYOUT_NEEDED;
                    if (!composite.layout.flushCache (child)) {
                        composite.state |= LAYOUT_CHANGED;
                    }
                }
                if (updateCount == update.length) {
                    Composite [] newUpdate = new Composite [update.length + 16];
                    System.arraycopy (update, 0, newUpdate, 0, update.length);
                    update = newUpdate;
                }
                child = update [updateCount++] = composite;
                composite = child.parent;
            }
        }
        if (!display.externalEventLoop && (flags & SWT.DEFER) != 0) {
            setLayoutDeferred (true);
            display.addLayoutDeferred (this);
        }
        // ① here! here! here!
        for (int i=updateCount-1; i>=0; i--) {
            update [i].updateLayout (false);
        }
    } else {
        if (layout == null && (flags & SWT.ALL) == 0) return;
        markLayout ((flags & SWT.CHANGED) != 0, (flags & SWT.ALL) != 0);
        if (!display.externalEventLoop && (flags & SWT.DEFER) != 0) {
            setLayoutDeferred (true);
            display.addLayoutDeferred (this);
        }
        updateLayout ((flags & SWT.ALL) != 0);
    }
}

In line ① , this control and all of its ancestors are laid out immediately , why does the documentation for Control.requestLayout() say that

The control will not be repositioned synchronously

Additionally, each time this method is called, this control and all of its ancestors are laid out, why does the documentation say that

This method is fast-running and only marks the control for future participation in a deferred layout.

Invoking this method multiple times before the layout occurs is aninexpensive no-op.


Solution

  • Yes, it is really done asynchronously.

    The SWT.DEFER flag causes the layout code to execute:

     setLayoutDeferred (true);
     display.addLayoutDeferred (this);
    

    before the calls to update.

    The setLayoutDeferred(true) call increments the value of the layoutCount field in the Composite. display.addLayoutDeferred adds the Composite to the list of deferred layouts.

    In the updateLayout method the first part of the code is

    void updateLayout (boolean all) {
        Composite parent = findDeferredControl ();
        if (parent != null) {
            parent.state |= LAYOUT_CHILD;
            return;
        }
        ...
    

    The findDeferredControl method tests the layoutCount value set earlier in the Composite or any of its parents:

    Composite findDeferredControl () {
        return layoutCount > 0 ? this : parent.findDeferredControl ();
    }
    

    So the layout is not updated during the call. Instead the update is done when Display processed its list of deferred layouts. This is done the next time the readAndDispatch method is executed.