Команда плагинов для настройки JavaFX компонент в настольном приложении

в 18:50, , рубрики: adapter, java, javafx, Node, plugin, Программирование

Всегда приятно общаться с приложением, которое запоминает твои повадки и словно чувствует тебя, того, что ты хочешь. Любая UI библиотека или платформа обладает увы лишь базовым функционалом и набором компонент. Например, если колонка в таблице не перемещается или по ней нельзя отсортировать, то такое приложение, в котором она используется сложно назвать дружелюбным. К счастью, сегодня таким функционалом никого не удивишь. Однако, не каждая программа запомнит положение этой колонки и в следующем сеансе отобразит ее именно на том же месте. Возможно, также будет раздражать каждый раз устанавливать положение разделителя в SplitPane или вводить одни и те же параметры фильтра. Как правило, такие удобства приходится обеспечивать самим разработчикам.

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

Но он далеко не единственный. Что, если взять возможности платформы, которая поддерживает шаблон обозреватель для дочерних Node, и при добавлении или удалении подграф Node прогонять через набор плагинов, каждый из которых занимается своей специфической работой? Один умеет все запоминать и восстанавливать при повторном сеансе, другой — указанным компонентам меняет контекстное меню, добавляя функцию копирования текста. Кто-то из них добавляет три точки в конце текста, если он не умещается, а при наведении мыши показывает подсказку с полным текстом, только если он не уместился. Самое главное, что не важно из какой библиотеки этот компонент, можем ли мы от него наследоваться и переопределить нужное нам поведение. Все что нам в таком случае надо — научить плагин работать с нужными компонентами при необходимости по разному.

Таким мог бы быть слушатель коллекции дочерних элементов:

private final ListChangeListener changeListener = (ListChangeListener<Node>) (ListChangeListener.Change<? extends Node> c) -> {
        if (c.next()) {
            c.getAddedSubList().forEach(this::applySettingsForNodeAndAddListenerForItsChild);
        }
    };

Таким бы — код обработки каждой измененной Node-ы:

private void applySettingsForNodeAndAddListenerForItsChild(Node n) {
        if (!checkApplySettings(n)) {
            apply(n);
            ObservableList<Node> children = getChildren(n);
            if (children != null) {
                addListnerForUpdateChildren(children);
            }
            markNodePropertyApplied(n);
        }
    }

А таким — непосредственно код вызова самого плагина, который зарегистрирован на этот тип компонент:

public Node apply(Node node) {
        List<SettingsPlugin> settingsPlugins = settingsMap.get(Node.class);
        if (settingsPlugins != null) {
            for (SettingsPlugin plugin : settingsPlugins) {
                node = plugin.apply(node, userSettings.getSettings());
            }
        }
        List<SettingsPlugin> settingList = settingsMap.get(node.getClass());
        if (settingList != null) {
            for (SettingsPlugin plugin : settingList) {
                node = plugin.apply(node, userSettings.getSettings());
            }
        }
        return node;
    }

Вот интерфейс самого плагина:

public interface SettingsPlugin {
    public Node apply(Node node, Map<String, Object> userSettings);
}

Необходимо только на коллекции дочерних элементов Root элемента Scene один раз зарегистрировать слушателя, а на остальном подграфе он зарегистрируется сам...

Последнее время провожу аналогию по возможностям платформ для настольных и веб приложений. Было бы интересно узнать, как подобный функционал можно реализовать на разных фреймворках.

Автор: Сергей Лысенко

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js