Россия, Звенигород |
XBL-связки
15.7. Практика: Тег <noteplacer>
В данном разделе "Практика" мы разработаем некоторый код общего назначения, использующий XBL. В частности, мы создадим новую XBL-связку и соответствующий ей XUL-тег, которые внесут свой вклад в содержание и поведение диалогового окна утилиты NoteTaker.
Не следует превращать контент XUL в связки XBL только потому, что есть такая возможность. Только код, используемый более одного раза на данной странице или в другом приложении, является подходящим кандидатом для включения в XBL. Связки XBL помещают свой контент в некоторую добавочную структуру. Обычный контент не всегда годится для XBL с точки зрения эффективности кода. Только если связка вызывается часто, она уменьшит, а не увеличит сложность результирующего кода.
В утилите NoteTaker нет повторяющихся частей кода в контенте панели инструментов или диалогового окна, так что вряд ли стоит использовать XBL. Но ведь нам нужен пример для иллюстрации этой технологии. Стоит поэкспериментировать с информацией о позиционировании панели Edit (параметры top, left, height, width ). Эти четыре параметра очень похожи на стили позиционирования в стандарте CSS2, и это может потом пригодиться в других приложениях. Более того, существующая реализация позиционирования несколько неуклюжа: пользователь вынужден гадать, вводя значения, а никакого отклика на вводимые данные нет до закрытия диалогового окна, а тогда уже слишком поздно.
Мы можем улучшить существующую систему позиционирования с помощью нового виджета. Создадим связку XBL, которой пользователь сможет манипулировать графически, чтобы указать будущее положение заметки. Эта связка будет виджетом специального назначения, в чем-то подобным тегу <colorpicker>, а в чем-то - окошку Print Preview.
Этот виджет не будет интегрирован в конечный код утилиты NoteTaker, мы просто проиллюстрируем такую возможность.
15.7.1. Конструирование интерфейсов виджета
Нам нужно продумать визуальный, XML, JavaScript интерфейсы, а также интерфейс пользовательского ввода, а потом воплотить эти интерфейсы в XBL-коде. Цель виджета - подсказать пользователю информацию о позиционировании и затем сделать эту информацию доступной основному XUL-документу.
Сначала взглянем на визуальный интерфейс. Визуальная часть - вдохновитель всей конструкции виджета. Она должна явиться нам как озарение, поскольку все виджеты по своей природе представляют собой нечто уникальное. Это вызов нашему творческому мышлению, ведь каждый виджет должен быть оригинален. Если он не оригинален, мы можем попросту скопировать и усовершенствовать существующий виджет, например <button>.
Есть, правда, один способ избежать необходимости быть слишком оригинальным. Виджет XBL может вбирать в себя другие, более простые виджеты и образовывать из них группу. Такую агрегированную систему часто называют консолью, потому что старомодные консоли состояли из множества переключателей, лампочек и ручек. Это и есть самые правильный способ использования XBL.
Поскольку все XUL-теги и комбинации тегов занимают на экране прямоугольную область, мы знаем, что должны начать с прямоугольника. Можно исхитриться и сконструировать сверхъестественный виджет, который нарушит это правило, но столь далеко в код приходится углубляться не слишком часто. На рисунке 15.2 изображен простой набросок нашегонового виджета.
Идея напоминает другие утилиты визуального конфигурирования, такие как подсистема печати или программы обработки изображений - с необходимыми нам изменениями. Суть дела в том, что наш виджет - небольшая модель целого десктопа. Поскольку здесь мы ограничены объемом книги, мы реализуем простенькую модель этого виджета. Полное решение требует технологий, подобных тем, что используются в подсистеме предварительного просмотра печати браузеров Mozilla.
На рисунке 15.2 главный прямоугольник - наш новый виджет. Он имеет три части: задник - <box>, представляющий весь десктоп полностью, опциональный белый <box>, представляющий окно или страницу браузера, и желтый <box>, представляющий заметку, которую нужно позиционировать. Каждый <box> имеет соответствующую надпись, на тот случай, если кому-то что-то не очевидно. Главный прямоугольник получит размеры согласно параметрам разрешения экрана.
Этот главный прямоугольник принимает пользовательский ввод. Если мы кликнем главной кнопкой мыши внутри прямоугольника, заметка расположится так, что ее верхний левый угол окажется как раз под местом клика. Если мы кликнем второй раз, мы позиционируем ее нижний правый угол. Эту серию кликов можно повторять сколько угодно раз. Если мы кликнем альтернативной кнопкой (right-click или apple-click), будет позиционирован, т.е. сдвинут в данном случае, ближайший диагональный (т.е. левый верхний или нижний правый) угол заметки.
Когда заметка позиционирована, вычисляются ее координаты. Если белого прямоугольника (браузера) нет, координаты относятся ко всему десктопу. Браузер может занимать и весь десктоп. Если белый прямоугольник имеет место, координаты отсчитываются от его угла. Белый прямоугольник нужен по психологическим соображениям: люди обычно предпочитают прямоугольники в "золотой пропорции", т.е. в пропорции 1:1,618. Окошки браузера в конце концов обычно оказываются именно в этой пропорции. Белый прямоугольник напоминает нам, как эта идеальная пропорция выглядит.
Второй интерфейс - пользовательский ввод. Виджет будет поддерживать разовый клик кнопкой ноль (первичной, или левой кнопкой мыши) и разовый клик кнопкой 2 (вторичной, или правой кнопкой). Кнопки устанавливают позиции либо верхнего левого, либо нижнего правого угла заметки. Кнопка 0 поочередно устанавливает диагональные углы, кнопка 2 сдвигает ближайший. Виджет не является ни членом фокусного кольца, ни использует систему для людей с ограниченными возможностями. Так что никакого добавочного планирования пользовательского ввода больше не требуется. Обе возможности игнорируются нами, потому что ни один XUL-тег в виджете не может иметь фокус.
Третий необходимый интерфейс - XML-интерфейс. Для нас это означает выбор имени тега и множество требуемых атрибутов, которые нам понадобятся, чтобы обеспечить его вид и поведение. Связку можно построить на любом теге, но мы выбираем <noteplacer>. Следующие атрибуты будут иметь специальные значения:
scale screenx screeny pageless
scale - целое, указывающее размер виджета noteplacer по отношению к размеру десктопа. Например, значение 4 (значение по умолчанию 2) означает, что noteplacer вчетверо меньше целого десктопа.
screenx означает ширину десктопа в пикселях. Значение по умолчанию дано свойством window.screen.width.
screeny означает высоту десктопа в пикселях. Значение по умолчанию дано свойством window.screen.height.
pageless может иметь значение true или false. В случае true белый прямоугольник не появится на фоне виджета. По умолчанию false.
Теперь вопрос верстки. Будет ли виджет способен изменять размеры? Какова его ориентация? Список вопросов растет. Поскольку виджет noteplacer изображает форму десктопа, он будет иметь фиксированный размер и не будет растягиваться. Он должен всегда иметь горизонтальную ориентацию и не изменять ее.
Последний интерфейс - JavaScript XBL-интерфейс граничного тега. Мы реализуем следующие свойства:
top left width height scale screenx screeny pageless setFromEvent(e,primary)
top, left, width, и height соответствуют полям в диалоговом окне Edit.
scale, screenx, screeny, и pageless относятся к атрибутам XML граничного тега.
setFromEvent() позиционирует один из углов заметки, используя объект event. Если второй аргумент true, устанавливается верхний левый угол, если нет, правый нижний.
Так вот, ни с того ни с сего, не приходится думать о поддержке каких-либо существующих интерфейсов платформы (например, nsIAccessible ), чего, может, и хотелось бы. Граничный тег будет автоматически поддерживать стандартные интерфейсы DOM для объекта DOM 2 Element, для этого ничего не нужно делать.
Все вместе эти четыре интерфейса полностью определяют новый виджет. Последний вопрос, который нужно задать: можем ли мы использовать какую-либо уже существующую XBL-связку? Похоже, что нет, наш новый виджет слишком прост.
15.7.2. Добавляем контент XBL
Чтобы создать собственно связку, начнем с простого ее скелета. На этом шаге мы определим ее части. Скелет связки приведен в листинге 15.14.
<?xml version="1.0"?> <bindings id="notetaker" xmlns="http://www.mozilla.org/xbl" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul" xmlns:xbl="http://www.mozilla.org/xbl"> <binding id="noteplacer"> <resources> </resources> <content> </content> <implementation> <constructor></constructor> <destructor></destructor> </implementation> <handlers></handlers> </binding> </bindings>Листинг 15.14. Простейший пример XBL связки.
Набор связок, необходимых для утилиты NoteTaker, мы назвали "notetaker", а связку тега <noteplacer> - "noteplacer". Поскольку здесь нет цепочки наследования, нет и атрибута extends в теге <binding>. Поскольку новый виджет основан на простых XUL-боксах, нет нужды и в атрибуте display. Нам нужно лишь заполнить эти четыре секции связки.
Начнем с секций <content> и <resources>. Используем контент, служивший основой для рисунка 15.3 и затем рассмотрим, как можно модифицировать его для XBL-системы. Используемый контент приведен в листинге 15.15.
<stack minwidth="320" minheight="240"> <description value="Screen"/> <box class="page" top="0" left="86" minwidth="148" minheight="240"> <description value="Page"/> </box> <box class="note" top="20" left="106" minwidth="40" minheight="40"> <description value="Note"/> </box> </stack>Листинг 15.15. Модельный контент XBL-связки noteplacer.
Этот контент имеет фиксированные параметры размера, указываемые прямо в коде. Большинство виджетов в вопросе размеров должно полагаться на платформу, а не устанавливать их жестко, чтобы верстка контента была аккуратной. Наш виджет, однако, имеет фиксированные размеры, поэтому мы здесь нарушили это правило.
Прежде чем этот код сгодится для секции <content> связки, нужно еще поработать. Существуют некоторые проблемы:
Часть page виджета всегда присутствует, нам нужно сделать ее невидимой, если используется полный размер экрана браузера.
Часть page слишком скучна. Мы можем захотеть поместить здесь что-то более наглядное, например, панель инструментов и меню браузера.
Фиксированные атрибуты размеров бесполезны для большинства тегов. Реальные значения будут вычисляться на основе атрибутов тега <noteplacer> и текущих значений разрешения экрана.
Размеры шрифтов также пока неясны. Если виджет представляет собой скалируемый десктоп, размеры шрифтов должны также быть скалируемыми.
Для всех названных проблем есть простые решения. Исчезновение блока page - самое простое. Позволим тегу <box>, содержащему блок page, наследовать атрибут pageless из тега <notepicker>:
<box class="page" xbl:inherits="collapsed=pageless" ...>
Чтобы сделать page не столь скучным, позволим эксплицитному контенту тега <noteplacer> определять контент тега <box class="page">. Аккуратно пересмотрев правила слияния анонимного и эксплицитного контентов, заключаем, что:
<description value="page"/>
нужно изменить на:
<children> <description value="page"/> </children>
Если эксплицитный контент есть, он будет добавлен в соответствующее место, если нет, по умолчанию имеется тег <description>.
Теперь избавимся от фиксированных размеров, указанных в пикселях. Они были получены для виджета на десктопе 640х480, с коэффициентом пропорциональности два. Вместо этого пусть значения вычисляются в конструкторе связки во время выполнения. Мы не можем просто наследовать их из атрибутов тега <noteplacer>, поскольку требуемые значения являются математической комбинацией значений этих атрибутов, а не просто копией. Вскоре мы увидим, как это делается.
Наконец, размеры шрифтов также пусть вычисляет конструктор. Добавим стиль inline всякому анонимному контенту, и для тега <description>, и для тега <label>. Этот стиль скалирует для нас размеры шрифтов, чтобы они соответствовали размерам виджета. В некоторых случаях текст получится нечитаемым, но и цель виджета - лишь дать некоторое наглядное представление.
Для выполнения этих двух пунктов добавим атрибут anonid анонимному контенту, который мы хотим позже модифицировать. Немного заглянув вперед, мы увидим, что это тег <stack> и два тега <box>. Результат показан в листинге 15.16. Обратите внимание на префикс xul: namespace каждого XUL-тега.
<content> <xul:stack anonid="desktop"> <xul:description value="Screen"/> <xul:box xbl:inherits="collapsed=pageless" class="page" anonid="page"> <xul:children> <xul:description value="Page"/> </xul:children> </xul:box> <xul:box class="note" anonid="note"> <xul:description value="Note"/> </xul:box> </xul:stack> </content>Листинг 15.16. Часть <content> XBL-связки noteplacer.
Если мы попробуем запустить связку на данном этапе (вообще-то неплохая идея), результат будет выглядеть не слишком хорошо. Он так выглядит, потому что виджет не имеет пока необходимой ему информации. Связка работает, она просто еще не закончена.
Наконец, мы можем создать простую стилевую таблицу, хранящую значения по умолчанию для анонимного контента, как показано в листинге 15.17.
stack { background-color : background; font-family : -moz-fixed; } box.page { background-color : white; border : solid thin; border-color : black; } box.note { background-color : lightyellow; border : solid thin; border-color : yellow; }Листинг 15.17. Стилевая таблица CSS2 XBL-связки noteplacer.
В этом случает цвет фона будет тот же, что и у реального десктопа. Шрифт -moz-fixed (системный) имеет одно важное свойство: он может скалироваться до любого размера. 7px это 14px, скалированный с коэффициентом два. Заметьте, что значение пространства имен по умолчанию для подключаемых стилевых таблиц XBL - это пространство имен XUL.
Это означает, что для тегов в стилевых таблицах не нужно указывать префикс с пространством имен CSS2. Такие пространства имен выглядят так:
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul"); xul|stack { ... styles ... };
Стилевая таблица помещается в секцию <resources> спецификации XBL с помощью относительного URL. Она имеет относительный URL, потому что связка noteplacer принадлежит только пакету notetaker и помещается в той же директории, что и он. Это не глобальная связка в глобальном пакете. В общем случае файл CSS следует использовать во всех связках пакета notetaker, и этот файл должен называться notetaker.css. Мы используем другое имя, чтобы подчеркнуть, что этот файл имеет только лишь стилевую информацию для XBL, и избежать конфликта со скинами, созданными ранее. Законченная часть XBL выглядит так:
<resources> <stylesheet src="noteplacer.css"/> </resources>
Создав контент и стилевую таблицу, мы выполнили половину работ по созданию связки. Мы пока что создали только ее визуальный и XML интерфейсы.