From 205b617cacae28ef71fc503e29cd4b9aefbacc12 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 30 Sep 2019 12:47:55 +0200 Subject: [PATCH] web - implement text input actions too --- .../browser/actions/textInputActions.ts | 100 ++++++++++++++++++ src/vs/workbench/electron-browser/window.ts | 33 +----- src/vs/workbench/workbench.common.main.ts | 1 + 3 files changed, 102 insertions(+), 32 deletions(-) create mode 100644 src/vs/workbench/browser/actions/textInputActions.ts diff --git a/src/vs/workbench/browser/actions/textInputActions.ts b/src/vs/workbench/browser/actions/textInputActions.ts new file mode 100644 index 00000000000..b9ae0528184 --- /dev/null +++ b/src/vs/workbench/browser/actions/textInputActions.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IAction, Action } from 'vs/base/common/actions'; +import { localize } from 'vs/nls'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { EventHelper } from 'vs/base/browser/dom'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { isNative } from 'vs/base/common/platform'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; + +export class TextInputActionsProvider extends Disposable implements IWorkbenchContribution { + + private textInputActions: IAction[] = []; + + constructor( + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IClipboardService private readonly clipboardService: IClipboardService + ) { + super(); + + this.createActions(); + + this.registerListeners(); + } + + private createActions(): void { + this.textInputActions.push( + + // Undo/Redo + new Action('undo', localize('undo', "Undo"), undefined, true, async () => document.execCommand('undo')), + new Action('redo', localize('redo', "Redo"), undefined, true, async () => document.execCommand('redo')), + new Separator(), + + // Cut / Copy / Paste + new Action('editor.action.clipboardCutAction', localize('cut', "Cut"), undefined, true, async () => document.execCommand('cut')), + new Action('editor.action.clipboardCopyAction', localize('copy', "Copy"), undefined, true, async () => document.execCommand('copy')), + new Action('editor.action.clipboardPasteAction', localize('paste', "Paste"), undefined, true, async (element: HTMLInputElement | HTMLTextAreaElement) => { + + // Native: paste is supported + if (isNative) { + document.execCommand('paste'); + } + + // Web: paste is not supported due to security reasons + else { + const clipboardText = await this.clipboardService.readText(); + if ( + element instanceof HTMLTextAreaElement || + element instanceof HTMLInputElement + ) { + const selectionStart = element.selectionStart || 0; + const selectionEnd = element.selectionEnd || 0; + + element.value = `${element.value.substring(0, selectionStart)}${clipboardText}${element.value.substring(selectionEnd, element.value.length)}`; + element.selectionStart = selectionStart + clipboardText.length; + element.selectionEnd = element.selectionStart; + } + } + }), + new Separator(), + + // Select All + new Action('editor.action.selectAll', localize('selectAll', "Select All"), undefined, true, async () => document.execCommand('selectAll')) + ); + } + + private registerListeners(): void { + + // Context menu support in input/textarea + this.layoutService.container.addEventListener('contextmenu', e => this.onContextMenu(e)); + + } + + private onContextMenu(e: MouseEvent): void { + if (e.target instanceof HTMLElement) { + const target = e.target; + if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) { + EventHelper.stop(e, true); + + this.contextMenuService.showContextMenu({ + getAnchor: () => e, + getActions: () => this.textInputActions, + getActionsContext: () => target, + onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948 + }); + } + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, LifecyclePhase.Ready); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index adc12573ed4..3647d409cb7 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -9,13 +9,12 @@ import * as errors from 'vs/base/common/errors'; import { equals, deepClone, assign } from 'vs/base/common/objects'; import * as DOM from 'vs/base/browser/dom'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as browser from 'vs/base/browser/browser'; @@ -62,17 +61,6 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; -const TextInputActions: IAction[] = [ - new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), - new Action('redo', nls.localize('redo', "Redo"), undefined, true, () => Promise.resolve(document.execCommand('redo'))), - new Separator(), - new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), undefined, true, () => Promise.resolve(document.execCommand('cut'))), - new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), undefined, true, () => Promise.resolve(document.execCommand('copy'))), - new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), undefined, true, () => Promise.resolve(document.execCommand('paste'))), - new Separator(), - new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), undefined, true, () => Promise.resolve(document.execCommand('selectAll'))) -]; - export class ElectronWindow extends Disposable { private touchBarMenu: IMenu | undefined; @@ -96,7 +84,6 @@ export class ElectronWindow extends Disposable { @INotificationService private readonly notificationService: INotificationService, @ICommandService private readonly commandService: ICommandService, @IKeybindingService private readonly keybindingService: IKeybindingService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService, @IFileService private readonly fileService: IFileService, @@ -239,9 +226,6 @@ export class ElectronWindow extends Disposable { } })); - // Context menu support in input/textarea - window.document.addEventListener('contextmenu', e => this.onContextMenu(e)); - // Listen to visible editor changes this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); @@ -300,21 +284,6 @@ export class ElectronWindow extends Disposable { } } - private onContextMenu(e: MouseEvent): void { - if (e.target instanceof HTMLElement) { - const target = e.target; - if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) { - DOM.EventHelper.stop(e, true); - - this.contextMenuService.showContextMenu({ - getAnchor: () => e, - getActions: () => TextInputActions, - onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948 - }); - } - } - } - private updateWindowZoomLevel(): void { const windowConfig: IWindowsConfiguration = this.configurationService.getValue(); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 4016b316a38..2e2055f35b1 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -15,6 +15,7 @@ import 'vs/workbench/browser/workbench.contribution'; //#region --- workbench actions +import 'vs/workbench/browser/actions/textInputActions'; import 'vs/workbench/browser/actions/developerActions'; import 'vs/workbench/browser/actions/helpActions'; import 'vs/workbench/browser/actions/layoutActions';