提交 2058bac9 编写于 作者: J Joao Moreno

ITypeLabelProvider

fixes #64233
上级 34f0b187
......@@ -52,4 +52,8 @@ export interface IListContextMenuEvent<T> {
export interface IIdentityProvider<T> {
getId(element: T): { toString(): string; };
}
export interface ITypeLabelProvider<T> {
getTypeLabel(element: T): { toString(): string; };
}
\ No newline at end of file
......@@ -14,9 +14,9 @@ import * as platform from 'vs/base/common/platform';
import { Gesture } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event';
import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent, debounceEvent, reduceEvent } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider } from './list';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, ITypeLabelProvider } from './list';
import { ListView, IListViewOptions } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
......@@ -24,6 +24,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISpliceable } from 'vs/base/common/sequence';
import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
import { clamp } from 'vs/base/common/numbers';
import { matchesPrefix } from 'vs/base/common/filters';
interface ITraitChangeEvent {
indexes: number[];
......@@ -322,6 +323,69 @@ class KeyboardController<T> implements IDisposable {
}
}
enum TypeLabelControllerState {
Idle,
Typing
}
class TypeLabelController<T> implements IDisposable {
private state: TypeLabelControllerState = TypeLabelControllerState.Idle;
private disposables: IDisposable[] = [];
constructor(
private list: List<T>,
private view: ListView<T>,
private typeLabelProvider: ITypeLabelProvider<T>
) {
const onChar = chain(domEvent(view.domNode, 'keydown'))
.map(event => new StandardKeyboardEvent(event))
.filter(event => {
if (event.ctrlKey || event.metaKey || event.altKey) {
return false;
}
return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z)
|| (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9)
|| (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE);
})
.map(event => event.browserEvent.key)
.event;
const onClear = debounceEvent<string, null>(onChar, () => null, 800);
const onInput = reduceEvent<string | null, string | null>(anyEvent(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i));
onInput(this.onInput, this, this.disposables);
}
private onInput(word: string | null): void {
if (!word) {
this.state = TypeLabelControllerState.Idle;
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;
for (let i = 0; i < this.list.length; i++) {
const index = (start + i + delta) % this.list.length;
const label = this.typeLabelProvider.getTypeLabel(this.view.element(index));
if (matchesPrefix(word, label.toString())) {
this.list.setFocus([index]);
this.list.reveal(index);
return;
}
}
}
dispose() {
this.disposables = dispose(this.disposables);
}
}
class DOMFocusController<T> implements IDisposable {
private disposables: IDisposable[] = [];
......@@ -666,6 +730,7 @@ export class DefaultStyleController implements IStyleController {
export interface IListOptions<T> extends IListViewOptions, IListStyles {
identityProvider?: IIdentityProvider<T>;
typeLabelProvider?: ITypeLabelProvider<T>;
ariaLabel?: string;
mouseSupport?: boolean;
selectOnMouseDown?: boolean;
......@@ -1002,6 +1067,11 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.disposables.push(controller);
}
if (options.typeLabelProvider) {
const controller = new TypeLabelController(this, this.view, options.typeLabelProvider);
this.disposables.push(controller);
}
if (typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true) {
this.disposables.push(new MouseController(this, this.view, options));
}
......
......@@ -34,6 +34,11 @@ function asListOptions<T, TFilterData>(options?: IAbstractTreeOptions<T, TFilter
getAriaLabel(e) {
return options.accessibilityProvider!.getAriaLabel(e.element);
}
},
typeLabelProvider: options.typeLabelProvider && {
getTypeLabel(e) {
return options.typeLabelProvider!.getTypeLabel(e.element);
}
}
};
}
......
......@@ -147,8 +147,13 @@ function asObjectTreeOptions<T, TFilterData>(options?: IAsyncDataTreeOptions<T,
}
},
filter: options.filter && {
filter(element, parentVisibility) {
return options.filter!.filter(element.element!, parentVisibility);
filter(e, parentVisibility) {
return options.filter!.filter(e.element!, parentVisibility);
}
},
typeLabelProvider: options.typeLabelProvider && {
getTypeLabel(e) {
return options.typeLabelProvider!.getTypeLabel(e.element!);
}
}
};
......
......@@ -410,6 +410,15 @@ export function anyEvent<T>(...events: Event<T>[]): Event<T> {
return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
}
export function reduceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O): Event<O> {
let output: O | undefined = undefined;
return mapEvent<I, O>(event, e => {
output = merger(output, e);
return output;
});
}
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<T>;
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<O>;
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event<O> {
......
......@@ -13,7 +13,7 @@ import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/b
import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener, removeClass } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent, ITypeLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/parts/scm/common/scm';
import { FileLabel } from 'vs/workbench/browser/labels';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
......@@ -557,7 +557,7 @@ class ProviderListDelegate implements IListVirtualDelegate<ISCMResourceGroup | I
}
}
const scmResourceIdentityProvider = {
const scmResourceIdentityProvider = new class implements IIdentityProvider<ISCMResourceGroup | ISCMResource> {
getId(r: ISCMResourceGroup | ISCMResource): string {
if (isSCMResource(r)) {
const group = r.resourceGroup;
......@@ -570,6 +570,16 @@ const scmResourceIdentityProvider = {
}
};
const scmTypeLabelProvider = new class implements ITypeLabelProvider<ISCMResourceGroup | ISCMResource> {
getTypeLabel(e: ISCMResourceGroup | ISCMResource) {
if (isSCMResource(e)) {
return basename(e.sourceUri.fsPath);
} else {
return e.label;
}
}
};
function isGroupVisible(group: ISCMResourceGroup) {
return group.elements.length > 0 || !group.hideWhenEmpty;
}
......@@ -869,7 +879,8 @@ export class RepositoryPanel extends ViewletPanel {
];
this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, {
identityProvider: scmResourceIdentityProvider
identityProvider: scmResourceIdentityProvider,
typeLabelProvider: scmTypeLabelProvider
}) as WorkbenchList<ISCMResourceGroup | ISCMResource>;
chain(this.list.onOpen)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册