Не подтверждена Коммит a0ad4006 создал по автору Tyler James Leonhardt's avatar Tyler James Leonhardt Зафиксировано автором GitHub
Просмотр файлов

Move navigation keybindings to Commands (#211756)

* Move navigation keybindings to Commands

Progress...

* revert .vscode-test change

* handle ctrl correctly

* Fix another ctrlCmd mix up

* Fix screen cheese by removing more keydown handlers

* Change to `quickInput.blah`

* stash commit

* Refactor to use `secondary` keybindings instead

* comments

* add a description

* revert launch.json change
владелец 145fcd4f
......@@ -9,7 +9,7 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit
import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IQuickInputService, IQuickInputButton, IQuickPickItem, IQuickPick, IInputBox, IQuickNavigateConfiguration, IPickOptions, QuickPickInput, IInputOptions, IQuickWidget } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem, IQuickPick, IInputBox, IQuickNavigateConfiguration, IPickOptions, QuickPickInput, IInputOptions, IQuickWidget } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
......@@ -18,7 +18,6 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { QuickInputController, IQuickInputControllerHost } from 'vs/platform/quickinput/browser/quickInputController';
import { QuickInputService } from 'vs/platform/quickinput/browser/quickInputService';
import { createSingleCallFunction } from 'vs/base/common/functional';
import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
class EditorScopedQuickInputService extends QuickInputService {
......@@ -100,10 +99,9 @@ export class StandaloneQuickInputService implements IQuickInputService {
return quickInputService;
}
get quickAccess(): IQuickAccessController { return this.activeService.quickAccess; }
get backButton(): IQuickInputButton { return this.activeService.backButton; }
get currentQuickInput() { return this.activeService.currentQuickInput; }
get quickAccess() { return this.activeService.quickAccess; }
get backButton() { return this.activeService.backButton; }
get onShow() { return this.activeService.onShow; }
get onHide() { return this.activeService.onHide; }
......
......@@ -20,18 +20,27 @@ import { Codicon } from 'vs/base/common/codicons';
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { isIOS, isMacintosh } from 'vs/base/common/platform';
import { isIOS } from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { ThemeIcon } from 'vs/base/common/themables';
import 'vs/css!./media/quickInput';
import { localize } from 'vs/nls';
import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, IQuickWidget, ItemActivation, NO_KEY_MODS, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput';
import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, IQuickWidget, ItemActivation, NO_KEY_MODS, QuickInputHideReason, QuickInputType } from 'vs/platform/quickinput/common/quickInput';
import { QuickInputBox } from './quickInputBox';
import { quickInputButtonToAction, renderQuickInputDescription } from './quickInputUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IHoverService, WorkbenchHoverDelegate } from 'vs/platform/hover/browser/hover';
import { QuickInputListFocus, QuickInputTree } from 'vs/platform/quickinput/browser/quickInputTree';
import { QuickInputTree } from 'vs/platform/quickinput/browser/quickInputTree';
import { QuickPickFocus } from '../common/quickInput';
import type { IHoverOptions } from 'vs/base/browser/ui/hover/hover';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
export const inQuickInputContextKeyValue = 'inQuickInput';
export const InQuickInputContextKey = new RawContextKey<boolean>(inQuickInputContextKeyValue, false, localize('inQuickInput', "Whether keyboard focus is inside the quick input control"));
export const inQuickInputContext = ContextKeyExpr.has(inQuickInputContextKeyValue);
export const quickInputTypeContextKeyValue = 'quickInputType';
export const QuickInputTypeContextKey = new RawContextKey<QuickInputType>(quickInputTypeContextKeyValue, undefined, localize('quickInputType', "The type of the currently visible quick input"));
export interface IQuickInputOptions {
idPrefix: string;
......@@ -130,7 +139,7 @@ export type Visibilities = {
progressBar?: boolean;
};
class QuickInput extends Disposable implements IQuickInput {
abstract class QuickInput extends Disposable implements IQuickInput {
protected static readonly noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel");
private _title: string | undefined;
......@@ -162,6 +171,8 @@ class QuickInput extends Disposable implements IQuickInput {
private busyDelay: TimeoutTimer | undefined;
abstract type: QuickInputType;
constructor(
protected ui: QuickInputUI
) {
......@@ -536,6 +547,8 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
private _hideCheckAll: boolean | undefined;
private _focusEventBufferer = new EventBufferer();
readonly type = QuickInputType.QuickPick;
get quickNavigate() {
return this._quickNavigate;
}
......@@ -818,7 +831,7 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
private trySelectFirst() {
if (!this.canSelectMany) {
this.ui.list.focus(QuickInputListFocus.First);
this.ui.list.focus(QuickPickFocus.First);
}
}
......@@ -831,46 +844,6 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
// Keybindings for the input box or list if there is no input box
this.visibleDisposables.add((this._hideInput ? this.ui.list : this.ui.inputBox).onKeyDown((event: KeyboardEvent | StandardKeyboardEvent) => {
switch (event.keyCode) {
case KeyCode.DownArrow:
// Don't support focusing next separator when quick navigate is enabled
// ref: https://github.com/microsoft/vscode/issues/210461
// TODO: Could we do this in a way that could play nice with quick navigate?
if (this.quickNavigate === undefined && (isMacintosh ? event.metaKey : event.altKey)) {
this.ui.list.focus(QuickInputListFocus.NextSeparator);
} else {
this.ui.list.focus(QuickInputListFocus.Next);
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
dom.EventHelper.stop(event, true);
break;
case KeyCode.UpArrow:
// Don't support focusing next separator when quick navigate is enabled
if (this.quickNavigate === undefined && (isMacintosh ? event.metaKey : event.altKey)) {
this.ui.list.focus(QuickInputListFocus.PreviousSeparator);
} else {
this.ui.list.focus(QuickInputListFocus.Previous);
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
dom.EventHelper.stop(event, true);
break;
case KeyCode.PageDown:
this.ui.list.focus(QuickInputListFocus.NextPage);
if (this.canSelectMany) {
this.ui.list.domFocus();
}
dom.EventHelper.stop(event, true);
break;
case KeyCode.PageUp:
this.ui.list.focus(QuickInputListFocus.PreviousPage);
if (this.canSelectMany) {
this.ui.list.domFocus();
}
dom.EventHelper.stop(event, true);
break;
case KeyCode.RightArrow:
if (!this._canAcceptInBackground) {
return; // needs to be enabled
......@@ -886,18 +859,6 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
this.handleAccept(true);
}
break;
case KeyCode.Home:
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
this.ui.list.focus(QuickInputListFocus.First);
dom.EventHelper.stop(event, true);
}
break;
case KeyCode.End:
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
this.ui.list.focus(QuickInputListFocus.Last);
dom.EventHelper.stop(event, true);
}
break;
}
}));
......@@ -1089,6 +1050,8 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
this.itemsUpdated = false;
this._focusEventBufferer.bufferEvents(() => {
this.ui.list.setElements(this.items);
// We want focus to exist in the list if there are items so that space can be used to toggle
this.ui.list.shouldLoop = !this.canSelectMany;
this.ui.list.filter(this.filterValue(this.ui.inputBox.value));
this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked();
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
......@@ -1098,11 +1061,11 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
this._itemActivation = ItemActivation.FIRST; // only valid once, then unset
break;
case ItemActivation.SECOND:
this.ui.list.focus(QuickInputListFocus.Second);
this.ui.list.focus(QuickPickFocus.Second);
this._itemActivation = ItemActivation.FIRST; // only valid once, then unset
break;
case ItemActivation.LAST:
this.ui.list.focus(QuickInputListFocus.Last);
this.ui.list.focus(QuickPickFocus.Last);
this._itemActivation = ItemActivation.FIRST; // only valid once, then unset
break;
default:
......@@ -1147,7 +1110,7 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
// Focus the first element in the list if multiselect is enabled
if (this.canSelectMany) {
this.ui.list.focus(QuickInputListFocus.First);
this.ui.list.focus(QuickPickFocus.First);
}
}
......@@ -1156,6 +1119,14 @@ export class QuickPick<T extends IQuickPickItem> extends QuickInput implements I
this.scrollTop = scrollTopBefore;
}
}
focus(focus: QuickPickFocus): void {
this.ui.list.focus(focus);
// To allow things like space to check/uncheck items
if (this.canSelectMany) {
this.ui.list.domFocus();
}
}
}
export class InputBox extends QuickInput implements IInputBox {
......@@ -1168,6 +1139,8 @@ export class InputBox extends QuickInput implements IInputBox {
private readonly onDidValueChangeEmitter = this._register(new Emitter<string>());
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
readonly type = QuickInputType.InputBox;
get value() {
return this._value;
}
......@@ -1274,6 +1247,8 @@ export class InputBox extends QuickInput implements IInputBox {
}
export class QuickWidget extends QuickInput implements IQuickWidget {
readonly type = QuickInputType.QuickWidget;
protected override update() {
if (!this.visible) {
return;
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { isMacintosh } from 'vs/base/common/platform';
import { PartialExcept } from 'vs/base/common/types';
import { localize } from 'vs/nls';
import { ICommandHandler } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ICommandAndKeybindingRule, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { inQuickInputContext, quickInputTypeContextKeyValue } from 'vs/platform/quickinput/browser/quickInput';
import { IQuickInputService, IQuickPick, QuickInputType, QuickPickFocus } from 'vs/platform/quickinput/common/quickInput';
const defaultCommandAndKeybindingRule = {
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.and(ContextKeyExpr.equals(quickInputTypeContextKeyValue, QuickInputType.QuickPick), inQuickInputContext),
metadata: { description: localize('quickPick', "Used while in the context of the quick pick. If you change one keybinding for this command, you should change all of the other keybindings (modifier variants) of this command as well.") }
};
function registerQuickPickCommandAndKeybindingRule(rule: PartialExcept<ICommandAndKeybindingRule, 'id' | 'handler'>, options: { withAltMod?: boolean; withCtrlMod?: boolean; withCmdMod?: boolean } = {}) {
KeybindingsRegistry.registerCommandAndKeybindingRule({
...defaultCommandAndKeybindingRule,
...rule,
secondary: getSecondary(rule.primary!, rule.secondary ?? [], options)
});
}
// This function will generate all the combinations of keybindings for the given primary keybinding
function getSecondary(primary: number, secondary: number[], options: { withAltMod?: boolean; withCtrlMod?: boolean; withCmdMod?: boolean } = {}): number[] {
if (options.withAltMod) {
secondary.push(KeyMod.Alt + primary);
}
const ctrlKeyMod = isMacintosh ? KeyMod.WinCtrl : KeyMod.CtrlCmd;
if (options.withCtrlMod) {
secondary.push(ctrlKeyMod + primary);
if (options.withAltMod) {
secondary.push(KeyMod.Alt + ctrlKeyMod + primary);
}
}
if (options.withCmdMod && isMacintosh) {
secondary.push(KeyMod.CtrlCmd + primary);
if (options.withCtrlMod) {
secondary.push(KeyMod.CtrlCmd + KeyMod.WinCtrl + primary);
}
if (options.withAltMod) {
secondary.push(KeyMod.CtrlCmd + KeyMod.Alt + primary);
if (options.withCtrlMod) {
secondary.push(KeyMod.CtrlCmd + KeyMod.Alt + KeyMod.WinCtrl + primary);
}
}
}
return secondary;
}
//#region Navigation
function focusHandler(focus: QuickPickFocus, focusOnQuickNatigate?: QuickPickFocus): ICommandHandler {
return accessor => {
// Assuming this is a quick pick due to above when clause
const currentQuickPick = accessor.get(IQuickInputService).currentQuickInput as IQuickPick<any> | undefined;
if (!currentQuickPick) {
return;
}
if (focusOnQuickNatigate && currentQuickPick.quickNavigate) {
return currentQuickPick.focus(focusOnQuickNatigate);
}
return currentQuickPick.focus(focus);
};
}
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.pageNext', primary: KeyCode.PageDown, handler: focusHandler(QuickPickFocus.NextPage) },
{ withAltMod: true, withCtrlMod: true, withCmdMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.pagePrevious', primary: KeyCode.PageUp, handler: focusHandler(QuickPickFocus.PreviousPage) },
{ withAltMod: true, withCtrlMod: true, withCmdMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.first', primary: KeyCode.Home, handler: focusHandler(QuickPickFocus.First) },
{ withAltMod: true, withCtrlMod: true, withCmdMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.last', primary: KeyCode.End, handler: focusHandler(QuickPickFocus.Last) },
{ withAltMod: true, withCtrlMod: true, withCmdMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.next', primary: KeyCode.DownArrow, handler: focusHandler(QuickPickFocus.Next) },
{ withCtrlMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{ id: 'quickInput.previous', primary: KeyCode.UpArrow, handler: focusHandler(QuickPickFocus.Previous) },
{ withCtrlMod: true }
);
// The next & previous separator commands are interesting because if we are in quick access mode, we are already holding a modifier key down.
// In this case, we want that modifier key+up/down to navigate to the next/previous item, not the next/previous separator.
// To handle this, we have a separate command for navigating to the next/previous separator when we are not in quick access mode.
// If, however, we are in quick access mode, and you hold down an additional modifier key, we will navigate to the next/previous separator.
const nextSeparatorFallbackDesc = localize('quickInput.nextSeparatorWithQuickAccessFallback', "If we're in quick access mode, this will navigate to the next item. If we are not in quick access mode, this will navigate to the next separator.");
const prevSeparatorFallbackDesc = localize('quickInput.previousSeparatorWithQuickAccessFallback', "If we're in quick access mode, this will navigate to the previous item. If we are not in quick access mode, this will navigate to the previous separator.");
if (isMacintosh) {
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.nextSeparatorWithQuickAccessFallback',
primary: KeyMod.CtrlCmd + KeyCode.DownArrow,
handler: focusHandler(QuickPickFocus.NextSeparator, QuickPickFocus.Next),
metadata: { description: nextSeparatorFallbackDesc }
},
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.nextSeparator',
primary: KeyMod.CtrlCmd + KeyMod.Alt + KeyCode.DownArrow,
// Since macOS has the cmd key as the primary modifier, we need to add this additional
// keybinding to capture cmd+ctrl+upArrow
secondary: [KeyMod.CtrlCmd + KeyMod.WinCtrl + KeyCode.DownArrow],
handler: focusHandler(QuickPickFocus.NextSeparator)
},
{ withCtrlMod: true }
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.previousSeparatorWithQuickAccessFallback',
primary: KeyMod.CtrlCmd + KeyCode.UpArrow,
handler: focusHandler(QuickPickFocus.PreviousSeparator, QuickPickFocus.Previous),
metadata: { description: prevSeparatorFallbackDesc }
},
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.previousSeparator',
primary: KeyMod.CtrlCmd + KeyMod.Alt + KeyCode.UpArrow,
// Since macOS has the cmd key as the primary modifier, we need to add this additional
// keybinding to capture cmd+ctrl+upArrow
secondary: [KeyMod.CtrlCmd + KeyMod.WinCtrl + KeyCode.UpArrow],
handler: focusHandler(QuickPickFocus.PreviousSeparator)
},
{ withCtrlMod: true }
);
} else {
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.nextSeparatorWithQuickAccessFallback',
primary: KeyMod.Alt + KeyCode.DownArrow,
handler: focusHandler(QuickPickFocus.NextSeparator, QuickPickFocus.Next),
metadata: { description: nextSeparatorFallbackDesc }
},
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.nextSeparator',
primary: KeyMod.CtrlCmd + KeyMod.Alt + KeyCode.DownArrow,
handler: focusHandler(QuickPickFocus.NextSeparator)
},
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.previousSeparatorWithQuickAccessFallback',
primary: KeyMod.Alt + KeyCode.UpArrow,
handler: focusHandler(QuickPickFocus.PreviousSeparator, QuickPickFocus.Previous),
metadata: { description: prevSeparatorFallbackDesc }
},
);
registerQuickPickCommandAndKeybindingRule(
{
id: 'quickInput.previousSeparator',
primary: KeyMod.CtrlCmd + KeyMod.Alt + KeyCode.UpArrow,
handler: focusHandler(QuickPickFocus.PreviousSeparator)
},
);
}
//#endregion
......@@ -18,11 +18,14 @@ import { isString } from 'vs/base/common/types';
import { localize } from 'vs/nls';
import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickInputHideReason, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
import { QuickInputBox } from 'vs/platform/quickinput/browser/quickInputBox';
import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPick, backButton, InputBox, Visibilities, QuickWidget } from 'vs/platform/quickinput/browser/quickInput';
import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPick, backButton, InputBox, Visibilities, QuickWidget, InQuickInputContextKey, QuickInputTypeContextKey } from 'vs/platform/quickinput/browser/quickInput';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { mainWindow } from 'vs/base/browser/window';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { QuickInputListFocus, QuickInputTree } from 'vs/platform/quickinput/browser/quickInputTree';
import { QuickInputTree } from 'vs/platform/quickinput/browser/quickInputTree';
import { QuickPickFocus } from '../common/quickInput';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import 'vs/platform/quickinput/browser/quickInputActions';
const $ = dom.$;
......@@ -40,6 +43,7 @@ export class QuickInputController extends Disposable {
private keyMods: Writeable<IKeyMods> = { ctrlCmd: false, alt: false };
private controller: IQuickInput | null = null;
get currentQuickInput() { return this.controller ?? undefined; }
private _container: HTMLElement;
get container() { return this._container; }
......@@ -54,10 +58,14 @@ export class QuickInputController extends Disposable {
private previousFocusElement?: HTMLElement;
private readonly inQuickInputContext = InQuickInputContextKey.bindTo(this.contextKeyService);
private readonly quickInputTypeContext = QuickInputTypeContextKey.bindTo(this.contextKeyService);
constructor(
private options: IQuickInputOptions,
@ILayoutService private readonly layoutService: ILayoutService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextKeyService private readonly contextKeyService: IContextKeyService
) {
super();
this.idPrefix = options.idPrefix;
......@@ -208,12 +216,14 @@ export class QuickInputController extends Disposable {
if (dom.isAncestor(e.relatedTarget as HTMLElement, container)) {
return;
}
this.inQuickInputContext.set(true);
this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined;
}, true));
this._register(focusTracker.onDidBlur(() => {
if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) {
this.hide(QuickInputHideReason.Blur);
}
this.inQuickInputContext.set(false);
this.previousFocusElement = undefined;
}));
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => {
......@@ -569,6 +579,7 @@ export class QuickInputController extends Disposable {
ui.container.style.display = '';
this.updateLayout();
ui.inputBox.setFocus();
this.quickInputTypeContext.set(controller.type);
}
isVisible(): boolean {
......@@ -659,7 +670,7 @@ export class QuickInputController extends Disposable {
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) {
if (this.isVisible() && this.getUI().list.isDisplayed()) {
this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous);
this.getUI().list.focus(next ? QuickPickFocus.Next : QuickPickFocus.Previous);
if (quickNavigate && this.controller instanceof QuickPick) {
this.controller.quickNavigate = quickNavigate;
}
......
......@@ -42,6 +42,7 @@ export class QuickInputService extends Themable implements IQuickInputService {
}
private get hasController() { return !!this._controller; }
get currentQuickInput() { return this.controller.currentQuickInput; }
private _quickAccess: IQuickAccessController | undefined;
get quickAccess(): IQuickAccessController {
......
......@@ -37,21 +37,10 @@ import { RenderIndentGuides } from 'vs/base/browser/ui/tree/abstractTree';
import { ThrottledDelayer } from 'vs/base/common/async';
import { isCancellationError } from 'vs/base/common/errors';
import type { IHoverWidget, IUpdatableHoverTooltipMarkdownString } from 'vs/base/browser/ui/hover/hover';
import { QuickPickFocus } from '../common/quickInput';
const $ = dom.$;
export enum QuickInputListFocus {
First = 1,
Second,
Last,
Next,
Previous,
NextPage,
PreviousPage,
NextSeparator,
PreviousSeparator
}
interface IQuickInputItemLazyParts {
readonly saneLabel: string;
readonly saneSortLabel: string;
......@@ -836,6 +825,14 @@ export class QuickInputTree extends Disposable {
this._sortByLabel = value;
}
private _shouldLoop = true;
get shouldLoop() {
return this._shouldLoop;
}
set shouldLoop(value: boolean) {
this._shouldLoop = value;
}
//#endregion
//#region register listeners
......@@ -864,22 +861,6 @@ export class QuickInputTree extends Disposable {
this._tree.setFocus(this._itemElements);
}
break;
// When we hit the top of the tree, we fire the onLeave event.
case KeyCode.UpArrow: {
const focus1 = this._tree.getFocus();
if (focus1.length === 1 && focus1[0] === this._itemElements[0]) {
this._onLeave.fire();
}
break;
}
// When we hit the bottom of the tree, we fire the onLeave event.
case KeyCode.DownArrow: {
const focus2 = this._tree.getFocus();
if (focus2.length === 1 && focus2[0] === this._itemElements[this._itemElements.length - 1]) {
this._onLeave.fire();
}
break;
}
}
this._onKeyDown.fire(event);
......@@ -1202,39 +1183,46 @@ export class QuickInputTree extends Disposable {
}
}
focus(what: QuickInputListFocus): void {
focus(what: QuickPickFocus): void {
if (!this._itemElements.length) {
return;
}
if (what === QuickInputListFocus.Second && this._itemElements.length < 2) {
what = QuickInputListFocus.First;
if (what === QuickPickFocus.Second && this._itemElements.length < 2) {
what = QuickPickFocus.First;
}
switch (what) {
case QuickInputListFocus.First:
case QuickPickFocus.First:
this._tree.scrollTop = 0;
this._tree.focusFirst(undefined, (e) => e.element instanceof QuickPickItemElement);
break;
case QuickInputListFocus.Second:
case QuickPickFocus.Second:
this._tree.scrollTop = 0;
this._tree.setFocus([this._itemElements[1]]);
break;
case QuickInputListFocus.Last:
case QuickPickFocus.Last:
this._tree.scrollTop = this._tree.scrollHeight;
this._tree.setFocus([this._itemElements[this._itemElements.length - 1]]);
break;
case QuickInputListFocus.Next:
this._tree.focusNext(undefined, true, undefined, (e) => {
case QuickPickFocus.Next: {
const prevFocus = this._tree.getFocus();
this._tree.focusNext(undefined, this._shouldLoop, undefined, (e) => {
if (!(e.element instanceof QuickPickItemElement)) {
return false;
}
this._tree.reveal(e.element);
return true;
});
const currentFocus = this._tree.getFocus();
if (prevFocus.length && prevFocus[0] === currentFocus[0] && prevFocus[0] === this._itemElements[this._itemElements.length - 1]) {
this._onLeave.fire();
}
break;
case QuickInputListFocus.Previous:
this._tree.focusPrevious(undefined, true, undefined, (e) => {
}
case QuickPickFocus.Previous: {
const prevFocus = this._tree.getFocus();
this._tree.focusPrevious(undefined, this._shouldLoop, undefined, (e) => {
if (!(e.element instanceof QuickPickItemElement)) {
return false;
}
......@@ -1247,8 +1235,13 @@ export class QuickInputTree extends Disposable {
}
return true;
});
const currentFocus = this._tree.getFocus();
if (prevFocus.length && prevFocus[0] === currentFocus[0] && prevFocus[0] === this._itemElements[0]) {
this._onLeave.fire();
}
break;
case QuickInputListFocus.NextPage:
}
case QuickPickFocus.NextPage:
this._tree.focusNextPage(undefined, (e) => {
if (!(e.element instanceof QuickPickItemElement)) {
return false;
......@@ -1257,7 +1250,7 @@ export class QuickInputTree extends Disposable {
return true;
});
break;
case QuickInputListFocus.PreviousPage:
case QuickPickFocus.PreviousPage:
this._tree.focusPreviousPage(undefined, (e) => {
if (!(e.element instanceof QuickPickItemElement)) {
return false;
......@@ -1271,7 +1264,7 @@ export class QuickInputTree extends Disposable {
return true;
});
break;
case QuickInputListFocus.NextSeparator: {
case QuickPickFocus.NextSeparator: {
let foundSeparatorAsItem = false;
const before = this._tree.getFocus()[0];
this._tree.focusNext(undefined, true, undefined, (e) => {
......@@ -1316,7 +1309,7 @@ export class QuickInputTree extends Disposable {
}
break;
}
case QuickInputListFocus.PreviousSeparator: {
case QuickPickFocus.PreviousSeparator: {
let focusElement: IQuickPickElement | undefined;
// If we are already sitting on an inline separator, then we
// have already found the _current_ separator and need to
......
......@@ -204,11 +204,25 @@ export interface IQuickInputHideEvent {
reason: QuickInputHideReason;
}
/**
* A collection of the different types of QuickInput
*/
export const enum QuickInputType {
QuickPick = 'quickPick',
InputBox = 'inputBox',
QuickWidget = 'quickWidget'
}
/**
* Represents a quick input control that allows users to make selections or provide input quickly.
*/
export interface IQuickInput extends IDisposable {
/**
* The type of the quick input.
*/
readonly type: QuickInputType;
/**
* An event that is fired when the quick input is hidden.
*/
......@@ -304,6 +318,12 @@ export interface IQuickInput extends IDisposable {
}
export interface IQuickWidget extends IQuickInput {
/**
* The type of the quick input.
*/
readonly type: QuickInputType.QuickWidget;
/**
* Should be an HTMLElement (TODO: move this entire file into browser)
* @override
......@@ -353,11 +373,58 @@ export enum ItemActivation {
LAST
}
/**
* Represents the focus options for a quick pick.
*/
export enum QuickPickFocus {
/**
* Focus the first item in the list.
*/
First = 1,
/**
* Focus the second item in the list.
*/
Second,
/**
* Focus the last item in the list.
*/
Last,
/**
* Focus the next item in the list.
*/
Next,
/**
* Focus the previous item in the list.
*/
Previous,
/**
* Focus the next page in the list.
*/
NextPage,
/**
* Focus the previous page in the list.
*/
PreviousPage,
/**
* Focus the first item under the next separator.
*/
NextSeparator,
/**
* Focus the first item under the current separator.
*/
PreviousSeparator
}
/**
* Represents a quick pick control that allows the user to select an item from a list of options.
*/
export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
/**
* The type of the quick input.
*/
readonly type: QuickInputType.QuickPick;
/**
* The current value of the quick pick input.
*/
......@@ -556,6 +623,12 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
* The toggle buttons to be added to the input box.
*/
toggles: IQuickInputToggle[] | undefined;
/**
* Focus a particular item in the list. Used internally for keyboard navigation.
* @param focus The focus behavior.
*/
focus(focus: QuickPickFocus): void;
}
/**
......@@ -574,6 +647,11 @@ export interface IQuickInputToggle {
*/
export interface IInputBox extends IQuickInput {
/**
* The type of the quick input.
*/
readonly type: QuickInputType.InputBox;
/**
* Value shown in the input box.
*/
......@@ -814,4 +892,9 @@ export interface IQuickInputService {
* Cancels quick input and closes it.
*/
cancel(): Promise<void>;
/**
* The current quick pick that is visible. Undefined if none is open.
*/
currentQuickInput: IQuickInput | undefined;
}
......@@ -2055,6 +2055,7 @@ export class TestQuickInputService implements IQuickInputService {
readonly onShow = Event.None;
readonly onHide = Event.None;
readonly currentQuickInput = undefined;
readonly quickAccess = undefined!;
backButton!: IQuickInputButton;
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать