TIP 740: Enhanced Treeview Widget

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Brian O'Hagan <brian199@comcast.net>
State:          Draft
Type:           Project
Tcl-Version:    9.1
Vote:           Pending
Created:        30-Nov-2025
Keywords:       Tk TTk Treeview
Tk-Branch:

Abstract

This tip proposes numerous functionality and performance improvements to the treeview widget. These address limitations of the treeview widget and improve its performance and usability.

Rationale

Although the treeview widget has sufficient functionality and performance to meet most user needs, there are a few things missing and a few areas where we can do better. Indeed it is much faster than non-C extensions such as tablelist, but several design decisions limit its performance and the missing functionality requires the users to implement work-arounds. This adds a burden to the users and limits its usefulness for non-Tcl users. This TIP attempts to address many of these performance improvements and missing functionality needs.

Specification

Demos

A new demo treeview.tcl has been added to the demos to show off many of the changes mentioned below.

Performance Enhancements

The base widget does not cache all useful info such that conversions/lookups are needed when getting the id for an item, last item in a list of children, etc. This leads to lower performance to convert these or find the values on the fly. This TIP stores the item id in the Item struct as a Tcl object to avoid the repeated conversion to/from a TCL object since many methods operate on or return the item id. A pointer to the last child in the list of children was added to the Item struct to make adding items to the end of the children an O(1) operation.

Bindings

The base treeview widget has a few useful bindings that allow for basic keyboard navigation and selection of items. However it is missing bindings to extend selections and navigate by page, row start/end, or to the edge of the view. It also does not use the virtual bindings introduced in Virtual Bindings and listed in library/tk.tcl to abstract the key sequence from the implementation. This TIP addresses these by using the virtual events and adding bindings to navigate between items and cells, extend the selection to the adjacent items or cells, move to first/last cell in row or item in column, extend the selection to the same, move to first/last visible row or column in view, extend selection to same, etc. Several of the handlers also needed to be updated to take advantage of the new capabilities in the following sections and increase performance. See the documentation or the library/ttk/treeview.tcl file for the full list of bindings. Support for the <<Copy>> virtual event was also added to copy selected items and cells to the clipboard without the use of -exportselection and the PRIMARY selection.

Cell Editing

This TIP does not fully add in-place cell editing. See the Out-of-Scope section for the rationale. However, it does add some support to make it easier to implement. See the Focus section for the new <<TreeviewFocus>> virtual event. The bindings have been updated such that if an item or cell is double-clicked, when an item or cell has the focus and the Return key is pressed, or the <<Invoke>> virtual event is triggered, and if the user provided ::ttk::treeview::EditItem procedure exists, it will will be called with 3 arguments, the widget path, item id, and column id. This can be used to trigger an in-line cell edit, to invoke the default method for an item or cell (i.e. open a file), etc.

Child Items

An area for improvement is the ability to determine if an item has any children without the need to query each item with the children method and its associated performance impact. This TIP adds the haschildren method to return whether an item has children. It also adds a new size method to return the number of children and optionally the descendants of an item with the option to include hidden items. A new identifier method (and shortcut id) is added to return the id of an item at an index in the children of an item. Lastly an optional index argument has been added to the index method to use the item at that index. Together these avoid the need to use the children method to get the list of child items and find the one we want.

pathName haschildren item

Returns 1 if item has children, 0 if not.

pathName identifier item index

Returns the identifier of the item at index within item's list of children.

pathName index item ?index?

Returns the numeric index of item within its parent's list of children or if index is specified, the index of the item at index in item's list of children.

pathName size ?-hidden? ?-recurse? item

Returns the count of children belonging to item. Use -hidden to include hidden items and -nohidden (default) to exclude them. Use -recurse to include descendants of item and -norecurse (default) to exclude them. If both -hidden and -recurse are used, descendants of closed items will be included.

Convenience Functions

My usage of the treeview widget has led to the need for several convenience functions that make life easier. This includes a depth method to return the depth of any item in the widget and a visible method to return if an item is currently visible or not.

pathName depth item

