Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SimpleFindWidget Add next,previous,history: Fixes #29708 #29661 #32113

Merged
merged 7 commits into from
Aug 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions src/vs/editor/contrib/find/browser/simpleFindWidget.contribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Command } from 'vs/editor/common/editorCommonExtensions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ISimpleFindWidgetService, KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE } from 'vs/editor/contrib/find/browser/simpleFindWidgetService';
import { SimpleFindWidget, KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED } from 'vs/editor/contrib/find/browser/simpleFindWidget';

export const SIMPLE_FIND_IDS = {
ShowWidgetSimpleFindCommand: 'simpleFind.show',
HideWidgetSimpleFindCommand: 'simpleFind.hide',
HistoryNextSimpleFindCommand: 'simpleFind.nextHistory',
HistoryPreviousSimpleFindCommand: 'simpleFind.previousHistory',
FindNextSimpleFindCommand: 'simpleFind.nextMatch',
FindPreviousSimpleFindCommand: 'simpleFind.previousMatch'
};


// This probably should be converted to actions
export abstract class SimpleFindCommand extends Command {

public abstract runCommand(accessor: ServicesAccessor, args: any): void;

protected getSimpleFindWidget(accessor: ServicesAccessor): SimpleFindWidget {
const activeSimpleFindWidget = accessor.get(ISimpleFindWidgetService).getActiveSimpleFindWidget() as SimpleFindWidget;
if (activeSimpleFindWidget) {
return activeSimpleFindWidget;
}
return activeSimpleFindWidget;
}

protected getFocusedSimpleFindWidgetInput(accessor: ServicesAccessor): SimpleFindWidget {
const activeSimpleFindWidgetInput = accessor.get(ISimpleFindWidgetService).getFocusedSimpleFindWidgetInput() as SimpleFindWidget;
if (activeSimpleFindWidgetInput) {
return activeSimpleFindWidgetInput;
}
return null;
}
}

// These commands may be more appropriate as workbench ids , left them as editor for now

