mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Support find widget in lists/trees (#152481)
* replace list type filter and tree type label controller with list type
navigation and tree find. use proper FindInput widget
* make sure vim doesn't break
* polish outline use case
* 💄
* remove unused import
This commit is contained in:
parent
34507a8db3
commit
4b0d6f3bbc
|
@ -6,7 +6,7 @@
|
|||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle';
|
||||
import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { CaseSensitiveToggle, RegexToggle, WholeWordsToggle } from 'vs/base/browser/ui/findinput/findInputToggles';
|
||||
import { HistoryInputBox, IInputBoxStyles, IInputValidator, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
|
@ -31,6 +31,7 @@ export interface IFindInputOptions extends IFindInputStyles {
|
|||
readonly appendWholeWordsLabel?: string;
|
||||
readonly appendRegexLabel?: string;
|
||||
readonly history?: string[];
|
||||
readonly additionalToggles?: Toggle[];
|
||||
readonly showHistoryHint?: () => boolean;
|
||||
}
|
||||
|
||||
|
@ -74,6 +75,7 @@ export class FindInput extends Widget {
|
|||
protected regex: RegexToggle;
|
||||
protected wholeWords: WholeWordsToggle;
|
||||
protected caseSensitive: CaseSensitiveToggle;
|
||||
protected additionalToggles: Toggle[] = [];
|
||||
public domNode: HTMLElement;
|
||||
public inputBox: HistoryInputBox;
|
||||
|
||||
|
@ -209,10 +211,6 @@ export class FindInput extends Widget {
|
|||
this._onCaseSensitiveKeyDown.fire(e);
|
||||
}));
|
||||
|
||||
if (this._showOptionButtons) {
|
||||
this.inputBox.paddingRight = this.caseSensitive.width() + this.wholeWords.width() + this.regex.width();
|
||||
}
|
||||
|
||||
// Arrow-Key support to navigate between options
|
||||
const indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode];
|
||||
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
|
||||
|
@ -250,6 +248,34 @@ export class FindInput extends Widget {
|
|||
this.controls.appendChild(this.wholeWords.domNode);
|
||||
this.controls.appendChild(this.regex.domNode);
|
||||
|
||||
if (!this._showOptionButtons) {
|
||||
this.caseSensitive.domNode.style.display = 'none';
|
||||
this.wholeWords.domNode.style.display = 'none';
|
||||
this.regex.domNode.style.display = 'none';
|
||||
}
|
||||
|
||||
for (const toggle of options?.additionalToggles ?? []) {
|
||||
this._register(toggle);
|
||||
this.controls.appendChild(toggle.domNode);
|
||||
|
||||
this._register(toggle.onChange(viaKeyboard => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
}));
|
||||
|
||||
this.additionalToggles.push(toggle);
|
||||
}
|
||||
|
||||
if (this.additionalToggles.length > 0) {
|
||||
this.controls.style.display = 'block';
|
||||
}
|
||||
|
||||
this.inputBox.paddingRight =
|
||||
(this._showOptionButtons ? this.caseSensitive.width() + this.wholeWords.width() + this.regex.width() : 0)
|
||||
+ this.additionalToggles.reduce((r, t) => r + t.width(), 0);
|
||||
|
||||
this.domNode.appendChild(this.controls);
|
||||
|
||||
parent?.appendChild(this.domNode);
|
||||
|
@ -282,6 +308,10 @@ export class FindInput extends Widget {
|
|||
this.regex.enable();
|
||||
this.wholeWords.enable();
|
||||
this.caseSensitive.enable();
|
||||
|
||||
for (const toggle of this.additionalToggles) {
|
||||
toggle.enable();
|
||||
}
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
|
@ -290,6 +320,10 @@ export class FindInput extends Widget {
|
|||
this.regex.disable();
|
||||
this.wholeWords.disable();
|
||||
this.caseSensitive.disable();
|
||||
|
||||
for (const toggle of this.additionalToggles) {
|
||||
toggle.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public setFocusInputOnOptionClick(value: boolean): void {
|
||||
|
@ -356,6 +390,10 @@ export class FindInput extends Widget {
|
|||
this.wholeWords.style(toggleStyles);
|
||||
this.caseSensitive.style(toggleStyles);
|
||||
|
||||
for (const toggle of this.additionalToggles) {
|
||||
toggle.style(toggleStyles);
|
||||
}
|
||||
|
||||
const inputBoxStyles: IInputBoxStyles = {
|
||||
inputBackground: this.inputBackground,
|
||||
inputForeground: this.inputForeground,
|
||||
|
|
|
@ -65,72 +65,7 @@
|
|||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Type filter */
|
||||
|
||||
.monaco-list-type-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
border-radius: 2px;
|
||||
padding: 0px 3px;
|
||||
max-width: calc(100% - 10px);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
box-sizing: border-box;
|
||||
cursor: all-scroll;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
height: 20px;
|
||||
z-index: 1;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter.dragging {
|
||||
transition: top 0.2s, left 0.2s;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter.ne {
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter.nw {
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter > .controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter.dragging > .controls,
|
||||
.monaco-list-type-filter:hover > .controls {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter > .controls > * {
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter > .controls > .filter {
|
||||
margin-left: 4px;
|
||||
}
|
||||
/* Filter */
|
||||
|
||||
.monaco-list-type-filter-message {
|
||||
position: absolute;
|
||||
|
@ -149,13 +84,3 @@
|
|||
.monaco-list-type-filter-message:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Electron */
|
||||
|
||||
.monaco-list-type-filter {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.monaco-list-type-filter.dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
|||
import { IThemable } from 'vs/base/common/styler';
|
||||
import 'vs/css!./list';
|
||||
import { IListContextMenuEvent, IListEvent, IListMouseEvent, IListRenderer, IListVirtualDelegate } from './list';
|
||||
import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List } from './listWidget';
|
||||
import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List, TypeNavigationMode } from './listWidget';
|
||||
|
||||
export interface IPagedRenderer<TElement, TTemplateData> extends IListRenderer<TElement, TTemplateData> {
|
||||
renderPlaceholder(index: number, templateData: TTemplateData): void;
|
||||
|
@ -95,8 +95,8 @@ class PagedAccessibilityProvider<T> implements IListAccessibilityProvider<number
|
|||
}
|
||||
|
||||
export interface IPagedListOptions<T> {
|
||||
readonly enableKeyboardNavigation?: boolean;
|
||||
readonly automaticKeyboardNavigation?: boolean;
|
||||
readonly typeNavigationEnabled?: boolean;
|
||||
readonly typeNavigationMode?: TypeNavigationMode;
|
||||
readonly ariaLabel?: string;
|
||||
readonly keyboardSupport?: boolean;
|
||||
readonly multipleSelectionSupport?: boolean;
|
||||
|
@ -282,8 +282,8 @@ export class PagedList<T> implements IThemable, IDisposable {
|
|||
this.list.layout(height, width);
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
this.list.toggleKeyboardNavigation();
|
||||
triggerTypeNavigation(): void {
|
||||
this.list.triggerTypeNavigation();
|
||||
}
|
||||
|
||||
reveal(index: number, relativeTop?: number): void {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { DomEmitter, stopEvent } from 'vs/base/browser/event';
|
|||
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Gesture } from 'vs/base/browser/touch';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
|
||||
import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
|
||||
import { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { binarySearch, firstOrDefault, range } from 'vs/base/common/arrays';
|
||||
|
@ -384,7 +385,12 @@ class KeyboardController<T> implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
enum TypeLabelControllerState {
|
||||
export enum TypeNavigationMode {
|
||||
Automatic,
|
||||
Trigger
|
||||
}
|
||||
|
||||
enum TypeNavigationControllerState {
|
||||
Idle,
|
||||
Typing
|
||||
}
|
||||
|
@ -402,12 +408,12 @@ export const DefaultKeyboardNavigationDelegate = new class implements IKeyboardN
|
|||
}
|
||||
};
|
||||
|
||||
class TypeLabelController<T> implements IDisposable {
|
||||
class TypeNavigationController<T> implements IDisposable {
|
||||
|
||||
private enabled = false;
|
||||
private state: TypeLabelControllerState = TypeLabelControllerState.Idle;
|
||||
private state: TypeNavigationControllerState = TypeNavigationControllerState.Idle;
|
||||
|
||||
private automaticKeyboardNavigation = true;
|
||||
private mode = TypeNavigationMode.Automatic;
|
||||
private triggered = false;
|
||||
private previouslyFocused = -1;
|
||||
|
||||
|
@ -424,20 +430,16 @@ class TypeLabelController<T> implements IDisposable {
|
|||
}
|
||||
|
||||
updateOptions(options: IListOptions<T>): void {
|
||||
const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation;
|
||||
|
||||
if (enableKeyboardNavigation) {
|
||||
if (options.typeNavigationEnabled ?? true) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
if (typeof options.automaticKeyboardNavigation !== 'undefined') {
|
||||
this.automaticKeyboardNavigation = options.automaticKeyboardNavigation;
|
||||
}
|
||||
this.mode = options.typeNavigationMode ?? TypeNavigationMode.Automatic;
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
trigger(): void {
|
||||
this.triggered = !this.triggered;
|
||||
}
|
||||
|
||||
|
@ -448,10 +450,10 @@ class TypeLabelController<T> implements IDisposable {
|
|||
|
||||
const onChar = this.enabledDisposables.add(Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event))
|
||||
.filter(e => !isInputElement(e.target as HTMLElement))
|
||||
.filter(() => this.automaticKeyboardNavigation || this.triggered)
|
||||
.filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered)
|
||||
.map(event => new StandardKeyboardEvent(event))
|
||||
.filter(e => this.delegate.mightProducePrintableCharacter(e))
|
||||
.forEach(e => e.preventDefault())
|
||||
.forEach(e => { e.preventDefault(); e.stopPropagation(); })
|
||||
.map(event => event.browserEvent.key)
|
||||
.event;
|
||||
|
||||
|
@ -490,15 +492,15 @@ class TypeLabelController<T> implements IDisposable {
|
|||
|
||||
private onInput(word: string | null): void {
|
||||
if (!word) {
|
||||
this.state = TypeLabelControllerState.Idle;
|
||||
this.state = TypeNavigationControllerState.Idle;
|
||||
this.triggered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const focus = this.list.getFocus();
|
||||
const start = focus.length > 0 ? focus[0] : 0;
|
||||
const delta = this.state === TypeLabelControllerState.Idle ? 1 : 0;
|
||||
this.state = TypeLabelControllerState.Typing;
|
||||
const delta = this.state === TypeNavigationControllerState.Idle ? 1 : 0;
|
||||
this.state = TypeNavigationControllerState.Typing;
|
||||
|
||||
for (let i = 0; i < this.list.length; i++) {
|
||||
const index = (start + i + delta) % this.list.length;
|
||||
|
@ -895,22 +897,6 @@ export class DefaultStyleController implements IStyleController {
|
|||
`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetBackground) {
|
||||
content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetOutline) {
|
||||
content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetNoMatchesOutline) {
|
||||
content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listMatchesShadow) {
|
||||
content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`);
|
||||
}
|
||||
|
||||
if (styles.tableColumnsBorder) {
|
||||
content.push(`
|
||||
.monaco-table:hover > .monaco-split-view2,
|
||||
|
@ -934,8 +920,8 @@ export class DefaultStyleController implements IStyleController {
|
|||
}
|
||||
|
||||
export interface IListOptionsUpdate extends IListViewOptionsUpdate {
|
||||
readonly enableKeyboardNavigation?: boolean;
|
||||
readonly automaticKeyboardNavigation?: boolean;
|
||||
readonly typeNavigationEnabled?: boolean;
|
||||
readonly typeNavigationMode?: TypeNavigationMode;
|
||||
readonly multipleSelectionSupport?: boolean;
|
||||
}
|
||||
|
||||
|
@ -964,7 +950,7 @@ export interface IListOptions<T> extends IListOptionsUpdate {
|
|||
readonly alwaysConsumeMouseWheel?: boolean;
|
||||
}
|
||||
|
||||
export interface IListStyles {
|
||||
export interface IListStyles extends IFindInputStyles {
|
||||
listBackground?: Color;
|
||||
listFocusBackground?: Color;
|
||||
listFocusForeground?: Color;
|
||||
|
@ -989,7 +975,7 @@ export interface IListStyles {
|
|||
listFilterWidgetBackground?: Color;
|
||||
listFilterWidgetOutline?: Color;
|
||||
listFilterWidgetNoMatchesOutline?: Color;
|
||||
listMatchesShadow?: Color;
|
||||
listFilterWidgetShadow?: Color;
|
||||
treeIndentGuidesStroke?: Color;
|
||||
tableColumnsBorder?: Color;
|
||||
tableOddRowsBackgroundColor?: Color;
|
||||
|
@ -1247,7 +1233,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
|||
protected view: ListView<T>;
|
||||
private spliceable: ISpliceable<T>;
|
||||
private styleController: IStyleController;
|
||||
private typeLabelController?: TypeLabelController<T>;
|
||||
private typeNavigationController?: TypeNavigationController<T>;
|
||||
private accessibilityProvider?: IListAccessibilityProvider<T>;
|
||||
private keyboardController: KeyboardController<T> | undefined;
|
||||
private mouseController: MouseController<T>;
|
||||
|
@ -1387,8 +1373,8 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
|||
|
||||
if (_options.keyboardNavigationLabelProvider) {
|
||||
const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
|
||||
this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider, delegate);
|
||||
this.disposables.add(this.typeLabelController);
|
||||
this.typeNavigationController = new TypeNavigationController(this, this.view, _options.keyboardNavigationLabelProvider, delegate);
|
||||
this.disposables.add(this.typeNavigationController);
|
||||
}
|
||||
|
||||
this.mouseController = this.createMouseController(_options);
|
||||
|
@ -1413,7 +1399,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
|||
updateOptions(optionsUpdate: IListOptionsUpdate = {}): void {
|
||||
this._options = { ...this._options, ...optionsUpdate };
|
||||
|
||||
this.typeLabelController?.updateOptions(this._options);
|
||||
this.typeNavigationController?.updateOptions(this._options);
|
||||
|
||||
if (this._options.multipleSelectionController !== undefined) {
|
||||
if (this._options.multipleSelectionSupport) {
|
||||
|
@ -1529,9 +1515,9 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
|||
this.view.layout(height, width);
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
if (this.typeLabelController) {
|
||||
this.typeLabelController.toggle();
|
||||
triggerTypeNavigation(): void {
|
||||
if (this.typeNavigationController) {
|
||||
this.typeNavigationController.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -265,8 +265,8 @@ export class Table<TRow> implements ISpliceable<TRow>, IThemable, IDisposable {
|
|||
this.list.layout(listHeight, width);
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
this.list.toggleKeyboardNavigation();
|
||||
triggerTypeNavigation(): void {
|
||||
this.list.triggerTypeNavigation();
|
||||
}
|
||||
|
||||
style(styles: ITableStyles): void {
|
||||
|
|
|
@ -148,6 +148,7 @@ export class Toggle extends Widget {
|
|||
this.checked = !this._checked;
|
||||
this._onChange.fire(true);
|
||||
keyboardEvent.preventDefault();
|
||||
keyboardEvent.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,27 +3,34 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DragAndDropData, IDragAndDropData, StaticDND } from 'vs/base/browser/dnd';
|
||||
import { $, addDisposableListener, append, clearNode, createStyleSheet, getDomNodePagePosition, hasParentWithClass } from 'vs/base/browser/dom';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { $, append, clearNode, createStyleSheet, h, hasParentWithClass } from 'vs/base/browser/dom';
|
||||
import { DomEmitter } from 'vs/base/browser/event';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IIdentityProvider, IKeyboardNavigationDelegate, IKeyboardNavigationLabelProvider, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IListMouseEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
|
||||
import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IListMouseEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
|
||||
import { DefaultKeyboardNavigationDelegate, IListOptions, IListStyles, isButton, isInputElement, isMonacoEditor, List, MouseController } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IListOptions, IListStyles, isButton, isInputElement, isMonacoEditor, List, MouseController, TypeNavigationMode } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Toggle } from 'vs/base/browser/ui/toggle/toggle';
|
||||
import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
import { ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeEvent, ITreeFilter, ITreeModel, ITreeModelSpliceEvent, ITreeMouseEvent, ITreeNavigator, ITreeNode, ITreeRenderer, TreeDragOverBubble, TreeError, TreeFilterResult, TreeMouseEventTarget, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { distinct, equals, firstOrDefault, range } from 'vs/base/common/arrays';
|
||||
import { disposableTimeout } from 'vs/base/common/async';
|
||||
import { disposableTimeout, timeout } from 'vs/base/common/async';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { SetMap } from 'vs/base/common/collections';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Emitter, Event, EventBufferer, Relay } from 'vs/base/common/event';
|
||||
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ScrollEvent } from 'vs/base/common/scrollable';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import 'vs/css!./media/tree';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
|
@ -194,8 +201,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
|
|||
getKeyboardNavigationLabel(node) {
|
||||
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(node.element);
|
||||
}
|
||||
},
|
||||
enableKeyboardNavigation: options.simpleKeyboardNavigation
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -563,7 +569,7 @@ class TreeRenderer<T, TFilterData, TRef, TTemplateData> implements IListRenderer
|
|||
|
||||
export type LabelFuzzyScore = { label: string; score: FuzzyScore };
|
||||
|
||||
class TypeFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDisposable {
|
||||
class FindFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDisposable {
|
||||
private _totalCount = 0;
|
||||
get totalCount(): number { return this._totalCount; }
|
||||
private _matchCount = 0;
|
||||
|
@ -590,10 +596,6 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDi
|
|||
if (this._filter) {
|
||||
const result = this._filter.filter(element, parentVisibility);
|
||||
|
||||
if (this.tree.options.simpleKeyboardNavigation) {
|
||||
return result;
|
||||
}
|
||||
|
||||
let visibility: TreeVisibility;
|
||||
|
||||
if (typeof result === 'boolean') {
|
||||
|
@ -611,7 +613,7 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDi
|
|||
|
||||
this._totalCount++;
|
||||
|
||||
if (this.tree.options.simpleKeyboardNavigation || !this._pattern) {
|
||||
if (!this._pattern) {
|
||||
this._matchCount++;
|
||||
return { data: FuzzyScore.Default, visibility: true };
|
||||
}
|
||||
|
@ -634,7 +636,7 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDi
|
|||
}
|
||||
}
|
||||
|
||||
if (this.tree.options.filterOnType) {
|
||||
if (this.tree.findMode === TreeFindMode.Filter) {
|
||||
return TreeVisibility.Recurse;
|
||||
} else {
|
||||
return { data: FuzzyScore.Default, visibility: true };
|
||||
|
@ -651,170 +653,259 @@ class TypeFilter<T> implements ITreeFilter<T, FuzzyScore | LabelFuzzyScore>, IDi
|
|||
}
|
||||
}
|
||||
|
||||
class TypeFilterController<T, TFilterData> implements IDisposable {
|
||||
export interface ICaseSensitiveToggleOpts {
|
||||
readonly isChecked: boolean;
|
||||
readonly inputActiveOptionBorder?: Color;
|
||||
readonly inputActiveOptionForeground?: Color;
|
||||
readonly inputActiveOptionBackground?: Color;
|
||||
}
|
||||
|
||||
private _enabled = false;
|
||||
get enabled(): boolean { return this._enabled; }
|
||||
export class ModeToggle extends Toggle {
|
||||
constructor(opts?: ICaseSensitiveToggleOpts) {
|
||||
super({
|
||||
icon: Codicon.filter,
|
||||
title: localize('filter', "Filter"),
|
||||
isChecked: opts?.isChecked ?? false,
|
||||
inputActiveOptionBorder: opts?.inputActiveOptionBorder,
|
||||
inputActiveOptionForeground: opts?.inputActiveOptionForeground,
|
||||
inputActiveOptionBackground: opts?.inputActiveOptionBackground
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface IFindWidgetStyles extends IFindInputStyles, IListStyles { }
|
||||
|
||||
export interface IFindWidgetOpts extends IFindWidgetStyles { }
|
||||
|
||||
export enum TreeFindMode {
|
||||
Highlight,
|
||||
Filter
|
||||
}
|
||||
|
||||
class FindWidget<T, TFilterData> extends Disposable {
|
||||
|
||||
private readonly elements = h('div.monaco-tree-type-filter', [
|
||||
h('div.monaco-tree-type-filter-grab.codicon.codicon-debug-gripper', { $: 'grab' }),
|
||||
h('div.monaco-tree-type-filter-input', { $: 'findInput' }),
|
||||
h('div.monaco-tree-type-filter-actionbar', { $: 'actionbar' }),
|
||||
]);
|
||||
|
||||
set mode(mode: TreeFindMode) {
|
||||
this.modeToggle.checked = mode === TreeFindMode.Filter;
|
||||
this.findInput.inputBox.setPlaceHolder(mode === TreeFindMode.Filter ? localize('type to filter', "Type to filter") : localize('type to search', "Type to search"));
|
||||
}
|
||||
|
||||
private readonly modeToggle: ModeToggle;
|
||||
private readonly findInput: FindInput;
|
||||
private readonly actionbar: ActionBar;
|
||||
private width = 0;
|
||||
private right = 4;
|
||||
|
||||
readonly _onDidDisable = new Emitter<void>();
|
||||
readonly onDidDisable = this._onDidDisable.event;
|
||||
readonly onDidChangeValue: Event<string>;
|
||||
readonly onDidChangeMode: Event<TreeFindMode>;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private tree: AbstractTree<T, TFilterData, any>,
|
||||
contextViewProvider: IContextViewProvider,
|
||||
mode: TreeFindMode,
|
||||
options?: IFindWidgetOpts
|
||||
) {
|
||||
super();
|
||||
|
||||
container.appendChild(this.elements.root);
|
||||
this._register(toDisposable(() => container.removeChild(this.elements.root)));
|
||||
|
||||
this.modeToggle = this._register(new ModeToggle({ ...options, isChecked: mode === TreeFindMode.Filter }));
|
||||
this.onDidChangeMode = Event.map(this.modeToggle.onChange, () => this.modeToggle.checked ? TreeFindMode.Filter : TreeFindMode.Highlight, this._store);
|
||||
|
||||
this.findInput = this._register(new FindInput(this.elements.findInput, contextViewProvider, false, {
|
||||
label: localize('type to search', "Type to search"),
|
||||
additionalToggles: [this.modeToggle]
|
||||
}));
|
||||
|
||||
this.actionbar = this._register(new ActionBar(this.elements.actionbar));
|
||||
this.mode = mode;
|
||||
|
||||
const emitter = this._register(new DomEmitter(this.findInput.inputBox.inputElement, 'keydown'));
|
||||
const onKeyDown = this._register(Event.chain(emitter.event))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.event;
|
||||
|
||||
this._register(onKeyDown((e): any => {
|
||||
switch (e.keyCode) {
|
||||
case KeyCode.DownArrow:
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.tree.domFocus();
|
||||
return;
|
||||
}
|
||||
}));
|
||||
|
||||
const closeAction = this._register(new Action('close', localize('close', "Close"), 'codicon codicon-close', true, () => this.dispose()));
|
||||
this.actionbar.push(closeAction, { icon: true, label: false });
|
||||
|
||||
const onGrabMouseDown = this._register(new DomEmitter(this.elements.grab, 'mousedown'));
|
||||
|
||||
this._register(onGrabMouseDown.event(e => {
|
||||
const disposables = new DisposableStore();
|
||||
const onWindowMouseMove = disposables.add(new DomEmitter(window, 'mousemove'));
|
||||
const onWindowMouseUp = disposables.add(new DomEmitter(window, 'mouseup'));
|
||||
|
||||
const startRight = this.right;
|
||||
const startX = e.pageX;
|
||||
this.elements.grab.classList.add('grabbing');
|
||||
|
||||
const update = (e: MouseEvent) => {
|
||||
const deltaX = e.pageX - startX;
|
||||
this.right = startRight - deltaX;
|
||||
this.layout();
|
||||
};
|
||||
|
||||
disposables.add(onWindowMouseMove.event(update));
|
||||
disposables.add(onWindowMouseUp.event(e => {
|
||||
update(e);
|
||||
this.elements.grab.classList.remove('grabbing');
|
||||
disposables.dispose();
|
||||
}));
|
||||
}));
|
||||
|
||||
|
||||
this.onDidChangeValue = this.findInput.onDidChange;
|
||||
this.style(options ?? {});
|
||||
}
|
||||
|
||||
style(styles: IFindWidgetStyles): void {
|
||||
this.findInput.style(styles);
|
||||
|
||||
if (styles.listFilterWidgetBackground) {
|
||||
this.elements.root.style.backgroundColor = styles.listFilterWidgetBackground.toString();
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetShadow) {
|
||||
this.elements.root.style.boxShadow = `0 0 8px 2px ${styles.listFilterWidgetShadow}`;
|
||||
}
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.findInput.focus();
|
||||
}
|
||||
|
||||
select() {
|
||||
this.findInput.select();
|
||||
}
|
||||
|
||||
layout(width: number = this.width): void {
|
||||
this.width = width;
|
||||
this.right = Math.min(Math.max(20, this.right), Math.max(20, width - 170));
|
||||
this.elements.root.style.right = `${this.right}px`;
|
||||
}
|
||||
|
||||
showMessage(message: IMessage): void {
|
||||
this.findInput.showMessage(message);
|
||||
}
|
||||
|
||||
clearMessage(): void {
|
||||
this.findInput.clearMessage();
|
||||
}
|
||||
|
||||
override async dispose(): Promise<void> {
|
||||
this._onDidDisable.fire();
|
||||
this.elements.root.classList.add('disabled');
|
||||
await timeout(300);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class FindController<T, TFilterData> implements IDisposable {
|
||||
|
||||
private _pattern = '';
|
||||
get pattern(): string { return this._pattern; }
|
||||
|
||||
private _filterOnType: boolean;
|
||||
get filterOnType(): boolean { return this._filterOnType; }
|
||||
private _mode: TreeFindMode;
|
||||
get mode(): TreeFindMode { return this._mode; }
|
||||
set mode(mode: TreeFindMode) {
|
||||
if (mode === this._mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
private _empty: boolean = false;
|
||||
get empty(): boolean { return this._empty; }
|
||||
this._mode = mode;
|
||||
|
||||
private readonly _onDidChangeEmptyState = new Emitter<boolean>();
|
||||
readonly onDidChangeEmptyState: Event<boolean> = Event.latch(this._onDidChangeEmptyState.event);
|
||||
if (this.widget) {
|
||||
this.widget.mode = this._mode;
|
||||
}
|
||||
|
||||
private positionClassName = 'ne';
|
||||
private domNode: HTMLElement;
|
||||
private messageDomNode: HTMLElement;
|
||||
private labelDomNode: HTMLElement;
|
||||
private filterOnTypeDomNode: HTMLInputElement;
|
||||
private clearDomNode: HTMLElement;
|
||||
private keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter;
|
||||
this.tree.refilter();
|
||||
this.render();
|
||||
this._onDidChangeMode.fire(mode);
|
||||
}
|
||||
|
||||
private automaticKeyboardNavigation = true;
|
||||
private triggered = false;
|
||||
private widget: FindWidget<T, TFilterData> | undefined;
|
||||
private styles: IFindWidgetStyles | undefined;
|
||||
private width = 0;
|
||||
|
||||
private readonly _onDidChangeMode = new Emitter<TreeFindMode>();
|
||||
readonly onDidChangeMode = this._onDidChangeMode.event;
|
||||
|
||||
private readonly _onDidChangePattern = new Emitter<string>();
|
||||
readonly onDidChangePattern = this._onDidChangePattern.event;
|
||||
|
||||
private readonly enabledDisposables = new DisposableStore();
|
||||
private readonly _onDidChangeOpenState = new Emitter<boolean>();
|
||||
readonly onDidChangeOpenState = this._onDidChangeOpenState.event;
|
||||
|
||||
private enabledDisposables = new DisposableStore();
|
||||
private readonly disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
private tree: AbstractTree<T, TFilterData, any>,
|
||||
model: ITreeModel<T, TFilterData, any>,
|
||||
private view: List<ITreeNode<T, TFilterData>>,
|
||||
private filter: TypeFilter<T>,
|
||||
private keyboardNavigationDelegate: IKeyboardNavigationDelegate
|
||||
private filter: FindFilter<T>,
|
||||
private readonly contextViewProvider: IContextViewProvider
|
||||
) {
|
||||
this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`);
|
||||
this.domNode.draggable = true;
|
||||
this.disposables.add(addDisposableListener(this.domNode, 'dragstart', () => this.onDragStart()));
|
||||
|
||||
this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`));
|
||||
|
||||
this.labelDomNode = append(this.domNode, $('span.label'));
|
||||
const controls = append(this.domNode, $('.controls'));
|
||||
|
||||
this._filterOnType = !!tree.options.filterOnType;
|
||||
this.filterOnTypeDomNode = append(controls, $<HTMLInputElement>('input.filter'));
|
||||
this.filterOnTypeDomNode.type = 'checkbox';
|
||||
this.filterOnTypeDomNode.checked = this._filterOnType;
|
||||
this.filterOnTypeDomNode.tabIndex = -1;
|
||||
this.updateFilterOnTypeTitleAndIcon();
|
||||
this.disposables.add(addDisposableListener(this.filterOnTypeDomNode, 'input', () => this.onDidChangeFilterOnType()));
|
||||
|
||||
this.clearDomNode = append(controls, $<HTMLInputElement>('button.clear' + Codicon.treeFilterClear.cssSelector));
|
||||
this.clearDomNode.tabIndex = -1;
|
||||
this.clearDomNode.title = localize('clear', "Clear");
|
||||
|
||||
this.keyboardNavigationEventFilter = tree.options.keyboardNavigationEventFilter;
|
||||
|
||||
this._mode = tree.options.defaultFindMode ?? TreeFindMode.Highlight;
|
||||
model.onDidSplice(this.onDidSpliceModel, this, this.disposables);
|
||||
this.updateOptions(tree.options);
|
||||
}
|
||||
|
||||
updateOptions(options: IAbstractTreeOptions<T, TFilterData>): void {
|
||||
if (options.simpleKeyboardNavigation) {
|
||||
this.disable();
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
|
||||
if (typeof options.filterOnType !== 'undefined') {
|
||||
this._filterOnType = !!options.filterOnType;
|
||||
this.filterOnTypeDomNode.checked = this._filterOnType;
|
||||
this.updateFilterOnTypeTitleAndIcon();
|
||||
}
|
||||
|
||||
if (typeof options.automaticKeyboardNavigation !== 'undefined') {
|
||||
this.automaticKeyboardNavigation = options.automaticKeyboardNavigation;
|
||||
}
|
||||
|
||||
this.tree.refilter();
|
||||
this.render();
|
||||
|
||||
if (!this.automaticKeyboardNavigation) {
|
||||
this.onEventOrInput('');
|
||||
}
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
this.triggered = !this.triggered;
|
||||
|
||||
if (!this.triggered) {
|
||||
this.onEventOrInput('');
|
||||
}
|
||||
}
|
||||
|
||||
private enable(): void {
|
||||
if (this._enabled) {
|
||||
open(): void {
|
||||
if (this.widget) {
|
||||
this.widget.focus();
|
||||
this.widget.select();
|
||||
return;
|
||||
}
|
||||
|
||||
const onRawKeyDown = this.enabledDisposables.add(new DomEmitter(this.view.getHTMLElement(), 'keydown'));
|
||||
const onKeyDown = Event.chain(onRawKeyDown.event)
|
||||
.filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode)
|
||||
.filter(e => e.key !== 'Dead' && !/^Media/.test(e.key))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(this.keyboardNavigationEventFilter || (() => true))
|
||||
.filter(() => this.automaticKeyboardNavigation || this.triggered)
|
||||
.filter(e => (this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) && !(e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.LeftArrow || e.keyCode === KeyCode.RightArrow)) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
|
||||
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
|
||||
.event;
|
||||
this.widget = new FindWidget(this.view.getHTMLElement(), this.tree, this.contextViewProvider, this._mode, this.styles);
|
||||
this.enabledDisposables.add(this.widget);
|
||||
|
||||
const onClearClick = this.enabledDisposables.add(new DomEmitter(this.clearDomNode, 'click'));
|
||||
this.widget.onDidChangeValue(this.onDidChangeValue, this, this.enabledDisposables);
|
||||
this.widget.onDidChangeMode(mode => this.mode = mode, undefined, this.enabledDisposables);
|
||||
this.widget.onDidDisable(this.close, this, this.enabledDisposables);
|
||||
|
||||
Event.chain(Event.any<MouseEvent | StandardKeyboardEvent>(onKeyDown, onClearClick.event))
|
||||
.event(this.onEventOrInput, this, this.enabledDisposables);
|
||||
this.widget.layout(this.width);
|
||||
this.widget.focus();
|
||||
|
||||
this.filter.pattern = '';
|
||||
this.tree.refilter();
|
||||
this.render();
|
||||
this._enabled = true;
|
||||
this.triggered = false;
|
||||
this._onDidChangeOpenState.fire(true);
|
||||
}
|
||||
|
||||
private disable(): void {
|
||||
if (!this._enabled) {
|
||||
close(): void {
|
||||
if (!this.widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.domNode.remove();
|
||||
this.enabledDisposables.clear();
|
||||
this.tree.refilter();
|
||||
this.render();
|
||||
this._enabled = false;
|
||||
this.triggered = false;
|
||||
this.widget = undefined;
|
||||
|
||||
this.enabledDisposables.dispose();
|
||||
this.enabledDisposables = new DisposableStore();
|
||||
|
||||
this.onDidChangeValue('');
|
||||
this.tree.domFocus();
|
||||
|
||||
this._onDidChangeOpenState.fire(false);
|
||||
}
|
||||
|
||||
private onEventOrInput(e: MouseEvent | StandardKeyboardEvent | string): void {
|
||||
if (typeof e === 'string') {
|
||||
this.onInput(e);
|
||||
} else if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) {
|
||||
this.onInput('');
|
||||
} else if (e.keyCode === KeyCode.Backspace) {
|
||||
this.onInput(this.pattern.length === 0 ? '' : this.pattern.substr(0, this.pattern.length - 1));
|
||||
} else {
|
||||
this.onInput(this.pattern + e.browserEvent.key);
|
||||
}
|
||||
}
|
||||
|
||||
private onInput(pattern: string): void {
|
||||
const container = this.view.getHTMLElement();
|
||||
|
||||
if (pattern && !this.domNode.parentElement) {
|
||||
container.append(this.domNode);
|
||||
} else if (!pattern && this.domNode.parentElement) {
|
||||
this.domNode.remove();
|
||||
this.tree.domFocus();
|
||||
}
|
||||
|
||||
private onDidChangeValue(pattern: string): void {
|
||||
this._pattern = pattern;
|
||||
this._onDidChangePattern.fire(pattern);
|
||||
|
||||
|
@ -836,75 +927,10 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
|
|||
}
|
||||
|
||||
this.render();
|
||||
|
||||
if (!pattern) {
|
||||
this.triggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private onDragStart(): void {
|
||||
const container = this.view.getHTMLElement();
|
||||
const { left } = getDomNodePagePosition(container);
|
||||
const containerWidth = container.clientWidth;
|
||||
const midContainerWidth = containerWidth / 2;
|
||||
const width = this.domNode.clientWidth;
|
||||
const disposables = new DisposableStore();
|
||||
let positionClassName = this.positionClassName;
|
||||
|
||||
const updatePosition = () => {
|
||||
switch (positionClassName) {
|
||||
case 'nw':
|
||||
this.domNode.style.top = `4px`;
|
||||
this.domNode.style.left = `4px`;
|
||||
break;
|
||||
case 'ne':
|
||||
this.domNode.style.top = `4px`;
|
||||
this.domNode.style.left = `${containerWidth - width - 6}px`;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: DragEvent) => {
|
||||
event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||
|
||||
const x = event.clientX - left;
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
if (x < midContainerWidth) {
|
||||
positionClassName = 'nw';
|
||||
} else {
|
||||
positionClassName = 'ne';
|
||||
}
|
||||
|
||||
updatePosition();
|
||||
};
|
||||
|
||||
const onDragEnd = () => {
|
||||
this.positionClassName = positionClassName;
|
||||
this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`;
|
||||
this.domNode.style.top = '';
|
||||
this.domNode.style.left = '';
|
||||
|
||||
dispose(disposables);
|
||||
};
|
||||
|
||||
updatePosition();
|
||||
this.domNode.classList.remove(positionClassName);
|
||||
|
||||
this.domNode.classList.add('dragging');
|
||||
disposables.add(toDisposable(() => this.domNode.classList.remove('dragging')));
|
||||
|
||||
disposables.add(addDisposableListener(document, 'dragover', e => onDragOver(e)));
|
||||
disposables.add(addDisposableListener(this.domNode, 'dragend', () => onDragEnd()));
|
||||
|
||||
StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui');
|
||||
disposables.add(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined));
|
||||
}
|
||||
|
||||
private onDidSpliceModel(): void {
|
||||
if (!this._enabled || this.pattern.length === 0) {
|
||||
if (!this.widget || this.pattern.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -912,46 +938,18 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
|
|||
this.render();
|
||||
}
|
||||
|
||||
private onDidChangeFilterOnType(): void {
|
||||
this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked });
|
||||
this.tree.refilter();
|
||||
this.tree.domFocus();
|
||||
this.render();
|
||||
this.updateFilterOnTypeTitleAndIcon();
|
||||
}
|
||||
|
||||
private updateFilterOnTypeTitleAndIcon(): void {
|
||||
if (this.filterOnType) {
|
||||
this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOff.classNamesArray);
|
||||
this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOn.classNamesArray);
|
||||
this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type");
|
||||
} else {
|
||||
this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOn.classNamesArray);
|
||||
this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOff.classNamesArray);
|
||||
this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type");
|
||||
}
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0;
|
||||
|
||||
if (this.pattern && this.tree.options.filterOnType && noMatches) {
|
||||
this.messageDomNode.textContent = localize('empty', "No elements found");
|
||||
this._empty = true;
|
||||
if (this.pattern && noMatches) {
|
||||
this.widget?.showMessage({ type: MessageType.WARNING, content: localize('not found', "No elements found.") });
|
||||
} else {
|
||||
this.messageDomNode.innerText = '';
|
||||
this._empty = false;
|
||||
this.widget?.clearMessage();
|
||||
}
|
||||
|
||||
this.domNode.classList.toggle('no-matches', noMatches);
|
||||
this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount);
|
||||
this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern;
|
||||
|
||||
this._onDidChangeEmptyState.fire(this._empty);
|
||||
}
|
||||
|
||||
shouldAllowFocus(node: ITreeNode<T, TFilterData>): boolean {
|
||||
if (!this.enabled || !this.pattern || this.filterOnType) {
|
||||
if (!this.widget || !this.pattern || this._mode === TreeFindMode.Filter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -962,16 +960,20 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
|
|||
return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._enabled) {
|
||||
this.domNode.remove();
|
||||
this.enabledDisposables.dispose();
|
||||
this._enabled = false;
|
||||
this.triggered = false;
|
||||
}
|
||||
style(styles: IFindWidgetStyles): void {
|
||||
this.styles = styles;
|
||||
this.widget?.style(styles);
|
||||
}
|
||||
|
||||
layout(width: number): void {
|
||||
this.width = width;
|
||||
this.widget?.layout(width);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidChangePattern.dispose();
|
||||
dispose(this.disposables);
|
||||
this.enabledDisposables.dispose();
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -982,6 +984,8 @@ function asTreeMouseEvent<T>(event: IListMouseEvent<ITreeNode<T, any>>): ITreeMo
|
|||
target = TreeMouseEventTarget.Twistie;
|
||||
} else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-contents', 'monaco-tl-row')) {
|
||||
target = TreeMouseEventTarget.Element;
|
||||
} else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tree-type-filter', 'monaco-list')) {
|
||||
target = TreeMouseEventTarget.Filter;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -1005,9 +1009,9 @@ export interface IKeyboardNavigationEventFilter {
|
|||
|
||||
export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
|
||||
readonly multipleSelectionSupport?: boolean;
|
||||
readonly automaticKeyboardNavigation?: boolean;
|
||||
readonly simpleKeyboardNavigation?: boolean;
|
||||
readonly filterOnType?: boolean;
|
||||
readonly typeNavigationEnabled?: boolean;
|
||||
readonly typeNavigationMode?: TypeNavigationMode;
|
||||
readonly defaultFindMode?: TreeFindMode;
|
||||
readonly smoothScrolling?: boolean;
|
||||
readonly horizontalScrolling?: boolean;
|
||||
readonly mouseWheelScrollSensitivity?: number;
|
||||
|
@ -1017,6 +1021,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
|
|||
}
|
||||
|
||||
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
|
||||
readonly contextViewProvider?: IContextViewProvider;
|
||||
readonly collapseByDefault?: boolean; // defaults to false
|
||||
readonly filter?: ITreeFilter<T, TFilterData>;
|
||||
readonly dnd?: ITreeDragAndDrop<T>;
|
||||
|
@ -1318,7 +1323,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
private selection: Trait<T>;
|
||||
private anchor: Trait<T>;
|
||||
private eventBufferer = new EventBufferer();
|
||||
private typeFilterController?: TypeFilterController<T, TFilterData>;
|
||||
private findController?: FindController<T, TFilterData>;
|
||||
readonly onDidChangeFindOpenState: Event<boolean> = Event.None;
|
||||
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
|
||||
private styleElement: HTMLStyleElement;
|
||||
protected readonly disposables = new DisposableStore();
|
||||
|
@ -1329,7 +1335,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
get onDidChangeSelection(): Event<ITreeEvent<T>> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); }
|
||||
|
||||
get onMouseClick(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); }
|
||||
get onMouseDblClick(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); }
|
||||
get onMouseDblClick(): Event<ITreeMouseEvent<T>> { return Event.filter(Event.map(this.view.onMouseDblClick, asTreeMouseEvent), e => e.target !== TreeMouseEventTarget.Filter); }
|
||||
get onContextMenu(): Event<ITreeContextMenuEvent<T>> { return Event.map(this.view.onContextMenu, asTreeContextMenuEvent); }
|
||||
get onTap(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onTap, asTreeMouseEvent); }
|
||||
get onPointer(): Event<ITreeMouseEvent<T>> { return Event.map(this.view.onPointer, asTreeMouseEvent); }
|
||||
|
@ -1348,8 +1354,11 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
private readonly _onWillRefilter = new Emitter<void>();
|
||||
readonly onWillRefilter: Event<void> = this._onWillRefilter.event;
|
||||
|
||||
get filterOnType(): boolean { return !!this._options.filterOnType; }
|
||||
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
|
||||
get findMode(): TreeFindMode { return this.findController?.mode ?? TreeFindMode.Highlight; }
|
||||
set findMode(findMode: TreeFindMode) { if (this.findController) { this.findController.mode = findMode; } }
|
||||
readonly onDidChangeFindMode: Event<TreeFindMode>;
|
||||
|
||||
get onDidChangeFindPattern(): Event<string> { return this.findController ? this.findController.onDidChangePattern : Event.None; }
|
||||
|
||||
get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; }
|
||||
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; }
|
||||
|
@ -1376,10 +1385,10 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
this.disposables.add(r);
|
||||
}
|
||||
|
||||
let filter: TypeFilter<T> | undefined;
|
||||
let filter: FindFilter<T> | undefined;
|
||||
|
||||
if (_options.keyboardNavigationLabelProvider) {
|
||||
filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as any as ITreeFilter<T, FuzzyScore>);
|
||||
filter = new FindFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as any as ITreeFilter<T, FuzzyScore>);
|
||||
_options = { ..._options, filter: filter as ITreeFilter<T, TFilterData> }; // TODO need typescript help here
|
||||
this.disposables.add(filter);
|
||||
}
|
||||
|
@ -1432,11 +1441,14 @@ 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 delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
|
||||
this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, delegate);
|
||||
this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node);
|
||||
this.disposables.add(this.typeFilterController!);
|
||||
if (_options.keyboardNavigationLabelProvider && _options.contextViewProvider) {
|
||||
this.findController = new FindController(this, this.model, this.view, filter!, _options.contextViewProvider);
|
||||
this.focusNavigationFilter = node => this.findController!.shouldAllowFocus(node);
|
||||
this.onDidChangeFindOpenState = this.findController.onDidChangeOpenState;
|
||||
this.disposables.add(this.findController!);
|
||||
this.onDidChangeFindMode = this.findController.onDidChangeMode;
|
||||
} else {
|
||||
this.onDidChangeFindMode = Event.None;
|
||||
}
|
||||
|
||||
this.styleElement = createStyleSheet(this.view.getHTMLElement());
|
||||
|
@ -1450,13 +1462,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
renderer.updateOptions(optionsUpdate);
|
||||
}
|
||||
|
||||
this.view.updateOptions({
|
||||
...this._options,
|
||||
enableKeyboardNavigation: this._options.simpleKeyboardNavigation,
|
||||
});
|
||||
|
||||
this.typeFilterController?.updateOptions(this._options);
|
||||
|
||||
this.view.updateOptions(this._options);
|
||||
this._onDidUpdateOptions.fire(this._options);
|
||||
|
||||
this.getHTMLElement().classList.toggle('always', this._options.renderIndentGuides === RenderIndentGuides.Always);
|
||||
|
@ -1483,21 +1489,11 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
}
|
||||
|
||||
get contentHeight(): number {
|
||||
if (this.typeFilterController && this.typeFilterController.filterOnType && this.typeFilterController.empty) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return this.view.contentHeight;
|
||||
}
|
||||
|
||||
get onDidChangeContentHeight(): Event<number> {
|
||||
let result = this.view.onDidChangeContentHeight;
|
||||
|
||||
if (this.typeFilterController) {
|
||||
result = Event.any(result, Event.map(this.typeFilterController.onDidChangeEmptyState, () => this.contentHeight));
|
||||
}
|
||||
|
||||
return result;
|
||||
return this.view.onDidChangeContentHeight;
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
|
@ -1559,6 +1555,10 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
|
||||
layout(height?: number, width?: number): void {
|
||||
this.view.layout(height, width);
|
||||
|
||||
if (isNumber(width)) {
|
||||
this.findController?.layout(width);
|
||||
}
|
||||
}
|
||||
|
||||
style(styles: IListStyles): void {
|
||||
|
@ -1571,6 +1571,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
}
|
||||
|
||||
this.styleElement.textContent = content.join('\n');
|
||||
|
||||
this.findController?.style(styles);
|
||||
this.view.style(styles);
|
||||
}
|
||||
|
||||
|
@ -1624,12 +1626,16 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
|||
return this.model.isCollapsed(location);
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
this.view.toggleKeyboardNavigation();
|
||||
triggerTypeNavigation(): void {
|
||||
this.view.triggerTypeNavigation();
|
||||
}
|
||||
|
||||
if (this.typeFilterController) {
|
||||
this.typeFilterController.toggle();
|
||||
}
|
||||
openFind(): void {
|
||||
this.findController?.open();
|
||||
}
|
||||
|
||||
closeFind(): void {
|
||||
this.findController?.close();
|
||||
}
|
||||
|
||||
refilter(): void {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
|
|||
import { IIdentityProvider, IListDragAndDrop, IListDragOverReaction, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
|
||||
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ComposedTreeDelegate, TreeFindMode as TreeFindMode, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
import { CompressibleObjectTree, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions, ICompressibleTreeRenderer, IObjectTreeOptions, IObjectTreeSetChildrenOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
|
@ -341,7 +341,12 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
|||
|
||||
get onDidUpdateOptions(): Event<IAsyncDataTreeOptionsUpdate> { return this.tree.onDidUpdateOptions; }
|
||||
|
||||
get filterOnType(): boolean { return this.tree.filterOnType; }
|
||||
get onDidChangeFindOpenState(): Event<boolean> { return this.tree.onDidChangeFindOpenState; }
|
||||
|
||||
get findMode(): TreeFindMode { return this.tree.findMode; }
|
||||
set findMode(mode: TreeFindMode) { this.tree.findMode = mode; }
|
||||
readonly onDidChangeFindMode: Event<TreeFindMode>;
|
||||
|
||||
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) {
|
||||
if (typeof this.tree.expandOnlyOnTwistieClick === 'boolean') {
|
||||
return this.tree.expandOnlyOnTwistieClick;
|
||||
|
@ -367,6 +372,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
|||
this.collapseByDefault = options.collapseByDefault;
|
||||
|
||||
this.tree = this.createTree(user, container, delegate, renderers, options);
|
||||
this.onDidChangeFindMode = this.tree.onDidChangeFindMode;
|
||||
|
||||
this.root = createAsyncDataTreeNode({
|
||||
element: undefined!,
|
||||
|
@ -616,8 +622,16 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
|||
return this.tree.isCollapsed(this.getDataNode(element));
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
this.tree.toggleKeyboardNavigation();
|
||||
triggerTypeNavigation(): void {
|
||||
this.tree.triggerTypeNavigation();
|
||||
}
|
||||
|
||||
openFind(): void {
|
||||
this.tree.openFind();
|
||||
}
|
||||
|
||||
closeFind(): void {
|
||||
this.tree.closeFind();
|
||||
}
|
||||
|
||||
refilter(): void {
|
||||
|
|
|
@ -67,3 +67,45 @@
|
|||
/* Use steps to throttle FPS to reduce CPU usage */
|
||||
animation: codicon-spin 1.25s steps(30) infinite;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: flex;
|
||||
padding: 3px;
|
||||
transition: top 0.3s;
|
||||
width: 160px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter.disabled {
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-grab {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-grab.grabbing {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-input .monaco-inputbox > .ibwrapper > .input,
|
||||
.monaco-tree-type-filter-input .monaco-inputbox > .ibwrapper > .mirror {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-actionbar {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.monaco-tree-type-filter-actionbar .monaco-action-bar .action-label {
|
||||
padding: 2px;
|
||||
}
|
||||
|
|
|
@ -141,7 +141,8 @@ export interface ITreeEvent<T> {
|
|||
export enum TreeMouseEventTarget {
|
||||
Unknown,
|
||||
Twistie,
|
||||
Element
|
||||
Element,
|
||||
Filter
|
||||
}
|
||||
|
||||
export interface ITreeMouseEvent<T> {
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createStyleSheet } from 'vs/base/browser/dom';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IListMouseEvent, IListRenderer, IListTouchEvent, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IPagedListOptions, IPagedRenderer, PagedList } from 'vs/base/browser/ui/list/listPaging';
|
||||
import { DefaultStyleController, IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IMultipleSelectionController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { DefaultStyleController, IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IMultipleSelectionController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List, TypeNavigationMode } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { ITableColumn, ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table';
|
||||
import { ITableOptions, ITableOptionsUpdate, Table } from 'vs/base/browser/ui/table/tableWidget';
|
||||
import { IAbstractTreeOptions, IAbstractTreeOptionsUpdate, IKeyboardNavigationEventFilter, RenderIndentGuides } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { TreeFindMode, IAbstractTreeOptions, IAbstractTreeOptionsUpdate, IKeyboardNavigationEventFilter, RenderIndentGuides } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { AsyncDataTree, CompressibleAsyncDataTree, IAsyncDataTreeOptions, IAsyncDataTreeOptionsUpdate, ICompressibleAsyncDataTreeOptions, ICompressibleAsyncDataTreeOptionsUpdate, ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
|
||||
import { CompressibleObjectTree, ICompressibleObjectTreeOptions, ICompressibleObjectTreeOptionsUpdate, ICompressibleTreeRenderer, IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
|
@ -17,13 +18,13 @@ import { IAsyncDataSource, IDataSource, ITreeEvent, ITreeRenderer } from 'vs/bas
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { attachListStyler, computeStyles, defaultListStyles, IColorMapping } from 'vs/platform/theme/common/styler';
|
||||
|
@ -112,7 +113,7 @@ export class ListService implements IListService {
|
|||
}
|
||||
}
|
||||
|
||||
const RawWorkbenchListFocusContextKey = new RawContextKey<boolean>('listFocus', true);
|
||||
export const RawWorkbenchListFocusContextKey = new RawContextKey<boolean>('listFocus', true);
|
||||
export const WorkbenchListSupportsMultiSelectContextKey = new RawContextKey<boolean>('listSupportsMultiselect', true);
|
||||
export const WorkbenchListFocusContextKey = ContextKeyExpr.and(RawWorkbenchListFocusContextKey, ContextKeyExpr.not(InputFocusedContextKey));
|
||||
export const WorkbenchListHasSelectionOrFocus = new RawContextKey<boolean>('listHasSelectionOrFocus', false);
|
||||
|
@ -123,7 +124,13 @@ export const WorkbenchTreeElementCanCollapse = new RawContextKey<boolean>('treeE
|
|||
export const WorkbenchTreeElementHasParent = new RawContextKey<boolean>('treeElementHasParent', false);
|
||||
export const WorkbenchTreeElementCanExpand = new RawContextKey<boolean>('treeElementCanExpand', false);
|
||||
export const WorkbenchTreeElementHasChild = new RawContextKey<boolean>('treeElementHasChild', false);
|
||||
export const WorkbenchListAutomaticKeyboardNavigationKey = 'listAutomaticKeyboardNavigation';
|
||||
export const WorkbenchTreeFindOpen = new RawContextKey<boolean>('treeFindOpen', false);
|
||||
const WorkbenchListTypeNavigationModeKey = 'listTypeNavigationMode';
|
||||
|
||||
/**
|
||||
* @deprecated in favor of WorkbenchListTypeNavigationModeKey
|
||||
*/
|
||||
const WorkbenchListAutomaticKeyboardNavigationLegacyKey = 'listAutomaticKeyboardNavigation';
|
||||
|
||||
function createScopedContextKeyService(contextKeyService: IContextKeyService, widget: ListWidget): IContextKeyService {
|
||||
const result = contextKeyService.createScoped(widget.getHTMLElement());
|
||||
|
@ -134,8 +141,9 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi
|
|||
const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier';
|
||||
const openModeSettingKey = 'workbench.list.openMode';
|
||||
const horizontalScrollingKey = 'workbench.list.horizontalScrolling';
|
||||
const defaultFindModeSettingKey = 'workbench.list.defaultFindMode';
|
||||
/** @deprecated in favor of workbench.list.defaultFindMode */
|
||||
const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation';
|
||||
const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation';
|
||||
const treeIndentKey = 'workbench.tree.indent';
|
||||
const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides';
|
||||
const listSmoothScrolling = 'workbench.list.smoothScrolling';
|
||||
|
@ -840,17 +848,16 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
|
|||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
options: IWorkbenchObjectTreeOptions<T, TFilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchObjectTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getTypeNavigationMode, disposable } = instantiationService.invokeFunction(workbenchTreeDataPreamble, container, options as any);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getTypeNavigationMode, options.overrideStyles, contextKeyService, listService, themeService, configurationService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
|
@ -882,17 +889,16 @@ export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilter
|
|||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
|
||||
options: IWorkbenchCompressibleObjectTreeOptions<T, TFilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchCompressibleObjectTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getTypeNavigationMode, disposable } = instantiationService.invokeFunction(workbenchTreeDataPreamble, container, options as any);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getTypeNavigationMode, options.overrideStyles, contextKeyService, listService, themeService, configurationService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
|
@ -930,17 +936,16 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
|
|||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IDataSource<TInput, T>,
|
||||
options: IWorkbenchDataTreeOptions<T, TFilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getTypeNavigationMode, disposable } = instantiationService.invokeFunction(workbenchTreeDataPreamble, container, options as any);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getTypeNavigationMode, options.overrideStyles, contextKeyService, listService, themeService, configurationService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
|
@ -978,17 +983,16 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
|
|||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IWorkbenchAsyncDataTreeOptions<T, TFilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchAsyncDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getTypeNavigationMode, disposable } = instantiationService.invokeFunction(workbenchTreeDataPreamble, container, options as any);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getTypeNavigationMode, options.overrideStyles, contextKeyService, listService, themeService, configurationService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
|
@ -1024,17 +1028,16 @@ export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> e
|
|||
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getTypeNavigationMode, disposable } = instantiationService.invokeFunction(workbenchTreeDataPreamble, container, options as any);
|
||||
super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, options, getTypeNavigationMode, options.overrideStyles, contextKeyService, listService, themeService, configurationService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
|
@ -1044,33 +1047,62 @@ export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> e
|
|||
}
|
||||
}
|
||||
|
||||
function getDefaultTreeFindMode(configurationService: IConfigurationService) {
|
||||
const value = configurationService.getValue<'highlight' | 'filter'>(defaultFindModeSettingKey);
|
||||
|
||||
if (value === 'highlight') {
|
||||
return TreeFindMode.Highlight;
|
||||
} else if (value === 'filter') {
|
||||
return TreeFindMode.Filter;
|
||||
}
|
||||
|
||||
const deprecatedValue = configurationService.getValue<'simple' | 'highlight' | 'filter'>(keyboardNavigationSettingKey);
|
||||
|
||||
if (deprecatedValue === 'simple' || deprecatedValue === 'highlight') {
|
||||
return TreeFindMode.Highlight;
|
||||
} else if (deprecatedValue === 'filter') {
|
||||
return TreeFindMode.Filter;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTreeOptions<T, TFilterData> | IAsyncDataTreeOptions<T, TFilterData>>(
|
||||
accessor: ServicesAccessor,
|
||||
container: HTMLElement,
|
||||
options: TOptions,
|
||||
contextKeyService: IContextKeyService,
|
||||
configurationService: IConfigurationService,
|
||||
keybindingService: IKeybindingService,
|
||||
accessibilityService: IAccessibilityService,
|
||||
): { options: TOptions; getAutomaticKeyboardNavigation: () => boolean | undefined; disposable: IDisposable } {
|
||||
const getAutomaticKeyboardNavigation = () => {
|
||||
// give priority to the context key value to disable this completely
|
||||
let automaticKeyboardNavigation = Boolean(contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey));
|
||||
): { options: TOptions; getTypeNavigationMode: () => TypeNavigationMode | undefined; disposable: IDisposable } {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const contextViewService = accessor.get(IContextViewService);
|
||||
const contextKeyService = accessor.get(IContextKeyService);
|
||||
|
||||
if (automaticKeyboardNavigation) {
|
||||
automaticKeyboardNavigation = Boolean(configurationService.getValue(automaticKeyboardNavigationSettingKey));
|
||||
const getTypeNavigationMode = () => {
|
||||
// give priority to the context key value to specify a value
|
||||
const modeString = contextKeyService.getContextKeyValue<'automatic' | 'trigger'>(WorkbenchListTypeNavigationModeKey);
|
||||
|
||||
if (modeString === 'automatic') {
|
||||
return TypeNavigationMode.Automatic;
|
||||
} else if (modeString === 'trigger') {
|
||||
return TypeNavigationMode.Trigger;
|
||||
}
|
||||
|
||||
return automaticKeyboardNavigation;
|
||||
// also check the deprecated context key to set the mode to 'trigger'
|
||||
const modeBoolean = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationLegacyKey);
|
||||
|
||||
if (modeBoolean === false) {
|
||||
return TypeNavigationMode.Trigger;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const accessibilityOn = accessibilityService.isScreenReaderOptimized();
|
||||
const keyboardNavigation = options.simpleKeyboardNavigation || accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
|
||||
const horizontalScrolling = options.horizontalScrolling !== undefined ? options.horizontalScrolling : Boolean(configurationService.getValue(horizontalScrollingKey));
|
||||
const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
|
||||
const additionalScrollHeight = options.additionalScrollHeight;
|
||||
|
||||
return {
|
||||
getAutomaticKeyboardNavigation,
|
||||
getTypeNavigationMode,
|
||||
disposable,
|
||||
options: {
|
||||
// ...options, // TODO@Joao why is this not splatted here?
|
||||
|
@ -1079,14 +1111,13 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
|
|||
indent: typeof configurationService.getValue(treeIndentKey) === 'number' ? configurationService.getValue(treeIndentKey) : undefined,
|
||||
renderIndentGuides: configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey),
|
||||
smoothScrolling: Boolean(configurationService.getValue(listSmoothScrolling)),
|
||||
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
|
||||
simpleKeyboardNavigation: keyboardNavigation === 'simple',
|
||||
filterOnType: keyboardNavigation === 'filter',
|
||||
defaultFindMode: getDefaultTreeFindMode(configurationService),
|
||||
horizontalScrolling,
|
||||
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService),
|
||||
additionalScrollHeight,
|
||||
hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements,
|
||||
expandOnlyOnTwistieClick: options.expandOnlyOnTwistieClick ?? (configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick')
|
||||
expandOnlyOnTwistieClick: options.expandOnlyOnTwistieClick ?? (configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick'),
|
||||
contextViewProvider: contextViewService as IContextViewProvider
|
||||
} as TOptions
|
||||
};
|
||||
}
|
||||
|
@ -1106,6 +1137,7 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
private treeElementHasParent: IContextKey<boolean>;
|
||||
private treeElementCanExpand: IContextKey<boolean>;
|
||||
private treeElementHasChild: IContextKey<boolean>;
|
||||
private treeFindOpen: IContextKey<boolean>;
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
private disposables: IDisposable[] = [];
|
||||
private styler: IDisposable | undefined;
|
||||
|
@ -1116,13 +1148,12 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
constructor(
|
||||
private tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchCompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData>,
|
||||
options: IWorkbenchObjectTreeOptions<T, TFilterData> | IWorkbenchCompressibleObjectTreeOptions<T, TFilterData> | IWorkbenchDataTreeOptions<T, TFilterData> | IWorkbenchAsyncDataTreeOptions<T, TFilterData> | IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>,
|
||||
getAutomaticKeyboardNavigation: () => boolean | undefined,
|
||||
getTypeNavigationMode: () => TypeNavigationMode | undefined,
|
||||
overrideStyles: IColorMapping | undefined,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, tree);
|
||||
|
||||
|
@ -1140,20 +1171,10 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
this.treeElementHasParent = WorkbenchTreeElementHasParent.bindTo(this.contextKeyService);
|
||||
this.treeElementCanExpand = WorkbenchTreeElementCanExpand.bindTo(this.contextKeyService);
|
||||
this.treeElementHasChild = WorkbenchTreeElementHasChild.bindTo(this.contextKeyService);
|
||||
this.treeFindOpen = WorkbenchTreeFindOpen.bindTo(this.contextKeyService);
|
||||
|
||||
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
|
||||
|
||||
const interestingContextKeys = new Set();
|
||||
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey);
|
||||
const updateKeyboardNavigation = () => {
|
||||
const accessibilityOn = accessibilityService.isScreenReaderOptimized();
|
||||
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
|
||||
tree.updateOptions({
|
||||
simpleKeyboardNavigation: keyboardNavigation === 'simple',
|
||||
filterOnType: keyboardNavigation === 'filter'
|
||||
});
|
||||
};
|
||||
|
||||
this.updateStyleOverrides(overrideStyles);
|
||||
|
||||
const updateCollapseContextKeys = () => {
|
||||
|
@ -1170,6 +1191,10 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
this.treeElementHasChild.set(!!tree.getFirstElementChild(focus));
|
||||
};
|
||||
|
||||
const interestingContextKeys = new Set();
|
||||
interestingContextKeys.add(WorkbenchListTypeNavigationModeKey);
|
||||
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationLegacyKey);
|
||||
|
||||
this.disposables.push(
|
||||
this.contextKeyService,
|
||||
(listService as ListService).register(tree),
|
||||
|
@ -1192,6 +1217,7 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
}),
|
||||
tree.onDidChangeCollapseState(updateCollapseContextKeys),
|
||||
tree.onDidChangeModel(updateCollapseContextKeys),
|
||||
tree.onDidChangeFindOpenState(enabled => this.treeFindOpen.set(enabled)),
|
||||
configurationService.onDidChangeConfiguration(e => {
|
||||
let newOptions: IAbstractTreeOptionsUpdate = {};
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
|
@ -1209,11 +1235,8 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
const smoothScrolling = Boolean(configurationService.getValue(listSmoothScrolling));
|
||||
newOptions = { ...newOptions, smoothScrolling };
|
||||
}
|
||||
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
|
||||
updateKeyboardNavigation();
|
||||
}
|
||||
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
|
||||
newOptions = { ...newOptions, automaticKeyboardNavigation: getAutomaticKeyboardNavigation() };
|
||||
if (e.affectsConfiguration(defaultFindModeSettingKey) || e.affectsConfiguration(keyboardNavigationSettingKey)) {
|
||||
tree.updateOptions({ defaultFindMode: getDefaultTreeFindMode(configurationService) });
|
||||
}
|
||||
if (e.affectsConfiguration(horizontalScrollingKey) && options.horizontalScrolling === undefined) {
|
||||
const horizontalScrolling = Boolean(configurationService.getValue(horizontalScrollingKey));
|
||||
|
@ -1236,10 +1259,9 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
|||
}),
|
||||
this.contextKeyService.onDidChangeContext(e => {
|
||||
if (e.affectsSome(interestingContextKeys)) {
|
||||
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
|
||||
tree.updateOptions({ typeNavigationMode: getTypeNavigationMode() });
|
||||
}
|
||||
}),
|
||||
accessibilityService.onDidChangeScreenReaderOptimized(() => updateKeyboardNavigation())
|
||||
})
|
||||
);
|
||||
|
||||
this.navigator = new TreeResourceNavigator(tree, { configurationService, ...options });
|
||||
|
@ -1334,6 +1356,16 @@ configurationRegistry.registerConfiguration({
|
|||
default: 5,
|
||||
description: localize('Fast Scroll Sensitivity', "Scrolling speed multiplier when pressing `Alt`.")
|
||||
},
|
||||
[defaultFindModeSettingKey]: {
|
||||
type: 'string',
|
||||
enum: ['highlight', 'filter'],
|
||||
enumDescriptions: [
|
||||
localize('defaultFindModeSettingKey.highlight', "Highlight elements when searching. Further up and down navigation will traverse only the highlighted elements."),
|
||||
localize('defaultFindModeSettingKey.filter', "Filter elements when searching.")
|
||||
],
|
||||
default: 'highlight',
|
||||
description: localize('defaultFindModeSettingKey', "Controls the default find mode for lists and trees in the workbench.")
|
||||
},
|
||||
[keyboardNavigationSettingKey]: {
|
||||
type: 'string',
|
||||
enum: ['simple', 'highlight', 'filter'],
|
||||
|
@ -1343,12 +1375,9 @@ configurationRegistry.registerConfiguration({
|
|||
localize('keyboardNavigationSettingKey.filter', "Filter keyboard navigation will filter out and hide all the elements which do not match the keyboard input.")
|
||||
],
|
||||
default: 'highlight',
|
||||
description: localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees in the workbench. Can be simple, highlight and filter.")
|
||||
},
|
||||
[automaticKeyboardNavigationSettingKey]: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('automatic keyboard navigation setting', "Controls whether keyboard navigation in lists and trees is automatically triggered simply by typing. If set to `false`, keyboard navigation is only triggered when executing the `list.toggleKeyboardNavigation` command, for which you can assign a keyboard shortcut.")
|
||||
description: localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees in the workbench. Can be simple, highlight and filter."),
|
||||
deprecated: true,
|
||||
deprecationMessage: localize('keyboardNavigationSettingKeyDeprecated', "Please use 'workbench.list.defaultFindMode' instead.")
|
||||
},
|
||||
[treeExpandMode]: {
|
||||
type: 'string',
|
||||
|
|
|
@ -445,9 +445,10 @@ export const listFocusHighlightForeground = registerColor('list.focusHighlightFo
|
|||
export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hcDark: '#B89500', hcLight: '#B5200D' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.'));
|
||||
export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hcDark: null, hcLight: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.'));
|
||||
export const listWarningForeground = registerColor('list.warningForeground', { dark: '#CCA700', light: '#855F00', hcDark: null, hcLight: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.'));
|
||||
export const listFilterWidgetBackground = registerColor('listFilterWidget.background', { light: '#efc1ad', dark: '#653723', hcDark: Color.black, hcLight: Color.white }, nls.localize('listFilterWidgetBackground', 'Background color of the type filter widget in lists and trees.'));
|
||||
export const listFilterWidgetBackground = registerColor('listFilterWidget.background', { light: darken(editorWidgetBackground, 0), dark: lighten(editorWidgetBackground, 0), hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, nls.localize('listFilterWidgetBackground', 'Background color of the type filter widget in lists and trees.'));
|
||||
export const listFilterWidgetOutline = registerColor('listFilterWidget.outline', { dark: Color.transparent, light: Color.transparent, hcDark: '#f38518', hcLight: '#007ACC' }, nls.localize('listFilterWidgetOutline', 'Outline color of the type filter widget in lists and trees.'));
|
||||
export const listFilterWidgetNoMatchesOutline = registerColor('listFilterWidget.noMatchesOutline', { dark: '#BE1100', light: '#BE1100', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('listFilterWidgetNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.'));
|
||||
export const listFilterWidgetShadow = registerColor('listFilterWidget.shadow', { dark: widgetShadow, light: widgetShadow, hcDark: widgetShadow, hcLight: widgetShadow }, nls.localize('listFilterWidgetShadow', 'Shadown color of the type filter widget in lists and trees.'));
|
||||
export const listFilterMatchHighlight = registerColor('list.filterMatchBackground', { dark: editorFindMatchHighlight, light: editorFindMatchHighlight, hcDark: null, hcLight: null }, nls.localize('listFilterMatchHighlight', 'Background color of the filtered match.'));
|
||||
export const listFilterMatchHighlightBorder = registerColor('list.filterMatchBorder', { dark: editorFindMatchHighlightBorder, light: editorFindMatchHighlightBorder, hcDark: contrastBorder, hcLight: activeContrastBorder }, nls.localize('listFilterMatchHighlightBorder', 'Border color of the filtered match.'));
|
||||
export const treeIndentGuidesStroke = registerColor('tree.indentGuidesStroke', { dark: '#585858', light: '#a9a9a9', hcDark: '#a9a9a9', hcLight: '#a5a5a5' }, nls.localize('treeIndentGuidesStroke', "Tree stroke color for the indentation guides."));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { Color } from 'vs/base/common/color';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemable, styleFn } from 'vs/base/common/styler';
|
||||
import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, selectBackground, selectBorder, selectForeground, selectListBackground, checkboxBackground, checkboxBorder, checkboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow, listFocusAndSelectionOutline, buttonSeparator } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, selectBackground, selectBorder, selectForeground, selectListBackground, checkboxBackground, checkboxBorder, checkboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow, listFocusAndSelectionOutline, listFilterWidgetShadow, buttonSeparator } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { isHighContrast } from 'vs/platform/theme/common/theme';
|
||||
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
|
@ -184,7 +184,7 @@ export interface IListStyleOverrides extends IStyleOverrides {
|
|||
listFilterWidgetBackground?: ColorIdentifier;
|
||||
listFilterWidgetOutline?: ColorIdentifier;
|
||||
listFilterWidgetNoMatchesOutline?: ColorIdentifier;
|
||||
listMatchesShadow?: ColorIdentifier;
|
||||
listFilterWidgetShadow?: ColorIdentifier;
|
||||
treeIndentGuidesStroke?: ColorIdentifier;
|
||||
tableColumnsBorder?: ColorIdentifier;
|
||||
tableOddRowsBackgroundColor?: ColorIdentifier;
|
||||
|
@ -217,10 +217,25 @@ export const defaultListStyles: IColorMapping = {
|
|||
listFilterWidgetBackground,
|
||||
listFilterWidgetOutline,
|
||||
listFilterWidgetNoMatchesOutline,
|
||||
listMatchesShadow: widgetShadow,
|
||||
listFilterWidgetShadow,
|
||||
treeIndentGuidesStroke,
|
||||
tableColumnsBorder,
|
||||
tableOddRowsBackgroundColor
|
||||
tableOddRowsBackgroundColor,
|
||||
inputActiveOptionBorder,
|
||||
inputActiveOptionForeground,
|
||||
inputActiveOptionBackground,
|
||||
inputBackground,
|
||||
inputForeground,
|
||||
inputBorder,
|
||||
inputValidationInfoBackground,
|
||||
inputValidationInfoForeground,
|
||||
inputValidationInfoBorder,
|
||||
inputValidationWarningBackground,
|
||||
inputValidationWarningForeground,
|
||||
inputValidationWarningBorder,
|
||||
inputValidationErrorBackground,
|
||||
inputValidationErrorForeground,
|
||||
inputValidationErrorBorder,
|
||||
};
|
||||
|
||||
export interface IButtonStyleOverrides extends IStyleOverrides {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
|||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent, WorkbenchListWidget, WorkbenchListSelectionNavigation, WorkbenchTreeElementCanCollapse, WorkbenchTreeElementHasParent, WorkbenchTreeElementHasChild, WorkbenchTreeElementCanExpand } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent, WorkbenchListWidget, WorkbenchListSelectionNavigation, WorkbenchTreeElementCanCollapse, WorkbenchTreeElementHasParent, WorkbenchTreeElementHasChild, WorkbenchTreeElementCanExpand, RawWorkbenchListFocusContextKey, WorkbenchTreeFindOpen } from 'vs/platform/list/browser/listService';
|
||||
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
|
||||
import { equals, range } from 'vs/base/common/arrays';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -17,6 +17,7 @@ import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
|
|||
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Table } from 'vs/base/browser/ui/table/tableWidget';
|
||||
import { AbstractTree, TreeFindMode } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
|
||||
function ensureDOMFocus(widget: ListWidget | undefined): void {
|
||||
// it can happen that one of the commands is executed while
|
||||
|
@ -607,27 +608,62 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
|||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'list.toggleKeyboardNavigation',
|
||||
id: 'list.triggerTypeNavigation',
|
||||
handler: (accessor) => {
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
widget?.toggleKeyboardNavigation();
|
||||
widget?.triggerTypeNavigation();
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'list.toggleFilterOnType',
|
||||
id: 'list.toggleFindMode',
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (widget instanceof AbstractTree || widget instanceof AsyncDataTree) {
|
||||
const tree = widget;
|
||||
tree.findMode = tree.findMode === TreeFindMode.Filter ? TreeFindMode.Highlight : TreeFindMode.Filter;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Deprecated commands
|
||||
CommandsRegistry.registerCommandAlias('list.toggleKeyboardNavigation', 'list.triggerTypeNavigation');
|
||||
CommandsRegistry.registerCommandAlias('list.toggleFilterOnType', 'list.toggleFindMode');
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.find',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: RawWorkbenchListFocusContextKey,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KeyF,
|
||||
secondary: [KeyCode.F3],
|
||||
handler: (accessor) => {
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
|
||||
if (widget instanceof List || widget instanceof PagedList || widget instanceof Table) {
|
||||
// TODO@joao
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
tree.updateOptions({ filterOnType: !tree.filterOnType });
|
||||
else if (widget instanceof AbstractTree || widget instanceof AsyncDataTree) {
|
||||
const tree = widget;
|
||||
tree.openFind();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.closeFind',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(RawWorkbenchListFocusContextKey, WorkbenchTreeFindOpen),
|
||||
primary: KeyCode.Escape,
|
||||
handler: (accessor) => {
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (widget instanceof AbstractTree || widget instanceof AsyncDataTree) {
|
||||
const tree = widget;
|
||||
tree.closeFind();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,8 +13,6 @@ import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
|
|||
import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/contrib/comments/common/commentModel';
|
||||
import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchAsyncDataTree, IListService, IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService';
|
||||
|
@ -266,8 +264,6 @@ export class CommentsList extends WorkbenchAsyncDataTree<any, any> {
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const delegate = new CommentsModelVirualDelegate();
|
||||
const dataSource = new CommentsAsyncDataSource();
|
||||
|
@ -311,12 +307,11 @@ export class CommentsList extends WorkbenchAsyncDataTree<any, any> {
|
|||
},
|
||||
overrideStyles: options.overrideStyles
|
||||
},
|
||||
instantiationService,
|
||||
contextKeyService,
|
||||
listService,
|
||||
themeService,
|
||||
configurationService,
|
||||
keybindingService,
|
||||
accessibilityService
|
||||
configurationService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,8 +116,6 @@ export class DebugHoverWidget implements IContentWidget {
|
|||
horizontalScrolling: true,
|
||||
useShadows: false,
|
||||
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression) => e.name },
|
||||
filterOnType: false,
|
||||
simpleKeyboardNavigation: true,
|
||||
overrideStyles: {
|
||||
listBackground: editorHoverBackground
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { TreeFindMode } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
|
||||
const NEW_STYLE_COMPRESS = true;
|
||||
|
||||
|
@ -585,8 +586,8 @@ export class LoadedScriptsView extends ViewPane {
|
|||
|
||||
// feature: expand all nodes when filtering (not when finding)
|
||||
let viewState: IViewState | undefined;
|
||||
this._register(this.tree.onDidChangeTypeFilterPattern(pattern => {
|
||||
if (!this.tree.options.filterOnType) {
|
||||
this._register(this.tree.onDidChangeFindPattern(pattern => {
|
||||
if (this.tree.findMode === TreeFindMode.Highlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,8 @@ import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/l
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { IColorMapping } from 'vs/platform/theme/common/styler';
|
||||
|
@ -244,8 +242,6 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, IExte
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IExtensionsWorkbenchService extensionsWorkdbenchService: IExtensionsWorkbenchService
|
||||
) {
|
||||
const delegate = new VirualDelegate();
|
||||
|
@ -278,7 +274,7 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, IExte
|
|||
}
|
||||
}
|
||||
},
|
||||
contextKeyService, listService, themeService, configurationService, keybindingService, accessibilityService
|
||||
instantiationService, contextKeyService, listService, themeService, configurationService
|
||||
);
|
||||
|
||||
this.setInput(input);
|
||||
|
|
|
@ -3,22 +3,20 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchListAutomaticKeyboardNavigationKey } from 'vs/platform/list/browser/listService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
||||
export const WorkbenchListSupportsKeyboardNavigation = new RawContextKey<boolean>('listSupportsKeyboardNavigation', true);
|
||||
export const WorkbenchListAutomaticKeyboardNavigation = new RawContextKey<boolean>(WorkbenchListAutomaticKeyboardNavigationKey, true);
|
||||
|
||||
export class ListContext implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
|
||||
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
|
||||
contextKeyService.createKey<boolean>('listSupportsTypeNavigation', true);
|
||||
|
||||
// @deprecated in favor of listSupportsTypeNavigation
|
||||
contextKeyService.createKey('listSupportsKeyboardNavigation', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/marke
|
|||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { MementoObject, Memento } from 'vs/workbench/common/memento';
|
||||
import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
|
@ -922,14 +921,13 @@ class MarkersTree extends WorkbenchObjectTree<MarkerElement, FilterData> impleme
|
|||
delegate: IListVirtualDelegate<MarkerElement>,
|
||||
renderers: ITreeRenderer<MarkerElement, FilterData, any>[],
|
||||
options: IWorkbenchObjectTreeOptions<MarkerElement, FilterData>,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
super(user, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, options, instantiationService, contextKeyService, listService, themeService, configurationService);
|
||||
this.visibilityContextKey = MarkersContextKeys.MarkersTreeVisibilityContextKey.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
|
|
|
@ -232,7 +232,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
keyboardSupport: false,
|
||||
mouseSupport: true,
|
||||
multipleSelectionSupport: false,
|
||||
enableKeyboardNavigation: true,
|
||||
typeNavigationEnabled: true,
|
||||
additionalScrollHeight: 0,
|
||||
// transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',
|
||||
styleController: (_suffix: string) => { return this._list!; },
|
||||
|
|
|
@ -445,22 +445,6 @@ export class NotebookTextDiffList extends WorkbenchList<DiffElementViewModelBase
|
|||
`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetBackground) {
|
||||
content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetOutline) {
|
||||
content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetNoMatchesOutline) {
|
||||
content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listMatchesShadow) {
|
||||
content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`);
|
||||
}
|
||||
|
||||
const newStyles = content.join('\n');
|
||||
if (newStyles !== this.styleElement.textContent) {
|
||||
this.styleElement.textContent = newStyles;
|
||||
|
|
|
@ -915,7 +915,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
mouseSupport: true,
|
||||
multipleSelectionSupport: true,
|
||||
selectionNavigation: true,
|
||||
enableKeyboardNavigation: true,
|
||||
typeNavigationEnabled: true,
|
||||
additionalScrollHeight: 0,
|
||||
transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',
|
||||
styleController: (_suffix: string) => { return this._list; },
|
||||
|
|
|
@ -1393,22 +1393,6 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
|
|||
`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetBackground) {
|
||||
content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetOutline) {
|
||||
content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFilterWidgetNoMatchesOutline) {
|
||||
content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`);
|
||||
}
|
||||
|
||||
if (styles.listMatchesShadow) {
|
||||
content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`);
|
||||
}
|
||||
|
||||
const newStyles = content.join('\n');
|
||||
if (newStyles !== this.styleElement.textContent) {
|
||||
this.styleElement.textContent = newStyles;
|
||||
|
|
|
@ -366,7 +366,6 @@ export function createNotebookCellList(instantiationService: TestInstantiationSe
|
|||
{
|
||||
supportDynamicHeights: true,
|
||||
multipleSelectionSupport: true,
|
||||
enableKeyboardNavigation: true,
|
||||
focusNextPreviousDelegate: {
|
||||
onFocusNext: (applyFocusNext: () => void) => { applyFocusNext(); },
|
||||
onFocusPrevious: (applyFocusPrevious: () => void) => { applyFocusPrevious(); },
|
||||
|
|
|
@ -35,7 +35,7 @@ import { EditorResourceAccessor, IEditorPane } from 'vs/workbench/common/editor'
|
|||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ITreeSorter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { AbstractTreeViewState, IAbstractTreeViewState } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { AbstractTreeViewState, IAbstractTreeViewState, TreeFindMode } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
|
||||
const _ctxFollowsCursor = new RawContextKey('outlineFollowsCursor', false);
|
||||
const _ctxFilterOnType = new RawContextKey('outlineFiltersOnType', false);
|
||||
|
@ -259,7 +259,7 @@ export class OutlinePane extends ViewPane {
|
|||
expandOnlyOnTwistieClick: true,
|
||||
multipleSelectionSupport: false,
|
||||
hideTwistiesOfChildlessElements: true,
|
||||
filterOnType: this._outlineViewState.filterOnType,
|
||||
defaultFindMode: this._outlineViewState.filterOnType ? TreeFindMode.Filter : TreeFindMode.Highlight,
|
||||
overrideStyles: { listBackground: this.getBackgroundColor() }
|
||||
}
|
||||
);
|
||||
|
@ -286,6 +286,7 @@ export class OutlinePane extends ViewPane {
|
|||
};
|
||||
updateTree();
|
||||
this._editorControlDisposables.add(newOutline.onDidChange(updateTree));
|
||||
tree.findMode = this._outlineViewState.filterOnType ? TreeFindMode.Filter : TreeFindMode.Highlight;
|
||||
|
||||
// feature: apply panel background to tree
|
||||
this._editorControlDisposables.add(this.viewDescriptorService.onDidChangeLocation(({ views }) => {
|
||||
|
@ -295,7 +296,7 @@ export class OutlinePane extends ViewPane {
|
|||
}));
|
||||
|
||||
// feature: filter on type - keep tree and menu in sync
|
||||
this._editorControlDisposables.add(tree.onDidUpdateOptions(e => this._outlineViewState.filterOnType = Boolean(e.filterOnType)));
|
||||
this._editorControlDisposables.add(tree.onDidChangeFindMode(mode => this._outlineViewState.filterOnType = mode === TreeFindMode.Filter));
|
||||
|
||||
// feature: reveal outline selection in editor
|
||||
// on change -> reveal/select defining range
|
||||
|
@ -328,7 +329,7 @@ export class OutlinePane extends ViewPane {
|
|||
this._editorControlDisposables.add(this._outlineViewState.onDidChange((e: { followCursor?: boolean; sortBy?: boolean; filterOnType?: boolean }) => {
|
||||
this._outlineViewState.persist(this._storageService);
|
||||
if (e.filterOnType) {
|
||||
tree.updateOptions({ filterOnType: this._outlineViewState.filterOnType });
|
||||
tree.findMode = this._outlineViewState.filterOnType ? TreeFindMode.Filter : TreeFindMode.Highlight;
|
||||
}
|
||||
if (e.followCursor) {
|
||||
revealActiveElement();
|
||||
|
@ -341,8 +342,8 @@ export class OutlinePane extends ViewPane {
|
|||
|
||||
// feature: expand all nodes when filtering (not when finding)
|
||||
let viewState: AbstractTreeViewState | undefined;
|
||||
this._editorControlDisposables.add(tree.onDidChangeTypeFilterPattern(pattern => {
|
||||
if (!tree.options.filterOnType) {
|
||||
this._editorControlDisposables.add(tree.onDidChangeFindPattern(pattern => {
|
||||
if (tree.findMode === TreeFindMode.Highlight) {
|
||||
return;
|
||||
}
|
||||
if (!viewState && pattern) {
|
||||
|
|
|
@ -54,7 +54,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { settingsMoreActionIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
|
||||
import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
@ -2334,8 +2333,6 @@ export class SettingsTree extends WorkbenchObjectTree<SettingsTreeElement> {
|
|||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ILanguageService languageService: ILanguageService
|
||||
) {
|
||||
|
@ -2356,12 +2353,11 @@ export class SettingsTree extends WorkbenchObjectTree<SettingsTreeElement> {
|
|||
smoothScrolling: configurationService.getValue<boolean>('workbench.list.smoothScrolling'),
|
||||
multipleSelectionSupport: false,
|
||||
},
|
||||
instantiationService,
|
||||
contextKeyService,
|
||||
listService,
|
||||
themeService,
|
||||
configurationService,
|
||||
keybindingService,
|
||||
accessibilityService,
|
||||
);
|
||||
|
||||
this.disposables.add(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
|
|
@ -9,11 +9,9 @@ import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/brow
|
|||
import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IListService, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { editorBackground, focusBorder, foreground, transparent } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachStyler } from 'vs/platform/theme/common/styler';
|
||||
|
@ -203,8 +201,6 @@ export class TOCTree extends WorkbenchObjectTree<SettingsTreeGroupElement> {
|
|||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
// test open mode
|
||||
|
@ -231,12 +227,11 @@ export class TOCTree extends WorkbenchObjectTree<SettingsTreeGroupElement> {
|
|||
new TOCTreeDelegate(),
|
||||
[new TOCRenderer()],
|
||||
options,
|
||||
instantiationService,
|
||||
contextKeyService,
|
||||
listService,
|
||||
themeService,
|
||||
configurationService,
|
||||
keybindingService,
|
||||
accessibilityService,
|
||||
);
|
||||
|
||||
this.disposables.add(attachStyler(themeService, {
|
||||
|
|
|
@ -485,7 +485,6 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
instantiationService.createInstance(ErrorRenderer),
|
||||
],
|
||||
{
|
||||
simpleKeyboardNavigation: true,
|
||||
identityProvider: instantiationService.createInstance(IdentityProvider),
|
||||
hideTwistiesOfChildlessElements: false,
|
||||
sorter: instantiationService.createInstance(TreeSorter, this),
|
||||
|
|
Loading…
Reference in a new issue