Returns the number of levels between item and the root item.

pathName visible item

Returns whether item is visible or not. An item is visible if it is not detached, hidden, and all ancestors are open and not hidden.

Detached Items

When items are detached, only the item itself is marked as detached. Its descendants are not, thus won't show up when queried with the detached method. This TIP adds an -all option to get all explicitly detached items and their descendants. It also changes the detached item check to check if any ancestors are detached and if so, return that.

pathName detached ?-all|item?

Without any arguments, returns a list of all detached items, but not their descendants, in an arbitrary order. If item is provided, returns a Boolean value indicating whether item or an ancestor is detached or not. If -all is specified, returns a list of all the detached items and their descendants in an arbitrary order. The root item is never detached.

Drag and Drop of Columns

The current bindings only supported column resizing and sorting if the -command option is specified. A missing need is for the users to be able to rearrange columns (aka move them). This could be implemented by custom code, but doing so could interfere with the column resize or sort bindings. This TIP changes the bindings to support all 3 functions (resize, sort, and move). Drag and drop of columns is accomplished by pressing Button 1 on the column header, then dragging it over another column or the separators, and releasing Button 1. The header states are now updated to reflect both the column to be moved and which column it will be moved before. Column #0 can't be moved.

Focus and Selection Modes

Treeview is patterned after the listbox widget and uses a few of the same selection modes. However, it is missing the multiple mode to allow the user to select items without changing the selection state of other items and without the need to use a key modifier such as Shift or Control. This mode is very useful for checklists and other use cases when multiple selections are allowed. This TIP adds the multiple mode and updates the binding handlers to support it.

The treeview widget supports the concept of a focus item. However, there is a bug in the current implementation which will cause a core dump when the focus is set to {}, since it is treated as the root node rather than clearing the focus. This has been fixed. Another issue is when cell selections were added in 'TIP 552', a similar cell focus was not also added. This TIP adds a cellfocus method too.

pathName cellfocus ?cell?

Sets the focus to cell. Use {} to clear the cell focus. Without cell, returns the current focus cell id or {} if none.

pathName focus ?item?

Sets the focus item to item. Use {} to clear focus item. Without item, returns the current focus item id or {} if none.

However, the bigger issue is the focus acts more as an anchor for selections rather than a true focus since there is no visual indicator to denote which item has the focus. For the single and browse selection modes, this is less of an issue since only one item can be selected. It is an issue for extended selections since there is no way for the user to know which item has the focus when multiple items are selected. This is a big issue for none and multiple selection modes since the selection and focus are separate concepts allowing the focus to change without changing the selection. This limitation makes it difficult for the user to use the key bindings to navigate between items/cells or use the spacebar binding to select items/cells since they don't know which item has the focus until they select it. This TIP corrects this issue by adding a focus ring to the row and cell components along with -focuscolor and -focuswidth options to control it. Use of -activestyle like listbox uses was considered, but rejected since a dotbox and underline indicator are dated "active" item concepts. The -focuscolor and -focuswidth options are already used by TTk and are configurable with ttk::style:

-focuscolor color

Specifies the color of the focus ring for tree entries. The default is #000000.

-focuswidth pixels

Specifies the thickness of the focus ring for tree entries in pixels. The default is 1 pixel.

The current defaults are set for a file or item browser style focus ring:

ttk::style configure Row -focuscolor #000000 -focuswidth 1

To set the focus ring options for a spreadsheet-style focus ring use:

ttk::style configure Row -focuscolor #008000 -focuswidth 2

The last change is related to detecting when the item or cell focus changes. This TIP adds a new virtual event <<TreeviewFocus>>, that is generated whenever the item or cell focus changes including when it is unset. This can be used to track when the current focus changes, for in-place cell edits to determine when the user has moved to another cell, etc.

Item Indicies

The treeview widget is not consistent in handling item indices. This TIP consolidates the index lookup process into a single function like how column ids are handled and uses the standard Tk API to support all common index forms. The index can use any of the following forms:

number

Specifies the numerical index of a child within its parent, where 0 corresponds to the first item.

end

