Создание тулбара для Mozilla FireFox - часть 2
Структурируем тулбар
Практически все расширения для FF пишутся на языке XUL (произносится как “zool”), языке разметки для написания пользовательских интерфейсов (XUL = XML User Interface Language — язык пользовательских интерфейсов XML). Красота XUL — это так называемые “динамические надстройки” (dynamic overlays). За счет них можно изменять поведение интерфейса окна, не внося изменений в исходники этого интерфейса. Таковая возможность не может не радовать, поскольку позволяет сосредоточиться на написании кода, непосредственно относящегося к нашему расширению, и избавляет нас от необходимости очередной раз изобретать велосипед.
В этом разделе мы рассмотрим XUL-разметку, которая нам понадобится для создания тулбара. Помните, что XUL — это всего лишь средство построения структуры нашего тулбара. Другими словами, это не то, что позволит “оживить” его. А для того, чтоб реализовать всю функциональность нашего расширения, мы будем использовать JavaScript, но об этом — в следующих сериях.
(Здесь и далее я буду использовать слово “надстройка” везде, где встречается “overlay”).
Наш первый XUL-файл будет использован для создания надстройки браузера. Мы расположим его в папке content — там же, где лежит contents.rdf. Обычно этот файл называется так же, как и все расширение, в нашем случае — gbltutorial.xul. Структура папок теперь выглядит так:
+- GBLTutorial/
+- install.rdf
+- chrome/
+- content/
+- contents.rdf
+- gbltutorial.xul
Поскольку XUL-файл в сущности своей является XML, надо дать соответсвующее объявление в первой строчке:
<?xml version="1.0"?>
Далее, создаем саму надстройку. Для этого добавляем элемент overlay, который будет корневым для нашего файла:
<overlay id="GBLTut-Overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> </overlay>
Этот элемент имеет два атрибута: id и xmlns. Значение атрибута id должно быть уникальным именем, указывающим на надстройку вашего расширения. По-хорошему, вообще когда какому-нибудь элементу присваивается атрибут id, он должен быть уникальным. Обратите внимание, что в этом примере я использую префикс “GBLTut-”, чтоб быть уверенным, что мое значение id не будет конфликтовать с другими расширениями.
Второй атрибут, xmlns, определяет пространство имен, которое я буду испольвозать в надстройке. Представленное значение — это URL пространства имен, который никогда не изменится. То есть, всегда используйте это значение.
Тулбокс и Тулбар
Все тулбары для FF должны располагаться внутри так называемого тулбокса (toolbox). Элемент toolbox — просто контейнер для тулбаров, и должен располагаться внутри элемента overlay, который мы только что создали. Выглядит он так:
<toolbox id="navigator-toolbox"> </toolbox>
Опять мы видим атрибут id в действии. На этот раз, однако, представлено специальное значение “navigator-toolbar“. Оно представляет тулбокс, содержащий панель меню Firefox, панель навигации и панель закладок. Указывая на этот конкретный тулбокс, мы располагаем наш тулбар в этой славной компании прочих тулбаров, как нам и хотелось. Все созданные вами тулбары должны использовать этот специальный ID для элемента toolbox. Не единой совместимости ради, но также для того, чтоб нахаляву добавить наше детище в меню Вид -> Панели инструментов (что позволит его быстро прятать-показывать). Круто, да?
Следующим действием мы добавляем элемент toolbar, располагая его в только что созданном toolbox. Пишем:
<toolbar id="GBLTut-Toolbar" accesskey="T" class="chromeclass-toolbar"
context="toolbar-context-menu" toolbarname="GBL Tutorial Toolbar"
hidden="false" persist="hidden">
</toolbar>
У нас появилось несколько новых атрибутов, давайте изучим их более пристрастно:
- accesskey — определяет букву, которая будет подчеркнута в имени тулбара, поскольку будет клавишей доступа с клавиатуры. Для нашего тулбара это будет большая “T”. Хотя этот атрибут и необязательный, очень рекомендуется им пользоваться.
- class — определяет класс стиля, который надо применить к этому тулбару. Указанное значение (chromeclass-toolbar) — имя класса для стандартного вида тулбара Firefox. Опять же, этот атрибут необязательный, но рекомендуемый.
- context — определяет контекстное меню, которое нужно использовать, когда юзер делает правый клик по нашему тулбару. Можете указать ID вашего собственного меню, или сослаться на стандартный вид, как это сделано в нашем примере.
- toolbarname — тут указываем имя нашего тулбара (текст, видимый пользователю в Вид -> Панели инструментов).
- hidden — определяет, должен ли тулбар быть спрятан. Мы устанавливаем значение в false, таким образом наш тулбар по умолчанию будет отображаться.
- persist — вы заметите, что я выставил значение этого атрибута в hidden. По сути, я говорю FF, что ему следует запоминать значение атрибута hidden между сеансами работы браузера. Фишка сего механизма в том, что FF сделает все за вас. Все, что вам надо сделать — указать, какие атрибуты браузер должен запомнить. Кстати, здесь можно указать больше одного атрибута, разделив имена атрибутов пробелами.
Более подробное описание атрибутов элемента toolbar вы найдете на XUL Planet.
Кнопки тулбара
Есть три типа кнопок, которые мы можем помещать на нашем тулбаре: обычные кнопки, кнопки меню и кнопки “кнопока-меню” (buttom-menu buttons — какая ересь! — прим. пер.)). Давайте взглянем на каждый тип отдельно. И помните, что все это добро нужно класть внутрь элемента toolbar, который мы давеча создали.
Обычные кнопки
Обычные кнопки — это просто: стандартные кнопки меню, кнопки как кнопки, ничего особенного. Разметка такова:
<toolbarbutton id="GBLTut-TB-Web" tooltiptext="Search the Web"
label="Web Search"
oncommand="GBLTut_Search(event, 'web')" />
Как видите, что создать обычную кнопку, надо использовать элемент toolbarbutton. Исследуем новые атрибуты:
- tooltiptext — указываем текст всплывающей подсказки, которая возникает, когда пользователь наводит курсор на кнопку
- label — указываем текст, написаный на кнопке
- oncommand — значение этого атрибута — это код, который должен быть выполнен, когда пользователь надавит кнопку. В нашем примере я вызываю тренировочную функцию GBLTut_Serach(). Написание скриптов для тулбара — это тема для следующего раздела.
Более полное описание атрибутов элемента toolbarbutton вы найдете на XUL Planet.
Кнопки меню
Второй доступный тип кнопок — это кнопки меню. Кнопки такого типа отображают выпадающее меню, когда на них нажимают. Разметка для создания таких кнопок во многом похожа на то, что мы уже видели. Однако, мы добавляем вложенный элемент menupopup. Дабы не раздувать размер кода, нижеприведенный пример содержит лишь 2 пункта меню.
<toolbarbutton id="GBLTut-MainMenu" type="menu"
tooltiptext="GBL Tutorial Toolbar Main Menu">
<menupopup>
<menuitem label="Google Home Page" accesskey="G"
tooltiptext="Browse to the Google home page"
oncommand="GBLTut_LoadURL('http://www.google.com/')" />
<menuseparator />
<menuitem label="Born Geek Website" accesskey="B"
tooltiptext="Visit the Born Geek website"
oncommand="GBLTut_LoadURL('http://www.borngeek.com/')" />
</menupopup>
</toolbarbutton>
Заметьте, в атрибутах элемента toolbarbutton произошло 2 важных изменения. Первое — появился новый атрибут type, которому в нашем примере присвоили значение “menu“. Второе — отсутствует атрибут oncommand. Поскольку теперь наша кнопка лишь отображает меню, нам не надо, чтоб она исполняла какой-то код.
Кроме того, мы добавили некоторую разметку между тегами элемента toolbarbutton. Обратите внимание на новые элементы menupopup, menuitem и menuseparator. Элемент menupopup отвечает за создание и отображение всплывающего окна со всеми пунктами меню (считайте его контейнером, в котором расположены все элементы menuitem). Элемент menuseparator так же прост: он всего лишь помещает горизонтальный разделитель в выпадающее меню.
Элемент menuitem немного сложнее. Вот новые атрибуты, обозначенные в примере:
- label — определяет текст, отображаемый для данного пункта меню
- tooltiptext — это то же, что мы уже видели раньше, но с одной оговоркой. Из-за досадной ошибки в FireFox 1.0, этот атрибут обязателен. Вы можете оставить его пустым, но будет появляться пустая подсказка, когда юзер будет наводить мышь на пункт меню. Учитывая, что пункты меню традиционно не имеют всплывающих подсказок, эта “особенность” выглядит неаккуратно и зело раздражает. Будем надеяться, что в следующих версиях FF баг поправят.
Подробнее об атрибутах элемента menuitem читайте на XUL Planet
Как видите, в создании кнопок меню нет ничего сложного. Обратим свой взор к третьему, более сложному, типу кнопок.
Кнопки “кнопка-меню”
Кнопки “кнопка-меню” — это именно то, о чем вы могли подумать: кнопки тулбара, которые ведут себя и как кнопки, и как меню. Кнопки навигации “Вперед” и “Назад” в FF как раз относятся к кнопкам этого типа. Реализовать это не так уж сложно:
<toolbarbutton id="GBLTut-TB-Combined" label="Search"
type="menu-button" tooltiptext="Combined Search Menu"
oncommand="GBLTut_Search(event, 'web')">
<menupopup>
<menuitem id="GBLTut-TB-Combined-Web" label="Web Search"
class="menuitem-iconic" tooltiptext="Search the Web"
oncommand="GBLTut_Search(event, 'web'); event.preventBubble();" />
<menuitem id="GBLTut-TB-Combined-Image" label="Image Search"
class="menuitem-iconic" tooltiptext="Search Images"
oncommand="GBLTut_Search(event, 'image'); event.preventBubble();" />
</menupopup>
</toolbarbutton>
Эта кнопка меню представляет собой комбинацию двух предыдущих типов, и включает одно значительное изменение: атрибут type принимает значение “menu-button“. Так, вы заметите, что этот элемент включает атрибут oncommand (как обычная кнопка), также как и вложенные элементы menupopup и menuitem (как кнопка меню). Все остальное должно само себя объяснять. Вы, возможно, заметили дополнительный код в атрибуте oncommand каждого элемента menuitem. Причины его появления обсудим в разделе про скрипты.
Единственное отличие от конечного варианта — в порядке расположения кнопок. В конечном варианте сначала идет кнопка меню, потом кнопка “кнопка-меню”, а потом просто кнопка. Такое решение принято исключительно из соображений стилевого оформления.
Выпадающее поле ввода
Если вам понадобится выпадающее поле ввода (как поле поиска в нашем расширении), вы обнаружите, что и такое создать труда не составит. Вот как это делается у нас в Северной Каролине:
<toolbaritem id="GBLTut-SearchTerms-TBItem" persist="width">
<menulist id="GBLTut-SearchTerms" editable="true" flex="1"
minwidth="100" width="250"
onkeypress="if(event.which == 13) { GBLTut_Search(event, 'web'); }">
<menupopup id="GBLTut-SearchTermsMenu" onpopupshowing="GBLTut_Populate()" />
</menulist>
</toolbaritem>
Мы начинаем с элемента toolbaritem. Присваиваем ему ID и просим FF запомнить его ширину, используя атрибут persist. Элемент toolbaritem нужен, чтоб выделять объекты, не являющиеся кнопками (в нашем случае — поле ввода).
Внутри элемента toolbaritem находится элемент menulist, который, собственно, и делает то, что нам надо. В этом примере употребляется несколько новых атрибутов:
- editable — когда установлен в true, пользователь может вбивать текст в поле ввода
- flex — указывает, что элемент может растягиваться. Значение атрибута — целое число, определяющее способность к растягиванию по сравнению с другими элементами тулбара (т.е. элемент со значением атрибута flex равным 100 может занимать больше, чем элемент с flex=’1′).
- minwidth — указывает минимальную допустимую ширину элемента в пикселах
- width — указывает исходную ширину в пикселах
- onkeypress — это событие происходит когда пользователь нажимает клавишу на элементе (например, начинает вбивать текст в поле ввода)
Более подробное описание атрибутов элемента menulist ищите на XUL Planet.
Наконец, внутри элемента menulist располагается элемент menupopup. Как и в случае с кнопками меню, это контейнер, который в итоге будет содержать все элементы menuitem. Событие onpopupshowing случается непосредственно перед тем, как пользователю будет показано низпадающее меню. Далее в этом руководстве мы научимся динамически наполнять это меню. Если вы хотите расположить здесь статически какие-то пункты меню, просто добавьте их так же, как мы это делали с кнопками меню.
Обратите внимание, что я расположил поле ввода между кнопкой главного меню и кнопкой комбинированного поиска — опять же, чисто дизайнерское решение. Наше расширение начинает преобретать форму!
Resizing Gripper
(элемент интерфейса для изменения размера поля ввода =) — прим.пер.)
Теперь мы добавим “resizing gripper“, с помощью которого пользователь сможет изменять размер поля ввода. Это делается так:
<splitter id="GBLTut-ResizeSplitter" state="open" collapse="none"
resizebefore="closest" resizeafter="farthest"
tooltiptext="Resize the Search Box" />
(”Splitter” — это что-то вроде бегунка, этот самый “resizing gripper“. Будем называть его просто “сплиттер” — прим.пер.).
Вот список атрибутов элемента splitter, которые мы использовали в вышеприведенном примере (кроме тех, с которыми уже разобрались):
- state — определяет, будет ли splitter иметь свернутое содержимое. Значение “open” указывает, что содержимое после или перед сплиттером (в нашем случае — с обоих сторон) будет видимым.
- collapse — определяет, какая сторона сплиттера свернута. Мы выставляем “none“, поскольку все отображается.
- resizebefore — указывает, какой элемент слева от сплиттера должен изменить размер при перемещении сплиттера. Мы выставляем “closest“, поскольку хотим, чтоб размер изменяло поле ввода, т.е. ближайший к сплиттеру элемент.
- resizeafter — указывает, какой элемент справа от сплиттера должен изменять размер, когда пользователь двигает сплиттер. В нашем расширении Googlebar Lite я использую значение “farthest“, поскольку хочу, чтоб изменялся размер свободного пространства на правом краю тулбара.
Подробнее об атрибутах элемнта splitter — XUL Planet.
Сплиттер должен появляться перед элементами внутри контейнера, или после них. В Googlebar Lite сплиттер должен располагаться между элементом toolbaritem, содержащим поле ввода, и элементом toolbaritem, содержащим поисковые кнопки. В нашем случае у нас нет двух контейнеров (есть только один, вокруг поля ввода), так что давайте добавим второй. Вот разметка:
<toolbaritem flex="0"> </toolbaritem>
Мы хотим, чтоб этот элемент окружал наши поисковые кнопки (кнопки “кнопка-меню” и обычную кнопку). Так что располагаем открывающий тег перед разметкой для кнопки “кнопка-меню”, а закрывающий — после разметки для обычной кнопки. В результате получаем следующий код: [XUL, Вариант 4]
Дабы избавиться от раздражающих проблем косметического плана с resizing gripper-ом, нужно поместить еще один элемент toolbaritem вокруг нашего первого toolbaritem-а (в нашем случае, кнопки меню). Это предотвратит выталкивание кнопки меню с левой стороны тулбара, когда gripper перетаскивается совсем влево. Раз уж мы добавляем этот элемент, давайте пойдем дальше и добавим еще парочку. Во-первых, вставим элемент toolbarseparator между двумя последними кнопками (из косметических соображений). Во-вторых, расположим элемент toolbarspring прямо после последнего элемента toolbaritem. Это позволит нам перетаскивать ресайзер вправо до конца. Разметка проста до безобразия:
<toolbarseparator /> <toolbarspring />
После внесения всех этих изменений, конечный XUL будет выглядеть следующим образом:
<?xml version="1.0"?> <overlay id="GBLTut-Overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul“> <toolbox id=”navigator-toolbox”> <toolbar id=”GBLTut-Toolbar” accesskey=”T” class=”chromeclass-toolbar” context=”toolbar-context-menu” toolbarname=”GBL Tutorial Toolbar” hidden=”false” persist=”hidden”> <toolbaritem flex=”0″> <toolbarbutton id=”GBLTut-MainMenu” type=”menu” tooltiptext=”GBL Tutorial Toolbar Main Menu”> <menupopup> <menuitem label=”Google Home Page” accesskey=”G” tooltiptext=”Browse to the Google home page” oncommand=”GBLTut_LoadURL(’http://www.google.com/’)” /> <menuseparator /> <menuitem label=”Born Geek Website” accesskey=”B” tooltiptext=”Visit the Born Geek website” oncommand=”GBLTut_LoadURL(’http://www.borngeek.com/’)” /> </menupopup> </toolbarbutton> </toolbaritem> <toolbaritem id=”GBLTut-SearchTerms-TBItem” persist=”width”> <menulist id=”GBLTut-SearchTerms” editable=”true” flex=”1″ minwidth=”100″ width=”250″ onkeypress=”if(event.which == 13) { GBLTut_Search(event, ‘web’); }”> <menupopup id=”GBLTut-SearchTermsMenu” onpopupshowing=”GBLTut_Populate()” /> </menulist> </toolbaritem> <splitter id=”GBLTut-ResizeSplitter” state=”open” collapse=”none” resizebefore=”closest” resizeafter=”farthest” tooltiptext=”Resize the Search Box” /> <toolbaritem flex=”0″> <toolbarbutton id=”GBLTut-TB-Combined” label=”Search” type=”menu-button” tooltiptext=”Combined Search Menu” oncommand=”GBLTut_Search(event, ‘web’)”> <menupopup> <menuitem id=”GBLTut-TB-Combined-Web” label=”Web Search” class=”menuitem-iconic” tooltiptext=”Search the Web” oncommand=”GBLTut_Search(event, ‘web’); event.preventBubble();” /> <menuitem id=”GBLTut-TB-Combined-Image” label=”Image Search” class=”menuitem-iconic” tooltiptext=”Search Images” oncommand=”GBLTut_Search(event, ‘image’); event.preventBubble();” /> </menupopup> </toolbarbutton> <toolbarbutton id=”GBLTut-TB-Web” tooltiptext=”Search the Web” label=”Web Search” oncommand=”GBLTut_Search(event, ‘web’)” /> </toolbaritem> <toolbarspring /> </toolbar> </toolbox> </overlay>
