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

Всегда приятно общаться с приложением, которое запоминает твои повадки и словно чувствует тебя, того, что ты хочешь. Любая 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 один раз зарегистрировать слушателя, а на остальном подграфе он зарегистрируется сам…

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

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *