Страницы: 1
Недавно понадобилось сделать одну функциональность, в которой используется отображение и редактирование иерархических данных в xul:tree. Поскольку реализация custom nsITreeView - дело нехитрое, и не раз пройденное, то, чтобы жизнь медом не казалась, я решил сделать дерево на основе rdf-источника. В процессе разбора документации я обнаружил, что, оказывается, на данный момент в качестве источников данных поддерживаются и шаблоны на основе XML, и даже SQLite-шаблоны. В свое время, пытаясь для себя уяснить специфику rdf-шаблонов, я был не особенно в восторге от её реализации (впечатление на тот момент - перегруженное достаточно неочевидной логикой надмножество XML). Теперь же источники данных вполне можно проектировать на основе XML, используя в качестве основы запросов давно известные и понятные XPath-выражения. Но здесь есть одно "но": RDF - фактически родная технология Mozilla, очень давно поддерживаемая, и хорошо обкатанная. Шаблоны и источники данных на других основах появились, насколько я понял, сравнительно недавно. Поэтому, если кому-то доводилось оценивать плюсы и минусы от использования перечисленных источников, то хотелось бы услышать мнение, какие источники данных всё же предпочтительнее использовать на данный момент.
Отсутствует
В результате экспериментов выяснилось следующее. Самый главный минус источников данных, отличных от rdf - очень скудная документация по работе c этими источниками. Если для rdf можно найти примеры кода почти на все случаи жизни, то для того же XML - только самые простейшие примеры.
Вот первая, и достаточно неприятная проблема, с которой я столкнулся. Допустим, у нас есть xml вот такого вида:
<?xml version="1.0" encoding="UTF-8"?> <root> <item name="ItemA" /> <group type="GroupA"> <item name="ItemAA" /> <group type="GroupAB"> <item name="ItemABA" /> <item name="ItemABB" /> </group> <item name="ItemAB" /> </group> <item name="ItemB" /> <group type="GroupB"> <item name="ItemBA" /> <item name="ItemBB" /> </group> <item name="ItemC" /> </root>
<tree id="xmltree" flex="1" datasources="chrome://addondevhelper/content/items.xml" ref="*" querytype="xml" flags="dont-build-content"> <treecols> <treecol id="name" primary="true" label="Name" flex="1"/> </treecols> <template> <query expr="*"/> <action> <treechildren> <treeitem uri="?"> <treerow> <treecell label="?name"/> </treerow> </treeitem> </treechildren> </action> </template> </tree>
<tree id="xmltree" flex="1" datasources="chrome://xmltest/content/items.xml" ref="*" querytype="xml" flags="dont-build-content"> <treecols> <treecol id="name" primary="true" label="Name" flex="1"/> </treecols> <template> <queryset> <query expr="group"/> <action> <treechildren> <treeitem uri="?"> <treerow> <treecell label="?type"/> </treerow> </treeitem> </treechildren> </action> </queryset> <queryset> <query expr="item"/> <action> <treechildren> <treeitem uri="?"> <treerow> <treecell label="?name"/> </treerow> </treeitem> </treechildren> </action> </queryset> </template> </tree>
<button label="xml menu" type="menu" datasources="chrome://xmltest/content/items.xml" ref="*" querytype="xml"> <template> <queryset> <query expr="group"/> <action> <menupopup> <menu uri="?" label="?type"/> </menupopup> </action> </queryset> <queryset> <query expr="item"/> <action> <menupopup> <menuitem uri="?" label="?name"/> </menupopup> </action> </queryset> </template> </button>
Отсутствует
Сухой остаток от данной темы: деревья с автоматически генерируемым по шаблону контентом достаточно удобны, если требуется только отображение контента, и не требуется редактирования. Потому что при заданном флаге dont-build-content внесенные изменения вообще не подхватываются деревом, и для элементов сгенерированного дерева не возникает событий drag'n'drop. А при не заданном флаге dont-build-content всего этого нет, но внесенные изменения не проецируются на builder datasource; метода для синхронизации datasource с данными в дереве я не нашел. Наверное, можно как-то спроецировать эти данные с помощью предоставляемых билдером nsITemplateResult, либо навесить на дерево листенеры, и в них менять исходные данные - но это мне видится очень муторным занятием; способа разом получить источник, содержащий измененные данные, нет, а из-за этого теряется весь смысл работы с таким типом деревьев - овчинка просто не стоит выделки. С помощью старого проверенного nsITreeView можно легко сделать всё то же самое. К тому же есть дополнительные проблемы с локализацией контента дерева, и проблемы, если в дереве нужно отображать сепараторы - это опять несколько запросов на дерево, проблемы с сортировкой, и т.д.
А сортировку сгенерированного многоуровневого меню, и с сепараторами в составе в т.ч. меню можно решить именно введением порядкового атрибута - просто надо применить с исходному XML копирующий XSLT, в котором дополнительно на каждый эелемент навешиваеатся атрибут с результатом отработки <xsl:number>:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:attribute name="ord"> <xsl:number count="*" level="any" format="000001"/> </xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet>
и для контейнера меню дополнительно задать
sort="?ord" sortDirection="ascending"
Отсутствует
Всё-таки не дает покоя мне эта тема, и видимо, вернусь к ней я еще не раз.
На данном этапе удалось разобраться, как всё-таки нормально генерить деревья и меню на основе xml с разнородным содержимым. Основная идея - обрабатывать все элементы в одном запросе, чтобы не нарушать порядок их следования, а обработку тэгов отличающихся типов делать внутри, в зависимости от совокупности условий. Приведенный ниже пример, кроме того, показывает, как можно извлекать из XML и применять в шаблоне treeitem properties для стилизации дерева. Может быть, кому-нибудь пригодится.
Исходный XML:
<?xml version="1.0" encoding="UTF-8"?> <root id="0"> <item id="1" name="ItemA" color="green"/> <item id="2" name="GroupA" color="yellow" exticon="jolly"> <item id="3" name="ItemAA" color="blue"/> <item id="4" name="GroupAB"> <item id="5" name="ItemABA" color="red"/> <separator id="13"/> <item id="6" name="ItemABB" /> </item> <item id="7" name="ItemAB" color="lightgray"/> </item> <item id="8" name="ItemB" /> <separator id="14"/> <item id="9" name="GroupB"> <item id="10" name="ItemBA" color="dialog"/> <item id="11" name="ItemBB" /> </item> <item id="12" name="ItemC" /> </root>
XUL:
<tree id="xmltree" flex="1" datasources="chrome://xmltemplatetest/content/items.xml" ref="*" querytype="xml" _flags="dont-build-content" draggable="true" editable="true"> <treecols> <treecol id="name" primary="true" label="Name" flex="1"/> </treecols> <template> <query expr="*"> <assign var="?nodename" expr="local-name(.)"/> <assign var="?props" expr="concat(@color, ' ', @exticon)"/> </query> <rule> <conditions> <where subject="?nodename" value="separator" rel="equals" ignorecase="true"/> </conditions> <action> <treechildren> <treeseparator uri="?" srcid="?id"/> </treechildren> </action> </rule> <rule> <conditions> <where subject="?nodename" value="item" rel="equals" ignorecase="true"/> </conditions> <action> <treechildren> <treeitem uri="?" srcid="?id"> <treerow properties="?props"> <treecell label="?name" properties="?props"/> </treerow> </treeitem> </treechildren> </action> </rule> </template> </tree> <button id="xml-menu-button" label="xml-generated menu" type="menu" querytype="xml" datasources="chrome://xmltemplatetest/content/items.xml" ref="*"> <template> <query expr="*"> <assign var="?count" expr="count(*)"/> <assign var="?nodename" expr="local-name(.)"/> </query> <rule> <conditions> <where subject="?count" value="0" rel="greater"/> </conditions> <action> <menupopup> <menu uri="?" label="?name" id="?id" color="?color" exticon="?exticon" class="menu-iconic"/> </menupopup> </action> </rule> <rule> <conditions> <where subject="?count" value="0" rel="equals"/> <where subject="?nodename" value="item" rel="equals" ignorecase="true"/> </conditions> <action> <menupopup> <menuitem uri="?" label="?name" id="?id" color="?color" exticon="?exticon" class="menuitem-iconic"/> </menupopup> </action> </rule> <rule> <conditions> <where subject="?nodename" value="separator" rel="equals" ignorecase="true"/> </conditions> <action> <menupopup> <menuseparator uri="?" id="?id"/> </menupopup> </action> </rule> </template> </button>
Стиль:
treechildren::-moz-tree-row(green) { background-color: lime; } treechildren::-moz-tree-row(red) { background-color: red; } treechildren::-moz-tree-row(yellow) { background-color: yellow; } treechildren::-moz-tree-row(blue) { background-color: lightblue; } treechildren::-moz-tree-row(lightgray) { background-color: lightgray; } treechildren::-moz-tree-row(dialog) { background-color: -moz-dialog; } treechildren::-moz-tree-row(yellow, jolly) { background-color: magenta; } treechildren::-moz-tree-image(open) { list-style-image: url(folderopen.png); } treechildren::-moz-tree-image(open, jolly) { list-style-image: url(jr.png); } treechildren::-moz-tree-image(closed) { list-style-image: url(folder.png); } menu[color], menuitem[color] { -moz-appearance: none; } menu { list-style-image: url(folder.png); } menu[open] { list-style-image: url(folderopen.png); } menu[open][exticon="jolly"] { list-style-image: url(jr.png); } menu[color="green"], menuitem[color="green"] { background-color: lime; } menu[color="red"], menuitem[color="red"] { background-color: red; } menu[color="yellow"], menuitem[color="yellow"] { background-color: yellow; color: black; } menu[color="blue"], menuitem[color="blue"] { background-color: lightblue; } menu[color="lightgray"], menuitem[color="lightgray"] { background-color: lightgray; } menu[color="dialog"], menuitem[color="dialog"] { background-color: -moz-dialog; }
Результат выглядит примерно так:
Примечания к коду: во-первых, не получилось использовать treeseparator в дереве с флагом dont-build-content - что-то вроде бы появляется, но как сепаратор оно не выглядит. Без флага dont-build-content всё в порядке. Некоторым объяснением этому может служить упоминание в документации о том, что с флагом dont-build-content в дереве надо использовать не контентные элементы, а элемент treeitem.
Во-вторых, с xml-шаблонами не работает спец. правило rule iscontainer="true"/"false" - видимо, это правило относится к специфике RDF-шаблонов. Для своего случая я просто вытащил в отдельную переменную количество дочерних узлов текущего узла обработки, и далее использовал эту переменную в условии обработки.
И, как видно из вышеприведенного, уже не нужен хак с нумерацией узлов через XSLT.
Теперь надо попытаться как-то редактировать это дерево.
Отсутствует
отбой. вопрос решил через string.split
Плохое решение. C xml надо работать как с DOM-документом, а не как со строкой. Соответствующими методами - getElementById, appendChild, возможно - XPath, и т.д.
Отсутствует
\n воспринимается не как текст а как перевод строки
Слэш заэкранировать нужно. Т.е. в строку вписывать "\\n".
Добавлено 12-11-2011 21:19:17
Кстати. В FF7 был исправлен Bug 663728 – XUL tree twisty state won't persist if XML template is used. И если дерево с флагом dont-build-content использует XML-шаблон, в котором каждый элемент имеет уникальный id, то FF сам будет запоминать состояние свернутости/развернутости узлов дерева, и восстанавливать это состояние.
Отредактировано hydrolizer (12-11-2011 21:22:14)
Отсутствует
hydrolizer
вопрос как к специалисту по nsITreeView
я сейчас делаю приложение(на xulrunner), которое работает с древовидными данными.
Работа с данными происходит через XPCOM C++(спасибо еще раз за помощь с этим).
я нашел достаточно примеров как работать custom treeview, но все они на js.
В моем случае логично бы было некоторые функции реализовать в XPCOM компоненте на с++.
Например getCellText будет многократно вызываться для построения дерева. Внутри
этой функции я буду каждый раз обращаться к XPCOM компоненту за данными.
Возможно ли это?
Отсутствует
YuryL
nsITreeView - это же, по сути, не класс, а интерфейс. Его реализация в рамках заданного контракта может быть любой - на чистом js, на XPCOM как js + .xpt-описатель интерфейса, на XPCOM/c++ (например, для тех же деревьев на основе шаблонов модель и часть контракта nsITreeView реализуются в XPCOM-компоненте nsXULTreeBuilder). Здесь дело в другом: методы nsITreeView, подобные getCellText, с большой частотой вызываются при отрисовке дерева, поэтому стоит заранее проверить скорость отработки и ресурсоемкость вызова метода, который вы планируете вызывать внутри getCellText - если он достаточно ресурсоемок, то он просто вам подвесит всё окно с деревом. В таком случае придется данные, которые возвращает метод, формировать заранее, и включать их в данные, на основе которых строится модель (view).
Отсутствует
не нашел описания nsXULTreeBuilder в MDN
ведь мне нужно свойству view дерева задать объект, который реализован на с++.
Как это сделать на js?
Либо в коде с++ каким-то образом это сделать. Где можно почитать об этом?
Или пример найти. Или я туплю, не могу найти в MDN ничего на эту тему...
Отсутствует
не нашел описания nsXULTreeBuilder в MDN
Его там как такового нет, потому что это - реализация интерфейса, о чем я писал выше. Описания самих интерфейсов, и описания способов работы посредством спец. элементов (template/query/rule etc.) - есть. Но если вам хочется взглянуть на детали реализации - смотрите здесь: http://mxr.mozilla.org/mozilla-central/ … uilder.cpp
ведь мне нужно свойству view дерева задать объект, который реализован на с++.
Как это сделать на js?
А как вы обычно получаете экземпляр XPCOM-компонента? Точно так же и здесь:
Интерфейс экземпляра полученного класса должен соответствовать контракту интерфейса nsITreeView. И если это так, то далее просто
Отсутствует
спасибо, мне не верилось, что свойству view может быть присвоена, как ссылка на объект в двоичном коде, так и на js-объект, который будет интерпретироваться. ЗдОрово! Буду пробовать..
Может и методы класса написанного на с++ можно для начала реализовать на js, а потом по одному переписывать на с++?
Кстати, еще вопрос, позволяет ли VS 2010 отлаживать XPCOM компоненты, т.е. будут ли срабатывать точки останова в среде VS 2010?
hydrolizer, ваши ответы очень помогают разобраться, спасибо еще раз..
Отсутствует
Кстати, еще вопрос, позволяет ли VS 2010 отлаживать XPCOM компоненты, т.е. будут ли срабатывать точки останова в среде VS 2010?
Не знаю - я пока что обходился простым отладочным выводом в консоль - не в консоль ошибок FF, а в консоль windows. Для этого достаточно обычного printf - например,
и при запуске FF с консолью (нужно добавить ключ -console в строку запуска FF) будет примерно следующее:
BTW, debugging with Visual Studio 6.0 works great. Just set the exectuable to firefox, set your breakpoints, hit F5, and it works perfectly.
Здесь: http://www.codeproject.com/KB/miscctrl/ … ation.aspx автор также пишет, что
An easy and simple way of learning this is to just use the projects and debug with break point.
так что вполне возможно, что и VS2010 позволит пошаговую отладку. Попробуйте.
Отсутствует
С файлом nsXULTreeBuilder.cpp не получается разобраться, даже откомпиллировать.
Хотелось бы найти рабочий пример для создания XPCOM компонента реализующего nsITreeView.
Отсутствует
Страницы: 1