Подтвердить что ты не робот

Почему SWT Composite иногда требует правильного преобразования размера() в макет?

Иногда мы сталкиваемся с SWT-композицией, который абсолютно отказывается правильно отлаживаться. Часто мы сталкиваемся с этим, когда мы вызываем dispose на композите, а затем заменяем его другим; хотя это, судя по всему, строго не ограничивается этим случаем.

Когда мы сталкиваемся с этой проблемой, примерно в 50% случаев, мы можем называть pack() и layout() на оскорбительном композите, и все будет хорошо. Примерно в 50% случаев мы должны это сделать:

Point p = c.getSize();
c.setSize(p.x+1, p.y+1);
c.setSize(p);

Мы это случались практически с каждой комбинацией менеджеров макетов и т.д.

Хотелось бы, чтобы у меня был хороший, простой, воспроизводимый случай, но я этого не делаю. Я надеюсь, что кто-то узнает эту проблему и скажет: "Ну, ну, тебе не хватает xyz...."

4b9b3361

Ответ 1

Мне кажется, что кеш-память устарела и нуждается в обновлении.

Макеты в SWT-кешах поддержки и обычно кэшируют предпочтительные размеры элементов управления или независимо от того, что им нравится кэшировать:

public abstract class Layout {
    protected abstract Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache);
    protected boolean flushCache (Control control) {...}
    protected abstract void layout (Composite composite, boolean flushCache);
}

Я относительно новичок в программировании SWT (бывший программист Swing), но столкнулся с аналогичными ситуациями, в которых макет не был должным образом обновлен. Обычно я мог их разрешить, используя другие методы компоновки, которые также приведут к тому, что макет будет очищать его кеш:

layout(boolean changed)

layout(boolean changed, boolean allChildren)

Надеюсь, что это поможет...

Ответ 2

Тем временем я узнал немного больше о недостатках SWT при изменении или изменении частей иерархии управления во время выполнения. ScrolledComposite и ExpandBar необходимо также явно обновлять, когда они должны адаптировать свои минимальные или предпочтительные размеры контента.

Я написал небольшой вспомогательный метод, который переопределяет макет иерархии элементов управления для измененного элемента управления:

public static void revalidateLayout (Control control) {

    Control c = control;
    do {
        if (c instanceof ExpandBar) {
            ExpandBar expandBar = (ExpandBar) c;
            for (ExpandItem expandItem : expandBar.getItems()) {
                expandItem
                    .setHeight(expandItem.getControl().computeSize(expandBar.getSize().x, SWT.DEFAULT, true).y);
            }
        }
        c = c.getParent();

    } while (c != null && c.getParent() != null && !(c instanceof ScrolledComposite));

    if (c instanceof ScrolledComposite) {
        ScrolledComposite scrolledComposite = (ScrolledComposite) c;
        if (scrolledComposite.getExpandHorizontal() || scrolledComposite.getExpandVertical()) {
            scrolledComposite
                .setMinSize(scrolledComposite.getContent().computeSize(SWT.DEFAULT, SWT.DEFAULT, true));
        } else {
            scrolledComposite.getContent().pack(true);
        }
    }
    if (c instanceof Composite) {
        Composite composite = (Composite) c;
        composite.layout(true, true);
    }
}

Ответ 3

Составная компоновка отвечает за укладку дочерних элементов этого композита. Поэтому, если составной размер не изменяется, но необходимо обновить относительные позиции и размеры детей, вы вызываете layout() в композите. Если, однако, размер или положение самого композита необходимо обновить, вам придется вызывать layout() в своем родительском составе (и так далее, пока не дойдете до оболочки).

Эмпирическое правило: если вы добавили или удалили элемент управления или иным образом сделали то, что требует ретрансляции, поднимите иерархию виджетов, пока не найдете композит с полосами прокрутки и вызовите layout() на нем. Причиной остановки на композите с полосами прокрутки является то, что его размер не изменится в ответ на изменение - его полоски прокрутки будут "поглощать" это.

Обратите внимание, что если изменение, требующее макета, не является новым дочерним или удаленным дочерним, вы должны вызвать Composite.changed(new Control[] {changedControl}) перед вызовом макета.

Ответ 4

Я только что узнал о Composite.changed(Control [] children). Существует обширная статья, которую я прочитал пару лет назад:

http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html

В этой статье также упоминается Composite.layout(boolean changed, boolean all), чтобы обновить макет: "Вызов макета() совпадает с вызовом layout (true), который сообщает ColumnLayout, чтобы очистить свои кеши до установки границ детей". Это все правильно и что я делаю с тех пор. Но это не то, что нужно, поскольку он в основном побеждает преимущество кеша макета, когда вы хотите обновить макет, потому что один или несколько элементов управления изменили требования.

Представьте, что у вас есть куча виджетов StyledText в GridLayout, и вам нужно изменить размер одного из них. Вызов computeSize() на StyledText очень дорого. Вместо этого:

Неправильно:

parent.layout(true);

... который вызывает computeSize() для всех детей, хотя их требования не изменились. Вы должны сделать это:

Справа:

parent.changed(new Control[] { theChangedChild });

Тогда либо

rootComposite.layout(false, true);

или

parent.layout(false);

Не очень интуитивно. Параметр layout() просто плохо назван. Вместо того, чтобы называть его "измененным", его следовало называть "ignoreCache" или что-то в этом роде. Интуитивная вещь - передать "истину", когда что-то изменилось. Вместо этого вам нужно передать "ложь", но недействить кеш только для измененного элемента управления с измененным(), прежде чем вы сделаете...

Обратите внимание, что вызов changed() также рекурсивно недействителен для кеша только для родительского элемента управления в его собственном родителе, что совершенно имеет смысл. Поэтому, когда вы вызываете layout(), вы должны вызывать его на корневой компоновке (чаще всего в оболочке) со значением all = true, если вы не знаете, что размер родителя измененного элемента управления будет или не сможет измениться в ответ.