export class ShowWidgetSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.ShowWidgetSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
accessor.get(ISimpleFindWidgetService).show();
}
}
const showWidgetSimpleFindCommand = new ShowWidgetSimpleFindCommand({
id: SIMPLE_FIND_IDS.ShowWidgetSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(showWidgetSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

export class HideWidgetSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.HideWidgetSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
accessor.get(ISimpleFindWidgetService).hide();
}
}
const hideWidgetSimpleFindCommand = new HideWidgetSimpleFindCommand({
id: SIMPLE_FIND_IDS.HideWidgetSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE,
kbOpts: {
primary: KeyCode.Escape,
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(hideWidgetSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

class HistoryNextSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.HistoryNextSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
const simpleFindWidget = this.getFocusedSimpleFindWidgetInput(accessor);

if (simpleFindWidget) {
simpleFindWidget.showNextFindTerm();
}
}
}
const historyNextSimpleFindCommand = new HistoryNextSimpleFindCommand({
id: SIMPLE_FIND_IDS.HistoryNextSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED,
kbOpts: {
primary: KeyMod.Alt | KeyCode.DownArrow,
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(historyNextSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

class HistoryPreviousSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.HistoryPreviousSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
const simpleFindWidget = this.getFocusedSimpleFindWidgetInput(accessor);

if (simpleFindWidget) {
simpleFindWidget.showPreviousFindTerm();
}
}
}
const historyPreviousSimpleFindCommand = new HistoryPreviousSimpleFindCommand({
id: SIMPLE_FIND_IDS.HistoryPreviousSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED,
kbOpts: {
primary: KeyMod.Alt | KeyCode.UpArrow,
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(historyPreviousSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

export class FindNextSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.FindNextSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
accessor.get(ISimpleFindWidgetService).find(false);
}
}
const findNextSimpleFindCommand = new FindNextSimpleFindCommand({
id: SIMPLE_FIND_IDS.FindNextSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE,
kbOpts: {
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(findNextSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

export class FindPreviousSimpleFindCommand extends SimpleFindCommand {
public static ID = SIMPLE_FIND_IDS.FindPreviousSimpleFindCommand;

public runCommand(accessor: ServicesAccessor, args: any): void {
accessor.get(ISimpleFindWidgetService).find(true);
}
}
const findPreviousSimpleFindCommand = new FindPreviousSimpleFindCommand({
id: SIMPLE_FIND_IDS.FindPreviousSimpleFindCommand,
precondition: KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_ACTIVE,
kbOpts: {
primary: KeyMod.Shift | KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(findPreviousSimpleFindCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
76 changes: 70 additions & 6 deletions src/vs/editor/contrib/find/browser/simpleFindWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { HistoryNavigator } from 'vs/base/common/history';
import { SimpleButton } from './findWidget';
import { Delayer } from 'vs/base/common/async';
import { ISimpleFindWidgetService } from 'vs/editor/contrib/find/browser/simpleFindWidgetService';

// We may need to add a ContextKey to track a focused widget (currently commands are executed within the action context)
// ContextKey to track widget with focused input
export const KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey<boolean>('simpleFindWidgetInputFocused', undefined);

const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
Expand All @@ -21,23 +29,37 @@ const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next mat
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");

export abstract class SimpleFindWidget extends Widget {

protected _findInput: FindInput;
protected _domNode: HTMLElement;
protected _isVisible: boolean;
protected _focusTracker: dom.IFocusTracker;
protected _findInputFocusTracker: dom.IFocusTracker;
protected _findInputFocused: IContextKey<boolean>;
protected _findHistory: HistoryNavigator<string>;
protected _updateHistoryDelayer: Delayer<void>;

constructor(
@IContextViewService private _contextViewService: IContextViewService,
private animate: boolean = true
@IContextKeyService private _contextKeyService: IContextKeyService,
@ISimpleFindWidgetService private _simpleFindWidgetService: ISimpleFindWidgetService,
private _animate: boolean = true
) {
super();
this._findInput = this._register(new FindInput(null, this._contextViewService, {
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
}));

// Find History with update delayer
this._findHistory = new HistoryNavigator<string>();
this._updateHistoryDelayer = new Delayer<void>(500);

this._findInputFocused = KEYBINDING_CONTEXT_SIMPLE_FIND_WIDGET_INPUT_FOCUSED.bindTo(this._contextKeyService);

this.oninput(this._findInput.domNode, (e) => {
this.onInputChanged();
this._delayedUpdateHistory();
});

this._register(this._findInput.onKeyDown((e) => {
Expand Down Expand Up @@ -100,20 +122,44 @@ export abstract class SimpleFindWidget extends Widget {
this._register(this._focusTracker.addFocusListener(this.onFocusTrackerFocus.bind(this)));
this._register(this._focusTracker.addBlurListener(this.onFocusTrackerBlur.bind(this)));

this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode));
this._register(this._findInputFocusTracker.addFocusListener(this._onFindInputFocusTrackerFocus.bind(this)));
this._register(this._findInputFocusTracker.addBlurListener(this._onFindInputFocusTrackerBlur.bind(this)));

this._register(dom.addDisposableListener(this._domNode, 'click', (event) => {
event.stopPropagation();
}));
}

public abstract find(previous: boolean);
protected abstract onInputChanged();
protected abstract find(previous: boolean);
protected abstract onFocusTrackerFocus();
protected abstract onFocusTrackerBlur();

private _onFindInputFocusTrackerFocus() {
this._findInputFocused.set(true);
this._simpleFindWidgetService.setFocusedSimpleFindWidgetInput(this);
}

private _onFindInputFocusTrackerBlur() {
this._findInputFocused.reset();
this._simpleFindWidgetService.setFocusedSimpleFindWidgetInput(null);
}

protected get inputValue() {
return this._findInput.getValue();
}

protected _delayedUpdateHistory() {
this._updateHistoryDelayer.trigger(this._updateHistory.bind(this));
}

protected _updateHistory() {
if (this.inputValue) {
this._findHistory.add(this._findInput.getValue());
}
}

public updateTheme(theme?: ITheme): void {
let inputStyles = {
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
Expand All @@ -134,12 +180,13 @@ export abstract class SimpleFindWidget extends Widget {
return this._domNode;
}

public reveal(initialInput?: string): void {
// Reveal is used to show widget and optionally focus on input
public reveal(focusInput: boolean, initialInput?: string): void {
if (initialInput) {
this._findInput.setValue(initialInput);
}

if (this._isVisible) {
if (this._isVisible && focusInput) {
this._findInput.select();
return;
}
Expand All @@ -149,12 +196,14 @@ export abstract class SimpleFindWidget extends Widget {
setTimeout(() => {
dom.addClass(this._domNode, 'visible');
this._domNode.setAttribute('aria-hidden', 'false');
if (!this.animate) {
if (!this._animate) {
dom.addClass(this._domNode, 'noanimation');
}
setTimeout(() => {
dom.removeClass(this._domNode, 'noanimation');
this._findInput.select();
if (focusInput) {
this._findInput.select();
}
}, 200);
}, 0);
}
Expand All @@ -167,6 +216,21 @@ export abstract class SimpleFindWidget extends Widget {
this._domNode.setAttribute('aria-hidden', 'true');
}
}

public showNextFindTerm() {
let next = this._findHistory.next();
if (next) {
this._findInput.setValue(next);
}
}

public showPreviousFindTerm() {
let previous = this._findHistory.previous();
if (previous) {
this._findInput.setValue(previous);
}
}

}

// theming
Expand Down
Loading