Skip to content

Commit 205b617

Browse files
author
Benjamin Pasero
committed
web - implement text input actions too
1 parent 078acb7 commit 205b617

3 files changed

Lines changed: 102 additions & 32 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IAction, Action } from 'vs/base/common/actions';
7+
import { localize } from 'vs/nls';
8+
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
9+
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
10+
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
11+
import { Disposable } from 'vs/base/common/lifecycle';
12+
import { EventHelper } from 'vs/base/browser/dom';
13+
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
14+
import { Registry } from 'vs/platform/registry/common/platform';
15+
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
16+
import { isNative } from 'vs/base/common/platform';
17+
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
18+
19+
export class TextInputActionsProvider extends Disposable implements IWorkbenchContribution {
20+
21+
private textInputActions: IAction[] = [];
22+
23+
constructor(
24+
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
25+
@IContextMenuService private readonly contextMenuService: IContextMenuService,
26+
@IClipboardService private readonly clipboardService: IClipboardService
27+
) {
28+
super();
29+
30+
this.createActions();
31+
32+
this.registerListeners();
33+
}
34+
35+
private createActions(): void {
36+
this.textInputActions.push(
37+
38+
// Undo/Redo
39+
new Action('undo', localize('undo', "Undo"), undefined, true, async () => document.execCommand('undo')),
40+
new Action('redo', localize('redo', "Redo"), undefined, true, async () => document.execCommand('redo')),
41+
new Separator(),
42+
43+
// Cut / Copy / Paste
44+
new Action('editor.action.clipboardCutAction', localize('cut', "Cut"), undefined, true, async () => document.execCommand('cut')),
45+
new Action('editor.action.clipboardCopyAction', localize('copy', "Copy"), undefined, true, async () => document.execCommand('copy')),
46+
new Action('editor.action.clipboardPasteAction', localize('paste', "Paste"), undefined, true, async (element: HTMLInputElement | HTMLTextAreaElement) => {
47+
48+
// Native: paste is supported
49+
if (isNative) {
50+
document.execCommand('paste');
51+
}
52+
53+
// Web: paste is not supported due to security reasons
54+
else {
55+
const clipboardText = await this.clipboardService.readText();
56+
if (
57+
element instanceof HTMLTextAreaElement ||
58+
element instanceof HTMLInputElement
59+
) {
60+
const selectionStart = element.selectionStart || 0;
61+
const selectionEnd = element.selectionEnd || 0;
62+
63+
element.value = `${element.value.substring(0, selectionStart)}${clipboardText}${element.value.substring(selectionEnd, element.value.length)}`;
64+
element.selectionStart = selectionStart + clipboardText.length;
65+
element.selectionEnd = element.selectionStart;
66+
}
67+
}
68+
}),
69+
new Separator(),
70+
71+
// Select All
72+
new Action('editor.action.selectAll', localize('selectAll', "Select All"), undefined, true, async () => document.execCommand('selectAll'))
73+
);
74+
}
75+
76+
private registerListeners(): void {
77+
78+
// Context menu support in input/textarea
79+
this.layoutService.container.addEventListener('contextmenu', e => this.onContextMenu(e));
80+
81+
}
82+
83+
private onContextMenu(e: MouseEvent): void {
84+
if (e.target instanceof HTMLElement) {
85+
const target = <HTMLElement>e.target;
86+
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
87+
EventHelper.stop(e, true);
88+
89+
this.contextMenuService.showContextMenu({
90+
getAnchor: () => e,
91+
getActions: () => this.textInputActions,
92+
getActionsContext: () => target,
93+
onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948
94+
});
95+
}
96+
}
97+
}
98+
}
99+
100+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, LifecyclePhase.Ready);

src/vs/workbench/electron-browser/window.ts

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ import * as errors from 'vs/base/common/errors';
99
import { equals, deepClone, assign } from 'vs/base/common/objects';
1010
import * as DOM from 'vs/base/browser/dom';
1111
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
12-
import { IAction, Action } from 'vs/base/common/actions';
12+
import { IAction } from 'vs/base/common/actions';
1313
import { IFileService } from 'vs/platform/files/common/files';
1414
import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor';
1515
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
1616
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1717
import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows';
18-
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1918
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
2019
import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
2120
import * as browser from 'vs/base/browser/browser';
@@ -62,17 +61,6 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
6261
import { IHostService } from 'vs/workbench/services/host/browser/host';
6362
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
6463

65-
const TextInputActions: IAction[] = [
66-
new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))),
67-
new Action('redo', nls.localize('redo', "Redo"), undefined, true, () => Promise.resolve(document.execCommand('redo'))),
68-
new Separator(),
69-
new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), undefined, true, () => Promise.resolve(document.execCommand('cut'))),
70-
new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), undefined, true, () => Promise.resolve(document.execCommand('copy'))),
71-
new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), undefined, true, () => Promise.resolve(document.execCommand('paste'))),
72-
new Separator(),
73-
new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), undefined, true, () => Promise.resolve(document.execCommand('selectAll')))
74-
];
75-
7664
export class ElectronWindow extends Disposable {
7765

7866
private touchBarMenu: IMenu | undefined;
@@ -96,7 +84,6 @@ export class ElectronWindow extends Disposable {
9684
@INotificationService private readonly notificationService: INotificationService,
9785
@ICommandService private readonly commandService: ICommandService,
9886
@IKeybindingService private readonly keybindingService: IKeybindingService,
99-
@IContextMenuService private readonly contextMenuService: IContextMenuService,
10087
@ITelemetryService private readonly telemetryService: ITelemetryService,
10188
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService,
10289
@IFileService private readonly fileService: IFileService,
@@ -239,9 +226,6 @@ export class ElectronWindow extends Disposable {
239226
}
240227
}));
241228

242-
// Context menu support in input/textarea
243-
window.document.addEventListener('contextmenu', e => this.onContextMenu(e));
244-
245229
// Listen to visible editor changes
246230
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
247231

@@ -300,21 +284,6 @@ export class ElectronWindow extends Disposable {
300284
}
301285
}
302286

303-
private onContextMenu(e: MouseEvent): void {
304-
if (e.target instanceof HTMLElement) {
305-
const target = <HTMLElement>e.target;
306-
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
307-
DOM.EventHelper.stop(e, true);
308-
309-
this.contextMenuService.showContextMenu({
310-
getAnchor: () => e,
311-
getActions: () => TextInputActions,
312-
onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948
313-
});
314-
}
315-
}
316-
}
317-
318287
private updateWindowZoomLevel(): void {
319288
const windowConfig: IWindowsConfiguration = this.configurationService.getValue<IWindowsConfiguration>();
320289

src/vs/workbench/workbench.common.main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'vs/workbench/browser/workbench.contribution';
1515

1616
//#region --- workbench actions
1717

18+
import 'vs/workbench/browser/actions/textInputActions';
1819
import 'vs/workbench/browser/actions/developerActions';
1920
import 'vs/workbench/browser/actions/helpActions';
2021
import 'vs/workbench/browser/actions/layoutActions';

0 commit comments

Comments
 (0)