@@ -60,9 +60,9 @@ def _getColumnContentSize(self, column:int, offset:int) -> int:
6060 if not limited_page :
6161 return 0
6262 if column == 0 :
63- size = max (max (_l + _i .icon (column ).termWidth ()+ _t .termWidth () for _t in _i .data (column ).split ('\n ' )) for _l ,_y ,_i in limited_page if not _y )
63+ size = max (( max (_l + _i .icon (column ).termWidth ()+ _t .termWidth () for _t in _i .data (column ).split ('\n ' )) for _l ,_y ,_i in limited_page if not _y ), default = 1 )
6464 else :
65- size = max (max ((_i .icon (column )+ _t ).termWidth () for _t in _i .data (column ).split ('\n ' )) for _l ,_y ,_i in limited_page if not _y )
65+ size = max (( max ((_i .icon (column )+ _t ).termWidth () for _t in _i .data (column ).split ('\n ' )) for _l ,_y ,_i in limited_page if not _y ), default = 1 )
6666 return size - 1
6767
6868 def _get_page_root (self , index :int , size :int ) -> List [Tuple [int ,int ,TTkTreeWidgetItem ]]:
@@ -246,7 +246,7 @@ class _DropTreeData:
246246 items : List [TTkTreeWidgetItem ]
247247
248248 def __init__ (self , * ,
249- header :List [TTkString ]= [] ,
249+ header :Optional [ List [TTkString ]] = None ,
250250 sortingEnabled :bool = True ,
251251 selectionMode :TTkK .SelectionMode = TTkK .SelectionMode .SingleSelection ,
252252 dragDropMode :TTkK .DragDropMode = TTkK .DragDropMode .NoDragDrop ,
@@ -278,7 +278,7 @@ def __init__(self, *,
278278 self ._sortOrder = TTkK .AscendingOrder
279279 self ._rootItem = _RootWidgetItem ()
280280 super ().__init__ (** kwargs )
281- self .setHeaderLabels (header )
281+ self .setHeaderLabels (header if header is not None else [] )
282282 self .setMinimumHeight (1 )
283283 self .setFocusPolicy (TTkK .ClickFocus )
284284 self .clear ()
@@ -321,6 +321,7 @@ def clear(self) -> None:
321321 if self ._rootItem :
322322 self ._rootItem .dataChanged .disconnect (self ._refreshCache )
323323 self ._rootItem = _RootWidgetItem ()
324+ self ._selected = []
324325 self ._rootItem .dataChanged .connect (self ._refreshCache )
325326 self .sortItems (self ._sortColumn , self ._sortOrder )
326327 self .viewChanged .emit ()
@@ -348,6 +349,24 @@ def addTopLevelItems(self, items:List[TTkTreeWidgetItem]) -> None:
348349 self .viewChanged .emit ()
349350 self .update ()
350351
352+ def _itemInTree (self , item :TTkTreeWidgetItem ) -> bool :
353+ if not item :
354+ return False
355+ if item is self ._rootItem :
356+ return True
357+ # Traverse up from the item to the root using parent chain
358+ current = item
359+ while current is not None :
360+ if current is self ._rootItem :
361+ return True
362+ current = current ._parent
363+ return False
364+
365+ def _pruneSelection (self ) -> None :
366+ if not self ._selected :
367+ return
368+ self ._selected = [_i for _i in self ._selected if self ._itemInTree (_i )]
369+
351370 def takeTopLevelItem (self , index :int ) -> Optional [TTkTreeWidgetItem ]:
352371 '''
353372 Removes the top-level item at the given index in the tree and returns it, otherwise returns None;
@@ -358,6 +377,7 @@ def takeTopLevelItem(self, index:int) -> Optional[TTkTreeWidgetItem]:
358377 :rtype: Optional[:py:class:`TTkTreeWidgetItem`]
359378 '''
360379 ret = self ._rootItem .takeChild (index )
380+ self ._pruneSelection ()
361381 self .viewChanged .emit ()
362382 self .update ()
363383 return ret
@@ -396,18 +416,87 @@ def setSelectionMode(self, mode:TTkK.SelectionMode) -> None:
396416 :param mode: the selection mode used in this tree
397417 :type mode: :py:class:`TTkK.SelectionMode`
398418 '''
419+ self ._pruneSelection ()
399420 self ._selectionMode = mode
421+ if mode == TTkK .SelectionMode .NoSelection :
422+ self .clearSelection ()
423+ elif mode == TTkK .SelectionMode .SingleSelection and len (self ._selected ) > 1 :
424+ self ._selected = self ._selected [:1 ]
425+ self .update ()
400426
401427 def selectedItems (self ) -> List [TTkTreeWidgetItem ]:
402428 '''
403429 Returns a list of all selected non-hidden items.
404430
405431 :rtype: List[:py:class:`TTkTreeWidgetItem`]
406432 '''
433+ self ._pruneSelection ()
407434 if self ._selected :
408435 return self ._selected
409436 return []
410437
438+ def clearSelection (self ) -> None :
439+ '''
440+ Deselects all selected items.
441+ '''
442+ if not self ._selected :
443+ return
444+ self ._selected = []
445+ self .update ()
446+
447+ def setCurrentItem (self , item :Optional [TTkTreeWidgetItem ]) -> None :
448+ '''
449+ Selects the specified item as the current one.
450+
451+ :param item: the item to be selected, None clears the selection
452+ :type item: :py:class:`TTkTreeWidgetItem` or None
453+ '''
454+ if item is None :
455+ self .clearSelection ()
456+ return
457+ if not self ._itemInTree (item ):
458+ return
459+ if self ._selectionMode == TTkK .SelectionMode .NoSelection :
460+ return
461+ self ._selected = [item ]
462+ self .update ()
463+
464+ def selectItem (self , item :TTkTreeWidgetItem ) -> None :
465+ '''
466+ Adds the specified item to the current selection.
467+
468+ In single selection mode this replaces the previous selection.
469+
470+ :param item: the item to be selected
471+ :type item: :py:class:`TTkTreeWidgetItem`
472+ '''
473+ if not self ._itemInTree (item ):
474+ return
475+ if self ._selectionMode == TTkK .SelectionMode .NoSelection :
476+ return
477+ if self ._selectionMode == TTkK .SelectionMode .SingleSelection :
478+ self ._selected = [item ]
479+ self .update ()
480+ return
481+ if item not in self ._selected :
482+ self ._selected .append (item )
483+ self .update ()
484+
485+ def deselectItem (self , item :TTkTreeWidgetItem ) -> None :
486+ '''
487+ Removes the specified item from the current selection.
488+
489+ :param item: the item to be deselected
490+ :type item: :py:class:`TTkTreeWidgetItem`
491+ '''
492+ if not self ._selected :
493+ return
494+ if item in self ._selected :
495+ self ._selected .remove (item )
496+ self .update ()
497+ return
498+ self ._pruneSelection ()
499+
411500 def setHeaderLabels (self , labels :List [TTkString ]) -> None :
412501 '''
413502 Adds a column in the header for each item in the labels list, and sets the label for each column.
@@ -570,7 +659,7 @@ def mouseDoubleClickEvent(self, evt:TTkMouseEvent) -> bool:
570659 self .itemExpanded .emit (item )
571660 else :
572661 self .itemCollapsed .emit (item )
573- self ._selected = [ item ]
662+ self .setCurrentItem ( item )
574663 col = - 1
575664 for i , c in enumerate (self ._columnsPos ):
576665 if x < c :
0 commit comments