@@ -8,7 +8,7 @@ import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/l
88import { IListOptions , List , IListStyles , mightProducePrintableCharacter , isSelectionRangeChangeEvent , isSelectionSingleChangeEvent } from 'vs/base/browser/ui/list/listWidget' ;
99import { IListVirtualDelegate , IListRenderer , IListMouseEvent , IListEvent , IListContextMenuEvent , IListDragAndDrop , IListDragOverReaction , IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list' ;
1010import { append , $ , toggleClass , getDomNodePagePosition , removeClass , addClass } from 'vs/base/browser/dom' ;
11- import { Event , Relay , Emitter } from 'vs/base/common/event' ;
11+ import { Event , Relay , Emitter , EventBufferer } from 'vs/base/common/event' ;
1212import { StandardKeyboardEvent , IKeyboardEvent } from 'vs/base/browser/keyboardEvent' ;
1313import { KeyCode } from 'vs/base/common/keyCodes' ;
1414import { ITreeModel , ITreeNode , ITreeRenderer , ITreeEvent , ITreeMouseEvent , ITreeContextMenuEvent , ITreeFilter , ITreeNavigator , ICollapseStateChangeEvent , ITreeDragAndDrop , TreeDragOverBubble , TreeVisibility , TreeFilterResult } from 'vs/base/browser/ui/tree/tree' ;
@@ -22,6 +22,7 @@ import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTr
2222import { localize } from 'vs/nls' ;
2323import { disposableTimeout } from 'vs/base/common/async' ;
2424import { isMacintosh } from 'vs/base/common/platform' ;
25+ import { values } from 'vs/base/common/map' ;
2526
2627function asTreeDragAndDropData < T , TFilterData > ( data : IDragAndDropData ) : IDragAndDropData {
2728 if ( data instanceof ElementsDragAndDropData ) {
@@ -641,21 +642,144 @@ export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTr
641642 readonly autoExpandSingleChildren ?: boolean ;
642643}
643644
645+ /**
646+ * The trait concept needs to exist at the tree level, because collapsed
647+ * tree nodes will not be known by the list.
648+ */
649+ class Trait < T > {
650+
651+ private nodes : ITreeNode < T , any > [ ] = [ ] ;
652+ private elements : T [ ] | undefined ;
653+
654+ private _onDidChange = new Emitter < ITreeEvent < T > > ( ) ;
655+ readonly onDidChange = this . _onDidChange . event ;
656+
657+ private _nodeSet : Set < ITreeNode < T , any > > | undefined ;
658+ private get nodeSet ( ) : Set < ITreeNode < T , any > > {
659+ if ( ! this . _nodeSet ) {
660+ this . _nodeSet = new Set ( ) ;
661+
662+ for ( const node of this . nodes ) {
663+ this . _nodeSet . add ( node ) ;
664+ }
665+ }
666+
667+ return this . _nodeSet ;
668+ }
669+
670+ set ( nodes : ITreeNode < T , any > [ ] , browserEvent ?: UIEvent ) : void {
671+ this . nodes = [ ...nodes ] ;
672+ this . elements = undefined ;
673+ this . _nodeSet = undefined ;
674+
675+ const that = this ;
676+ this . _onDidChange . fire ( { get elements ( ) { return that . get ( ) ; } , browserEvent } ) ;
677+ }
678+
679+ get ( ) : T [ ] {
680+ if ( ! this . elements ) {
681+ this . elements = this . nodes . map ( node => node . element ) ;
682+ }
683+
684+ return [ ...this . elements ] ;
685+ }
686+
687+ has ( node : ITreeNode < T , any > ) : boolean {
688+ return this . nodeSet . has ( node ) ;
689+ }
690+
691+ remove ( nodes : ITreeNode < T , any > [ ] ) : void {
692+ const set = this . nodeSet ;
693+
694+ for ( const node of nodes ) {
695+ set . delete ( node ) ;
696+ }
697+
698+ this . set ( values ( set ) ) ;
699+ }
700+ }
701+
702+ /**
703+ * We use this List subclass to restore selection and focus as nodes
704+ * get rendered in the list, possibly due to a node expand() call.
705+ */
706+ class TreeNodeList < T , TFilterData > extends List < ITreeNode < T , TFilterData > > {
707+
708+ constructor (
709+ container : HTMLElement ,
710+ virtualDelegate : IListVirtualDelegate < ITreeNode < T , TFilterData > > ,
711+ renderers : IListRenderer < any /* TODO@joao */ , any > [ ] ,
712+ private focusTrait : Trait < T > ,
713+ private selectionTrait : Trait < T > ,
714+ options ?: IListOptions < ITreeNode < T , TFilterData > >
715+ ) {
716+ super ( container , virtualDelegate , renderers , options ) ;
717+ }
718+
719+ splice ( start : number , deleteCount : number , elements : ITreeNode < T , TFilterData > [ ] = [ ] ) : void {
720+ super . splice ( start , deleteCount , elements ) ;
721+
722+ if ( elements . length === 0 ) {
723+ return ;
724+ }
725+
726+ const additionalFocus : number [ ] = [ ] ;
727+ const additionalSelection : number [ ] = [ ] ;
728+
729+ elements . forEach ( ( node , index ) => {
730+ if ( this . selectionTrait . has ( node ) ) {
731+ additionalFocus . push ( start + index ) ;
732+ }
733+
734+ if ( this . selectionTrait . has ( node ) ) {
735+ additionalSelection . push ( start + index ) ;
736+ }
737+ } ) ;
738+
739+ if ( additionalFocus . length > 0 ) {
740+ super . setFocus ( [ ...super . getFocus ( ) , ...additionalFocus ] ) ;
741+ }
742+
743+ if ( additionalSelection . length > 0 ) {
744+ super . setSelection ( [ ...super . getSelection ( ) , ...additionalSelection ] ) ;
745+ }
746+ }
747+
748+ setFocus ( indexes : number [ ] , browserEvent ?: UIEvent , fromAPI = false ) : void {
749+ super . setFocus ( indexes , browserEvent ) ;
750+
751+ if ( ! fromAPI ) {
752+ this . focusTrait . set ( indexes . map ( i => this . element ( i ) ) , browserEvent ) ;
753+ }
754+ }
755+
756+ setSelection ( indexes : number [ ] , browserEvent ?: UIEvent , fromAPI = false ) : void {
757+ super . setSelection ( indexes , browserEvent ) ;
758+
759+ if ( ! fromAPI ) {
760+ this . selectionTrait . set ( indexes . map ( i => this . element ( i ) ) , browserEvent ) ;
761+ }
762+ }
763+ }
764+
644765export abstract class AbstractTree < T , TFilterData , TRef > implements IDisposable {
645766
646- private view : List < ITreeNode < T , TFilterData > > ;
767+ private view : TreeNodeList < T , TFilterData > ;
647768 private renderers : TreeRenderer < T , TFilterData , any > [ ] ;
648769 private focusNavigationFilter : ( ( node : ITreeNode < T , TFilterData > ) => boolean ) | undefined ;
649770 protected model : ITreeModel < T , TFilterData , TRef > ;
771+ private focus = new Trait < T > ( ) ;
772+ private selection = new Trait < T > ( ) ;
773+ private eventBufferer = new EventBufferer ( ) ;
650774 protected disposables : IDisposable [ ] = [ ] ;
651775
652776 private _onDidUpdateOptions = new Emitter < IAbstractTreeOptions < T , TFilterData > > ( ) ;
653777 readonly onDidUpdateOptions = this . _onDidUpdateOptions . event ;
654778
655779 get onDidScroll ( ) : Event < void > { return this . view . onDidScroll ; }
656780
657- get onDidChangeFocus ( ) : Event < ITreeEvent < T > > { return Event . map ( this . view . onFocusChange , asTreeEvent ) ; }
658- get onDidChangeSelection ( ) : Event < ITreeEvent < T > > { return Event . map ( this . view . onSelectionChange , asTreeEvent ) ; }
781+ readonly onDidChangeFocus : Event < ITreeEvent < T > > = this . eventBufferer . wrapEvent ( this . focus . onDidChange ) ;
782+ readonly onDidChangeSelection : Event < ITreeEvent < T > > = this . eventBufferer . wrapEvent ( this . selection . onDidChange ) ;
659783 get onDidOpen ( ) : Event < ITreeEvent < T > > { return Event . map ( this . view . onDidOpen , asTreeEvent ) ; }
660784
661785 get onMouseClick ( ) : Event < ITreeMouseEvent < T > > { return Event . map ( this . view . onMouseClick , asTreeMouseEvent ) ; }
@@ -697,11 +821,18 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
697821 this . disposables . push ( filter ) ;
698822 }
699823
700- this . view = new List ( container , treeDelegate , this . renderers , asListOptions ( ( ) => this . model , _options ) ) ;
824+ this . view = new TreeNodeList ( container , treeDelegate , this . renderers , this . focus , this . selection , asListOptions ( ( ) => this . model , _options ) ) ;
701825
702826 this . model = this . createModel ( this . view , _options ) ;
703827 onDidChangeCollapseStateRelay . input = this . model . onDidChangeCollapseState ;
704828
829+ this . model . onDidSplice ( e => {
830+ this . eventBufferer . bufferEvents ( ( ) => {
831+ this . focus . remove ( e . deletedNodes ) ;
832+ this . selection . remove ( e . deletedNodes ) ;
833+ } ) ;
834+ } , null , this . disposables ) ;
835+
705836 this . view . onTap ( this . reactOnMouseClick , this , this . disposables ) ;
706837 this . view . onMouseClick ( this . reactOnMouseClick , this , this . disposables ) ;
707838
@@ -853,18 +984,23 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
853984 }
854985
855986 setSelection ( elements : TRef [ ] , browserEvent ?: UIEvent ) : void {
856- const indexes = elements . map ( e => this . model . getListIndex ( e ) ) ;
857- this . view . setSelection ( indexes , browserEvent ) ;
987+ const nodes = elements . map ( e => this . model . getNode ( e ) ) ;
988+ this . selection . set ( nodes , browserEvent ) ;
989+
990+ const indexes = elements . map ( e => this . model . getListIndex ( e ) ) . filter ( i => i > - 1 ) ;
991+ this . view . setSelection ( indexes , browserEvent , true ) ;
858992 }
859993
860994 getSelection ( ) : T [ ] {
861- const nodes = this . view . getSelectedElements ( ) ;
862- return nodes . map ( n => n . element ) ;
995+ return this . selection . get ( ) ;
863996 }
864997
865998 setFocus ( elements : TRef [ ] , browserEvent ?: UIEvent ) : void {
866- const indexes = elements . map ( e => this . model . getListIndex ( e ) ) ;
867- this . view . setFocus ( indexes , browserEvent ) ;
999+ const nodes = elements . map ( e => this . model . getNode ( e ) ) ;
1000+ this . focus . set ( nodes , browserEvent ) ;
1001+
1002+ const indexes = elements . map ( e => this . model . getListIndex ( e ) ) . filter ( i => i > - 1 ) ;
1003+ this . view . setFocus ( indexes , browserEvent , true ) ;
8681004 }
8691005
8701006 focusNext ( n = 1 , loop = false , browserEvent ?: UIEvent ) : void {
@@ -892,8 +1028,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
8921028 }
8931029
8941030 getFocus ( ) : T [ ] {
895- const nodes = this . view . getFocusedElements ( ) ;
896- return nodes . map ( n => n . element ) ;
1031+ return this . focus . get ( ) ;
8971032 }
8981033
8991034 open ( elements : TRef [ ] ) : void {
0 commit comments