Опубликован: 10.12.2007 | Доступ: свободный | Студентов: 822 / 20 | Оценка: 5.00 / 5.00 | Длительность: 58:33:00
Лекция 13:

Списки и Деревья

13.3.15 Интерфейсы AOM and XPCOM

Особенности скриптов в XUL-деревьях довольно сложны. Здесь мы рассмотрим их в общем и более конкретно в приложении к простым деревьям. Простые деревья - те, что не привлекают RDF или шаблоны. Все примеры в этой лекции - простые деревья. Более сложные случаи рассматриваются в "Шаблоны" , "Шаблоны".

Тег <tree> является примером специализированного блокообразного тега, наподобие <listbox>, <iframe> и <scrollbox>. Поэтому его DOM объект имеет свойство boxObject. Данный объект поддерживает интерфейс, специфичный для деревьев. Этот интерфейс отвечает за скролинг, навигацию, выбор и методы извлечения данных для дерева. Как и для тега <listbox>, XBL код дерева дает доступ к этому интерфейсу. К нему можно получить доступ и из компонента:

@mozilla.org/layout/xul-boxobject-tree;1 nsITreeBoxObject

Этот интерфейс подобен во многих отношения интерфейсу nsIListBoxObject. В отличие от тега <listbox>, лишь немногие его свойства были экспортированы в XBL код тега <tree>. Это значит, что придется работать непосредственно с nsITreeBoxObject. Этот объект доступен как свойство treeBoxObject DOM объекта тега дерево.

Таблица 13.3 демонстрирует возможности этого интерфейса по управлению областью, занятой деревом на экране.

Таблица 13.3. Свойства и методы интерфейса nsITreeBoxObject
Свойство или метод Описание
Selection объект nsITreeSelection, знающий, какие строки выбраны
rowHeight Высота строк в пикселах (все строки имеют одинаковую высоту)
getColumnIndex(id) Порядковый номер колонки с id="id"
getColumnId(index) id колонки с порядковым номером index
getKeyColumnIndex() порядковый номер первичной колонки
getFirstVisibleRow() index верхней видимой строки
getLastVisibleRow() index нижней видимой строки
getPageCount() Общее число строк, деленное на число видимых строк, то же, что и число страниц верстки
ensureRowIsVisible(index) Пролистывать контент, пока строка index не станет видима
scrollToRow(index) Пролистывать контент, пока строка index не станет верхней
scrollByLines(count) Пролистывать вниз (>0) или вверх (<0) count строк; остановить пролистывание, если пролистывать нечего
scrollByPages(count) Пролистывать вниз (>0) или вверх (<0) count страниц; страница – число строк, видимых в окне
invalidate() Перерисовать указанную часть дерева, index – номер строки, id – номер колонки
invalidateColumn(id)
invalidateRow(index)
invalidateCell(index, id)
invalidatePrimaryCell(index)
invalidateRange(index1, index2)
invalidateScrollbar()
getRowAt(x,y) Возвращает index строки с относительными координатами (x,y), либо -1
getCellAt(x,y,r,c,type) Возвращает index строки, id колонки и тип ячейки с относительными координатами (x,y); r,c,type должны быть пустыми объектами {}, которые получат свойство value, содержащее требуемое значение
getCoordsForCellItem(index, id, type, x, y, w, h) Возвращает значения x-, y-, width-, и height- элемента в данной строке и колонке, type может быть "cell," "twisty," или "image. x-, y-, width-, и height- должны быть пустыми объектами {}, каждый объект получит свойство value
isCellCropped(index, id) Возвращает true, если строка index есть в колонке id
rowCountChanged(index, total) Строки числом total начиная со строки index были изменены, перерисовать их
beginBatchUpdate() Указание дереву останавливать перерисовку после малейшего изменения
endUpdateBatch() Указание продолжить перерисовку до конца
clearStyleAndImageCache() Удалить всю стилевую информацию при подготовке к смене темы

Деревья XUL имеют очень гибкие особенности реализации. Не только уже знакомые нам интерфейсы, но и некоторые важные концепции их строения также очень важно хорошо понимать. Деревья XUL сконструированы в рамках подхода, называемого Контроллер Модель-Представление (Model-View Controller, MVC), но используют свои термины для всех частей этой модели. Если коротко, этот подход означает следующее: Модель содержит данные, Представление их отображает, а Контроллер оба компонента координирует с внешним миром, т.е. пользователем.

Дерево XUL воплощает концепцию MVC с помощью графического виджета, исходных (seminal) данных, конструктора (builder) и снимка (view). Снимок - это часть кода, обрабатывающая исходные данные так, чтобы их можно было отобразить графически в данный момент. В терминах MVC исходные данные и снимок - это MVC-модель, а не MVC-представление. Виджет - часть MVC-представления, комплектуемая конструктором. Конструктор также играет роль Контроллера в модели MVC. Существует единственный виджет - дерево; он изображен на рисунке 13.6. Создавая дерево, программист приложения должен работать со всеми тремя частями модели MVC: конструктором, снимком и исходными данными. Некоторые из этих концепций требуют обращения к шаблонам. В последующем обсуждении замечание, заключенное в круглые скобки ( "Шаблоны" , "Шаблоны") означает, что данный вопрос подробно рассматривается в "Шаблоны" .

13.3.15.1 Исходные данные

Исходные данные - это данные, из которых появляется содержание дерева. Это не технический, а описательный термин, введенный для того, чтобы избежать повторения технических терминов. Чтобы создать дерево, необходим тег <tree>, но данные для строк и ячеек могут появляться из трех источников: XUL, JavaScript, или RDF.

Листинг 13.4 - пример данных для дерева, описанных в XUL. Содержание находится там же, где и тег <tree>. Это самый прямой путь определения содержания. Даже если мы используем оверлеи, чтобы подгрузить контент из другого документа, это все еще чистое XUL-решение.

Контент дерева может быть определен и в JavaScript. Есть два способа сделать это. Первый способ - использовать интерфейс DOM 1, чтобы создать объекты элемента, обратившись к document.createElement(). Модифицируя дерево DOM, можно добавлять контент к существующему XUL-дереву. Листинг 13.4 - это пример добавления строки к дереву, созданному в Листинге 13.3. Это обычная DOM 1 операция, с помощью которой создается и добавляется новый тег.

var tree = doc.getElementById("topchildren"); 
var item = doc.createElement("treeitem"); 
var row = doc.createElement("treerow");

for (var i=1; i=7; i++) // there are six columns { 
  var cell = doc.createElement("treecell");
  cell.setAttribute("label","NewCell"+i);
  row.appendChild(cell);
} 
item.appendChild(row);
tree.appendChild(item); // item, row and cells now appear
Листинг 13.4. DOM манипуляции XUL деревом.

Другой способ использовать JavaScript как исходные данные для заполнения дерева содержанием - создать пользовательский снимок. Снимок - это множество методов JavaScript, которые могут использоваться для обработки контента дерева.

Наконец, исходные данные могут появиться из RDF-документа. Это достигается с помощью шаблонов, используется лишь немного XUL кода ( "Шаблоны" , "Шаблоны").

13.3.15.2 Конструкторы дерева

