0

I'm writing a feature that allows the user to create controls by dragging and dropping, similar to Keynote.

enter image description here enter image description here

The simplified code is as follows :

Display display = Display.getDefault() ;
Shell shell = new Shell( display ) ;
shell.setSize( 500, 500 ) ;
shell.setLayout( new FillLayout() ) ;
Composite container = new Composite( shell, SWT.NONE ) ;
GridLayoutFactory.swtDefaults().numColumns(2).applyTo( container ) ;

Composite infoComp = new Composite( container, SWT.NONE ) ;
GridLayoutFactory.swtDefaults().applyTo( infoComp ) ;

Label info = new Label( infoComp, SWT.BORDER ) ;
info.setText( "Create a Button" ) ;
DragSource dragSource = new DragSource( info, DND.DROP_MOVE ) ;
dragSource.setTransfer( new Transfer[] { TextTransfer.getInstance() } ) ;
dragSource.addListener( DND.DragSetData, e -> {
    e.data = "Button" ;
} ) ;

info = new Label( infoComp, SWT.BORDER ) ;
info.setText( "Create a Label" ) ;
dragSource = new DragSource( info, DND.DROP_MOVE ) ;
dragSource.setTransfer( new Transfer[] { TextTransfer.getInstance() } ) ;
dragSource.addListener( DND.DragSetData, e -> {
    e.data = "Label" ;
} ) ;

Composite comp = new Composite( container, SWT.BORDER ) ;
GridDataFactory.swtDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(comp);
GridLayoutFactory.swtDefaults().applyTo( comp ) ;

DropTarget dropTarget = new DropTarget( comp,  DND.DROP_MOVE ) ;
dropTarget.setTransfer( new Transfer[] { TextTransfer.getInstance() } ) ;
dropTarget.addListener( DND.Drop, e -> {
    String data = (String) e.data ;
    switch (data) {
        case "Button" -> {
            Button button = new Button( comp, SWT.NONE ) ;
            button.setText( "Button" ) ;
            button.requestLayout() ;
        }
        case "Label" -> {
            Label label = new Label( comp, SWT.NONE ) ;
            label.setText( "Label" ) ;
            label.requestLayout() ;
        }
    }
} ) ;

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

enter image description here

Due to the special treatment of DragSource.drag(Event)

private void drag(Event dragEvent) {
    ...

    String externalLoopKey = "org.eclipse.swt.internal.win32.externalEventLoop";
    int result = COM.DRAGDROP_S_CANCEL;
    try {
        createCOMInterfaces();
        display.setData(externalLoopKey, Boolean.TRUE);
        result = COM.DoDragDrop(iDataObject.getAddress(), iDropSource.getAddress(), operations, pdwEffect);
    } finally {
        display.setData(externalLoopKey, Boolean.FALSE);

        ...
}

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

public void layout (Control [] changed, int flags) {
    ...
        /**
         * display.externalEventLoop is true here
         */
        if (!display.externalEventLoop && (flags & SWT.DEFER) != 0) {
            setLayoutDeferred (true);
            display.addLayoutDeferred (this);
        }
        for (int i=updateCount-1; i>=0; i--) {
            update [i].updateLayout (false);
        }
    ...
}

Each Control.requestLayout() call in the handling of the DND.Drop event causes an update to the layout immediately.

In the actual code, the creation process is far more complex than in the example, and need to update the settings area like the one on the right side in Keynote, so requires multiple calls to Control.requestLayout(), there is a lot of overhead.

Is there any way to delay the processing of the layout as normal?

2
  • Maybe use Display.asyncExec to delay things until after the drop when the flag has been reset? macOS doesn't have the externalEventLoop flag so I can't test this here.
    – greg-449
    Commented Jul 8 at 12:05
  • Right, Display.asyncExec can do it. Thanks!
    – Bourbon_7
    Commented Jul 8 at 12:30

0

Browse other questions tagged or ask your own question.