SWT Text Widgets in StackLayout—Can't adjust height when switching top control

621 views Asked by At

I have a SWT Composite in a JFace Dialog. On one row of the composite I have a Group with 4 buttons stacked vertically. Next to it is a Text widget which needs to toggle back and forth, by user input, between being multi and single-line.

I have two different Text widgets, in a StackLayout, and I show one or the other as needed. But I can't find a way to adjust the height of the Stack Composite and Text widget depending on which Text is the top control. I need only one row height when in single mode, and to fill up the same vertical space as the Group next to it when in multi mode. I can adjust the height of the Text to either of those conditions when I create the Composite, but they don't adjust their height when I tell them to after switching from one Text to the other.

Here's a code snippet, which results in the Text Widget always being too tall, and never reducing to a size of one row height when in single mode.

This is the Composite

        @Override
        protected Control createDialogArea(Composite parent) {
            container = (Composite) super.createDialogArea(parent);
            GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
            GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(container);

            Group group = new Group(container, SWT.NONE);
            GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true,  true).applyTo(group);
            GridLayoutFactory.fillDefaults().applyTo(group);        
            for (M_ATTRIBUTE attr : M_ATTRIBUTE.values()) {
                Button button = new Button(group, SWT.CHECK);
                GridDataFactory.fillDefaults().applyTo(button);
                button.setText(attr.getDisplayValue());
            }

            stack = new Composite(container, SWT.NONE);
            GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(stack);
            stackLayout = new StackLayout();
            stack.setLayout(stackLayout);
            valueTextMulti = new Text(stack, SWT.BORDER | SWT.MULTI);
            GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(valueTextMulti);
            valueTextMulti.addModifyListener(valueTextModifyListener);

            valueTextSingle = new Text(stack, SWT.BORDER | SWT.SINGLE);
            GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(valueTextSingle);
            valueTextSingle.addModifyListener(valueTextModifyListener);

            stackLayout.topControl = valueTextMulti;

            return container;
        }

And here's a switch statement that shows what I do to switch between single and mult line Text Widgets.

        @Override
        public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection sel = (IStructuredSelection) event.getSelection();
                M_DATA_TYPE type = (M_DATA_TYPE) sel.getFirstElement();
                switch (type) {
                case LIST:
                case HASH_TABLE:
                    stackLayout.topControl = valueTextMulti;
                    setNumRows(valueTextMulti, 10);
                    ((GridData) valueTextMulti.getLayoutData()).verticalAlignment = SWT.FILL;
                    ((GridData) valueTextMulti.getLayoutData()).grabExcessVerticalSpace = true;
                    ((GridData) stack.getLayoutData()).grabExcessVerticalSpace = true;
                    container.getParent().layout();
                    break;
                default:
                    stackLayout.topControl = valueTextSingle;
                    ((GridData) valueTextSingle.getLayoutData()).verticalAlignment = SWT.TOP;
                    ((GridData) valueTextSingle.getLayoutData()).grabExcessVerticalSpace = false;
                    ((GridData) stack.getLayoutData()).grabExcessVerticalSpace = false;
                    setNumRows(valueTextSingle, 1);
                    container.getParent().layout();
                    break;
                }
            }

And here's the method that adjusts the height of the Text in terms of character rows, which I got from another StackOverflow post.

protected void setNumRows(Text text, int rows) {
            GC gc = new GC(text);
            try
            {
                gc.setFont(text.getFont());
                FontMetrics fm = gc.getFontMetrics();

                int height = rows * fm.getHeight();
                text.setSize(text.getSize().x, height);
                text.getParent().layout();
            }
            finally
            {
                gc.dispose();
            }
        }
1

There are 1 answers

0
MidnightJava On BEST ANSWER

I got it to work by creating additional composites inside the StackLayout composite, one for each Text widget instance. Each Text widget has layout data that is fixed, one with align = SWT.FILL and the other with SWT.TOP. Then I just switch the container composites in the StackLayout rather than switching the Text widgets. This has the added advantage of not requiring to directly set the widget height. Also layout() must be called on the StackLayout composite after switching the contents. Calling layout() on containers higher up in the hierarchy did not work.

Here is my modified code:

@Override
protected Control createDialogArea(Composite parent) {
    container = (Composite) super.createDialogArea(parent);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
    GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(container);

    Group group = new Group(container, SWT.NONE);
    GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true,  false).applyTo(group);
    GridLayoutFactory.fillDefaults().applyTo(group);        
    for (M_ATTRIBUTE attr : M_ATTRIBUTE.values()) {
        Button button = new Button(group, SWT.CHECK);
        GridDataFactory.fillDefaults().applyTo(button);
        button.setText(attr.getDisplayValue());
        this.buttons.put(attr, button);
    }
    stack = new Composite(container, SWT.NONE);
    GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(stack);
    stackLayout = new StackLayout();
    stack.setLayout(stackLayout);               
    compMultiLine = new Composite(stack, SWT.NONE);
    GridDataFactory.fillDefaults().applyTo(compMultiLine);
    GridLayoutFactory.fillDefaults().applyTo(compMultiLine);
    valueTextMulti = new Text(compMultiLine, SWT.BORDER | SWT.MULTI |SWT.WRAP);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(valueTextMulti);
    valueTextMulti.addModifyListener(valueTextModifyListener);              
    compSingleLine = new Composite(stack, SWT.NONE);
    GridDataFactory.fillDefaults().applyTo(compSingleLine);
    GridLayoutFactory.fillDefaults().applyTo(compSingleLine);
    valueTextSingle = new Text(compSingleLine, SWT.BORDER | SWT.SINGLE | SWT.WRAP);
    GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(valueTextSingle);
    valueTextSingle.addModifyListener(valueTextModifyListener);         
    stackLayout.topControl = compMultiLine;
    return container;
}


@Override
public void selectionChanged(SelectionChangedEvent event) {
    IStructuredSelection sel = (IStructuredSelection) event.getSelection();
    M_DATA_TYPE type = (M_DATA_TYPE) sel.getFirstElement();
    switch (type) {
    case LIST:
    case HASH_TABLE:
        stackLayout.topControl = compMultiLine;
        stack.layout();
        break;
    default:
        stackLayout.topControl = compSingleLine;
        stack.layout();
        break;
    }
}