Тема "конструкторы" не слишком проста, в основном потому, что они явно используются только с XUL-деревьями. А в этом случае "за деревьями не видно леса", то есть скрыты основные причины, по которым конструкторы вообще существуют. Рассмотрим простейшие случаи. Любой тег XUL начинает свою жизнь как простая текстовая строчка. Если это отображаемый визуально тег, например, <button>, в конце концов он должен проявиться пикселями на экране. Внутренний механизм Gecko отвечает за верстку, рендеринг, стилевое оформление этой трансформации. Если тег относительно прост, как <box>, его информация может быть послана непосредственно в этот механизм. Однако существует всего несколько простых XUL-тегов. Например, уже тег <button> может быть сочетанием тегов и включать в себя теги <label> и <image>. XBL код тега <button> обрабатывает эти теги и посылает результат в отображающий механизм Gecko. Таким образом в коде XBL выполняется некоторый дополнительный предварительный шаг, необходимый, чтобы Gecko мог получить что-то для своей работы. А некоторые XUL-теги требуют преобразований еще сверх тех, которые может выполнить XBL. Например, <menulist>. Какая-то часть платформы должна сконструировать и потом уничтожить выпадающие окошки тега <menulist> в момент их использования. XBL за это не отвечает. Эта функциональность должна быть частью самой платформы.

Такая функциональность и называется конструктором, просто потому, что он собирает и разбирает тот контент, который должен быть отображен (или убран с экрана). Каждый XUL-тег имеет свой конструктор, по крайней мере концептуально, но в большинстве случаев конструктор тривиален. Почти всегда конструктор невидим для приложения и работает автоматически. Лишь самые сложные теги имеют конструктор, доступный в приложении. <listbox> имеет сложный конструктор, но он невидим. Только теги <tree> и <template> имеют доступные программисту конструкторы, но даже эти конструкторы доступны не всегда, а лишь часть времени. В Mozilla есть два конструктора для деревьев. Конструктор XUL-контента, или конструктор дерева по умолчанию, используется при верстке XUL-деревьев и деревьев, конструируемых с помощью DOM. Усилий программиста в этом случае не требуется. Этот конструктор сам создает дерево, используя пакетный (batch) процесс, и все дерево создается за один проход. Если DOM- операция модифицирует дерево, созданное XUL, конструктор вообще не требуется. Уже созданный код сам по себе достаточно умен, чтобы реагировать на эти изменения самостоятельно.

Этот конструктор деревьев невидим. У него нет XPCOM-компонента или интерфейсов. Он не программируем. Ему присвоено имя, только чтобы отделить его от конструктора шаблонов. Контент-конструктор, или конструктор дерева по умолчанию, это часть Mozilla, которая выполняет функции конструктора, когда никакого специализированного, доступного конструктора нет.

Конструктор XUL-шаблонов ( "Шаблоны" , "Шаблоны") используется для создания любого контента, основанного на шаблоне, включая деревья. Он возникает автоматически при использовании шаблонов. Он имеет специализированную версию, применяемую при верстке деревьев, которая и называется конструктором дерева. Конструктор дерева должен был бы иметь какое-то более описательное имя, например, builder-for-special-combination-of-template-and-tree. Конструктор дерева может создавать дерево "лениво", то есть части дерева остаются несозданными и неотображенными до тех пор, пока реально не понадобятся. Конструктор дерева доступен программисту. Для своей работы он может использовать контент-конструктор. Но конструктор дерева может выполнять свою работу и самостоятельно, полагаясь на иные части кода. Он имеет программируемый компонент:

@mozilla.org/xul/xul-tree-builder;1

Интерфейс конструктора ( "Шаблоны" , "Шаблоны") для этого объекта:

nsIXULTemplateBuilder nsIXULTreeBuilder

Второй конструктор - то, что мы используем в работе с деревом. Если конструктор существует для конкретного дерева, то свойство builder соответствующего DOM-элемента будет содержать этот конструктор.

13.3.15.3 Снимки

