Верстка с XUL
2.3.3. Стеки и колоды
Теги XUL можно помещать друг поверх друга, но XUL не поддерживает абсолютное или фиксированное позиционирование CSS 2. Так что размещение поверх других элементов выполняется с помощью техники, использовавшейся еще во времена HyperCard для компьютеров Macintosh в 80-е годы: прямоугольник экрана представляется как самая верхняя карта из колоды игральных карт, в которой все карты расположены рубашкой вниз. XUL-содержимое рисуется на картах и видно, если смотреть на колоду сверху.
Mozilla поддерживает "колоды" двух типов: <stack> и <deck>. Первые похожи на колоду карт, напечатанных на прозрачной пленке. Вторые - колода карт, напечатанных на обычной бумаге, причем каждый раз видна только одна карта. В обоих случаях длина и ширина колод равны размерам самых больших карт в колоде. Следовательно, изначально у всех карт один и тот же размер, даже если содержимое некоторых из них занимает меньше места. Стандартный размер может быть уменьшен для любой карты, если она позиционируется относительно. В этом случае верхний левый угол карты немного смещается относительно верхнего левого угла остальных карт, а ее размеры уменьшаются на использованное смещение.
Другими вариантами <stack> и <deck> являются <bulletinboard>, <tabbox> и <wizard>.
2.3.3.1 Тег <stack>
В листинге 2.5 показан рабочий пример использования <stack>.
<stack> <image src="spade.gif"/> <box style="left:30px; top:30px;"> <description>Другая карта</description> </box> <description top="10" left="10">Я карта</description> </stack>Листинг 2.5. Пример использования <stack>.
XUL-атрибуты left и top по своим свойствам аналогичны left и top CSS 2. Задействовать встроенные стили не рекомендуется, лучше пользоваться отдельной таблицей стилей. Здесь мы применяли встроенные стили, только чтобы проиллюстрировать действие описанной техники. Каждый тег-потомок тега <stack> формирует отдельную карту, так что в итоге у нас три карты. Карты, которые появляются наверху, накрывают карты, лежащие ниже, если их содержимое непрозрачно, как чаще всего и бывает. Последний тег - самая верхняя карта, поэтому при создании стека нужно начинать с самого нижнего слоя содержимого. Самая широкая часть - текст, но самая высокая - изображение, поэтому конечный размер карт будет высотой с изображение и шириной с самую длинную строку текста. На рисунке 2.7 показан результат нашего кода. Для наглядности были использованы некоторые простые стили.
Элемент <stack> наиболее важен для создания эффектов анимации, например, в играх или в тех случаях, когда иначе применялся бы динамический HTML. Так как в XUL нет абсолютного позиционирования, простая анимация обычно выполняется полностью в пределах стека. Система шаблонов - другой вариант динамического размещения содержимого, но он не имеет ничего общего с анимацией.
Есть и другое ограничение на анимацию: перетасовывать карты в стеке сложно. Их порядок не привязан к свойству CSS 2 z-index, поэтому его нельзя изменить с помощью динамического HTML. То есть одна карта всегда находится поверх других. Две карты не могут менять положение с помощью CSS-стилей, однако это можно сделать с помощью JavaScript и DOM: изменить порядок следования тегов внутри <stack>. Для этого приходится выполнять операции удаления и вставки. Все это требует сложной обработки внутри Mozilla. Оптимальным решением будет повторное использование содержимого тега так, как показано в листинге 2.6.
<stack> <description id="a2">Первая рыба</description> <description id="b1">Вторая рыба</description> <description id="a1" hidden="true">Первая рыба</description> </stack>Листинг 2.6. Дублирование карт в стеке.
В этом стеке самый верхний элемент - "Вторая рыба", а под ним находится "Первая рыба". Если видимый тег "Первая рыба" скрыт ( hidden="true" ), а предыдущий невидимый тег стал видимым, порядок видимых карт в стеке меняется. При использовании этой методики для N элементов требуется NAA-1 тегов, что довольно много уже при N, равном 10 или более. Уменьшить это количество можно, представив, что мы имеем дело с плоскостями, а не с отдельными картами. Каждая карта принадлежит какой-то плоскости. Обычно нужна самая нижняя плоскость (фон), плоскость для основных элементов анимации (космических кораблей и пришельцев), промежуточная плоскость (для бомб и бонусов) и, конечно, плоскость для эффектов (взрывов). С помощью такой системы можно избежать использования всех сочетаний элементов; в худшем случае придется дублировать всего лишь часть анимации. Таким образом, можно контролировать движение с помощью JavaScript, CSS-стилей и в гораздо меньшей степени - DOM 1.
Если требуется анимировать только один элемент сцены, можно использовать или GIF или сделать эту карту стека также стеком. Вложенные теги <stack> поддерживаются.
Расширение стиля Mozilla -moz-opacity можно использовать, чтобы сделать содержимое карты стека полупрозрачным. Обычно прозрачна только та часть карты, в которой нет содержимого.
Атрибут selectedIndex тега <deck> (мы рассмотрим его ниже) не работает со стеками. Если атрибут flex="1" добавить к любому тегу-потомку стека, он также не окажет никакого действия.
2.3.3.2. Тег <deck>
Тег <deck> очень похож на <stack>, но в нем в каждый момент времени может быть видимой только одна карта. Все остальные не находятся под ней, они полностью "вытаскиваются" из колоды. Или можно сказать, что они по-прежнему в колоде, но невидимы. Позади видимой карты находится любое содержимое, окружавшее тег <deck>. В листинге 2.7 приведен пример того же содержимого, что и в более раннем примере для <stack>.
<deck selectedIndex="1"> <image src="spade.gif"/> <box style="left:30px; top:30px;"> <description>Другая карта</description> </box> <description top="10" left="10">Я карта</description> </deck>Листинг 2.7. Пример использования <deck>
В <deck> порядок следования объектов не такой, как в <stack>. Теги-потомки <deck> нумеруются сверху вниз, начиная с нуля. По умолчанию первый непустой элемент находится наверху, так что порядок следования карт в <deck> - обратный порядку следования карт в <stack>. На самом деле <deck> дает меньше возможностей упорядочивания карт, чем <stack>. В <deck> "наверху" находится только одна карта. Индексные номера, данные отдельным картам, выполняют функции идентификаторов. Если значением атрибута selectedIndex является идентификатор несуществующей карты, отображается пустая карта. В листинге 2.7 атрибут selectedIndex используется, чтобы был виден второй непустой тег, а не первый. На рисунке 2.8 показан результат этого кода.
Анимация и другие сложные эффекты выполнять с помощью <deck> довольно бессмысленно. Единственное, что можно попытаться сделать - пробежаться по всем картам, меняя значение selectedIndex. При работе с картами маленькой площади это может проходить довольно быстро даже без использования очень мощного компьютера.
2.3.3.3. Теги <bulletinboard>, <tabbox> и <wizard>
Когда <stack> был впервые добавлен в Mozilla, относительное позиционирование его потомков еще не поддерживалось. Тогда был придуман элемент <bulletinboard>, поддерживающий атрибуты left, top и стили. Представьте себе пробковую доску, на которую пришпилены бумажки с объявлениями - это доска объявлений (по-английски "bulletin board"). В конце концов, поддержка этих атрибутов и соответствующих стилей была добавлена в <stack>, как уже было сказано выше, так что <bulletinboard> оказался лишним. Он все еще прячется в стандартных таблицах стилей Mozilla, но у него больше нет какого-то особого предназначения. Вместо него следует использовать <stack>, тем более что синтаксис остается таким же.
Более серьезная проблема возникла с <deck>. Как, работая с ним, пользователю выбирать отдельные карты? Обычный элемент <deck> не дает ответа на этот вопрос, придется добавлять какие-нибудь кнопки или скрипты (или еще что-то в этом роде).
<tabbox> и <wizard> - сложные XUL-теги, решающие эту проблему: <tabbox> обертывает <deck> так, что до каждой карты колоды можно добраться, один раз щелкнув мышью по пиктограмме. <wizard> обертывает <deck> так, что для перехода между картами можно использовать кнопки "Назад" и "Далее". Оба эти тега автоматизируют процесс выкладывания отдельной карты поверх стопки, чтобы сделать ее видимой.
<tabbox> обсуждается в "Навигация" , "Навигация". <wizard> - в "Система распространения и установки - XPInstall" , "Установка приложений". Тег <wizard> обычно служит для того, чтобы дать конечному пользователю возможность установить и настроить дополнительное ПО для браузера Mozilla.
2.3.4. Таблицы
При использовании обычного <box> нельзя добиться того, чтобы визуально элементы выравнивались и горизонтально, и вертикально, если только вы не собираетесь задействовать кучу дополнительных тегов. Поэтому <box> сам по себе не эквивалентен табличной верстке HTML. Для организации элементов в двух измерениях в XUL используется тег <grid>. С его помощью можно отображать статические и динамические таблицы, матрицы и т.д. Есть и другие, более специализированные теги: <listbox> и <tree>.
Таблицу в XUL можно создать с помощью пяти элементов:
<grid> <columns> <column> <rows> <row>
XUL-документы хранят информацию в иерархическом виде, а иерархические системы - неуклюжий способ представления двумерных структур. Используемый в XUL метод поэтому также уродлив, но свое дело он делает и его даже можно назвать гибким. Таблица - это всего лишь набор блоков <vbox> и <hbox>, расположенных друг поверх друга, плюс некоторая дополнительная поддержка, чтобы сделать это нагромождение? более похожим на таблицу.
Создание таблицы происходит так: нужно указать все столбцы и все строки и для точки пересечения каждой строки и каждого столбца использовать один непустой тег. Фактически этот тег - ячейка. В нем самом может содержаться какое угодно число XUL-тегов. При этом возникает проблема: куда помещать тег-ячейку: внутрь тегов строк или столбцов? В XUL рабочими будут оба варианта, но первый лучше. При этом неважно, что в коде будет идти сначала: строки или столбцы - важно, где будут ячейки. В листинге 2.8 показан пример таблицы 2х3, реализованный в обоих вариантах.
<grid> <columns> <column/> <column/> <column/> </columns> <rows> <row> <description>One</description> <description>Two</description> <description>Three</description> </row> <row> <description>Four</description> <description>Five</description> <description>Six</description> </row> </rows> </grid> <grid> <rows> <row/><row/> </rows> <columns> <column> <description>One</description> <description>Four</description> </column> <column> <description>Two</description> <description>Five</description> </column> <column> <description>Three</description> <description>Six</description> </column> </columns> </grid>Листинг 2.8. Два варианта одной таблицы
Хотя некоторые теги пусты ( <column> в первом примере и <row> - во втором), их все равно нужно объявить. Так мы даем системе визуализации знать, каков будет конечный размер таблицы - та же проблема, что и с таблицами в HTML. В XUL нет атрибутов, с помощью которых можно указать размер таблицы. Также у таблиц нет заголовков, названий и прочих подобных признаков. Они так же просты, как и <stack>.
Включим использование границ и увидим, что таблица стала похожа на обычную электронную таблицу. Отключим рисование границ, и края столбцов и строк будут играть роль направляющих для содержимого. Определение направляющих для нового интерфейса - первый шаг на пути верстки пользовательских графических интерфейсов. На рисунке 2.9 показан пример, в котором четыре раза используются таблицы с атрибутом flex, чтобы точнее расположить их содержимое. В первом случае нет границ. Во втором границы есть везде, и они аккуратные. Два других варианта иллюстрируют различия между стратегиями, использованными в листинге 2.8.
Серые границы использовались для пустых столбцов и строк, а черные - для непустых. Как видно из рисунка, для "стилизации" таблиц порядок вложенных элементов очень важен. Если используются какие-нибудь поля, внешние или внутренние, содержимое ячеек выравнивается корректно только при использовании второго способа (ячейки находятся в столбцах). Если внимательно рассмотреть левый верхний пример, можно заметить серую границу толщиной в пиксел. Код Mozilla, отвечающий за рисование границ таблиц, не лишен недостатков. Но возможно, к моменту публикации этой книги их уже не будет.
В таблице общее число строк должно совпадать с числом ячеек в данном столбце. Если это не так, Mozilla справится с проблемой, но общее размещение может быть не совсем таким, как ожидалось. Аналогичное правило должно выполняться, если ячейки будут размещаться в строках.
Наконец, можно добавлять содержимое и в строки, и в столбцы. Если это сделать, каждая ячейка будет получать один тег с содержимым внутри <row> и один - внутри <column>. Эта ячейка будет вести себя как двухэлементный стек, причем первый (в порядке следования) элемент будет находиться внизу, под вторым. Это бесполезная функция, если, конечно, нет какой-то специальной цели для ее использования. Если нужен стек в ячейке, следует просто поместить в нее <stack>.
Основная функция <grid> - размещение элементов графического интерфейса. При частом использовании flex для тегов <column> и <row> таблицы их содержимое само аккуратно разместится в окне. В окне поиска Mozilla (вызывается из меню "Правка") для выравнивания объектов используется таблица.
Вместо тега <grid> можно применить <listbox> и <tree>, их функциональность описана в "Списки и Деревья" , "Списки и деревья".