xmltree — u_pbt_xmltree #
← Component reference · Guide contents
xmltree shows a XML value as a collapsible tree, colour-coded: a config screen, an API response, an integration debug. You set the XML text in
is_xml, the component shows it; a click on a node reports its path (ue_node_clicked), invalid XML raisesue_error. It changes nothing: it is a VIEWER.
▶ See it live — Demo application, XML tree tile, with the code and this page side by side.
At a glance #
| Userobject | u_pbt_xmltree |
| Input | The XML text, in is_xml (a string) |
| Fold / unfold | Each element by its + / − marker in the line-number gutter, by its path (of_expand_path, of_collapse_path), or all at once (of_expand_all, of_collapse_all) |
| Feedback | A click on a node gives its path (ue_node_clicked), an XPath n_pbt_xml reads as it is: /order/lines/line[2], /order/@id for an attribute, /order/comment()[1] for a comment — every line has its own; invalid XML raises ue_error and shows the source, the faulty line marked |
| Fidelity | Every node in its place, in document order: text between two children, CDATA sections, comments, processing instructions, the XML declaration |
Quick start #
// Show an XML response as a tree
uo_xml.is_xml = inv_rest.of_response_text()
// Fold everything, then let the user open what interests them
uo_xml.of_collapse_all()
// ue_node_clicked : the path of the node clicked, an XPath ("/order/lines/line[2]",
// "/order/@id" for an attribute) -> n_pbt_xml.of_get_value(as_path) reads its value
Properties #
| Property | Type | Default | Description |
|---|---|---|---|
is_xml | string | "" | The XML text to show, read and displayed as a tree: every node in its place. Empty clears the tree, without an error; invalid XML raises ue_error and shows the source around the faulty line. Reads back exactly as it was set, an invalid text included. A new document starts with nothing selected and every element open: of_get_layout before and of_set_layout after keep what the user opened |
ib_wrap | boolean | true | Long lines wrap (true, the default) or stay on one line with a horizontal scrollbar (false) — a code-editor feel |
ib_search_enabled | boolean | true | Ctrl+F in the tree opens a search box — the same search as of_search: type to highlight, Enter or F3 next, Shift+Enter or Shift+F3 previous, Escape closes. false leaves the shortcut to an application that searches from its own box. The box carries three toggles before the counter, Aa (ib_find_match_case), ab (ib_find_whole_word) and & (ib_find_decoded); changing an option restarts the current search from its first occurrence |
ib_find_match_case | boolean | false | Search option: finds only the text with the same case. The Aa toggle of the search box (Ctrl+F) is the same switch; applies to of_search and to what the user types. Read live; of_reset puts it back to false |
ib_find_whole_word | boolean | false | Search option: finds the text only as a whole word (a letter, a digit or a _ next to it belongs to the word: id is found neither inside user_id nor inside ids). The ab toggle of the search box is the same switch. Read live; of_reset puts it back to false |
ib_find_decoded | boolean | false | Search option: searches the decoded VALUES — a text and an attribute value read as n_pbt_xml reads them (Durand & Co finds Durand & Co, x < y finds x < y); the highlight covers the whole escape. false searches the text as it is shown. The & toggle of the search box is the same switch. Read live; of_reset puts it back to false |
is_theme_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
is_tooltip | string | "" | Simple tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip |
Methods #
| Method | Description |
|---|---|
of_expand_all ( ) → long | Unfolds every element. No ue_node_toggled: no gesture opens the whole tree at once. Returns 0, -2 when the component is not created |
of_collapse_all ( ) → long | Folds every nested element (the root stays visible). A selection hidden by a fold moves onto the folded element, and ue_selection_changed says so. No ue_node_toggled: no gesture folds the whole tree at once. Returns 0, -2 when the component is not created |
of_clear ( ) → long | Empties the tree (like is_xml = ""); ib_wrap, ib_search_enabled, ib_find_match_case and ib_find_whole_word keep their values (of_reset is the one that puts every property back). Returns 0, -2 when the component is not created |
of_expand_to_level ( long al_level ) → long | Shows the tree down to al_level: elements at that depth or deeper fold shut (1 = the root's direct children, 0 folds the root). No ue_node_toggled; a selection it moves onto a folded element raises ue_selection_changed. Returns 0, -2 when the component is not created |
of_expand_path ( string as_path ) → long | Opens ONE element by its path — any XPath, like of_select_path — and the elements above it so it can be seen. Like its + marker, it raises ue_node_toggled for the element and for each folded element above it opened on the way (nothing for an element already open). Returns 0, -5 if no element with children matches, -2 if the component is not created |
of_collapse_path ( string as_path ) → long | Folds ONE element by its path (any XPath). Like its − marker, it raises ue_node_toggled when the element was open, and ue_selection_changed when the selection has to move onto it. Returns 0, -5 if no element with children matches, -2 if the component is not created |
of_is_expanded ( string as_path ) → boolean | true when the element at that path (any XPath) is open, read LIVE; false when it is folded, and for a node without children or a path that matches nothing. Enough to save what the user opened and restore it |
of_get_layout ( ) → string | The layout of the tree, read LIVE, as a JSON text to store: the folded elements (collapsed, or expanded when that list is shorter — every element it does not name is then folded), the selected path (selected, null with no selection) and the line at the top of the view (top, a path, never pixels). Give it back to of_set_layout, after a new document too |
of_set_layout ( string as_layout_json ) → long | Gives the tree back a layout of_get_layout gave: the folds (a path the document no longer has is ignored, a new element keeps its default), the selection (ue_selection_changed, as of_select_path) and the line at the top. ue_layout_changed follows once when the folds changed. Returns 0, -5 if the text is not a JSON object, -2 if the component is not created |
of_search ( string as_query ) → long | Highlights every occurrence (case-insensitive by default — see ib_find_match_case —, whole words only with ib_find_whole_word) and jumps to the first; only the elements that hide the CURRENT occurrence open, and of_clear_search folds them back. ue_search_result says how many. The search looks in the text as it is SHOWN: in a text, Durand & Co is written Durand & Co and is searched so (a CDATA section shows it as it is) — with ib_find_decoded, in the decoded values, where Durand & Co finds both. An empty query is of_clear_search; a search ends an of_find_xpath. The search box opens with the query in it and the focus on it (the same as Ctrl+F): Enter moves to the next; with ib_search_enabled at false, the tree only highlights. Returns 0, or a negative code |
of_search_next ( ) → long | Moves to the NEXT match (wraps to the first after the last); during an of_find_xpath, to the next node, which is selected. ue_search_result reports the new position. Returns 0, or a negative code |
of_search_prev ( ) → long | Moves to the PREVIOUS match (wraps to the last before the first); during an of_find_xpath, to the previous node, which is selected. ue_search_result reports the new position. Returns 0, or a negative code |
of_clear_search ( ) → long | Clears the search, or the of_find_xpath: no more highlight nor current match (of_match_count returns 0), the search box empties and closes, and the elements the search opened fold back as they were before it. Returns 0, or a negative code |
of_find_xpath ( string as_xpath ) → long | Highlights EVERY node of the tree an XPath matches (//line, //line[@qty > 1], //@sku) and selects the first; of_search_next / of_search_prev (and F3) walk them, each one selected, and ue_search_result counts them. A text search, of_clear_search, Ctrl+F or a new document ends it. Returns the number of nodes found (0 when none), -5 if the text is not an XPath of nodes (a syntax error, a number such as count(…), an unknown prefix), -2 if the component is not created. An empty text is of_clear_search |
of_select_path ( string as_path ) → long | Selects and scrolls to the node at as_path; like the keyboard, it raises ue_selection_changed (nothing when the node is already selected); the folded elements that hide it open. as_path is ANY XPath 1.0 that designates a node of the tree: a path as the tree gives it (/order/@id), //line[@sku='A'], /order/lines/line[1], another prefix bound to the same namespace; the first node it matches that the tree SHOWS is selected (a blank of layout has no line), and of_selected_path then gives the tree's own path. Returns 0, -5 when no node of the tree matches (the selection does not move), -2 when the component is not created |
of_selected_path ( ) → string | The path of the selected node, read LIVE: an XPath 1.0 location path to hand as it is to n_pbt_xml.of_get_value on the same document. EVERY line has its own: /order/lines/line[2] when a name repeats, /order/@id for an attribute, /p/text()[2] for a text, /order/comment()[1], /order/processing-instruction('x')[1], /comment()[1] before the root. An element in a DEFAULT namespace is written *[local-name()='Body'] — a bare name matches nothing in XPath; a prefixed name (soap:Body) is kept when its prefix designates the same namespace, otherwise the step names the namespace too (namespace-uri()). A closing tag gives its element. Empty when nothing is selected, and for the XML declaration or the DOCTYPE (not nodes) |
of_selected_value ( ) → string | The value of the selection, read LIVE and decoded — what n_pbt_xml.of_get_value reads at of_selected_path: an attribute's value, the text of a LEAF element (CDATA included, blanks kept), the text of a text line, a comment or a processing instruction; an element shown <x/> that holds only line breaks gives them, as n_pbt_xml does. Empty for an element with children, or when nothing is selected |
of_selected_xml ( ) → string | The XML of the selected node, read LIVE: an element with everything it holds, an attribute as name="value", a text escaped, a comment, a CDATA or a processing instruction as written — what you copy from an XML editor. Empty when nothing is selected |
of_selected_kind ( ) → string | What the selected node is, read LIVE: KIND_ELEMENT, KIND_ATTRIBUTE, KIND_TEXT, KIND_CDATA, KIND_COMMENT, KIND_PI (a processing instruction), KIND_DECLARATION (the XML declaration) or KIND_DOCTYPE. Empty when nothing is selected |
of_selected_name ( ) → string | The qualified name of the selected node, read LIVE: an element or an attribute as written (soap:Body, id), the target of a processing instruction. Empty for a text, a CDATA, a comment, the declaration, the DOCTYPE, or when nothing is selected |
of_selected_namespace ( ) → string | The namespace URI of the selected element or attribute, read LIVE (http://schemas.xmlsoap.org/soap/envelope/). Empty when it has none (an attribute without a prefix has none), for any other node, or when nothing is selected |
of_copy ( ) → long | Puts the XML of the selection on the clipboard, like Ctrl+C in the tree (the text of of_selected_xml); a piece of text highlighted with the mouse is what Ctrl+C copies. Returns 0, -4 if nothing is selected, -2 if the component is not created |
of_copy_path ( ) → long | Puts the PATH of the selected node on the clipboard (/order/lines/line[2], /order/@id), an XPath ready for n_pbt_xml. Returns 0, -4 if nothing is selected or if the node has no path (the XML declaration, the DOCTYPE), -2 if the component is not created |
of_match_count ( ) → long | Returns how many OCCURRENCES the current search found (two on one line count two), read LIVE — the 12 of a 3 / 12 status line. 0 without a search |
of_match_index ( ) → long | Returns the 1-based position of the current match, read LIVE — the 3 of a 3 / 12 status line. 0 with no match |
Events #
| Event | When |
|---|---|
ue_node_clicked (string as_path) | The user clicked a node, or pressed Enter on a selected leaf: its XPath (/order/lines/line[2]); an attribute, a text, a comment or a processing instruction gives its own (/order/@id, /order/comment()[1]), a closing tag its element. Hand it to n_pbt_xml.of_get_value on the same document |
ue_node_toggled (string as_path, boolean ab_expanded) | An element was folded (ab_expanded = false) or opened (true): by the user (its + / − marker, the arrows, Enter or Space) or by of_expand_path / of_collapse_path. of_expand_all, of_collapse_all and of_expand_to_level do not raise it: no gesture folds the whole tree |
ue_node_rclicked (string as_path, long al_x, long al_y) | A right click on a node, or the Menu key (Shift+F10) in the tree: the node is selected first (ue_selection_changed when the selection moved), as_path is its path, al_x / al_y the point on the SCREEN, in pixels. Open your own menu there (a u_pbt_contextmenu): the tree has none |
ue_layout_changed (string as_layout_json) | The folds changed — a gesture, an order of your code (of_collapse_all included), a new document, of_set_layout: as_layout_json is the whole layout, as of_get_layout gives it, ready to store. A change of selection or of scrolling does not raise it |
ue_selection_changed (string as_path) | The selection moved without a click, by the user or by your code: arrows, Home/End, Page Up/Down, of_select_path, a step of of_find_xpath, or a fold that hides the selected line (the selection moves onto the folded element). Raised only when the selection really changes; a new document starts with nothing selected and does not raise it — a mouse click reports ue_node_clicked |
ue_search_result (long al_count, long al_index) | A search started or moved: al_count occurrences in all, the current one is al_index (1-based, 0 when none) — enough to show “3 / 12”; of_find_xpath raises it too (al_count = the nodes found), and (0, 0) when it ends; when a search closes (Escape in the box, of_clear_search, of_reset, ib_search_enabled = false), (0, 0) once |
ue_error (string as_message) | The text in is_xml is not valid XML: the message says where, in the display language (“Invalid XML: line 3, column 16 (…)”, the engine's own detail between brackets), and the tree shows the source around that line, the faulty one marked. Also raised when the document is too large to be shown |
Example #
Explore an API response #
// Show the XML an API answered, or nothing if the call failed
if inv_rest.of_get(/*url*/ "https://api.example.com/orders/4152") = 200 then
uo_xml.is_xml = inv_rest.of_response_text()
uo_xml.of_collapse_all() // the reader opens what interests them
else
uo_xml.of_clear()
end if
Read the value of a clicked node #
// Instance variable of the window : the answer, loaded ONCE by n_pbt_xml
n_pbt_xml inv_xml
// open event of the window : n_pbt_xml is created once, owned by the window
inv_xml = create n_pbt_xml
inv_xml.ipo_owner = this
// When the answer arrives : the tree shows it, n_pbt_xml loads the same text once
uo_xml.is_xml = inv_rest.of_response_text()
inv_xml.of_load(/*xml*/ inv_rest.of_response_text())
// ue_node_clicked of uo_xml : the value at the clicked path, nothing loaded again
st_value.text = inv_xml.of_get_value(/*xpath*/ as_path)
// or, without a second parser : the value of the selection, decoded
st_value.text = uo_xml.of_selected_value()
Search whole words, case included #
// Whole words only, with the same case : "id" is not found inside "user_id"
uo_xml.ib_find_match_case = true
uo_xml.ib_find_whole_word = true
uo_xml.of_search(/*query*/ "id")
Keep the folds when the response is refreshed #
// Local variables
string ls_layout
// The folds, the selected node and the line at the top, kept before the new text
ls_layout = uo_xml.of_get_layout()
uo_xml.is_xml = inv_rest.of_response_text()
// The same elements fold again ; a path the new text no longer has is ignored
uo_xml.of_set_layout(/*layout_json*/ ls_layout)
Find every node of an XPath #
// Local variables
long ll_found
// Every order line of more than one item : highlighted, the first one selected
ll_found = uo_xml.of_find_xpath(/*xpath*/ "//line[@qty > 1]")
// A Next button walks them, like F3 ; ue_search_result feeds a "2 / 5" status line
if ll_found > 1 then uo_xml.of_search_next()
// What is selected, without a second parser
st_kind.text = uo_xml.of_selected_kind() + " " + uo_xml.of_selected_name()
A menu on a node #
// ue_node_rclicked of uo_xml : the node is selected, open YOUR menu at that point of the screen
uo_menu.of_show(/*x*/ al_x, /*y*/ al_y)
// ue_item_selected of uo_menu : copy the XML of the node, or its path for n_pbt_xml
choose case as_keys
case "copy"
uo_xml.of_copy()
case "copypath"
uo_xml.of_copy_path()
end choose
Good practice #
- It is a viewer, not an editor: it shows the XML, it does not change it. To read a value, hand the path from
ue_node_clickedton_pbt_xml.of_get_valueon the same text, or readof_selected_value. - A large XML stays smooth: only the lines on screen are drawn, an export of several thousand lines is browsed with the keyboard without waiting.
of_collapse_allfirst is still the most readable. - Invalid XML breaks nothing:
ue_errorsays so (with the line and the column of the fault, in the display language), and the tree shows the source around the faulty line. That is the feedback to show, not a crash. - A default namespace (
xmlns="urn:…", a SOAP response): the path is written*[local-name()='Body'], the only form an XPath without a registered prefix can read; a prefix declared again with another URI, or a sibling of the same name in another namespace, is written*[local-name()='x' and namespace-uri()='urn:…'].@xml:langreads as it is. Anxmlnsdeclaration is shown but has no path: XPath does not see it as an attribute. - What is shown is XML: an attribute value keeps its quotes and its encoded line breaks (
), a text its&and<and its significant blanks; only the layout line breaks around a text go. A blank between two children (the space of<b>x</b> <i>y</i>) has a line of its own, shown ␠: it is part of the text. - With the keyboard: the arrows move from line to line, Right walks the attributes of a tag before going down, Page Down moves by one screen (a CDATA section of three hundred lines counts for its height), Ctrl+C copies the XML of the selection.
- Right to left: an XML document is a left-to-right text, the tree stays so in an RTL application (only the search box follows the application's direction).
Inherited from the common base #
These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.
| Members | Role | Detailed in |
|---|---|---|
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.4 Optional properties and events |