FilteredTree (JFace): пользовательский фильтр, показывающий всех дочерних элементов

#swt #eclipse-rcp #jface

#swt #eclipse-rcp #jface

Вопрос:

В настоящее время я использую FilteredTree с PatternFilter для отображения данных в виде дерева.
Моя проблема в том, что он показывает только элементы, соответствующие шаблону, и скрывает их дочерние элементы (за исключением того, что они также соответствуют шаблону).

Пример:
Дерево:

A
-B
—1
—2
-C
—1
—2

Шаблон «B» дает мне:

A
-B

Но мне нужно:

A
-B
—1
—2

Я попробовал несколько вещей, но не нашел хорошего / простого способа сделать это. Есть идеи?

Ответ №1:

Это поведение по умолчанию. Переопределите PatternFilter.isParentMatch() и PatternFilter.isLeafMatch() для получения правильных результатов.

Комментарии:

1. Я уже точно не помню, но я думаю, что соответствующие методы не могли быть переопределены. Поэтому мне пришлось скопировать весь класс и изменить их.

Ответ №2:

У меня была та же проблема: элемент был отфильтрован правильно, но дочерние элементы узла дерева не отображались. Пользователь должен был выбрать найденный узел (в надежде, что это был только один результат), затем сбросить фильтр и посмотреть, где появился выбор вместе с дочерними элементами.

Для решения этой проблемы требуется перезапись PatternFilter#isLeafMatch() и извлечение кэша (частного) поля из PatternFilter с помощью JavaReflection:

 public class MyPatternFilter extends PatternFilter {

    private final Map<Object, Object> patternFilterCache;

    public MyPatternFilter () {
        this.patternFilterCache = getCache();
    }

    @Override
    protected boolean isLeafMatch(final Viewer viewer, final Object element) {
        boolean result = super.isLeafMatch(viewer, element);
        if (result) { // element matches, now add all its children
            traverseChildren(((MyTreeNode) element).getChildren());
        }
        return resu<
    }

    // this is to traverse the children of the element found before
    // these children need to be added to the Map 'cache' to be displayed
    void traverseChildren(List<MyTreeNode> children) {  
        // assuming that child.getChildren() is never null!
        for(MyTreeNode child : children) {
            this.patternFilterCache.put(child, child.getChildren().stream().toArray(MyTreeNode[]::new));
            traverseChildren(child.getChildren());
        }
    }

    private Map<Object, Object> getCache() {
        try {
            Field cacheField = this.getClass().getSuperclass().getDeclaredField("cache"); //$NON-NLS-1$
            cacheField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map<Object, Object> cache = (Map<Object, Object>) cacheField.get(this);
            cacheField.setAccessible(false);
            return cache;
        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }

}
  

Очевидным недостатком является то, что Map ‘cache’ не является API, поэтому — теоретически — при изменении имени это решение завершится неудачей.

Ответ №3:

Так приятно опубликовать мой первый ответ!

Переопределите isElementVisible() и добавьте условие возврата.

 public class FilePatternFilter extends PatternFilter {
    @Override
    public boolean isElementVisible(Viewer viewer, Object element) {
        File file = (File) element;
        return isParentMatch(viewer, element) || isLeafMatch(viewer, element) || isLeafMatch(viewer, file.getParent());
    }
}