Конструктор должен сделать всю работу, чтобы создать дерево из исходных данных. Но он может и передать часть работы субподрядчику, который специализируется именно на подготовке данных к отображению. В этом случае конструктор освобождается и может тратить свое время более эффективно, руководя процессом. Этот субподрядчик в данном случае называют снимок (a view). Он дает нам представление, или взгляд на исходные данные, это не взгляд на требуемый графический (GUI) результат. В терминах объектно-ориентированного программирования такой подход называется делегированием. Без части, называемой снимок, конструктор неполон и не может выполнить никакой работы. Без конструктора снимок готов к работе, но у него нет "начальника", который скажет, что, собственно, делать. Снимок используется конструктором, но его может задействовать и программист. В некоторых случаях снимок может быть программистом создан. В любом случае, снимок должен иметь интерфейсы:

nsITreeView nsITreeContentView

В таблице 13.4 приведены свойства, создаваемые XBL-определением тега <tree> для поддержки снимков.

Таблица 13.4. JavaScript свойства тега <tree> для снимков
Свойство Соотносится с другим свойством? Содержание
treeBoxObject.view Да Объект view, снимок, предоставляющий интерфейс nsITreeView
view Да Объект view, предоставляющий nsITreeView
contentView Да Объект view, предоставляющий nsITreeContentView
builderView Да Объект view, предоставляющий nsIXULTreeBuilder

Если один снимок заменяется другим, теоретически следует обновить все эти свойства. На практике достаточно обновить свойство treeBoxObject.view или свойство view и избегать использования остальных. Таблица 13.5 описывает интерфейс, представляемый снимком.

Таблица 13.5. Свойства и методы интерфейсов TreeView
Свойство или метод Используется конструктором XUL-контента? Описание
NsITreeContentView root Указатель DOM-объекта тега <tree>
getItemAtIndex(index) + Возвращает <treerow> видимой строки с номером index ; строки учитываются, если они могут быть пролистаны до видимого состояния, и нет, если требуется раскрытие поддерева, счет начинается с 0
getIndexOfItem(treerow) + Возвращает index -позицию <treerow> в дереве
canDropBeforeAfter(index) + True если вставляемый элемент может быть помещен до или после данной строки
canDropOn(index) + True, если данная строка может быть местом вставки в drag-drop операции
cycleCell(index, id) Выполняется, когда выбранная ячейка в строке index, столбце id является колонкой - маркером (cycle)
cycleHeader(id, element) Выполняется, когда выбрана ячейка с маркером
drop(index, where) Выполняется, когда отпускается перетаскиваемая строка, index – куда она перетаскивается, where – ее действие, может быть 0, 1 или 2
getCellProperties(index, column_index, array) + Заполняет массив nsISupportsAarray значениями, найденными в свойствах ячейки, и возвращает массив
getCellText(index, id) + Возвращает текстовое значение value= строки index, колонки id
getColumnProperties(index, column-id, array) + Заполняет массив nsISupportsAarray значениями, найденными в свойствах колонки, и возвращает массив
getImageSrc(index, id) + Возвращает URL изображения в ячейке строки index, колонки id
getLevel(index) + Возвращает глубину уровня строки в дереве.
getParentIndex(index) + Возвращает index родительской строки, -1, если родительской строки нет.
getProgressMode(index,id) + Возвращает тип <progressmeter> в ячейке строки index, колонки id, может быть 1, 2 или 3
getRowProperties(index, array) + Заполняет массив nsISupportsAarray значениями, найденными в свойствах строки, и возвращает массив
hasNextSibling(index, start_index) + True, если данная строка – первая из нескольких дочерних
isContainer(index) + True, если данная строка имеет свойство container="true" (содержит поддерево из нуля или более элементов)
isContainerEmpty(index) + True, если данная строка имеет container="true" и ноль дочерних тегов <treechildren>
isContainerOpen(index) + True, если данная строка имеет open="true" и container="true"
isEditable(index, id) Всегда false Возвращает true, если ячейка строки index и колонки id может редактироваться
isSeparator(index) + True, если данная строка есть <treeseparator>
isSorted() Всегда false True, если любая колонка в строке отсортирована
performAction(command) performActionOnRow(command, index) performActionOnCell(command, index, column id) Послать команду целому дереву, строке, или отдельной ячейке
rowCount + Общее число строк в дереве
selection + Возвращает объект nsITreeSelection, содержащий детали выбранной области
selectionChanged() + Выполняется, когда выбранная область изменяется
setCellText(index, id, value) Тексту ячейки дать значение value
setTree(nsITreeBoxObject) Применяется во время инициализации дерева, избегайте использовать
toggleOpenState(index) + Выполняется, когда конструктор поддерева открывается или закрывается. Может быть использовано напрямую