Indicates the end of the list of children. For most commands this refers to the last child, but for a few methods such as index, insert, and move it refers to the item just after the last one.

first

Indicates the first child in the list of children.

last

Indicates the last child in the list of children.

end-n

Indicates the item at position n relative to the end.

m+n or m-n

Indicates the item at position n relative to index m.

Item Insertion and Movement

An interesting design choice of the treeview widget is to insert and move items based on an index in the parent's children. If the user only needs to insert at the beginning or end of the children, this works ok. In this TIP with the previously mentioned addition of a pointer to the last child in the Item struct, this makes inserts and moves to the end an O(1) operation. However, if the user wants to insert/move before or after an existing item they need to lookup the index for the item to insert at, then call the insert/move function which needs to convert it back to an item. Why not skip this and allow for the user to provide the item? This TIP addresses this by adding before and after options. The only limitation is items won't be able to use before or after as item ids.

pathName insert parent index ?-id id? options...

(Existing) Creates a new item, inserts it in parent's children at position index, and returns its identifier. See ITEM OPTIONS for the list of available options. Use the empty string {} for parent to create a new top-level item. See INDICES for valid forms of index. If index is negative or zero, the new item is inserted at the beginning; if index is greater than or equal to the current number of children, it is inserted at the end. If -id is specified, then id is used as the identifier of the item; id must not already exist in the tree. Otherwise, a new unique identifier is generated.

pathName insert before|after item ?-id id? options...

Creates a new item, inserts it into item's parent before or after item, and returns its identifier. See ITEM OPTIONS for the list of available options. If -id is specified, then id is used as the identifier of the item; id must not already exist in the tree. Otherwise, a new unique identifier is generated.

pathName move item parent index

(Existing) Moves item to position index in parent's list of children. If index is negative or zero, item is moved to the beginning; if greater than or equal to the number of children, it is moved to the end. See INDICES for valid forms of index. It is illegal to move an item under one of its descendants.

pathName move item before|after otherItem

Moves item to before or after otherItem in otherItem's parent's children. It is illegal to move an item under one of its descendants.

Item Navigation

The existing next and previous methods only return siblings of the specified item. This limits their usefulness when the user wants to move to the next or previous visible item which can be a sibling, child, parent, etc. Also, the next and previous methods do not honor the hidden attribute which confuses the user when a hidden item is the next/previous item so the focus/selection disappears. This TIP addresses these and other limitations with the new after and before methods. These methods are used to find the next or previous visible item, but also have options to control whether hidden or child/ancestor items can be chosen. The reason new methods were added is backwards compatibility since after and before have different hidden and recurse defaults from next and previous.

pathName after ?-hidden? ?-norecurse? item

Returns the identifier of the item after item in the current view. It can be the first child, next sibling, next sibling of an ancestor, or {} for none. Use -hidden to include hidden items and -nohidden (default) to exclude them. Use -recurse to include descendants/ancestors of item (default) and -norecurse to exclude them. If both -hidden and -recurse are used, descendants of closed items will be included. The next method is equivalent to pathName after -hidden -norecurse item.

pathName before ?-hidden? ?-norecurse? item

Returns the identifier of the item before item in the current view. It can be the previous sibling, parent of item, or {} for none. Use -hidden to include hidden items and -nohidden (default) to exclude them. Use -recurse to include descendants/ancestors of item (default) and -norecurse to exclude them. If both -hidden and -recurse are used, descendants of closed items will be included. The prev method is equivalent to pathName before -hidden -norecurse item.

In a similar manner the current widget lacks the ability to select a range of visible items at differing depths as needed by the extend bindings. This TIP adds a between method to return all of the visible items between the first and last (inclusive) items. The method name between was chosen over range since it has a more clear intention, but range can also be used as an alias for between.

pathName between ?-hidden? ?-norecurse? first last

Returns a list of items from first through last, inclusive. Use -hidden to include hidden items and -nohidden (default) to exclude them. Use -recurse to include descendants/ancestors of item (default) and -norecurse to exclude them. If both -hidden and -recurse are used, descendants of closed items will be included.

