在为其子级使用自定义行布局时,SWT Section保留了过多的垂直空间 [英] SWT Section reserves too much vertical space when using a custom row layout for its children

查看:87
本文介绍了在为其子级使用自定义行布局时,SWT Section保留了过多的垂直空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将GridLayoutSection中的一列一起使用.网格中有三个行",背景为白色.我希望Section抓住水平空间,但只获得所需的垂直空间:

I use a GridLayout with one column inside a Section. There are three "rows" in the grid, having a white background. I would like the Section to grab horizontal space but only as much vertical space as needed:

每行都使用自定义行布局,并且有两个子级.此外,该行布局具有两种状态:

Each row uses a custom row layout and has two children. Furthermore, that row layout has two states:

a)单行显示(如果总宽度足够大)

a) show on a single line (if total width is large enough)

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

b) show second child (red) on an extra line (if total width of window is too small for both children)

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

For both states I would expect the total height of the Section content (blue) to equal the sum of the heights of the rows (neglecting spacing).

但是,该部分的总高度为状态a)是我对情况b)所期望的高度.该部分似乎以某种方式保留了垂直空间,因此在调整宽度时无需更改其高度.

However, the total height of the Section is state a) is the height that I expect for case b). The section somehow seems to reserve vertical space so that it does not need to change its height when the width is adapted.

如果GridLayout未放置在节内,则高度可以.如果我使用标准的RowLayout而不是自定义的行布局,则总高度也可以.多余的垂直空间随着行数的增加而增加.

If the GridLayout is not placed inside a section, the height is fine. If I use a standard RowLayout instead of my custom row layout, the total height is also fine. The excess vertical space increases with the number of rows.

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

=> 还是我必须为Section设置一个选项以不保留空间?

=> Or is there an option that I have to set for the Section to not reserve space?

我的自定义布局是否应该以某种方式传达"父布局的第二种高度/提示类型?由于白色区域的高度很好,并且所有行都位于顶部,因此 computeSize 方法似乎很好.

Should my custom layout "somehow communicate" a second type of height/hint for the parent layout? Since the height of the white region is fine and all rows are located at the top, the method computeSize seems to be fine.

我不希望Section保留空间.我希望它根据需要懒惰地调整其高度.

I don't want the Section to reserve space. I want it to adapt its height lazily / as required.

相关问题:

带有最后一个元素的SWT RowLayout占用了多余的水平空间吗? /a>

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();
    }

}

推荐答案

对于您尝试做的事情,我认为避免扩展Layout是最安全(也是最简单)的做法,而是使用现有的实施.看到附加的两张图片时,首先想到的是,当达到某个任意的最小尺寸时,您将在布局中的一列和两列之间进行切换.

For what you're trying to do, I think it would be safest (and easiest) to avoid extending Layout, and instead use an existing Layout implementation. The first thought that comes to mind when seeing the two images you've attached is that you're switching between having one and two columns in a layout when some arbitrary minimum size is reached.

请记住,您可以改用ControlListener(实际上是ControlAdapter,因为我们只关心调整大小),并在达到所需的大小阈值时更新列数.例如:

With that in mind, you could instead use a ControlListener (actually ControlAdapter because we only care about resizing) and update the number of columns when the desired size threshold is reached. For example:

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);
        }
    }
}

现在我们有了一个侦听器,唯一的其他更改是添加了侦听器,然后进行了一些小小的外观更新:

Now that we have a listener, the only other changes are to add the listener, and then some minor cosmetic updates:

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 ...
}

LabelButton上的设置GridData的微小更改.然后,将GridData上的widthHint用于Label,以便所有内容对齐.在实际的设置中,应该计算并传递该值(类似于您通过80传递当前所做的操作),以确保在所有情况下都大于文本的长度.

Minor changes of setting GridData on the Label and Button. We then use the widthHint on the GridData for the Label so that everything lines up. In an actual setup, that should be computed and passed in (similar to what you're doing currently by passing in 80) so that it's guaranteed to be greater than the length of the text in all cases.

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的大小,可以执行的操作是任意选择1或2,但是在添加侦听器之后调用section.layout(),以便适当地重新安排内容.

It's worth mentioning that I've initially specified the GridLayout to have 2 columns because I knew that the Shell would be large enough to accommodate. In reality, if you don't know the Shell size in advance, what you can do is arbitrarily pick 1 or 2, but call section.layout() after the listeners are added, so that things are rearranged appropriately.

结果:

这篇关于在为其子级使用自定义行布局时,SWT Section保留了过多的垂直空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