Mozilla имеет полдюжины снимков, написанных на C/C++, и дюжину - на JavaScript. Один специальный снимок принадлежит конструктору XUL- контента. Его называют просто "tree content view", это снимок для доступа к содержимому дерева, построенному на XUL-контенте. Этот простой снимок - не полный XPCOM компонент; это просто часть конструктора XUL-контента. Он, однако, полностью поддерживает описанные выше интерфейсы.

Когда конструктор XUL контента строит дерево, объект с этим интерфейсом присоединяется к свойству view DOM объекта <tree>. Этот снимок доступен программисту приложения. Он используется конструктором во время конструирования дерева, и может быть использован им после того, как дерево появится на экране.

Существует одно ограничение по использованию этого снимка. Он - read-only система. Метод снимка isEditable() вернет значение false ; это означает, что метод снимка set-CellText() не сделает ничего. Методы, способные изменять данные, могут быть вызваны внутри системы, но не программистом. Также, из-за того способа, каким эта система взаимодействует с деревом, методы данного интерфейса не могут быть заменены методами, определенными пользователем. Все, что мы можем делать с этим интерфейсом - извлекать информацию о дереве.

Если снимок должен быть перезаписываемый, или мы создаем свой, он должен быть основан на шаблонах ( "Шаблоны" , "Шаблоны").

Не только снимок содержания дерева, но и многие специфичные для конкретного приложения XPCOM-компоненты имеют доступ к интерфейсу nsITreeView и могут использоваться как готовые к употреблению снимки. Это, однако, очень специфичные для каждого приложения компоненты. Их использование требует внимательного изучения кода существующих приложений, например, почтового клиента. В версии 1.4 компоненты, имеющие снимок содержания дерева, это:

@mozilla.org/addressbook/abview;1
@mozilla.org/filepicker/fileview;1 @mozilla.org/inspector/dom-view;1
@mozilla.org/messenger/msgdbview;1?type=quicksearch
@mozilla.org/messenger/msgdbview;1?type=search
@mozilla.org/messenger/msgdbview;1?type=threaded
@mozilla.org/messenger/msgdbview;1?type=threadswithunread
@mozilla.org/messenger/msgdbview;1?type=watchedthreadswithunread
@mozilla.org/messenger/server;1?type=nntp
@mozilla.org/network/proxy_autoconfig;1
@mozilla.org/xul/xul-tree-builder;1

Чтобы использовать эти компоненты, за исключением последнего (используемого по умолчанию), мы настоятельно рекомендуем сначала внимательно изучить, как они применяются в файлах chrome. Директория chrome классической Mozilla также содержит множество реализаций nsITreeView на чистом JavaScript, их тоже можно изучить. Функциональность панели Navigator View | Page Info имеет пять снимков (в файле pageInfo.js архива comm.jar в директории chrome) и ее легче всего изучить, см. также страницу Info.xul. DOM-инспектор имеет два снимка (в jsObjectView.js и stylesheets.js), XUL <textbox type="autocomplete"> имеет один (файл autocomplete.xml) и Navigator about:config UR - один (в файле config.js). Дебаггер JavaScript и некоторые другие инструменты, например, Component Viewer также имеют реализации функции снимка на JavaScript. Некоторые приложения на JavaScript тоже реализовывали собственные снимки, которые мы можем использовать. Эти примеры помогут уменьшить количество работы, необходимой для реализации снимка.