pathName range ?-hidden? ?-norecurse? first last

Same as between.

Multi Item Operations

The treeview widget uses item attributes to denote hidden and collapsed/open items. To change multiple items requires an individual call to the item method for every item to be changed. This can be a performance impact when changing a large number of items. This TIP addresses this limitation by adding new collapse and expand methods to do bulk changes to the open/close state of items. It also adds hide and unhide methods to do the same for the hidden state.

pathName collapse ?-recurse? itemList

Sets all of the items in itemList to closed state. Same as "pathName item item -open 0" for each item. With the -recurse option, will also close all of their descendants. Use {} for root item.

pathName expand ?-recurse? itemList

Set all of the items in itemList to open state. Same as "pathName item item -open 1" for each item. With the -recurse option, will also open all of their descendants. Use {} for root item.

pathName hide ?-recurse? itemList

Hide all of the items in itemList and all child items. Same as "pathName item item -hidden 1" for each item. With the -recurse option, will also hide all of their descendants. Use {} for root item.

pathName unhide ?-recurse? itemList

Unhide all of the items in itemList. Same as "pathName item item -hidden 0" for each item. With the -recurse option, will also unhide all of their descendants. Use {} for root item.

See Items

Treeview supports the see method to scroll the widget in the vertical direction so an item is visible. This TIP adds a optional column argument to also scroll in the horizontal direction to see a column. This can also be used to see a cell using the expand operator.

pathName see item ?column?

Ensure that item is visible by setting all of item's ancestors to -open true, and scroll the widget in the vertical direction, if necessary, so that item is within the visible portion of the tree. If column is specified, then the widget is scrolled in the horizontal direction, if necessary, so that column is within the visible portion of the tree. Only non-title columns and rows will be scrolled.

pathName see {*}cell

Same as previous, but uses expand operator to convert cell into item and column components.

Search

A key missing function is a built-in search function. Besides the obvious performance improvement and avoiding the need for everyone to reinvent the wheel to write Tcl code to manually search by getting each item and calling lsearch, this makes it much easier for the users to interface with the widget. This TIP implements the search method and uses similar options to the lsearch command and the text widget search function but tailored to the treeview widget needs. This implementation uses the public TCL APIs for string matching.

pathName search parent ?-option ...? pattern

Searches within parent's children for pattern and returns the identifier of the first match. Valid options are:

Comparisons

-ascii

The values are to be compared to pattern as Unicode strings (the name is for backward-compatibility reasons.) This is the default.

-dictionary

The values are to be compared to pattern using dictionary-style comparisons. For search, this is the same as -ascii but also sets -nocase. NOTE: This means I18n and I018N are not the same.

-integer

The values are to be compared to pattern as integers. Empty values will be ignored, but non-numeric values will generate an error.

-nocase

Comparisons are to be performed in a case-insensitive manner. Only used with -ascii and -dictionary options for the -exact, -glob, and -regexp matching.

-real

The values are to be compared to pattern as floating-point values. Empty values will be ignored, but non-numeric values will generate an error.

-unicode

Alias for -ascii.

Direction

-backwards

The search is to be performed backwards starting from -start item or cell.

-forwards

The search is to be performed forwards starting from -start item or cell. This is the default.

Pattern Matching

-exact

Pattern is a literal string that is compared for exact equality against each cell value. Used with -ascii and -dictionary options. This is the default.

-glob

Pattern is a glob-style pattern which is matched against each cell value using the same rules as the string match command. Used with -ascii and -dictionary options.

-regexp

Pattern is an advanced regular expression that is matched against each cell value using the rules described in the re_syntax reference page. Used with -ascii and -dictionary options.

Search Modifiers

-columns columns

Limit search to only the columns specified in columns list. The default is to search in all displayed columns. See COLUMN IDENTIFIERS for valid forms of column.

-hidden

Search within hidden and closed items too. The default is to exclude hidden and descendants of closed items from the search.

-recurse

Search within all descendants of parent. The default is to only search within the children of parent.

-start item|cell

