当为子项使用自定义行布局时,SWT部分保留了太多的垂直空间

时间:2017-07-23 10:15:39

标签: layout grid swt vertical-alignment

我在GridLayout内使用Section一列。有三个"行"在网格中,具有白色背景。我希望Section抓住水平空间,但只需要根据需要增加垂直空间

每行使用自定义行布局,并有两个子项。此外,该行布局有两种状态:

a)在一条线上显示(如果总宽度足够大)

enter image description here

b)在额外的一行显示第二个孩子(红色)(如果两个孩子的窗口总宽度太小)

enter image description here

对于这两种状态,我希望截面内容(蓝色)的总高度等于行高(忽略间距)的总和。

然而,该部分的总高度是状态a)是我期望的情况b)的高度。该部分似乎保留了垂直空间,因此在调整宽度时不需要改变其高度。

如果GridLayout未放置在某个部分内,则高度正常。如果我使用标准RowLayout而不是我的自定义行布局,则总高度也很好。多余的垂直空间随着行数的增加而增加。

=> 我猜我的自定义行布局有问题吗?

=> 或者我是否需要为Section设置一个不保留空间的选项?

我的自定义布局"以某种方式沟通"父布局的第二种高度/提示?由于白色区域的高度很好而且所有行都位于顶部,因此 computeSize 方法似乎没问题。

我不希望Section保留空间。我希望它能够根据需要懒洋洋地调整其高度。

相关问题:

SWT RowLayout with last element grabbing excess horizontal space?

我的自定义布局:

package org.treez.core.adaptable.composite;

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

/**
 * A custom row layout for exactly two children. If there is not enough space for the second child it is wrapped to a
 * second line. The second child grabs excess horizontal space (which would not be possible with a normal RowLayout).
 */
public final class ExtendingRowLayout extends Layout {

    //#region ATTRIBUTES

    private int minWidthForFirstChild;

    private int minWidthForSecondChild;

    private int spacing = 5;

    //#end region

    //#region CONSTRUCTORS

    public ExtendingRowLayout() {
        this(80, 200);
    }

    public ExtendingRowLayout(int minWidthForFirstChild, int minWidthForSecondChild) {
        this.minWidthForFirstChild = minWidthForFirstChild;
        this.minWidthForSecondChild = minWidthForSecondChild;
    }

    //#end region

    //#region METHODS

    @Override
    protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
        Point extent = layoutHorizontal(composite, false, wHint, flushCache);
        return extent;
    }

    @Override
    protected void layout(Composite composite, boolean flushCache) {
        Rectangle clientArea = composite.getClientArea();
        layoutHorizontal(composite, true, clientArea.width, flushCache);
    }

    @Override
    protected boolean flushCache(Control control) {
        return true;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    private Point layoutHorizontal(
            Composite composite,
            boolean doPositionChildren,
            int clientWidth,
            boolean flushCache) {

        Control[] children = composite.getChildren();

        if (children.length != 2) {
            String message = "There must be exactly two children and not " + children.length + ".";
            throw new IllegalStateException(message);
        }

        int clientX = 0;
        int clientY = 0;
        if (doPositionChildren) {
            Rectangle rect = composite.getClientArea();
            clientX = rect.x;
            clientY = rect.y;
        }

        Control firstChild = children[0];
        Control secondChild = children[1];

        Point firstSize = firstChild.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
        Point secondSize = secondChild.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);

        int firstChildWidth = Math.max(firstSize.x, minWidthForFirstChild);

        int correctedSpacing = spacing;
        if (firstChildWidth == 0) {
            correctedSpacing = 0;
        }

        int minForSecondChildWidth = Math.max(secondSize.x, minWidthForSecondChild);
        int minForTotalWidthOfSingleLine = firstChildWidth + correctedSpacing + minForSecondChildWidth;

        int maxHeight = Math.max(firstSize.y, secondSize.y);

        int firstY = maxHeight / 2 - firstSize.y / 2;

        if (doPositionChildren) {
            firstChild.setBounds(clientX, clientY + firstY, firstChildWidth, firstSize.y);
        }

        boolean showOnSingleLine = minForTotalWidthOfSingleLine + spacing <= clientWidth;

        if (showOnSingleLine) {
            int x = clientX + firstChildWidth + correctedSpacing;
            int y = clientY + maxHeight / 2 - secondSize.y / 2;
            int width = clientWidth - firstChildWidth - correctedSpacing;
            int height = secondSize.y;

            if (doPositionChildren) {
                secondChild.setBounds(x, y, width, height);
            }

            int totalWidth = Math.max(minForTotalWidthOfSingleLine, clientWidth);
            return new Point(totalWidth, maxHeight);
        } else {
            int x = clientX;
            int y = (int) (clientY + firstSize.y + 1.5 * spacing);
            int width = Math.max(clientWidth, minWidthForSecondChild);
            int height = secondSize.y;

            if (doPositionChildren) {
                secondChild.setBounds(x, y, width, height);
            }

            int totalHeight = (int) (firstSize.y + 1.5 * spacing + secondSize.y);

            return new Point(width, totalHeight);
        }
    }

    //#end region

}

使用示例:

    package org.treez.core.adaptable.composite;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
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.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Section;

public class ExtendingRowLayoutDemo {

    public static void main(String[] args) {

        Shell shell = createShell();

        shell.setSize(500, 300);

        Section section = createSection(shell);

        Composite parentComposite = createParentComposite(section);

        createRow(parentComposite, "first");

        createRow(parentComposite, "second");

        createRow(parentComposite, "third");

        showUntilClosed(shell);

    }

    private static Shell createShell() {
        Display display = new Display();
        Shell shell = new Shell(display);

        GridLayout shellGridLayout = new GridLayout(1, false);
        shell.setLayout(shellGridLayout);
        return shell;
    }

    private static Section createSection(Shell shell) {
        Section section = new Section(
                shell,
                ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR);

        GridData gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
        section.setLayoutData(gridData);
        return section;
    }

    private static Composite createParentComposite(Section section) {

        Composite parentComposite = new Composite(section, SWT.NONE);
        section.setClient(parentComposite);

        parentComposite.setBackground(new Color(null, 0, 0, 255));

        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        parentComposite.setLayoutData(gridData);

        GridLayout gridLayout = new GridLayout(1, false);
        parentComposite.setLayout(gridLayout);

        return parentComposite;
    }

    private static Composite createRow(Composite parent, String text) {

        Composite row = new Composite(parent, SWT.NONE);
        row.setBackground(new Color(null, 255, 255, 255));

        GridData rowGridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        row.setLayoutData(rowGridData);

        Label label = new Label(row, SWT.NONE);
        label.setText(text);

        Button checkBox = new Button(row, SWT.CHECK);
        checkBox.setBackground(new Color(null, 255, 0, 0));

        ExtendingRowLayout rowLayout = new ExtendingRowLayout();
        row.setLayout(rowLayout);

        //RowLayout standardRowLayout = new RowLayout();
        //row.setLayout(standardRowLayout);

        return row;

    }

    private static void showUntilClosed(Shell shell) {

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

}

1 个答案:

答案 0 :(得分:2)

对于您要做的事情,我认为避免扩展Layout最安全(也是最简单),而是使用现有的Layout实现。在看到你附加的两个图像时想到的第一个想法是,当达到任意最小尺寸时,你在布局中有一列和两列之间切换。

考虑到这一点,您可以使用ControlListener(实际上ControlAdapter,因为我们只关心调整大小)并在达到所需的大小阈值时更新列数。例如:

public class RowControlListener extends ControlAdapter {

    // This can be computed from other values instead of being constant
    private static final int MIN_WIDTH = 200;

    private final Composite row;
    private final Section section;

    public RowControlListener(final Composite row, final Section section) {
        this.row = row;
        this.section = section;
    }

    @Override
    public void controlResized(final ControlEvent e) {
        final int width = row.getClientArea().width;
        final GridLayout rowLayout = (GridLayout) row.getLayout();
        final int numColumns = rowLayout.numColumns;
        final int updatedNumColumns = width < MIN_WIDTH ? 1 : 2;
        // Only do this if there's a change
        if (numColumns != updatedNumColumns) {
            rowLayout.numColumns = updatedNumColumns;
            section.layout(true, true);
        }
    }
}

现在我们有了一个监听器,唯一的其他变化是添加监听器,然后是一些小的化妆品更新:

public static void main(final String[] args) {
    // ... other setup ...
    final Composite row1 = createRow(parentComposite, "first");
    final Composite row2 = createRow(parentComposite, "second");
    final Composite row3 = createRow(parentComposite, "third");

    row1.addControlListener(new RowControlListener(row1, section));
    row2.addControlListener(new RowControlListener(row2, section));
    row3.addControlListener(new RowControlListener(row3, section));
    // ... other setup ...
}

GridDataLabel设置Button的细微更改。然后,我们使用widthHint上的GridData作为Label,以便所有内容排成一行。在实际设置中,应该计算并传入(类似于您当前通过传递80所做的事情),以确保它在所有情况下都大于文本的长度。

public static Composite createRow(final Composite parent, final String text) {
    // ... other setup ...
    final Label label = new Label(row, SWT.NONE);
    final GridData labelGridData = new GridData(SWT.FILL, SWT.FILL, false,
            false);
    labelGridData.widthHint = 80;
    label.setLayoutData(labelGridData);
    label.setText(text);

    final Button checkBox = new Button(row, SWT.CHECK);
    checkBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    checkBox.setBackground(new Color(null, 255, 0, 0));

    final GridLayout rowLayout = new GridLayout(2, false);
    rowLayout.marginHeight = 0;
    rowLayout.marginWidth = 0;
    row.setLayout(rowLayout);

    return row;
}

值得一提的是,我最初指定GridLayout有2列,因为我知道Shell足够容纳Shell。实际上,如果您事先不知道section.layout()大小,那么您可以随意选择1或2,但在添加侦听器后调用pan: { enabled: true, mode: 'x' }, zoom: { enabled: true, mode: 'x' } ,以便适当地重新排列。

结果:

enter image description here enter image description here