Merge pull request #9461 from Microsoft/tyriar/8333_terminal_context_menu

Add a context menu to the terminal
This commit is contained in:
Daniel Imms 2016-07-18 22:10:55 -07:00 committed by GitHub
commit 8ecf6f2a53
2 changed files with 86 additions and 28 deletions

View file

@ -7,10 +7,11 @@ import DOM = require('vs/base/browser/dom');
import lifecycle = require('vs/base/common/lifecycle');
import nls = require('vs/nls');
import os = require('os');
import platform = require('vs/base/common/platform');
import xterm = require('xterm');
import {Dimension} from 'vs/base/browser/builder';
import {IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding';
import {IContextMenuService} from 'vs/platform/contextview/browser/contextView';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding';
import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {ITerminalFont} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import {ITerminalProcess, ITerminalService} from 'vs/workbench/parts/terminal/electron-browser/terminal';
@ -31,7 +32,10 @@ export class TerminalInstance {
public constructor(
private terminalProcess: ITerminalProcess,
private parentDomElement: HTMLElement,
private contextMenuService: IContextMenuService,
private contextService: IWorkspaceContextService,
private instantiationService: IInstantiationService,
private keybindingService: IKeybindingService,
private terminalService: ITerminalService,
private messageService: IMessageService,
private terminalFocusContextKey: IKeybindingContextKey<boolean>,
@ -66,24 +70,6 @@ export class TerminalInstance {
this.onExitCallback(this);
}
});
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event) => {
// Drop selection and focus terminal on Linux to enable middle button paste when click
// occurs on the selection itself.
if (event.which === 2 && platform.isLinux) {
this.focus(true);
}
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => {
if (event.which !== 3) {
this.focus();
}
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => {
// Keep terminal open on escape
if (event.keyCode === 27) {
event.stopPropagation();
}
}));
this.xterm.open(this.terminalDomElement);

View file

@ -4,25 +4,29 @@
*--------------------------------------------------------------------------------------------*/
import DOM = require('vs/base/browser/dom');
import {getBaseThemeId} from 'vs/platform/theme/common/themes';
import lifecycle = require('vs/base/common/lifecycle');
import nls = require('vs/nls');
import platform = require('vs/base/common/platform');
import {Action, IAction} from 'vs/base/common/actions';
import {Builder, Dimension} from 'vs/base/browser/builder';
import {KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem} from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
import {getBaseThemeId} from 'vs/platform/theme/common/themes';
import {IActionItem} from 'vs/base/browser/ui/actionbar/actionbar';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding';
import {IContextMenuService} from 'vs/platform/contextview/browser/contextView';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding';
import {IMessageService} from 'vs/platform/message/common/message';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {ITerminalFont, TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import {ITerminalProcess, ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/electron-browser/terminal';
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem, CopyTerminalSelectionAction, TerminalPasteAction} from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
import {Panel} from 'vs/workbench/browser/panel';
import {TPromise} from 'vs/base/common/winjs.base';
import {ITerminalFont, TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import {Separator} from 'vs/base/browser/ui/actionbar/actionbar';
import {StandardMouseEvent} from 'vs/base/browser/mouseEvent';
import {TerminalInstance} from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
import {TPromise} from 'vs/base/common/winjs.base';
export class TerminalPanel extends Panel {
@ -30,6 +34,7 @@ export class TerminalPanel extends Panel {
private terminalInstances: TerminalInstance[] = [];
private actions: IAction[];
private contextMenuActions: IAction[];
private parentDomElement: HTMLElement;
private terminalContainer: HTMLElement;
private currentBaseThemeId: string;
@ -41,7 +46,9 @@ export class TerminalPanel extends Panel {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService private configurationService: IConfigurationService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IKeybindingService private keybindingService: IKeybindingService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@ITerminalService private terminalService: ITerminalService,
@IThemeService private themeService: IThemeService,
@ -67,15 +74,28 @@ export class TerminalPanel extends Panel {
this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL),
this.instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL)
];
this.actions.forEach(a => {
this.toDispose.push(a);
});
}
return this.actions;
}
private getContextMenuActions(): IAction[] {
if (!this.contextMenuActions) {
this.contextMenuActions = [
this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New terminal")),
new Separator(),
this.instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")),
this.instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste"))
];
this.contextMenuActions.forEach(a => {
this.toDispose.push(a);
});
}
return this.contextMenuActions;
}
public getActionItem(action: Action): IActionItem {
if (action.id === SwitchTerminalInstanceAction.ID) {
return this.instantiationService.createInstance(SwitchTerminalInstanceActionItem, action);
@ -98,11 +118,63 @@ export class TerminalPanel extends Panel {
this.parentDomElement.appendChild(this.fontStyleElement);
this.parentDomElement.appendChild(this.terminalContainer);
this.attachEventListeners();
this.configurationHelper = new TerminalConfigHelper(platform.platform, this.configurationService, parent);
return this.terminalService.createNew();
}
private attachEventListeners(): void {
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event: MouseEvent) => {
if (this.terminalInstances.length === 0) {
return;
}
if (event.which === 2 && platform.isLinux) {
// Drop selection and focus terminal on Linux to enable middle button paste when click
// occurs on the selection itself.
this.terminalInstances[this.terminalService.getActiveTerminalIndex()].focus(true);
} else if (event.which === 3) {
// Trigger the context menu on right click
let anchor: HTMLElement | { x: number, y: number } = this.parentDomElement;
if (event instanceof MouseEvent) {
const standardEvent = new StandardMouseEvent(event);
anchor = { x: standardEvent.posx, y: standardEvent.posy };
}
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(this.getContextMenuActions()),
getActionsContext: () => this.parentDomElement,
getKeyBinding: (action) => {
const opts = this.keybindingService.lookupKeybindings(action.id);
if (opts.length > 0) {
return opts[0]; // only take the first one
}
return null;
}
});
}
event.stopImmediatePropagation();
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => {
if (this.terminalInstances.length === 0) {
return;
}
if (event.which !== 3) {
this.terminalInstances[this.terminalService.getActiveTerminalIndex()].focus();
}
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => {
if (event.keyCode === 27) {
// Keep terminal open on escape
event.stopPropagation();
}
}));
}
public createNewTerminalInstance(terminalProcess: ITerminalProcess, terminalFocusContextKey: IKeybindingContextKey<boolean>): TPromise<void> {
return this.createTerminal(terminalProcess, terminalFocusContextKey).then(() => {
this.updateConfig();
@ -137,7 +209,7 @@ export class TerminalPanel extends Panel {
private createTerminal(terminalProcess: ITerminalProcess, terminalFocusContextKey: IKeybindingContextKey<boolean>): TPromise<TerminalInstance> {
return new TPromise<TerminalInstance>(resolve => {
var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextService, this.terminalService, this.messageService, terminalFocusContextKey, this.onTerminalInstanceExit.bind(this));
var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextMenuService, this.contextService, this.instantiationService, this.keybindingService, this.terminalService, this.messageService, terminalFocusContextKey, this.onTerminalInstanceExit.bind(this));
this.terminalInstances.push(terminalInstance);
this.setActiveTerminal(this.terminalInstances.length - 1);
this.toDispose.push(this.themeService.onDidThemeChange(this.updateTheme.bind(this)));