
Портянкин И. Swing
.pdf
Управление пространством |
375 |
что язык HTML можно также использовать в тексте подсказки для вкладки, а к тексту ярлычка вкладки нетрудно сразу же присоединить значок, у вас есть отличный способ сообщить пользователю все необходимое о вкладках в вашем интерфейсе, так что ему не придется слишком часто обращаться к системе помощи и работать он сможет гораздо эффективнее.
Созданные панели с вкладками JTabbedPane мы добавляем в наше окно, для которого предварительно устанавливается табличное расположение GridLayout (конструктор GridLayout без параметров позволяет располагать компоненты в одну строку). Благодаря этому окно будет разделено на две одинаковые части, в которых и расположатся наши панели с вкладками. Остается только вывести окно на экран. Запустив программу с примером, вы сможете оценить различные варианты расположения вкладок и их внешний вид, а в случае со второй панелью с вкладками посмотреть на всплывающие подсказки.
Описанные нами параметры панели JTabbedPane и отдельных вкладок можно изменять не только при вызове конструктора или метода addTab(). В классе JTabbedPane все свойства, использованные нами, описаны как свойства JavaBeans с набором методов get/set, так что параметры панели с вкладками и сами вкладки в любой момент нетрудно изменить, вызвав соответствующие методы. Опишем теперь все задействованные нами свойства, чтобы они всегда были у вас «под рукой» (табл. 13.1).
Таблица 13.1. Основные свойства панели с вкладками JTabbedPane
Свойства |
Описание |
(и методы get/set) |
|
tabPlacement |
Задает расположение ярлычков вкладок у одной из четырех сторон панели. |
|
По умолчанию (если вызывается конструктор JTabbedPane без параметров) |
|
ярлычки располагаются вверху панели (TOP). Располагать ярлычки справа |
|
или слева лучше, если связанные с вашими вкладками компоненты занима- |
|
ют по оси Y больше места, чем по оси X, в противном случае ярлычки удоб- |
|
нее поместить сверху или снизу |
tabLayoutPolicy |
Определяет, как будут располагаться вкладки в том случае, если в контей- |
|
нере не хватит места для их размещения в один ряд. Как мы уже отмеча- |
|
ли, режим WRAP_TAB_LAYOUT (используемый по умолчанию) позволяет |
|
разместить вкладки в несколько рядов, а режим SCROLL_TAB_LAYOUT — |
|
оставить их в одном ряду с добавлением кнопки прокрутки. В большинстве |
|
случаев пользователю лучше подойдет режим с прокруткой, так как не- |
|
сколько рядов вкладок занимают слишком много места на экране (особенно |
|
когда вкладок много), что затрудняет переход на нужную вкладку |
376 |
ГЛАВА 13 |
Таблица 13.1 (продолжение) |
|
|
|
Свойства |
Описание |
(и методы get/set) |
|
titleAt, iconAt |
Эти свойства позволяют задать надпись и значок для ярлычка любой вклад- |
|
ки (не забывайте о том, что у вас в руках вся «магия» HTML). В качестве пер- |
|
вого параметра требуется указать индекс вкладки (отсчет ведется с нуля) |
toolTipTextAt |
Это свойство отвечает за текст всплывающей подсказки к каждой вкладке. |
|
Первым параметром метода также является индекс вкладки. |
componentAt |
Данное свойство позволяет получить или заменить компонент, который по- |
|
казывает вкладка уже после того, как вы настроили ее методом addTab(). |
|
Не следует путать со свойством tabComponentAt, которое отвечает за саму |
|
вкладку и которое мы рассмотрим чуть позже |
foregroundAt, |
Удобные свойства для задания цвета шрифта и фона отдельной вкладки (ко- |
backgroundAt |
торая, как нетрудно догадаться, задается индексом). В примере мы не ис- |
|
пользовали эти свойства, но иногда они помогают просто и быстро выделить |
|
одну из вкладок (или щедро расцветить все вкладки, если это соответствует |
|
интерфейсу вашего приложения) |
Все свойства панели с вкладками JTabbedPane довольно стандартны, отметить можно лишь необычный формат этих свойств и суффикс «At» в конце их названий. Эти свойства индексированные (в качестве первого параметра методов get/set нужно указывать числовой индекс), так как вкладок в панели может быть много, а свойства надо задавать отдельно для каждой из них. Вам не обязательно добавлять все вкладки и соответствующие им компоненты перед выводом панели с вкладками на экран, это вполне можно делать динамически, прямо во время работы программы, вызывая метод addTab() в нужный момент. Это может пригодиться, если какая-то часть вашего пользовательского интерфейса должна стать доступной позже остальных (хотя, как мы увидим чуть позже, можно отключить вкладку и включать ее при необходимости). Добавлять вкладки можно не только методом addTab(). Вы можете попробовать метод insertTab(), параметры которого аналогичны параметрам метода addTab() и который позволяет вставить вкладку в произвольное место панели JTabbedPane.
Таким образом, обычно работа с панелью JTabbedPane сводится к заданию текста (и/ или значка) для ярлычка вкладки и ее содержимого, в качестве которого чаще всего выступает панель JPanel с группой компонентов вашего пользовательского интерфейса (хотя содержимым вкладки может быть любой компонент, унаследованный от базового класса JComponent). При необходимости в панель нетрудно динамически добавить новые вкладки или изменить все свойства уже имеющихся вкладок.
Модель выделения и обработка событий
Панель с вкладками JTabbedPane чаще всего используется в качестве вспомогательного инструмента, позволяющего эффективно распределить доступное пространство контейнера, и поэтому дополнительные действия с ней проводить требуется крайне редко. На самом деле, вы сопоставляете вкладкам набор компонентов вашего пользовательского интерфейса, а о переключении вкладок вам заботиться не нужно. Когда пользователь переходит на другую вкладку (щелчком мыши или нажатием клавиш), все необходимые действия выполняют внутренние механизмы класса JTabbedPane, так что для программиста все в итоге сводится к выводу панели с вкладками на экран.
Впрочем, при необходимости вы можете расширить «свое сотрудничество» с панелью JTabbedPane и более подробно отслеживать, что с ней происходит. Относится это
Управление пространством |
377 |
прежде всего к переключению вкладок. Вы можете следить за этим и в любой момент времени узнать, какая вкладка активна, поскольку у класса JTabbedPane есть помощник — модель выделения единственного элемента SingleSelectionModel. Эта модель и хранит информацию об активной вкладке и при необходимости (когда ей сообщает об этом UIпредставитель) меняет текущий индекс вкладки. Модель SingleSelectionModel является, пожалуй, самой простой моделью из всех моделей Swing. Она хранит целое число — индекс активной вкладки (когда это число равно —1, считается, что в данный момент активных вкладок нет) и поддерживает методы для присоединения и отсоединения слушателей ChangeListener, которые оповещаются при смене активной вкладки. Действительно, невозможно придумать модель проще. По умолчанию в панели JTabbedPane используется стандартная реализация этой модели, класс с именем DefaultSingleSelectionModel, и вряд ли вам понадобится вмешиваться в его работу или писать свою модель «с нуля». Хранение одного числа не стоит таких усилий, с ним вполне может справиться и стандартная модель. Тем не менее, это самая настоящая модель, так что вы можете разделять ее между несколькими панелями с вкладками, сохраняя в ней информацию о выделенном элементе. Такое поведение может вам пригодится, если у вас в программе есть несколько одинаковых панелей JTabbedPane, активная вкладка в которых должна быть всегда одной и той же.
Единственное событие, поддерживаемое панелью с вкладками JTabbedPane (за исключением низкоуровневых событий, общих для всех графических компонентов), относится именно к модели ее активизации. Добавив к панели JTabbedPane (или к ее модели выделения, это одно и то же, различным будет лишь источник события) слушателя ChangeListener, вы сможете узнавать об активизации следующей вкладки. Практическое применение такому событию придумать непросто, однако оно может быть полезно при реализации разного рода отложенных вычислений. Например, если какая-либо вкладка содержит сложные компоненты, выводить на экран которые дорого и долго, вы можете отложить их создание до того момента, когда пользователь реально перейдет на эту вкладку. Он может этого и не сделать — тогда вы сэкономите его время и ресурсы компьютера. Кроме того, панель JTabbedPane позволяет узнать, в каком месте экрана находится та или иная вкладка, а также к какой вкладке относится некоторая точка экрана. Иногда, при нестандартной обработке событий от мыши, такая возможность бывает полезной, например, при реализации своей системы помощи, когда для каждой вкладки имеются свои советы и свои разделы в справочной системе (прототип подобной системы был создан нами в главе 6).
Создадим несложный пример и посмотрим, как можно использовать события панели с вкладками JTabbedPane:
//TabSelection.java
//Работа с активными вкладками и обработка событий import javax.swing.*;
import javax.swing.event.*; import java.awt.event.*; import java.awt.*;
public class TabSelection extends JFrame { public TabSelection() {
super("TabSelection"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем нашу панель с вкладками
JTabbedPane tabs = new JTabbedPane();
378 |
ГЛАВА 13 |
tabs.addTab("Вкладка", new JPanel()); tabs.addTab("Еще вкладка", new JPanel()); // добавляем слушателя событий
tabs.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) {
// добавляем на вкладку новый компонент
JPanel panel = (JPanel) ((JTabbedPane)e.getSource()).
getSelectedComponent(); panel.add(new JButton("Кнопка"));
}
}); // работа с низкоуровневыми событиями
tabs.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { // узнаем, на какой вкладке был щелчок int idx = ((JTabbedPane)e.getSource()). indexAtLocation(e.getX(), e.getY());
JOptionPane.showMessageDialog( null, "Index: " + idx);
}
}); // выводим окно на экран
add(tabs); setSize(400, 300); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new TabSelection(); } });
}
}
Здесь мы создаем небольшое окно, в которого добавляем панель с вкладками JTabbedPane. Для создания последней используется конструктор без параметров, так что ярлычки вкладок будут располагаться наверху панели (так принято по умолчанию). Сама панель с вкладками очень проста: мы добавляем в нее только две вкладки, которым сопоставляем пустые панели JPanel (как вы помните, по умолчанию в таких панелях имеет место последовательное расположение FlowLayout). В эти панели мы будем динамически добавлять компоненты. Интереснее посмотреть на то, как к панели с вкладками присоединяются слушатели событий. Первый слушатель (ChangeListener) следит за переходами между вкладками. Можно было бы присоединить его и к модели выделения SingleSelectionModel, но нам в примере удобнее получать в качестве источника события

