提交 fa6ac999 编写于 作者: J Joao Moreno

introduce workbench.list.keyboardNavigation

上级 89e86a5a
......@@ -336,7 +336,9 @@ export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
class TypeLabelController<T> implements IDisposable {
private enabled = false;
private state: TypeLabelControllerState = TypeLabelControllerState.Idle;
private enabledDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
constructor(
......@@ -344,17 +346,45 @@ class TypeLabelController<T> implements IDisposable {
private view: ListView<T>,
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>
) {
const onChar = Event.chain(domEvent(view.domNode, 'keydown'))
list.onDidUpdateOptions(this.onDidUpdateListOptions, this, this.disposables);
this.onDidUpdateListOptions(list.options);
}
private onDidUpdateListOptions(options: IListOptions<T>): void {
if (options.enableKeyboardNavigation) {
this.enable();
} else {
this.disable();
}
}
private enable(): void {
if (this.enabled) {
return;
}
const onChar = Event.chain(domEvent(this.view.domNode, 'keydown'))
.filter(e => !isInputElement(e.target as HTMLElement))
.map(event => new StandardKeyboardEvent(event))
.filter(keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e))
.filter(this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e))
.map(event => event.browserEvent.key)
.event;
const onClear = Event.debounce<string, null>(onChar, () => null, 800);
const onInput = Event.reduce<string | null, string | null>(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i));
onInput(this.onInput, this, this.disposables);
onInput(this.onInput, this, this.enabledDisposables);
this.enabled = true;
}
private disable(): void {
if (!this.enabled) {
return;
}
this.enabledDisposables = dispose(this.enabledDisposables);
this.enabled = false;
}
private onInput(word: string | null): void {
......@@ -381,6 +411,7 @@ class TypeLabelController<T> implements IDisposable {
}
dispose() {
this.disable();
this.disposables = dispose(this.disposables);
}
}
......@@ -747,6 +778,7 @@ export class DefaultStyleController implements IStyleController {
export interface IListOptions<T> extends IListStyles {
readonly identityProvider?: IIdentityProvider<T>;
readonly dnd?: IListDragAndDrop<T>;
readonly enableKeyboardNavigation?: boolean;
readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
readonly ariaLabel?: string;
readonly keyboardSupport?: boolean;
......@@ -1004,6 +1036,10 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
}
}
export interface IListOptionsUpdate {
readonly enableKeyboardNavigation?: boolean;
}
export class List<T> implements ISpliceable<T>, IDisposable {
private static InstanceCount = 0;
......@@ -1017,6 +1053,9 @@ export class List<T> implements ISpliceable<T>, IDisposable {
private styleElement: HTMLStyleElement;
private styleController: IStyleController;
private _onDidUpdateOptions = new Emitter<IListOptions<T>>();
readonly onDidUpdateOptions = this._onDidUpdateOptions.event;
protected disposables: IDisposable[];
@memoize get onFocusChange(): Event<IListEvent<T>> {
......@@ -1098,24 +1137,24 @@ export class List<T> implements ISpliceable<T>, IDisposable {
container: HTMLElement,
virtualDelegate: IListVirtualDelegate<T>,
renderers: IListRenderer<any /* TODO@joao */, any>[],
options: IListOptions<T> = DefaultOptions
private _options: IListOptions<T> = DefaultOptions
) {
this.focus = new FocusTrait(i => this.getElementDomId(i));
this.selection = new Trait('selected');
mixin(options, defaultStyles, false);
mixin(_options, defaultStyles, false);
const baseRenderers: IListRenderer<T, ITraitTemplateData>[] = [this.focus.renderer, this.selection.renderer];
if (options.accessibilityProvider) {
baseRenderers.push(new AccessibiltyRenderer<T>(options.accessibilityProvider));
if (_options.accessibilityProvider) {
baseRenderers.push(new AccessibiltyRenderer<T>(_options.accessibilityProvider));
}
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r]));
const viewOptions: IListViewOptions<T> = {
...options,
dnd: options.dnd && new ListViewDragAndDrop(this, options.dnd)
..._options,
dnd: _options.dnd && new ListViewDragAndDrop(this, _options.dnd)
};
this.view = new ListView(container, virtualDelegate, renderers, viewOptions);
......@@ -1125,11 +1164,11 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.styleElement = DOM.createStyleSheet(this.view.domNode);
this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix);
this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix);
this.spliceable = new CombinedSpliceable([
new TraitSpliceable(this.focus, this.view, options.identityProvider),
new TraitSpliceable(this.selection, this.view, options.identityProvider),
new TraitSpliceable(this.focus, this.view, _options.identityProvider),
new TraitSpliceable(this.selection, this.view, _options.identityProvider),
this.view
]);
......@@ -1140,28 +1179,37 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.disposables.push(new DOMFocusController(this, this.view));
if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) {
const controller = new KeyboardController(this, this.view, options);
if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) {
const controller = new KeyboardController(this, this.view, _options);
this.disposables.push(controller);
}
if (options.keyboardNavigationLabelProvider) {
const controller = new TypeLabelController(this, this.view, options.keyboardNavigationLabelProvider);
if (_options.keyboardNavigationLabelProvider) {
const controller = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider);
this.disposables.push(controller);
}
if (typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true) {
this.disposables.push(new MouseController(this, this.view, options));
if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) {
this.disposables.push(new MouseController(this, this.view, _options));
}
this.onFocusChange(this._onFocusChange, this, this.disposables);
this.onSelectionChange(this._onSelectionChange, this, this.disposables);
if (options.ariaLabel) {
this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel));
if (_options.ariaLabel) {
this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel));
}
this.style(options);
this.style(_options);
}
updateOptions(optionsUpdate: IListOptionsUpdate = {}): void {
this._options = { ...this._options, ...optionsUpdate };
this._onDidUpdateOptions.fire(this._options);
}
get options(): IListOptions<T> {
return this._options;
}
splice(start: number, deleteCount: number, elements: T[] = []): void {
......
......@@ -140,7 +140,13 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
return node.depth;
}
},
keyboardNavigationLabelProvider: undefined
keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && {
...options.keyboardNavigationLabelProvider,
getKeyboardNavigationLabel(node) {
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(node.element);
}
},
enableKeyboardNavigation: options.simpleKeyboardNavigation
};
}
......@@ -287,6 +293,11 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore> {
filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult<FuzzyScore> {
if (this._filter) {
const result = this._filter.filter(element, parentVisibility);
if (this.tree.options.simpleKeyboardNavigation) {
return result;
}
let visibility: TreeVisibility;
if (typeof result === 'boolean') {
......@@ -302,7 +313,7 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore> {
}
}
if (!this._pattern) {
if (this.tree.options.simpleKeyboardNavigation || !this._pattern) {
return { data: FuzzyScore.Default, visibility: true };
}
......@@ -310,7 +321,7 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore> {
const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, label, label.toLowerCase(), 0, true);
if (!score) {
if (this.tree.configuration.filterOnType) {
if (this.tree.options.filterOnType) {
return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse;
} else {
return { data: FuzzyScore.Default, visibility: true };
......@@ -333,47 +344,70 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
private _onDidChangePattern = new Emitter<string>();
readonly onDidChangePattern = this._onDidChangePattern.event;
private enabled = false;
private positionClassName = 'ne';
private domNode: HTMLElement;
private label: HTMLElement;
private filterOnType: HTMLInputElement;
private labelDomNode: HTMLElement;
private filterOnTypeDomNode: HTMLInputElement;
private clearDomNode: HTMLElement;
private _pattern = '';
private enabledDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
constructor(
private tree: AbstractTree<T, TFilterData, any>,
private view: List<ITreeNode<T, TFilterData>>,
private filter: TypeFilter<T>,
keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>
) {
this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`);
this.domNode.draggable = true;
domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables);
this.label = append(this.domNode, $('span.label'));
this.labelDomNode = append(this.domNode, $('span.label'));
const controls = append(this.domNode, $('.controls'));
this.filterOnType = append(controls, $<HTMLInputElement>('input.filter'));
this.filterOnType.type = 'checkbox';
this.filterOnType.checked = !!tree.configuration.filterOnType;
this.filterOnType.tabIndex = -1;
this.filterOnTypeDomNode = append(controls, $<HTMLInputElement>('input.filter'));
this.filterOnTypeDomNode.type = 'checkbox';
this.filterOnTypeDomNode.checked = !!tree.options.filterOnType;
this.filterOnTypeDomNode.tabIndex = -1;
this.updateFilterOnTypeTitle();
domEvent(this.filterOnType, 'input')(this.onDidChangeFilterOnType, this, this.disposables);
domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables);
this.clearDomNode = append(controls, $<HTMLInputElement>('button.clear'));
this.clearDomNode.tabIndex = -1;
this.clearDomNode.title = localize('clear', "Clear");
tree.onDidUpdateOptions(this.onDidUpdateTreeOptions, this, this.disposables);
this.onDidUpdateTreeOptions(tree.options);
}
const clear = append(controls, $<HTMLInputElement>('button.clear'));
clear.tabIndex = -1;
clear.title = localize('clear', "Clear");
const onClear = domEvent(clear, 'click');
private onDidUpdateTreeOptions(options: IAbstractTreeOptions<T, TFilterData>): void {
if (options.simpleKeyboardNavigation) {
this.disable();
} else {
this.enable();
}
this.filterOnTypeDomNode.checked = !!options.filterOnType;
this.tree.refilter();
}
private enable(): void {
if (this.enabled) {
return;
}
const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e);
const onKeyDown = Event.chain(domEvent(view.getHTMLElement(), 'keydown'))
.filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnType)
const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e);
const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown'))
.filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode)
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e))
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
.event;
const onClear = domEvent(this.clearDomNode, 'click');
const onInput = Event.chain(Event.any<MouseEvent | StandardKeyboardEvent>(onKeyDown, onClear))
.reduce((previous: string, e) => {
if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape) {
......@@ -388,7 +422,20 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
}, '')
.event;
onInput(this.onInput, this, this.disposables);
onInput(this.onInput, this, this.enabledDisposables);
this.tree.refilter();
this.enabled = true;
}
private disable(): void {
if (!this.enabled) {
return;
}
this.domNode.remove();
this.enabledDisposables = dispose(this.enabledDisposables);
this.tree.refilter();
this.enabled = false;
}
private onInput(pattern: string): void {
......@@ -401,7 +448,7 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
this.tree.domFocus();
}
this.label.textContent = pattern.length > 16
this.labelDomNode.textContent = pattern.length > 16
? '' + pattern.substr(pattern.length - 16)
: pattern;
......@@ -485,20 +532,22 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
}
private onDidChangeFilterOnType(): void {
this.tree.updateConfiguration({ filterOnType: this.filterOnType.checked });
this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked });
this.tree.refilter();
this.tree.domFocus();
this.updateFilterOnTypeTitle();
}
private updateFilterOnTypeTitle(): void {
if (this.filterOnType.checked) {
this.filterOnType.title = localize('disable filter on type', "Disable Filter on Type");
if (this.filterOnTypeDomNode.checked) {
this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type");
} else {
this.filterOnType.title = localize('enable filter on type', "Enable Filter on Type");
this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type");
}
}
dispose() {
this.disable();
this.disposables = dispose(this.disposables);
}
}
......@@ -530,6 +579,7 @@ function asTreeContextMenuEvent<T>(event: IListContextMenuEvent<ITreeNode<T, any
}
export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly simpleKeyboardNavigation?: boolean;
readonly filterOnType?: boolean;
}
......@@ -540,19 +590,17 @@ export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTr
readonly autoExpandSingleChildren?: boolean;
}
export interface IAbstractTreeConfiguration {
readonly filterOnType: boolean;
}
export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable {
private view: List<ITreeNode<T, TFilterData>>;
private renderers: TreeRenderer<T, TFilterData, any>[];
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
private _configuration: IAbstractTreeConfiguration;
protected model: ITreeModel<T, TFilterData, TRef>;
protected disposables: IDisposable[] = [];
private _onDidUpdateOptions = new Emitter<IAbstractTreeOptions<T, TFilterData>>();
readonly onDidUpdateOptions = this._onDidUpdateOptions.event;
get onDidChangeFocus(): Event<ITreeEvent<T>> { return Event.map(this.view.onFocusChange, asTreeEvent); }
get onDidChangeSelection(): Event<ITreeEvent<T>> { return Event.map(this.view.onSelectionChange, asTreeEvent); }
get onDidOpen(): Event<ITreeEvent<T>> { return Event.map(this.view.onDidOpen, asTreeEvent); }
......@@ -577,33 +625,29 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<any /* TODO@joao */, TFilterData, any>[],
options: IAbstractTreeOptions<T, TFilterData> = {}
private _options: IAbstractTreeOptions<T, TFilterData> = {}
) {
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
const onDidChangeCollapseStateRelay = new Relay<ICollapseStateChangeEvent<T, TFilterData>>();
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, options));
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, _options));
this.disposables.push(...this.renderers);
this._configuration = {
filterOnType: typeof options.filterOnType === 'boolean' ? options.filterOnType : false
};
let filter: ITreeFilter<T, TFilterData> | undefined;
if (options.keyboardNavigationLabelProvider) {
filter = new TypeFilter(this, options.keyboardNavigationLabelProvider, options.filter as ITreeFilter<T, FuzzyScore>) as ITreeFilter<T, TFilterData>; // TODO need typescript help here
options = { ...options, filter };
if (_options.keyboardNavigationLabelProvider) {
filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as ITreeFilter<T, FuzzyScore>) as ITreeFilter<T, TFilterData>; // TODO need typescript help here
_options = { ..._options, filter };
}
this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, options));
this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, _options));
this.model = this.createModel(this.view, options);
this.model = this.createModel(this.view, _options);
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;
this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables);
if (options.keyboardSupport !== false) {
if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
.filter(e => !isInputElement(e.target as HTMLElement))
.map(e => new StandardKeyboardEvent(e));
......@@ -613,8 +657,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables);
}
if (options.keyboardNavigationLabelProvider) {
const typeFilterController = new TypeFilterController(this, this.view, filter as TypeFilter<T>, options.keyboardNavigationLabelProvider);
if (_options.keyboardNavigationLabelProvider) {
const typeFilterController = new TypeFilterController(this, this.view, filter as TypeFilter<T>, _options.keyboardNavigationLabelProvider);
this.focusNavigationFilter = node => !typeFilterController.pattern || !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); // TODO@joao
this.disposables.push(typeFilterController);
......@@ -622,19 +666,19 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
}
updateOptions(options: IAbstractTreeOptionsUpdate = {}): void {
updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void {
this._options = { ...this._options, ...optionsUpdate };
for (const renderer of this.renderers) {
renderer.updateOptions(options);
renderer.updateOptions(optionsUpdate);
}
}
updateConfiguration(update: Partial<IAbstractTreeConfiguration>): void {
this._configuration = { ...this._configuration, ...update };
this.refilter();
this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation });
this._onDidUpdateOptions.fire(this._options);
}
get configuration(): IAbstractTreeConfiguration {
return this._configuration;
get options(): IAbstractTreeOptions<T, TFilterData> {
return this._options;
}
// Widget
......
......@@ -113,6 +113,7 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi
export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier';
export const openModeSettingKey = 'workbench.list.openMode';
export const horizontalScrollingKey = 'workbench.tree.horizontalScrolling';
export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation';
const treeIndentKey = 'workbench.tree.indent';
function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
......@@ -905,12 +906,16 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService
) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
super(container, delegate, renderers, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...toWorkbenchListOptions(options, configurationService, keybindingService),
indent: configurationService.getValue(treeIndentKey)
indent: configurationService.getValue(treeIndentKey),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
......@@ -950,6 +955,13 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
}
})
);
}
......@@ -986,12 +998,16 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService
) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
super(container, delegate, renderers, dataSource, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...toWorkbenchListOptions(options, configurationService, keybindingService),
indent: configurationService.getValue(treeIndentKey)
indent: configurationService.getValue(treeIndentKey),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
......@@ -1031,6 +1047,13 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
}
})
);
}
......@@ -1062,12 +1085,16 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService
) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
super(container, delegate, renderers, dataSource, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...toWorkbenchListOptions(options, configurationService, keybindingService),
indent: configurationService.getValue<number>(treeIndentKey)
indent: configurationService.getValue<number>(treeIndentKey),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
......@@ -1107,6 +1134,13 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
const keyboardNavigation = configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
}
})
);
}
......@@ -1158,6 +1192,17 @@ configurationRegistry.registerConfiguration({
'type': 'number',
'default': 8,
'description': localize('tree indent setting', "Controls tree indentation in pixels.")
}
},
[keyboardNavigationSettingKey]: {
'type': 'string',
'enum': ['simple', 'highlight', 'filter'],
'enumDescriptions': [
localize('keyboardNavigationSettingKey.simple', "Sets simple keyboard navigation for lists and trees."),
localize('keyboardNavigationSettingKey.highlight', "Enables highlighting keyboard navigation for lists and trees."),
localize('keyboardNavigationSettingKey.filter', "Enables filtering keyboard navigation for lists and trees.")
],
'default': 'highlight',
'description': localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees.")
},
}
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册