Tree view — version history
Data shown as a tree, and as a tree list with columns: nodes addressed by path, three-state check boxes, branches loaded on demand, renaming, drag and drop, search and sorting, fed by hand or from a hierarchical DataStore. Introduced in 5.0.
5.0 #
New #
- Nodes added at their address (
of_add_node):"europe"at the top,"europe/france"inside it, a key unique under its parent; inserted at a place counted from 1 (of_insert_node), moved with their subtree under another parent or to another place (of_move_node), removed with everything under them (of_remove_node,of_clear,of_reset), their handles freed. An address already taken, an unknown parent, an empty level, a key holding a|or starting with__is refused with-5, andis_last_error(read only) says why; it is emptied by every method that acts and every property written, and never reports a message another component left.of_count,of_keys_atandof_haswalk the tree level by level,of_keys_atreturning whole addresses. - The handle of a node (
of_node) writes and reads its text (rich text markup), its picture and the picture shown while it is open (is_image,is_expanded_image), whether it is open, greyed or visible, its check and a small badge, and reads its address, level, place among its siblings counted from 1, parent and check state through read-only properties (is_address,il_level,il_position,is_parent_keys,is_check_state); a tooltip and colours per node, its accent taken by its check box and by its selection while the tree has the focus. A move frees the handles taken at the old address at once, and a handle taken at the new address stays valid; an address that cannot be one gives an inert handle. - Nodes open and close by their chevron, a double click, the keyboard or the code (
ib_expanded,of_expand_all,of_collapse_all,of_scroll_to_node), every one of them raisingue_node_toggled; thin lines join the nodes on option (ib_show_lines), and the indentation of a level is yours to set (ii_indent). - Branches loaded on demand: a node promises children (
ib_has_children) and shows its chevron although it holds nothing; opening it — by a gesture, by the code or byof_select_nodeopening what holds a node — raisesue_children_needed, where the application adds them, and the branch opens with them once the event has returned — nothing added and the promise is withdrawn. A promise holds until it is answered, even when a node was added or dropped inside meanwhile; setting it again asks again, andof_expand_allleaves closed a branch whose promise has not been answered. - Single, multiple (Ctrl and Shift, like the Explorer) or no selection (
is_selection_mode), by click, keyboard or code (of_select_node,of_clear_selection), read live by two read-only properties (is_selected_key,is_selected_keysas a JSON array of addresses);ue_selection_changedtells every change, including selected nodes that leave the screen and a selected node the search hides, which leaves the selection. Closing a branch brings the focus back onto it.of_select_nodeopens what holds a node to select it, like the TreeView of Windows, but judges first: a node hidden or left out by the search is refused with-5and nothing opens;of_scroll_to_nodejudges the same way and leaves the selection as it is. - Three-state check boxes (
ib_checkboxes): a check of the user on a node checks its subtree, except a greyed node and its subtree, and a node whose visible children are shared shows mixed (ib_check_cascade, on by default); a check written by the code (ib_checked,of_check_all) checks everything and stays silent,ue_node_checkedtelling the checks of the user;is_checked_keys(read only) reads the checked nodes live. An empty box on a selected row stays an empty box, ringed with the colour of the row text, in every theme. The children received under a checked leaf — the whole answer toue_children_needed— take its check, until a check or a node moved inside ends it. - The user renames a node (
ib_editable: F2, or a slow second click on the selected node; Enter or leaving the field keeps, Escape gives back, an emptied text is refused) and drags it before, after or inside another one (ib_drag_drop; on the bottom quarter of an open branch it becomes its first child), a closed branch held under the pointer opening, the tree scrolling near its edges, Escape — or the window losing the focus — cancelling;ue_node_editedandue_node_movedreport the change, a node dropped where it already is reporting nothing. A command of the code during an edit keeps the field and what is typed. Withib_veto_changes,ue_node_editingandue_node_movingare asked first and can refuse it, gestures and code alike (of_move_nodethen returns-4);ue_node_movingreceives the final place, the oneue_node_movedwill say. - A tree list as soon as a column is added (
of_add_column,of_remove_column,of_clear_columns): the first column carries the tree (is_tree_title), the others show the fields of each node (of_set_field,of_get_field); the handle of a column (of_column) sets its title, width, alignment, number or date format — the date tokens of the datagrid,AM/PM,[shortdate]and literals included —, visibility, place, whether its header sorts, a tooltip and the five colours of its header. The user widens and moves the columns with the mouse — a ghost and an insertion bar show where, Escape cancels — or with the keyboard: the header is one stop of the Tab key, Left and Right go from header to header, Enter or Space sorts, Ctrl with the arrows widens, Ctrl + Shift moves. - Sorting by a click on a header or by the code (
of_sort,ue_sort_changed): the siblings of every level, ascending, descending or back to the order of addition, equal values in the order of the tree, without changing the order the code reads; the sort is made again when the display language changes, and removing the sorted column raisesue_sort_changedwith the next key of the sort,SORT_NONEwhen it was the only one. - A search (
is_filter) that keeps the nodes whose text holds it, with what holds them shown open and the text found highlighted, and an optional toolbar with a search field, Expand all and Collapse all (ib_toolbar). - The tree is fed from a hierarchical DataStore (
of_from_datastore): one node per row, its key an id column or the RowID, its parent named by a column — with a key column, a parent value names a key, never a RowID —, a column playing a role when it is named like it or whenof_mapsays so (text, picture, check, badge, open at the load), every column a field; what a column holds is shown as text, never as markup. Dates and numbers are read whatever the regional settings, in one form (yyyy-mm-dd,yyyy-mm-dd hh:mm:ss, a number with a dot), so a decimal column is formatted and sorted as a number. What the load had to put right — missing parents, loops, keys taken twice or impossible — is said byis_last_error, and a role given byof_mapto a column the main DataStore lacks is refused with-5.of_reload_rowupdates a node in place and brings a new row at its rank, and the handle of a node reads the number of its row (il_row, read only); a node of a row removed byof_remove_nodestays out until the next load, unless its own row is reloaded. - With
ib_write_back, a renamed text, and the new parent of a node that changes parent, are written into the DataStore before the event (NULLat the top); a node of a row does not go under a node added by hand nor under a row without a key value. A value the DataStore refuses, or a DataStore destroyed since the load, puts the node back and raisesue_write_back_failed. Without it, a drag moves the node only. - The layout the user arranged — widths and order of the columns, the sort, the open nodes — is read and given back by ONE property,
is_layout— the same on every component that can be rearranged; a value it cannot take changes nothing andis_last_errorsays why —, and carried whole byue_layout_changedafter every change. ue_node_clicked,ue_node_opened(double click or Enter) andue_node_rclickedfor your menus, with the screen position; pictures from a file, a resource of a DLL, a glyph drawn in the theme or anhttps://address, anhttp://address or a missing file reported once byue_image_failed.- Keyboard: arrows, Home, End and pages to move, Right and Left to open and close (inverted right to left),
*to open a whole subtree, Space to check or select, Enter, F2, the context-menu key, the first letters of a text, Shift and Ctrl with the arrows in a multiple selection; greyed nodes are skipped; a greyed tree (ib_enabled) answers nothing, takes no focus and leaves the Tab order. - Thousands of nodes scroll like twenty: only the rows in sight are drawn, and nodes added by the code in a loop are drawn once, after the loop. Each
of_add_nodecosts about 0.7 ms (2,000 nodes in about 1.3 s); a large tree loads faster from a DataStore, in one go, or on demand. - Asking first: with
ib_veto_selection,ue_selection_changingreceives the selection now and the one asked before any choice — a click, the keyboard, F3,of_select_node,of_clear_selection,of_search_next,of_search_prev— and can refuse it: nothing moves, not even the keyboard focus, and the method returns-4; withib_veto_toggle,ue_node_togglingis asked before each opening or closing, by a gesture or by the code (of_expand_all,of_collapse_allandof_expand_to_levelapply the other nodes and return-4, a refusedib_expandedsends nothing).of_select_nodeasks the openings before the selection.ib_single_expandkeeps one open branch under each parent: opening a node closes its open siblings.of_select_nodes(as_keys_json)selects several nodes at once, with one question and oneue_selection_changed. - Lots of children:
of_from_datastore(ads, as_parent_keys)— and its DataWindow and DataWindowChild overloads — makes the rows of a DataStore the children of a node in one call, the ItemPopulate → Retrieve pattern of the PowerBuilder TreeView; the same DataStore given again under the same node replaces its lot,il_rowof a node of a lot reads its row in its own DataStore, andib_write_backwrites there; one DataStore per lot — a DataStore loaded again for another node leaves the nodes of the earlier lot without a row (il_row0, a move by the code-5, a write-back refused and said), since PowerBuilder gives the same RowIDs again after aReset. A lot under an empty parent is refused (-5). A columnhas_children(ROLE_HAS_CHILDREN) makes a row promise its own children.of_clear_childrenempties a node and makes it promise its children again, the refresh of a folder;of_row_keysgives the node of a row. - A tree grouped by columns, like the TreeView style of a DataWindow (
is_group_by = "region|country"): one level of group nodes per column, the rows being the leaves; the handle of a group node reads its column and its value (is_group_column,is_group_value, read only), every level takes its picture (of_set_level_image), and withib_write_backa leaf moved under another group of the last level writes the values of its groups into its row (a date and time keeps its time, a time column is written ashh:mm:ss); the text of a group follows a format set after the load and the display language. - Branch totals: a column sums, averages, counts or takes the minimum or maximum of what its branches hold (
is_summary,SUMMARY_SUM,SUMMARY_AVG,SUMMARY_MIN,SUMMARY_MAX,SUMMARY_COUNT), the total following every change, read byof_get_fieldand used by a sort; a branch keeps its own value unlessib_summary_over_valuesays otherwise, andib_summary_filteredcounts only what the search shows. A count is written as a plain number, a minimum or a maximum also compares dates, and a sum of whole numbers stays exact beyond 2^53. In demo mode a total never counts what the cap hides. - Showing the tree down to a level (
of_expand_to_level), opening or closing one branch with everything under it (of_expand_all(as_keys),of_collapse_all(as_keys)), the branches before the leaves whatever the sort (ib_parents_first, where a drop only goes inside). - Searching:
of_find_nodegives the address of the first node whose text or field holds a value, closed branches included, and goes on from an address;of_find_nodesgives every match at once;of_search_next,of_search_prev(F3, Shift+F3, and Enter in the search field) select the matches ofis_filterone after the other — inSELECT_NONEthey move the focus —,il_match_count(read only) counts them. - Sorting on several keys (
of_sort("type A, text D"), written as a DataWindow'sSetSortreads it), by a column or by a field no column shows;ue_sort_changedreceives the whole sort as its third argument, andis_layoutcarries every key of the sort, the selected nodes and the node at the top of the view, and gives them back when written. - Node locks:
ib_read_only(neither renamed nor dragged by the user),ib_drop_target(nothing dropped into it),ib_show_checkbox(no check box, outside the check system). Delete raisesue_delete_requestedand Ctrl+Cue_copywith the selected nodes (the focused one inSELECT_NONE) as tab-separated text, numbers raw; the tree removes nothing by itself. - Files dropped from the Windows Explorer onto a node (
ib_allow_drop): the node under the cursor lights up,ue_drop_filesnames it with the full paths,ue_drag_enterandue_drag_leaveframe the drag. - With
ib_write_back, a check of the user is written into the check column of every row it changes, the branches whose state turns checked or unchecked included, spelled as the column writes it (1/0, Y/N, T/F, O/N, -1/0) — a column that shows only its no writes the usual yes, Y for N and 1 for 0, O and -1 only when the column already holds one; every write-back is all or nothing — each value judged first, one refused puts everything back and raisesue_write_back_failed. - Writing the width, the place or the visibility of a column from the code raises
ue_layout_changedwhen the layout changes; writingis_layoutopens by itself an address it asked open in a branch answered later. - A node of a row dragged without
ib_write_backcomes back to the place its row says at the next reading of the rows; a new load frees only the handles of what it replaces, and the report ofis_last_errorcounts the nodes added by hand lifted to the top and the lots that left with their node. - In a tree list, the tree column stays in place when the other columns scroll sideways.
- A role resolves for each DataStore:
of_map(ads, as_role, as_column)— and its DataWindow and DataWindowChild overloads — gives one DataStore its own roles, and a lot read from a DataStore without the columns ofof_map(as_role, as_column)takes the columns named like the roles instead of being refused.is_root_parent_valuenames the parent values of the top ("0","0|-1"): such rows stand at the top without being reported as orphans, and a node moved to the top withib_write_backwrites the first of them instead of NULL. - The code opens the text of a node for editing (
of_edit_node, the EditLabel of PowerBuilder) — withib_editableoff and on a read-only node too — and closes it (of_cancel_edit). - A state picture, an overlay picture and a picture for the selected node (
is_state_image,is_overlay_image,is_selected_image, and theROLE_STATE_IMAGEandROLE_OVERLAY_IMAGEcolumns of a DataStore); a click on the state picture raisesue_state_clickedwithout selecting the node. - Export with the hierarchy:
of_export_csv(one cell per level) andof_export_xlsx(indented by level, every branch grouped under its parent with Excel's outline levels, foldable in Excel), every node or the rows shown (is_export_scope), confirmed byue_csv_savedandue_xlsx_saved; refused in demo mode. - The search ignores accents by default, like the sort (
ib_filter_accentsto make them count), and finds a dotted capital I; Ctrl+A selects every node shown in a multiple selection; a label cut by the width shows its whole text in a tooltip (ib_auto_tooltip). - The
+,-and*keys of the main keyboard are typed to reach a node, only those of the numeric keypad open and close; the menu key and Shift+F10 name the bottom-left corner of the node. - In demo mode the tree draws the first 8 nodes of each level and 200 nodes in all.
is_accessible_nameandis_accessible_description: the name and the description a screen reader says for the whole component, like theAccessibleNameandAccessibleDescriptionof a PowerBuilder control. Both read back, go throughof_set_property, andof_resetempties them.- Building a tree by hand costs one message per node: an address taken or an unknown parent is refused without asking the page, as long as the nodes were added by the code (after a DataStore load or a move the page is asked again). The search field of the toolbar draws the tree once the typing stops.