Управление пространством |
379 |
саму панель с вкладками, так что слушателя мы присоединяем к ней. Когда пользователь переходит на другую вкладку, вызывается метод stateChanged() нашего слушателя, в котором мы определяем источник события (объект JTabbedPane), с помощью удобного метода getSelectedComponent() получаем компонент, соответствующий активной вкладке, преобразуем его к панели JPanel (а мы знаем, что нашим вкладкам соответствуют панели) и добавляем в нее новую кнопку JButton. Таким образом каждый раз при смене вкладки в нее добавляется еще одна кнопка. Не слишком полезное поведение программы, но как мы уже упомянули, в реальных приложениях примерно таким же образом можно реализовать отложенные вычисления.
Далее демонстрируется, как можно работать с панелью JTabbedPane на самом низком уровне. К ней присоединяется слушатель событий от мыши, позволяющий следить за щелчками мыши внутри нашей панели с вкладками. Когда пользователь щелкает мышью где-то на панели с вкладками, нам передаются только координаты щелчка. Определить, на какой вкладке щелкнул пользователь, позволяет метод indexAtLocation(). Он возвращает индекс вкладки, если пользователь щелкнул на вкладке, или —1, если щелчок был не на вкладке, а где-то в другом месте панели. Вы можете применять подобный подход, к примеру, если вам потребуется отдельное выпадающее меню для каждой вкладки, а не единственное меню для компонента JTabbedPane целиком.
Запустив программу с примером, вы сможете увидеть, как панель с вкладками позволяет следить за активной вкладкой и динамически обновлять панель содержимого при переходе на другую вкладку. Вы также сможете увидеть, как работает метод indexAtLocation(), и при случае использовать его в своих программах, хотя такая необходимость возникает редко.
Дополнительные возможности компонента JTabbedPane
Рассмотренных нами возможностей панели с вкладками JTabbedPane с лихвой хватает для ее традиционной работы в хорошем пользовательском интерфейсе: вы можете задавать любые надписи и значки для ярлычков вкладок, сопоставлять с вкладками любые компоненты, следить за изменениями состояния вкладок, и пользователь без затруднений сможет переключаться между вкладками щелчками мыши или нажатиями клавиш (список поддерживаемых панелью JTabbedPane сочетаний клавиш вы можете найти в интерактивной документации Java, эти клавиатурные сокращения практически идентичны для всех стандартных внешних видов). Тем не менее, создатели Swing на этом не остановились и добавили в класс JTabbedPane несколько возможностей, которые помогут сделать ваш пользовательский интерфейс отточенным и продуманным до мелочей.
Очень полезной является возможность задать для вкладки мнемонику, знакомую нам «со времен» изучения глав 8 и 9. Как вы помните, это сочетание управляющей клави-
380 |
ГЛАВА 13 |
ши (как правило, Alt) и клавиши латинского символа (проблемы с русскими символами мы уже обсудили), при нажатии которых подходящая вкладка мгновенно активизируется. Мнемоники позволяют максимально ускорить процесс работы с приложением для опытных пользователей, позволяя перемещаться по интерфейсу молниеносными комбинациями клавиш. Однако, как мы уже знаем, в Swing из-за проблем с символами других языков число мнемоник довольно ограничено (приходится укладываться в 26 символов латинского алфавита). Впрочем, панель с вкладками чаще всего встречается в диалоговых окнах, а в отдельных окнах все сочетания клавиш собственные, так что за нехватку символов, как правило, можно не опасаться.
Также весьма полезной является возможность отключения вкладок. Она особенно эффективна в приложениях, работающих с несколькими видами документов или с многочисленными пользователями, когда доступность некоторых частей пользовательского интерфейса определяется тем, кто или как работает с приложением. Отключив вкладку, вы отключаете сразу целую группу элементов пользовательского интерфейса и недвусмысленно даете понять пользователю, что данные функции в текущем режиме работы приложения недоступны.
Рассмотрим небольшой пример использования описанных возможностей панели
свкладками, чтобы сразу же освоиться с ними:
//TabAdditionalFeatures.java
//Дополнительные возможности панелей с вкладками import javax.swing.*;
import java.awt.*;
public class TabAdditionalFeatures extends JFrame { public TabAdditionalFeatures() {
super("TabAdditionalFeatures"); setDefaultCloseOperation(EXIT_ON_CLOSE); // панель с вкладками
JTabbedPane tabs = new JTabbedPane(); tabs.addTab("Первая вкладка", new JPanel()); tabs.addTab("Вторая вкладка (S)", new JPanel()); tabs.addTab("Интересная вкладка", new JPanel());
//зададаем мнемоники tabs.setMnemonicAt(0, 'П'); tabs.setMnemonicAt(1, 'S'); tabs.setMnemonicAt(2, 'И');
//активизируем последнюю вкладку tabs.setEnabledAt(2, false);
//выводим окно на экран add(tabs);
setSize(430, 300); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(

Управление пространством |
381 |
new Runnable() {
public void run() { new TabAdditionalFeatures(); } });
}
}
Мы наследуем наш класс от окна с рамкой JFrame и добавляем в панель содержимого панель с вкладками JTabbedPane. У нас будет три вкладки, для простоты мы сопоставляем им пустые панели JPanel. Для каждой из вкладок мы устанавливаем мнемоники методом setMnemonicAt(); этому методу помимо символа мнемоники нужно указать индекс вкладки, для которой меняется мнемоника. Для двух вкладок мы передали в качестве мнемоник русские символы (и вы увидите их подчеркнутыми на экране, только вот работать они, увы, не будут; подробно причины и возможные решения этой проблемы мы обсуждали в главе 9), а для третьей использовали не слишком эстетичный, но зато надежный способ, включив в мнемонику латинский символ, который мы к тому же добавили к русской надписи в скобках, так что пользователь сможет увидеть мнемонику вкладки. Помимо мнемоник пример демонстрирует и отключение вкладок: методом setEnabledAt() мы отключили последнюю (судя по надписи, самую интересную) вкладку, так что перейти на нее пользователь не сможет.
Запустив программу с примером, вы сможете увидеть, как работает мнемоника с латинским символом и как не работают мнемоники с символами русскими (к этому мы уже успели привыкнуть), а также убедитесь в том, что на отключенную вкладку перейти на самом деле нельзя, как бы ни было интересно ее содержимое. Мы уже обсудили ситуации, в которых можно применить это свойство панелей с вкладками, оно на самом деле довольно полезно.
Размещение компонентов во вкладках
До сих пор рассматриваемые нами вкладки были полностью информационными компонентами — мы могли размещать на них текст и значки, а компонент JTabbedPane сам отслеживал способы их переключения и решал, как рисовать на них. Однако в последнее время вкладки наделяются все большими полномочиями, в качестве самого популярного расширения можно вспомнить маленькие кнопки на вкладках, закрывающие их, если интерфейс приложения подразумевает такое поведение (вспомните все популярные браузеры). Библиотека Swing также не осталась в стороне и позволяет тонко настроить внешний вид вкладок.
Вспомнив другие компоненты библиотеки Swing, можно было бы подумать, что компонент JTabbedPane предлагает рисовать вкладки отдельному рисующему объекту (renderer), как мы это уже видели в списках и еще не раз увидим. Однако в случае с вкладками все одновременно и проще и сложнее. Как таковой «прорисовки» нет, вы просто можете добавить в то место, где рисуется вкладка, свой собственный компонент, будь то простая кнопка или плотно упакованная компонентами сложная составная панель. Казалось бы, нет ничего проще, чем добавить свой компонент и нарисовать в нем все, что нам нужно, однако здесь есть свои весьма острые подводные камни. Давайте сразу же
382 |
ГЛАВА 13 |
рассмотрим несложный пример, в котором попробуем добавить кнопку для закрытия вкладки и флажок, а затем обсудим все его тонкости:
//TabComponents.java
//Размещение компонентов во вкладках import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class TabComponents extends JFrame { public TabComponents() {
super("TabComponents"); setDefaultCloseOperation(EXIT_ON_CLOSE); // панель с вкладками
final JTabbedPane tabs = new JTabbedPane(); tabs.addTab(null, new JPanel()); tabs.addTab(null, new JPanel());
// флажок во вкладке
JCheckBox checkBox = new JCheckBox("Флажок!"); checkBox.setOpaque(false); tabs.setTabComponentAt(0, checkBox);
// вкладка с надписью и кнопкой закрытия
final JPanel panel = new JPanel();
JLabel label = new JLabel("Можно закрыть!");
JButton closeButton = new JButton(new AbstractAction() {
{
putValue(SMALL_ICON, new ImageIcon("close.png"));
}
public void actionPerformed(ActionEvent e) {
// нужно определить вкладку, в которой находится кнопка tabs.removeTabAt(
tabs.indexOfTabComponent(panel));
}
});
closeButton.setBorderPainted(false);
closeButton.setContentAreaFilled(false);
panel.setOpaque(false);
panel.add(label);
panel.add(closeButton); tabs.setTabComponentAt(1, panel); // выводим окно на экран add(tabs);