Start the search at item or if the -cell option is also specified, then start the search at cell. The default start item and cell when used with the -forwards option is the first child of parent and the first display column. For the -backwards option, the default is the last child of parent (or descendant when used with -recurse) and the last display column. The item or cell must be a descendant of parent. When resuming a search, use the before or after methods to get the previous or next item to start the search at. All other options should be the same as the initial search including parent.

-stop item|cell

Stop the search at item or if the -cell option is also specified, then stop the search at cell. The default stop item and cell when used with the -forwards option is the last child of parent (or descendant when used with -recurse) and the last display column. For the -backwards option, the default is the first child of parent and the first display column. When used with the -wraparound option, the default is just before the start item or cell (in other words, the start item or cell won't be searched again). This means, to wrap-around and stop at the -start item, only use the -start and -wraparound options. If the -start and -stop options both use the same item, then only that item will be searched. The item or cell must be a descendant of parent.

-wraparound

When used with the -start option, enables the search to wrap-round to the first/last item in parent to continue to the -stop item or cell.

Result Modifiers

-all

Return the identifiers of all of the items that match pattern rather than only the first one.

-cell

Return cell ids instead of item ids. The default is to return item ids. Also switches the -start and -stop options to use cell values.

-not

Negates the match, so only the items that do not match pattern, will be returned.

Another option considered, but not included is the -operator option. This would allow the user to specify a math operator to be used with searching integer and real values. For example pathName search -all -column Value -integer -operator "<" 42, would return the ids for all items with a value of less than 42 in column Value. Also, the in or ni operators could be used to check for elements in a list. For example pathName search -all -column Color -operator "in" [list red green blue], would return the ids for all items with red, green, or blue values in the Color column. This effectively could be used as a filter, which is a common function for spreadsheets. This was left out since only one filter could be applied and with the limitations of the expression operators.

Selection

The selection and cellselection methods are key functions of the widget since they allow the user to select items or cells to interact with. However, the current implementation is limited in that the only way to know if items/cells are selected is to get the selected items/cells list. This has obvious performance impacts when a large number of items/cells are selected. To avoid this, this TIP adds a present method to return a boolean with whether there are selected items/cells or not, a size method to return a count of the number of selected items/cells, and an includes method (with alias has) to return whether a list of items/cells are selected or not.

pathName cellselection has|includes cellList

Returns 1 if cell selection includes all of the cells in cellList or 0 if any are not selected.

pathName cellselection present

Returns 1 if there are selected cells or 0 if none.

pathName cellselection size

Returns the count of selected cells.

pathName selection has|includes itemList

Returns 1 if selection includes all items in itemList or 0 if any are not selected.

pathName selection present

Returns 1 if there are selected items or 0 if none.

pathName selection size

Returns the count of selected items.

Another limitation is the need to have the complete list of items before calling the selection add, remove, set, or toggle functions. Often times when a user is selecting items they may want to select a range by defining an anchor at the focus item/cell and then the end item/cell. This is how the extended selection mode operates. The current binding handlers would then need to enumerate the list of items/cells and pass it to the selection or cellselection methods. This TIP adds the ability to select a range of items to the add, remove, set, and toggle methods similar to what cellselection already supports from TIP 552. It also adds options to control whether no hidden and no descendants are also included. In addition, these functions were rewritten for better performance. The key bindings are set to skip hidden items, but will include visible descendants. This TIP also adds an anchor method for the binding handlers to use to mark the selection anchor item or cell.

pathName cellselection add|remove|set|toggle ?-nohidden? ?-norecurse? cellFirst cellLast

Add|remove|set|toggle the cells within the rectangle defined by cellFirst and cellLast to/from the cell selection. With -nohidden, hidden cells will be excluded and with -norecurse, cell in descendant items will be excluded.

pathName cellselection anchor ?cell?

Sets the cell selection anchor to cell. The anchor is used as the cellFirst for the selection extend bindings. Without cell, the current cell selection anchor is returned. Use empty string {} to unset the anchor.

pathName selection add|remove|set|toggle ?-nohidden? ?-norecurse? first last

Add|remove|set|toggle the items from first to last (inclusive) to/from the selection. Use -hidden to include hidden items (default) and -nohidden to exclude them. Use -recurse to include descendants and ancestors (default) and -norecurse to exclude them. If -hidden and -recurse are used, all descendants of closed items will be included.

pathName selection anchor ?item?

Sets the selection anchor to item. The anchor is used as the first item for the selection extend bindings. Without item, the current selection anchor is returned. Use empty string {} to unset the anchor.

These changes also enable a new interactive extending of the item and cell selection by pressing B1 on and item or cell, then dragging the selection to expand it. This only works for multiple and extended selection modes.

Set method

The set method is very handy for setting and getting the value of an item for a column. However it is limited in that it won't work for the #0 column or allow the user to set multiple values at one time. This TIP addresses this by adding #0 to the columns that can be set or get and allowing multiple values to be set.

pathName set item ?column? ?value? ?column value ...?

Set or get item column values. With no column or value arguments, returns a dictionary of all column/value pairs. With only the column argument, returns the current value for column. With one or more column and value pair arguments, sets the value of each column to the associated value. Use column #0 for the tree column. See COLUMN IDENTIFIERS for valid forms of column.

Sort

Another key missing function is a built-in sort function. In addition to the obvious performance improvement by skipping the need to get the widget contents, put into a list, call lsort, extract the item id from the sorted list, and then update the widget children to reflect the new order, this makes it much easier for the users to interface with the widget. This TIP implements the sort method and uses similar options to lsort but tailored to the treeview widget needs. See below for performance comparisons. This implementation borrows much of the lsort code since there are no public TCL APIs for sorting.

pathName sort parent ?-option ...?

Sort the children of parent. Valid options are:

-ascii

The values are to be sorted in Unicode character code order (the name is for backward-compatibility reasons.) This is the default.

-dictionary

The values are to be sorted using dictionary-style comparisons. This is the same as -ascii except (a) case is ignored except as a tie-breaker and (b) if two strings contain embedded numbers, the numbers compare as integers, not characters. For example, in -dictionary mode, "bigBoy" sorts between "bigbang" and "bigboy", and "x10y" sorts between "x9y" and "x11y". Overrides the -nocase option.

-integer

The values are to be sorted as integers. Non-integer values will generate an error.

-nocase

Comparisons are to be performed in a case-insensitive manner. Only used with -ascii option.

-real

The values are to be sorted as floating-point values. Non-numeric values will generate an error.

-unicode

Alias for -ascii.

-command command

Use command as a comparison command. To compare two elements, evaluate a Tcl script consisting of command with the two elements appended as additional arguments. The script should return an integer less than, equal to, or greater than zero if the first element is to be considered less than, equal to, or greater than the second, respectively.

-increasing

Sort the list in increasing order (“smallest” items first). This is the default.

-decreasing

Sort the list in decreasing order (“largest” items first).

-column column

Sort based on values in column. See also COLUMN IDENTIFIERS. The default is the first display column (#0 if tree in -show or #1 if not).

-ignoreempty

Used with -integer or -real to ignore empty entries. Normally these would trigger an error and abort the sort. Empty entries will be sorted first for -increasing and last for -decreasing.

-recurse

Sort all descendants of parent. The default is to only sort the children of parent.

Another function considered but not included is support for locale-based collate sorting. The C lib does provide the strcoll command, but it isn't kept current for all locales. A better option is to use the International Components for Unicode (ICU) library which TCL/Tk kinda supports (several more TIPs in work for 9.1). In any case this should likely be added to the lsort command first, then here. These are the proposed sort options if it were to be implemented here:

-collate

The values are to be sorted in the -locale option specified order.

-locale name

Use name as the locale for collate sorts. If not specified or set to {}, will default to the current locale for the LC_COLLATE category. Will also set the sort mode to collate.

Sort Performance Comparisons

The following are the performance comparisons for treeview sorting using lsort vs the new built-in sort option. All tests used 10 iterations to generate the average microseconds value. The test cases use random values, so only values on the same line can be directly compared as they used the same values. The table below shows a built-in sort performance improvement of 10% to 500% over the lsort-based approach.

TCL/Tk 8.6.14

Sort using lsort command (microseconds)

# Items ASCII Dictionary Integer Real
1 4.8 9.5 8.8 8.5
10 12.3 14.6 16.0 17.9
100 58.1 81.2 58.0 57.9
1,000 493.6 854.3 513.5 447.2
10,000 6,174.7 12,471.6 7,068.5 7,086.5
100,000 116,739.1 216,388.1 131,833.2 131,983.0
1,000,000 1,913,023.6 3,379,958.6 2,130,087.6 2,121,458.4
10,000,000 31,142,539.8 52,089,703.7 37,374,689.5 37,737,530.6

TCL/Tk 9.0.2

Sort using lsort command (microseconds)

# Items ASCII Dictionary Integer Real
1 7.3 7.4 9.4 9.7
10 13.2 15.9 16.3 15.3
100 69.3 79.6 73.0 66.9
1,000 574.2 850.7 542.6 592.0
10,000 8,114.0 13,123.9 7,334.9 8,068.7
100,000 134,758.6 205,805.2 129,762.2 132,376.2
1,000,000 2,081,523.7 3,280,798.4 2,111,405.7 2,115,432.9
10,000,000 32,273,038.9 49,774,938.8 36,779,597.7 37,018,572.1

Sort using built-in treeview sort (microseconds)

# Items ASCII Dictionary Integer Real
1 5.9 4.3 4.2 4.7
10 7.1 7.6 6.4 5.3
100 18.4 37.1 14.9 15.2
1,000 183.6 513.9 101.5 117.5
10,000 2,278.3 8,363.5 1,592.4 1,593.6
100,000 37,645.5 137,373.5 31,237.9 32,378.0
1,000,000 536,106.2 2,007,398.3 466,426.5 482,019.3
10,000,000 7,934,092.6 29,230,320.3 7,030,695.2 7,282,455.3

States

The treeview widget supports several states. In TK 9.0, the alternate state is used to denote which items should use the stripe background. On a Mac when used with the user1 state, the selected state is used to indicate (and show sort direction arrow) an ascending sort and alternate state for descending sort. This TIP adds use of the pressed state to denote which column is to be moved and the active state to indicate the drop column for column moves.

Styles

This TIP also includes a new CheckTreeview style that uses check buttons to denote which items are selected. This works well with the new multiple select mode option. There is an open issue where check marks are not shown in several themes. Also, this style should only be used with -selecttype item.

Bug fixes, Test Suite, etc.

This TIP fixes numerous bugs in the current implementation and also adds an extensive test suite to thoroughly test all of the existing and new functionality. This includes checking for the root item when it is not valid, error conditions, etc. All functionality in the previous test suite has been included in the new test file, but the test cases and numbering have been changed to better organize the tests. The old test file has been renamed treeview_old.test and the test cases were updated to be compatible with the changes. It can be deleted when it is no longer needed.

Out-of-Scope Items

The above is not a complete list of everything that people would want treeview to do. Probably the biggest function not implemented is in-place editing of cell values. See the Cell Editing section for what is implemented. A full implementation depends on how the user wants to use it. The included changes should be adequate for users to implement basic single-line cell editing using the entry, spinbox, combobox, etc. widgets when using treeview as a directory/file browser, email client, database viewer, or other entry-based needs where only limited cell navigation is needed and multi-line cells are not needed. Database record editors are usually better served with a multi-entry dialog to view large records and to edit them with callbacks to validate the data. For spreadsheet or cell-based usage needs, TkTable 3.0 is usually a better choice since it offers in-place cell editing with seamless navigation between cells, selecting text in cells, multi-line cells, easy row resizing, etc. This is not possible in treeview without extensive rework. I'm sure there are others who may disagree with this assessment and want for a built-in cell edit capability. That is left for a future TIP and implementor.

Implementation

See repo: Enhanced Treeview

Copyright

This document has been placed in the public domain.

History