Россия, Звенигород |
Списки и Деревья
13.3. Деревья
Если тег <listbox> - это смесь концепций тегов <grid> и <menulist>, то тег <tree> - это смесь <listbox> и <iframe>. Так же, как и <listbox>, тег <tree> - это упорядоченное по вертикали множество записей. Тег <tree> позволяет графически подчеркнуть иерархическую структуру данных благодаря различной величине отступов и графическим украшениям- подсказкам. Если иерархическое выделение не используется, тег <tree> выглядит так же, как и тег <listbox>.
Тег <tree> дает нам возможность управлять графическим представлением данных. Мы можем группировать и раскрывать ветви дерева интерактивно. Колонки можно переставлять, скрывать, изменять их размер, а их содержание можно сортировать и выбирать.
Тег <tree> предоставляет программисту приложения множество возможностей. Свойства сортировки и отображения данных дают программисту непосредственный доступ к управлению представлением данных. Вместе с оверлеями и шаблонами тег <tree> дает в результате панель, в которой можно манипулировать данными очень динамично. Внешний вид деревьев можно модифицировать стилями в широком диапазоне.
Если не рассматривать такой графический элемент управления, как <iframe>, то дерево - это самый сложный виджет XUL.
13.3.1 Внешний вид дерева
Чтобы увидеть тег <tree> в работе, посмотрите на любое из следующих окошек классической Mozilla: "Настройки" (левая панель), две панели почтового клиента, окошко менеджера закладок, менеджер загрузок. Все эти окна содержат тег <tree>. На самом деле Mozilla использует в своей работе десятки деревьев.
На Рисунке 13.5 показаны свойства деревьев. На этом снимке мы видим тему Modern.
Дерево на картинке выглядит как набор колонок, подобно списку. В отличие от списка, в правом верхнем углу есть выпадающее меню. Это меню (не показано) - набор флажков, которые можно использовать, чтобы спрятать или вновь показать колонки.
Дерево имеет несколько колонок специального типа. Колонка А - первичная. Первичная колонка отображает иерархическую организацию строк в дереве. Посмотрим на эту колонку поближе: мы видим четыре строки верхнего уровня: 1, 2, 7, и 9 (то есть это даже не дерево, а лес). Вторая строка имеет поддерево, которое видимо - раскрыто. Маленький направленный вниз треугольничек (стрелка) также свидетельствует о том, что поддерево раскрыто. Это поддерево имеет четыре строки, одна из которых сама имеет свое поддерево, которое также раскрыто. Наконец, это второе поддерево имеет единственную строку, также имеющую поддерево. Это последнее поддерево закрыто, схлопнуто (стрелка указывает вправо), так что из рисунка неясно, сколько строк имеет последнее поддерево. На стрелки можно кликать, открывая и закрывая поддеревья. Короткие линии между стрелками и строками обозначают для нас уровень дерева, к которому данная строка принадлежит. Наконец, мы видим, что колонка А имеет отступы, также обозначающие уровень дерева.
Оставшиеся колонки не так сложны. Колонка B - обычная колонка. Колонка C - обычная колонка, но ее заголовок заменен пиктограммой. Колонка D может быть отсортирована: ее можно использовать, чтобы переупорядочить строки дерева, вопреки его исходной структуре. Это свойство сортировки обозначено маленькой стрелкой в заголовке колонки. Чтобы сортировка работала, к тегу <tree> необходимо добавить технологии, которые будут обсуждаться далее, в "Шаблоны" , "Шаблоны"; здесь она изображена только для полноты картины. Колонка E - маркер. Такая колонка содержит лишь одну пиктограмму и взаимодействует с пользователем особенным образом. Колонка F содержит тег <progressmeter>. Колонка G должна содержать флажок, но эта функциональность еще не закончена и не работает в версии 1.4. и более ранних.
Скобки в названиях не являются частью конструкции, это просто часть заголовка. Полоса прокрутки справа появляется и исчезает по мере необходимости, в зависимости от количества контента в окошке. Полоса прокрутки на этом рисунке - часть тега <tree>, а не отличная от него часть chrome. Горизонтальные полосы прокрутки не поддерживаются.
Мы можем теперь рассмотреть рисунок 13.5 строка за строкой. Первая интересная строчка - 3. Одна ее ячейка имеет иной цвет и рамку. Mozilla имеет специальную систему стилей для деревьев, которая описывается ниже, в разделе "Стилевые возможности" этой лекции. Строка 6 является выбранной, так что дерево в настоящий момент получило фокус. Строка 7 показывает нам, что ячейка может содержать также и изображение; это не встроенная стилевая пиктограмма. На строке 7 видно также, что изображение может заместить все содержание строки и встроенное содержание тега <progressmeter> можно заместить своим. Строчка 8 (тонкая горизонтальная линия) - это <treeseparator> ; он работает так же, как <menuseparator> в меню. Строчка 9 демонстрирует, что ячейка обрежет содержание, если места недостаточно. Если в ячейке очень мало места, то не будут отображены даже первые буквы содержания. Наконец, последняя строка пуста: не слишком удачная идея в реальном приложении, но технически это возможно.
Ячейка дерева не может содержать произвольный код XUL. Она может содержать лишь строку текста и то, что было перечислено ранее. Она не может включать тег <box>.
13.3.2 Конструкция тега "дерево"
Рисунок 13.6 повторяет Рисунок 13.5 с включенной диагностикой. С ее помощью мы можем узнать кое-что новое о внутренней структуре дерева. Заголовки колонок напоминают другие виджеты, такие как кнопки и лейблы. Очевидно, что тег <tree> не основывается на табличной структуре и отличен от тега <listbox>.
Фактически, область данных дерева напоминает тег <iframe>. Это прямоугольная область в XUL документе, чье содержание хранится отдельно от остального документа. В теге <iframe> содержание - это отдельный XUL документ. В случае дерева, контент не имеет отдельного XUL-документа, но все равно хранится отдельно от другого контента.
Невозможно оформлять части дерева, используя обычные CSS2 стили. Вместо этого существует специальная система стилей. Причина в том, что каждая ячейка и строка имеют фреймы, которые не полностью описываются стандартными стилями. Так исторически сложилось при проектировании и разработке тега "дерево".
Рисунки 13.5 и 13.6 требуют сотни линий кода для реализации, поэтому рассмотрим более простой пример, имеющий всего две строки, на рисунке 13.7.
Фрагмент кода для этого рисунка приведен в листинге 13.3
<tree flex="1"> <treecols> <treecol flex="1" id="A" label="primary" primary="true"/> <treecol flex="1" id="B" label="normal"/> <treecol flex="1" id="C" label="icon" class="treecol-image" src="face.png"/> <treecol flex="1" id="D" label="sorted" sortDirection="ascending"/> <treecol flex="1" id="E" label="cycler" cycler="true"/> <treecol flex="1" id="F" label="progressmeter" type="progressmeter"/> </treecols> <treechildren id="topchildren" flex="1"> <treeitem container="true" open="true"> <treerow> <treecell label="Cell"/> <treecell label="Cell"/> <treecell label="Cell"/> <treecell label="Cell"/> <treecell label="Cell"/> <treecell label="Cell" mode="undetermined"/> </treerow> <treechildren> <treeitem> <treerow> <treecell label="Cell"/> <treecell src="face.png" label="Cell"/> <treecell label="Cell"/> <treecell label="Cell"/> <treecell src="face.png" label="Cell"/> <treecell label="Cell" mode="normal" value="40"/> </treerow> </treeitem> </treechildren> </tree>Листинг 13.3. Базовая конструкция тега <tree>
Тег <tree> имеет вложенный тег <treecols>, в котором определены колонки, и вложенный тег <treechildren>. Идентификаторы колонок очень важны. Тег верхнего уровня <treechildren> напоминает <listbox> из <listboxbody>, за исключением того, что он определяется программистом приложения и может находиться внутри других тегов. "Элемент" дерева - это целое поддерево, тем не менее, список таких единиц - это последовательность строк. Код примера имеет единственный тег верхнего уровня <treeitem>. Атрибут container говорит нам, что эта единица - не просто строка, но тоже верхушка дерева. open говорит, что следующий уровень поддерева раскрыт. Если появится второй или третий теги верхнего уровня <treeitem>, они должны появиться после заключительного тега </treeitem>. Наше единственное поддерево верхнего уровня имеет две части: содержание строки и дочернее поддерево. Поддерево начинается со второго тега <treechildren>, но на этот раз это не поддерево, но лишь строка. Если бы было еще одно поддерево, оно появилось бы после внутреннего тега </treeitem>.
XBL код тега <tree> хранится в файле tree.xml в архиве toolkit.jar в каталоге chrome.
Есть и иные структурные аспекты деревьев: RDF, сортировка, кадры (views), конструкторы и шаблоны. Их описание будет дано после того, как мы рассмотрим XUL-теги деревьев.
13.3.3 Тег <tree>
Все содержание дерева находится внутри тега <tree>. В одном XUL документе может содержаться более одного дерева. Тег <tree> имеет следующие особенные атрибуты:
seltype hidecolumnpicker enableColumnDrag disableKeyNavigation
seltype со значением multiple (значение по умолчанию) позволяет нам выбирать одновременно несколько ветвей дерева. Значение single означает, что может быть выбрана лишь одна ветвь.
hidecolumnpicker со значением true схлопывает тег <treecolpicker> в правом верхнем углу дерева.
enableColumnDrag со значением true позволяет нам менять колонки местами.
disableKeyNavigation со значением true не позволяет выбирать колонки, используя клавиатуру.
Тег <tree> и остальные подобные ему теги также поддерживают RDF и шаблоны. Атрибуты, необходимые для этого, рассматриваются в "Шаблоны" , "Шаблоны".
Максимальная высота тега <tree> может быть определена с помощью стандартного для блоков атрибута height. Тег <tree> не поддерживает атрибут rows.
13.3.4 Тег <treecols>
Описания колонок заключены в тег <treecols>. У него нет специальных атрибутов. Ему может быть присвоен идентификатор id, если дерево частично построено из оверлеев. Тег <treecols> - это простой тег-контейнер. Этот тег должен быть первым дочерним тегом внутри тега <tree>. Это обязательно.
Тег <treecols> может содержать два типа тегов: <treecol> и <splitter>.
Число тегов <treecol> в теге <treecols> дает нам число колонок в дереве. По меньшей мере один тег <treecol> присутствовать должен. Один из тегов <treecol> может иметь атрибут primary со значением "true".
Если присутствует разделитель <splitter>, он должен находиться между двумя тегами <treecol>. Разделить - точка, передвигая которую мы можем изменять размеры колонок. Если присутствуют все возможные разделители, получается, что они должны чередоваться с тегами колонок. Результатом такого перетаскивания будет то, что размеры колонок по обе стороны разделителя изменятся. XBL код разделителя обеспечивает такое его поведение. Каждый разделитель должен быть оформлен классом class="treesplitter", благодаря чему этот тег имеет нулевую ширину. Если этого не сделать, заголовки колонок и сами колонки могут не соответствовать друг другу. В силу механизма, которым Mozilla идентифицирует текущий тег под курсором мыши, разделитель может быть текущим тегом, несмотря на то, что он не имеет видимого отображения. И его можно передвигать, несмотря на нулевую ширину.
13.3.5 <treecol>
Тег <treecol> определяет колонку дерева. Он не может содержать тегов. Каждый тег <treecol> должен иметь уникальный id. Этот id используется платформой Mozilla. Заголовок колонки - это тег <button>, содержащий теги <label> и <image>, или только <image>, а именно те, что были оформлены классом treecol-image. Заголовки колонок могут иметь дополнительную пиктограмму, если оформлены классом list-styleimage.
Тег <treecol> имеет следующие атрибуты:
label display crop src hideheader ignorecolumnpicker fixed sortDirection sortActive sortSeparators cycler primary properties
label определяет текст заголовка колонки. Тег <label> не может быть использован как содержание атрибута.
display определяет текст, появляющийся в выпадающем меню (picker) для этой колонки.
crop применяется к тегу <label> содержимого колонки.
src заменяет label колонки изображением. Тег <treecol> должен быть оформлен стилем class="treecol-image", чтобы это работало.
В результате использования hideheader="true" заголовок не ведет себя, как кнопка. Пространство для заголовка выделяется по-прежнему -- но заголовок больше не может быть ни схлопнут, ни спрятан. Если используется этот атрибут, атрибут label должен также отсутствовать. Этот атрибут может быть применен ко всем заголовкам колонок, и, вместе с атрибутом hidecolumnpicker, схлопнуть всю зону заголовков.
ignorecolumnpicker="true" не позволит выпадающему меню прятать и вновь раскрывать колонки.
fixed="true" не даст колонке возможности реагировать, если соседние с ней колонки меняют свой размер. Другими словами, эта колонка не изменит свой размер, если только все дерево не изменит размер. Это следствие поведения тега <splitter> в заголовке.
Если sortActive="true", эта колонка может сортироваться. Значение sortDirection может быть ascending, descending, или normal. Если sortSeparators имеет значение true, имеет место специальный вид сортировки, когда строки, находившиеся между тегами <treeseparator>, так и останутся между этими тегами. Сортировка осуществляется автоматически, только если использовались RDF или шаблоны - см. "Шаблоны" , "Шаблоны".
cycler="true" означает, что эта колонка является маркером и содержит только одну пиктограмму. Эта пиктограмма может вести себя подобно кнопке и иметь обработчик события onclick. Если мы кликнем маркер, строчка под указателем мыши не будет выбрана.
primary="true" означает, что это первичная колонка дерева и она должна отразить иерархическую структуру данных. Этот атрибут может иметь только одна колонка в каждом дереве.
атрибут properties поддерживает специальную систему стилей. См. раздел "Стилевые возможности" в этой лекции.
Колонки могут быть спрятаны или схлопнуты, при использовании стандартных атрибутов XUL.