Управление пространством |
383 |
setSize(430, 300); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new TabComponents(); } });
}
}
Пример очень прост — мы создаем всего лишь одну панель со вкладками JTabbedPane и добавляем ее в центр окна. В ней будут находиться две вкладки, содержащие две пустые панели. Так как мы собираемся отдать отображение вкладок отдельным компонентам, вместо названия вкладок в метод addTab() первым параметром передаются пустые ссылки null.
Далее настает черед настроить компоненты. Первой вкладкой будет заниматься флажок JCheckBox. Мы создаем его и устанавливаем свойство непрозрачности (opaque) в false, чтобы он своим цветом фона не перекрывал фон и внешний вид вкладки, в противном случае картина получится не слишком привлекательная. Не во всех внешних видах Swing флажки непрозрачны, однако во внешнем виде по умолчанию это именно так, и на всякий случай стоит всегда менять это свойство. После этого нам остается указать, что для первой вкладки мы будем использовать флажок методом setTabComponentAt(), требующим в качестве параметров номер вкладки и ссылку на компонент.
Для второй вкладки настраивается отдельная панель, которая будет отображать название вкладки (с помощью надписи) и хранить кнопку для закрытия вкладки. Если с надписью все очевидно, то кнопка создается на основе команды Action, что позволяет нам «одним махом» указать и значок кнопки, и описать, что будет происходить при нажатии кнопки1. Закрыть вкладку оказывается не совсем просто — как нам узнать, на какой вкладке пользовать нажал кнопку? Следить за курсором мыши в этом случае не слишком-то рационально — в разных внешних видах и операционных системах кнопку можно нажать по-разному. В примере нам помогает метод indexOfTabComponent() класса JTabbedPane, который позволяет узнать, на какой вкладке находится данный компонент. Так как наша кнопка находится во вспомогательной панели, нам приходится передавать ее в этот метод, чтобы узнать, в какой же вкладке мы находимся, и наконец удалять ее. Если бы у нас было много вкладок с кнопками, нам бы пришлось хранить в слушателе нажатия кнопки ссылку на панель, которая размещается во вкладке.
1 Мы подробно обсуждали команды Action в главе 9, посвященной кнопкам.
384 |
ГЛАВА 13 |
Запустив пример, вы увидите совершенно отдельные компоненты на вкладках, и сможете с легкостью закрыть вторую вкладку. Однако нетрудно будет заметить довольно много мелочей, которые делают работу с вкладками не такой гладкой, как нам хотелось бы:
Флажок «отбирает» у вкладки нажатие мыши для того чтобы сменить свое состояние, и вкладка в этом случае не переключается, что с точки зрения пользователя неудобно и недопустимо. Решением станет дополнительный слушатель событий от мыши, который в этом случае станет переключать вкладку, даже если пользователь щелкнул на флажке. Еще один вопрос — если пользователь переключился на вкладку, не нажимая на флажок, нужно ли переключать последний? Если да, нужно будет отдельно следить и за этим событием.
У флажка и у кнопки закрытия есть свой фокус ввода. Эффект получается такой, как если бы вкладка получала фокус несколько раз. Чтобы сделать работу более приятной, скорее всего, имеет смысл отключить фокус ввода для кнопок, если это приемлемо.
Мы задаем текст для флажка и надписи при их создании, но что будет, если надпись для вкладки поменяется динамически во время работы программы? Опятьтаки необходимо следить за этим вручную, неплохим вариантом может быть слежение за привязанным свойством «indexForTitle», с помощью метода JavaBeans addPropertyChangeListener(), доступного в каждом компоненте Swing.
Как видно, для того чтобы вкладки с отдельными компонентами работали неотличимо от своих стандартных собратьев, нужны немалые усилия и тонкая настройка внешнего вида. Наверняка некоторые готовые решения можно найти среди бесплатно доступных библиотек, однако в общем случае стоит применять стандартные вкладки, а возможность размещать на них компоненты, как бы она не была привлекательна, использовать лишь в случае, когда это действительно оправдано, а не просто для украшения интерфейса.
Разделяемая панель JSplitPane
Разделяемая панель JSplitPane служит для гибкого распределения пространства между двумя компонентами, разместить которые на экране одновременно обычным порядком (просто добавляя в контейнер) по каким-либо причинам не получается (чаще всего из-за нехватки места в этом самом контейнере). Сама по себе разделяемая панель незатейлива и не производит особого впечатления: это разделенный тонкой полосой контейнер с двумя содержащимися в нем компонентами. Перетаскивая мышью или назначенными внешним видом клавишами полосу (так называемую вешку разбивки), пользователь может увеличивать размер одного компонента, «отбирая» пространство у другого компонента.
Использовать разделяемую панель особенно удобно там, где есть требовательные к пространству контейнера компоненты, одновременная работа с которыми маловероятна. Работая с одним компонентом, пользователь сможет для удобства увеличить его, а при переключении на другой компонент увеличить размер последнего. В качестве примера можно вспомнить разнообразные среды разработки программ, использующие разделяемые панели для размещения редактора кода или форм и дерева проекта. При работе с проектом вам нужно больше места под его дерево и таблицу свойств, а при написании кода место требуется уже под редактор и диагностические сообщения компилятора. Разделяемая панель в этом случае случает удачной альтернативой многооконному интерфейсу (MDI), который в последнее время потерял свои позиции при проектировании интерфейсов.