diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 1735d6c3691ed1375872e4802b7c9271b8aee93c..73704291d8e34a1f7aecb90a66ccd0e97887850e 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -29,7 +29,7 @@ import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTr import { ITreeEvent, ITreeRenderer, IAsyncDataSource, IDataSource, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree'; import { AsyncDataTree, IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree'; import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree'; -import { IKeyboardNavigationEventFilter } from 'vs/base/browser/ui/tree/abstractTree'; +import { IKeyboardNavigationEventFilter, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; export type ListWidget = List | PagedList | ITree | ObjectTree | DataTree | AsyncDataTree; @@ -780,15 +780,9 @@ function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingS export class WorkbenchObjectTree, TFilterData = void> extends ObjectTree { - readonly contextKeyService: IContextKeyService; - - protected disposables: IDisposable[]; - - private hasSelectionOrFocus: IContextKey; - private hasDoubleSelection: IContextKey; - private hasMultiSelection: IContextKey; - - private _useAltAsMultipleSelectionModifier: boolean; + private internals: WorkbenchTreeInternals; + get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } + get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } constructor( container: HTMLElement, @@ -802,132 +796,19 @@ export class WorkbenchObjectTree, TFilterData = void> @IKeybindingService keybindingService: IKeybindingService, @IAccessibilityService accessibilityService: IAccessibilityService ) { - WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService); - - if (!didBindWorkbenchListAutomaticKeyboardNavigation) { - WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); - didBindWorkbenchListAutomaticKeyboardNavigation = true; - } - - const getAutomaticKeyboardNavigation = () => { - // give priority to the context key value to disable this completely - let automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); - - if (automaticKeyboardNavigation) { - automaticKeyboardNavigation = configurationService.getValue(automaticKeyboardNavigationSettingKey); - } - - return automaticKeyboardNavigation; - }; - - const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); - const openOnSingleClick = useSingleClickToOpen(configurationService); - const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); - - super(container, delegate, renderers, { - keyboardSupport: false, - styleController: new DefaultStyleController(getSharedListStyleSheet()), - ...computeStyles(themeService.getTheme(), defaultListStyles), - ...workbenchListOptions, - indent: configurationService.getValue(treeIndentKey), - automaticKeyboardNavigation: getAutomaticKeyboardNavigation(), - simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter', - horizontalScrolling, - openOnSingleClick, - keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) - }); - - this.disposables.push(workbenchListOptionsDisposable); - - this.contextKeyService = createScopedContextKeyService(contextKeyService, this); - - const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); - listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); - - this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); - this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); - this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); - - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - - const interestingContextKeys = new Set(); - interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey); - const updateKeyboardNavigation = () => { - const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - this.updateOptions({ - simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter' - }); - }; - - this.disposables.push( - this.contextKeyService, - (listService as ListService).register(this), - attachListStyler(this, themeService), - this.onDidChangeSelection(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); - - this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - this.hasMultiSelection.set(selection.length > 1); - this.hasDoubleSelection.set(selection.length === 2); - }), - this.onDidChangeFocus(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); - - this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - }), - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(openModeSettingKey)) { - this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); - } - if (e.affectsConfiguration(multiSelectModifierSettingKey)) { - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - } - if (e.affectsConfiguration(treeIndentKey)) { - const indent = configurationService.getValue(treeIndentKey); - this.updateOptions({ indent }); - } - if (e.affectsConfiguration(keyboardNavigationSettingKey)) { - updateKeyboardNavigation(); - } - if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); - } - }), - this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(interestingContextKeys)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); - } - }), - accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation()) - ); - } - - get useAltAsMultipleSelectionModifier(): boolean { - return this._useAltAsMultipleSelectionModifier; - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); + const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService); + super(container, delegate, renderers, treeOptions); + this.disposables.push(disposable); + this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.disposables.push(this.internals); } } export class WorkbenchDataTree extends DataTree { - readonly contextKeyService: IContextKeyService; - - private hasSelectionOrFocus: IContextKey; - private hasDoubleSelection: IContextKey; - private hasMultiSelection: IContextKey; - - private _useAltAsMultipleSelectionModifier: boolean; + private internals: WorkbenchTreeInternals; + get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } + get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } constructor( container: HTMLElement, @@ -942,127 +823,19 @@ export class WorkbenchDataTree extends DataTree { - // give priority to the context key value to disable this completely - let automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); - - if (automaticKeyboardNavigation) { - automaticKeyboardNavigation = configurationService.getValue(automaticKeyboardNavigationSettingKey); - } - - return automaticKeyboardNavigation; - }; - - const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); - const openOnSingleClick = useSingleClickToOpen(configurationService); - const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); - - super(container, delegate, renderers, dataSource, { - keyboardSupport: false, - styleController: new DefaultStyleController(getSharedListStyleSheet()), - ...computeStyles(themeService.getTheme(), defaultListStyles), - ...workbenchListOptions, - indent: configurationService.getValue(treeIndentKey), - automaticKeyboardNavigation: getAutomaticKeyboardNavigation(), - simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter', - horizontalScrolling, - openOnSingleClick, - keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) - }); - - this.disposables.push(workbenchListOptionsDisposable); - - this.contextKeyService = createScopedContextKeyService(contextKeyService, this); - - const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); - listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); - - this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); - this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); - this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); - - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - - const interestingContextKeys = new Set(); - interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey); - const updateKeyboardNavigation = () => { - const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - this.updateOptions({ - simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter' - }); - }; - - this.disposables.push( - this.contextKeyService, - (listService as ListService).register(this), - attachListStyler(this, themeService), - this.onDidChangeSelection(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); - - this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - this.hasMultiSelection.set(selection.length > 1); - this.hasDoubleSelection.set(selection.length === 2); - }), - this.onDidChangeFocus(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); - - this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - }), - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(openModeSettingKey)) { - this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); - } - if (e.affectsConfiguration(multiSelectModifierSettingKey)) { - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - } - if (e.affectsConfiguration(treeIndentKey)) { - const indent = configurationService.getValue(treeIndentKey); - this.updateOptions({ indent }); - } - if (e.affectsConfiguration(keyboardNavigationSettingKey)) { - updateKeyboardNavigation(); - } - if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); - } - }), - this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(interestingContextKeys)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); - } - }), - accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation()) - ); - } - - get useAltAsMultipleSelectionModifier(): boolean { - return this._useAltAsMultipleSelectionModifier; + const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService); + super(container, delegate, renderers, dataSource, treeOptions); + this.disposables.push(disposable); + this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.disposables.push(this.internals); } } export class WorkbenchAsyncDataTree extends AsyncDataTree { - readonly contextKeyService: IContextKeyService; - - private hasSelectionOrFocus: IContextKey; - private hasDoubleSelection: IContextKey; - private hasMultiSelection: IContextKey; - - private _useAltAsMultipleSelectionModifier: boolean; + private internals: WorkbenchTreeInternals; + get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } + get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } constructor( container: HTMLElement, @@ -1077,31 +850,51 @@ export class WorkbenchAsyncDataTree extends Async @IKeybindingService keybindingService: IKeybindingService, @IAccessibilityService accessibilityService: IAccessibilityService ) { - WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService); + const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService); + super(container, delegate, renderers, dataSource, treeOptions); + this.disposables.push(disposable); + this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.disposables.push(this.internals); + } +} - if (!didBindWorkbenchListAutomaticKeyboardNavigation) { - WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); - didBindWorkbenchListAutomaticKeyboardNavigation = true; - } +function workbenchTreeDataPreamble( + container: HTMLElement, + options: IAbstractTreeOptions, + contextKeyService: IContextKeyService, + themeService: IThemeService, + configurationService: IConfigurationService, + keybindingService: IKeybindingService, + accessibilityService: IAccessibilityService, +): { options: IAbstractTreeOptions, getAutomaticKeyboardNavigation: () => boolean | undefined, disposable: IDisposable } { + WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService); + + if (!didBindWorkbenchListAutomaticKeyboardNavigation) { + WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); + didBindWorkbenchListAutomaticKeyboardNavigation = true; + } - const getAutomaticKeyboardNavigation = () => { - // give priority to the context key value to disable this completely - let automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); + const getAutomaticKeyboardNavigation = () => { + // give priority to the context key value to disable this completely + let automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); - if (automaticKeyboardNavigation) { - automaticKeyboardNavigation = configurationService.getValue(automaticKeyboardNavigationSettingKey); - } + if (automaticKeyboardNavigation) { + automaticKeyboardNavigation = configurationService.getValue(automaticKeyboardNavigationSettingKey); + } - return automaticKeyboardNavigation; - }; + return automaticKeyboardNavigation; + }; - const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); - const openOnSingleClick = useSingleClickToOpen(configurationService); - const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); + const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; + const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); + const openOnSingleClick = useSingleClickToOpen(configurationService); + const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService); - super(container, delegate, renderers, dataSource, { + return { + getAutomaticKeyboardNavigation, + disposable, + options: { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), @@ -1113,11 +906,30 @@ export class WorkbenchAsyncDataTree extends Async horizontalScrolling, openOnSingleClick, keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) - }); + } + }; +} - this.disposables.push(workbenchListOptionsDisposable); +class WorkbenchTreeInternals { - this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + readonly contextKeyService: IContextKeyService; + private hasSelectionOrFocus: IContextKey; + private hasDoubleSelection: IContextKey; + private hasMultiSelection: IContextKey; + private _useAltAsMultipleSelectionModifier: boolean; + private disposables: IDisposable[] = []; + + constructor( + tree: WorkbenchObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree, + options: IAbstractTreeOptions, + getAutomaticKeyboardNavigation: () => boolean | undefined, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IAccessibilityService accessibilityService: IAccessibilityService, + ) { + this.contextKeyService = createScopedContextKeyService(contextKeyService, tree); const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); @@ -1133,7 +945,7 @@ export class WorkbenchAsyncDataTree extends Async const updateKeyboardNavigation = () => { const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue(keyboardNavigationSettingKey); - this.updateOptions({ + tree.updateOptions({ simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter' }); @@ -1141,43 +953,43 @@ export class WorkbenchAsyncDataTree extends Async this.disposables.push( this.contextKeyService, - (listService as ListService).register(this), - attachListStyler(this, themeService), - this.onDidChangeSelection(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); + (listService as ListService).register(tree), + attachListStyler(tree, themeService), + tree.onDidChangeSelection(() => { + const selection = tree.getSelection(); + const focus = tree.getFocus(); this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); this.hasMultiSelection.set(selection.length > 1); this.hasDoubleSelection.set(selection.length === 2); }), - this.onDidChangeFocus(() => { - const selection = this.getSelection(); - const focus = this.getFocus(); + tree.onDidChangeFocus(() => { + const selection = tree.getSelection(); + const focus = tree.getFocus(); this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(openModeSettingKey)) { - this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); + tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } if (e.affectsConfiguration(treeIndentKey)) { const indent = configurationService.getValue(treeIndentKey); - this.updateOptions({ indent }); + tree.updateOptions({ indent }); } if (e.affectsConfiguration(keyboardNavigationSettingKey)) { updateKeyboardNavigation(); } if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); + tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); } }), this.contextKeyService.onDidChangeContext(e => { if (e.affectsSome(interestingContextKeys)) { - this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); + tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); } }), accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation()) @@ -1187,6 +999,10 @@ export class WorkbenchAsyncDataTree extends Async get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } + + dispose(): void { + this.disposables = dispose(this.disposables); + } } const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration);