Merge from vscode merge-base (#22780)

* Revert "Revert "Merge from vscode merge-base (#22769)" (#22779)"

This reverts commit 47a1745180.

* Fix notebook download task

* Remove done call from extensions-ci
This commit is contained in:
Karl Burtram
2023-04-19 21:48:46 -07:00
committed by GitHub
parent decbe8dded
commit e7d3d047ec
2389 changed files with 92155 additions and 42602 deletions
+18 -54
View File
@@ -88,15 +88,6 @@ function pipeLoggingToParent() {
}
}
// Add the stack trace as payload if we are told so. We remove the message and the 2 top frames
// to start the stacktrace where the console message was being written
if (process.env['VSCODE_LOG_STACK'] === 'true') {
const stack = new Error().stack;
if (stack) {
argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') });
}
}
try {
const res = JSON.stringify(argsArray, function (key, value) {
@@ -155,13 +146,8 @@ function pipeLoggingToParent() {
safeSend({ type: '__$console', severity, arguments: args });
}
let isMakingConsoleCall = false;
/**
* Wraps a console message so that it is transmitted to the renderer. If
* native logging is turned on, the original console message will be written
* as well. This is needed since the console methods are "magic" in V8 and
* are the only methods that allow later introspection of logged variables.
* Wraps a console message so that it is transmitted to the renderer.
*
* The wrapped property is not defined with `writable: false` to avoid
* throwing errors, but rather a no-op setting. See https://github.com/microsoft/vscode-extension-telemetry/issues/88
@@ -170,26 +156,10 @@ function pipeLoggingToParent() {
* @param {'log' | 'warn' | 'error'} severity
*/
function wrapConsoleMethod(method, severity) {
if (process.env['VSCODE_LOG_NATIVE'] === 'true') {
const original = console[method];
const stream = method === 'error' || method === 'warn' ? process.stderr : process.stdout;
Object.defineProperty(console, method, {
set: () => { },
get: () => function () {
safeSendConsoleMessage(severity, safeToArray(arguments));
isMakingConsoleCall = true;
stream.write('\nSTART_NATIVE_LOG\n');
original.apply(console, arguments);
stream.write('\nEND_NATIVE_LOG\n');
isMakingConsoleCall = false;
},
});
} else {
Object.defineProperty(console, method, {
set: () => { },
get: () => function () { safeSendConsoleMessage(severity, safeToArray(arguments)); },
});
}
Object.defineProperty(console, method, {
set: () => { },
get: () => function () { safeSendConsoleMessage(severity, safeToArray(arguments)); },
});
}
/**
@@ -211,13 +181,11 @@ function pipeLoggingToParent() {
Object.defineProperty(stream, 'write', {
set: () => { },
get: () => (chunk, encoding, callback) => {
if (!isMakingConsoleCall) {
buf += chunk.toString(encoding);
const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n');
if (eol !== -1) {
console[severity](buf.slice(0, eol));
buf = buf.slice(eol + 1);
}
buf += chunk.toString(encoding);
const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n');
if (eol !== -1) {
console[severity](buf.slice(0, eol));
buf = buf.slice(eol + 1);
}
original.call(stream, chunk, encoding, callback);
@@ -231,7 +199,7 @@ function pipeLoggingToParent() {
wrapConsoleMethod('log', 'log');
wrapConsoleMethod('warn', 'warn');
wrapConsoleMethod('error', 'error');
} else if (process.env['VSCODE_LOG_NATIVE'] !== 'true') {
} else {
console.log = function () { /* ignore */ };
console.warn = function () { /* ignore */ };
console.info = function () { /* ignore */ };
@@ -273,17 +241,13 @@ function listenForMessagePort() {
// We need to listen for the 'port' event as soon as possible,
// otherwise we might miss the event. But we should also be
// prepared in case the event arrives late.
// @ts-ignore
if (process.parentPort) {
// @ts-ignore
process.parentPort.on('message', (e) => {
if (global.vscodePortsCallback) {
global.vscodePortsCallback(e.ports);
} else {
global.vscodePorts = e.ports;
}
});
}
process.on('port', (e) => {
if (global.vscodePortsCallback) {
global.vscodePortsCallback(e.ports);
} else {
global.vscodePorts = e.ports;
}
});
}
//#endregion
+1 -1
View File
@@ -205,7 +205,7 @@
}
/**
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process}
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process | undefined}
*/
function safeProcess() {
const sandboxGlobals = safeSandboxGlobals();
+7 -6
View File
@@ -32,16 +32,17 @@ exports.base = [
{
name: 'vs/editor/common/services/editorSimpleWorker',
include: ['vs/base/common/worker/simpleWorker'],
prepend: ['vs/loader.js', 'vs/nls.js'],
append: ['vs/base/worker/workerMain'],
exclude: ['vs/nls'],
prepend: [
{ path: 'vs/loader.js' },
{ path: 'vs/nls.js', amdModuleId: 'vs/nls' },
{ path: 'vs/base/worker/workerMain.js' }
],
dest: 'vs/base/worker/workerMain.js'
},
{
name: 'vs/base/common/worker/simpleWorker',
},
{
name: 'vs/platform/extensions/node/extensionHostStarterWorker',
exclude: ['vs/base/common/worker/simpleWorker']
exclude: ['vs/nls'],
}
];
+3 -12
View File
@@ -160,9 +160,6 @@ function configureCommandlineSwitchesSync(cliArgs) {
// alias from us for --disable-gpu
'disable-hardware-acceleration',
// provided by Electron
'disable-color-correct-rendering',
// override for the color profile to use
'force-color-profile'
];
@@ -254,9 +251,7 @@ function readArgvConfigSync() {
// Fallback to default
if (!argvConfig) {
argvConfig = {
'disable-color-correct-rendering': true // Force pre-Chrome-60 color profile handling (for https://github.com/microsoft/vscode/issues/51791)
};
argvConfig = {};
}
return argvConfig;
@@ -286,11 +281,7 @@ function createDefaultArgvConfigSync(argvConfigPath) {
'{',
' // Use software rendering instead of hardware accelerated rendering.',
' // This can help in cases where you see rendering issues in VS Code.',
' // "disable-hardware-acceleration": true,',
'',
' // Enabled by default by VS Code to resolve color issues in the renderer',
' // See https://github.com/microsoft/vscode/issues/51791 for details',
' "disable-color-correct-rendering": true',
' // "disable-hardware-acceleration": true',
'}'
];
@@ -329,7 +320,7 @@ function configureCrashReporter() {
if (!fs.existsSync(crashReporterDirectory)) {
try {
fs.mkdirSync(crashReporterDirectory);
fs.mkdirSync(crashReporterDirectory, { recursive: true });
} catch (error) {
console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory does not seem to exist or cannot be created.`);
app.exit(1);
+1 -1
View File
@@ -55,7 +55,7 @@ async function start() {
* @typedef { import('./vs/server/node/remoteExtensionHostAgentServer').IServerAPI } IServerAPI
*/
/** @type {IServerAPI | null} */
let _remoteExtensionHostAgentServer = null;
const _remoteExtensionHostAgentServer = null;
/** @type {Promise<IServerAPI> | null} */
let _remoteExtensionHostAgentServerPromise = null;
/** @returns {Promise<IServerAPI>} */
+5
View File
@@ -4761,6 +4761,7 @@ declare module 'azdata' {
* @deprecated please use the method createModelViewDialog(title: string, dialogName?: string, width?: DialogWidth) instead.
* Create a dialog with the given title
* @param title The title of the dialog, displayed at the top
* @param dialogName Name of the dialog.
* @param isWide Indicates whether the dialog is wide or normal
*/
export function createModelViewDialog(title: string, dialogName?: string, isWide?: boolean): Dialog;
@@ -5391,6 +5392,7 @@ declare module 'azdata' {
*/
export function getQueryDocument(fileUri: string): Thenable<QueryDocument>;
/* eslint-disable */
/**
* Opens an untitled text document. The editor will prompt the user for a file
* path when the document is to be saved. The `options` parameter allows to
@@ -5401,6 +5403,7 @@ declare module 'azdata' {
* @return A promise that resolves to a {@link QueryDocument}.
*/
export function openQueryDocument(options?: { content?: string; }, providerId?: string): Thenable<QueryDocument>;
/* eslint-enable */
}
/**
@@ -5655,6 +5658,7 @@ declare module 'azdata' {
*/
export const onDidChangeActiveNotebookEditor: vscode.Event<NotebookEditor>;
/* eslint-disable */
/**
* Show the given document in a notebook editor. A {@link vscode.ViewColumn} can be provided
* to control where the editor is being shown. Might change the {@link nb.activeNotebookEditor}.
@@ -5674,6 +5678,7 @@ declare module 'azdata' {
* @return A promise that resolves to a {@link NotebookEditor}.
*/
export function showNotebookDocument(uri: vscode.Uri, showOptions?: NotebookShowOptions): Thenable<NotebookEditor>;
/* eslint-enable */
export interface NotebookDocument {
/**
+3 -3
View File
@@ -679,13 +679,13 @@ declare module 'azdata' {
/**
* Enters the workspace with the provided path
* @param workspacefile
* @param workspaceFile
*/
export function enterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
/**
* Saves and enters the workspace with the provided path
* @param workspacefile
* @param workspaceFile
*/
export function saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
}
@@ -848,7 +848,7 @@ declare module 'azdata' {
* Open a table designer window.
* @param providerId The table designer provider Id.
* @param tableInfo The table information. The object will be passed back to the table designer provider as the unique identifier for the table.
* @param telemetryInfo: Optional Key-value pair containing any extra information that needs to be sent via telemetry
* @param telemetryInfo Optional Key-value pair containing any extra information that needs to be sent via telemetry
*/
export function openTableDesigner(providerId: string, tableInfo: TableInfo, telemetryInfo?: { [key: string]: string }): Thenable<void>;
@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
export interface IPointerMoveEventData {
leftButton: boolean;
buttons: number;
pageX: number;
pageY: number;
}
export interface IEventMerger<R> {
(lastEvent: R | null, currentEvent: PointerEvent): R;
}
export interface IPointerMoveCallback<R> {
(pointerMoveData: R): void;
}
export interface IOnStopCallback {
(browserEvent?: PointerEvent | KeyboardEvent): void;
}
export function standardPointerMoveMerger(lastEvent: IPointerMoveEventData | null, currentEvent: PointerEvent): IPointerMoveEventData {
currentEvent.preventDefault();
return {
leftButton: (currentEvent.button === 0),
buttons: currentEvent.buttons,
pageX: currentEvent.pageX,
pageY: currentEvent.pageY
};
}
export class GlobalPointerMoveMonitor<R extends { buttons: number } = IPointerMoveEventData> implements IDisposable {
private readonly _hooks = new DisposableStore();
private _pointerMoveEventMerger: IEventMerger<R> | null = null;
private _pointerMoveCallback: IPointerMoveCallback<R> | null = null;
private _onStopCallback: IOnStopCallback | null = null;
public dispose(): void {
this.stopMonitoring(false);
this._hooks.dispose();
}
public stopMonitoring(invokeStopCallback: boolean, browserEvent?: PointerEvent | KeyboardEvent): void {
if (!this.isMonitoring()) {
// Not monitoring
return;
}
// Unhook
this._hooks.clear();
this._pointerMoveEventMerger = null;
this._pointerMoveCallback = null;
const onStopCallback = this._onStopCallback;
this._onStopCallback = null;
if (invokeStopCallback && onStopCallback) {
onStopCallback(browserEvent);
}
}
public isMonitoring(): boolean {
return !!this._pointerMoveEventMerger;
}
public startMonitoring(
initialElement: Element,
pointerId: number,
initialButtons: number,
pointerMoveEventMerger: IEventMerger<R>,
pointerMoveCallback: IPointerMoveCallback<R>,
onStopCallback: IOnStopCallback
): void {
if (this.isMonitoring()) {
this.stopMonitoring(false);
}
this._pointerMoveEventMerger = pointerMoveEventMerger;
this._pointerMoveCallback = pointerMoveCallback;
this._onStopCallback = onStopCallback;
let eventSource: Element | Window = initialElement;
try {
initialElement.setPointerCapture(pointerId);
this._hooks.add(toDisposable(() => {
initialElement.releasePointerCapture(pointerId);
}));
} catch (err) {
// See https://github.com/microsoft/vscode/issues/144584
// See https://github.com/microsoft/vscode/issues/146947
// `setPointerCapture` sometimes fails when being invoked
// from a `mousedown` listener on macOS and Windows
// and it always fails on Linux with the exception:
// DOMException: Failed to execute 'setPointerCapture' on 'Element':
// No active pointer with the given id is found.
// In case of failure, we bind the listeners on the window
eventSource = window;
}
this._hooks.add(dom.addDisposableThrottledListener<R, PointerEvent>(
eventSource,
dom.EventType.POINTER_MOVE,
(data: R) => {
if (data.buttons !== initialButtons) {
// Buttons state has changed in the meantime
this.stopMonitoring(true);
return;
}
this._pointerMoveCallback!(data);
},
(lastEvent: R | null, currentEvent) => this._pointerMoveEventMerger!(lastEvent, currentEvent)
));
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_UP,
(e: PointerEvent) => this.stopMonitoring(true)
));
}
}
@@ -261,6 +261,7 @@ export class PanelComponent extends Disposable implements IThemable {
this.selectTab(nextTabIndex);
}
/* eslint-disable */
/**
* Updates the specified tab with new config values
* @param tabId The id of the tab to update
@@ -285,6 +286,7 @@ export class PanelComponent extends Disposable implements IThemable {
tabHeader?.refresh();
}
}
/* eslint-enable */
private findAndRemoveTabFromMRU(tab: TabComponent): void {
let mruIndex = this._mru.findIndex(i => i === tab);
@@ -10,9 +10,10 @@ import { MenuItemAction } from 'vs/platform/actions/common/actions';
import { ICommandAction } from 'vs/platform/action/common/action';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
const ids = new IdGenerator('menu-item-action-item-icon-');
@@ -32,8 +33,10 @@ export class LabeledMenuItemActionItem extends MenuEntryActionViewItem {
@IKeybindingService labeledkeybindingService: IKeybindingService,
@INotificationService _notificationService: INotificationService,
@IContextKeyService _contextKeyService: IContextKeyService,
@IThemeService _themeService: IThemeService,
@IContextMenuService _contextMenuService: IContextMenuService
) {
super(_action, undefined, labeledkeybindingService, _notificationService, _contextKeyService);
super(_action, undefined, labeledkeybindingService, _notificationService, _contextKeyService, _themeService, _contextMenuService);
}
override updateLabel(): void {
@@ -104,9 +107,11 @@ export class MaskedLabeledMenuItemActionItem extends MenuEntryActionViewItem {
action: MenuItemAction,
@IKeybindingService keybindingService: IKeybindingService,
@INotificationService notificationService: INotificationService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IThemeService _themeService: IThemeService,
@IContextMenuService _contextMenuService: IContextMenuService
) {
super(action, undefined, keybindingService, notificationService, contextKeyService);
super(action, undefined, keybindingService, notificationService, contextKeyService, _themeService, _contextMenuService);
}
override updateLabel(): void {
@@ -42,14 +42,14 @@ export class ConnectionStore {
@ICapabilitiesService private capabilitiesService: ICapabilitiesService
) {
try {
const configRaw = this.storageService.get(RECENT_CONNECTIONS_STATE_KEY, StorageScope.GLOBAL, '[]');
const configRaw = this.storageService.get(RECENT_CONNECTIONS_STATE_KEY, StorageScope.APPLICATION, '[]');
this.mru = JSON.parse(configRaw);
} catch (e) {
this.mru = [];
}
this.storageService.onWillSaveState(() =>
this.storageService.store(RECENT_CONNECTIONS_STATE_KEY, JSON.stringify(this.mru), StorageScope.GLOBAL, StorageTarget.MACHINE));
this.storageService.store(RECENT_CONNECTIONS_STATE_KEY, JSON.stringify(this.mru), StorageScope.APPLICATION, StorageTarget.MACHINE));
}
/**
@@ -15,8 +15,7 @@ import * as vsTreeExt from 'vs/workbench/api/common/extHostTreeViews';
import { Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer';
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { SqlMainContext, DataTransferDTO } from 'vs/workbench/api/common/extHost.protocol';
export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape {
private _proxy: MainThreadModelViewShape;
@@ -86,7 +85,7 @@ export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape
return Promise.resolve(undefined);
}
$handleDrop(destinationViewId: string, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void> {
$handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void> {
return Promise.resolve(undefined);
}
@@ -26,7 +26,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
import { IQueryEvent } from 'sql/workbench/services/query/common/queryModel';
import { DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer';
import { DataTransferDTO } from 'vs/workbench/api/common/extHost.protocol';
export abstract class ExtHostAzureBlobShape {
public $createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Thenable<mssql.CreateSasResponse> { throw ni(); }
@@ -794,7 +794,7 @@ export interface ExtHostModelViewShape {
export interface ExtHostModelViewTreeViewsShape {
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeComponentItem[]>;
$handleDrop(destinationViewId: string, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
$handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
$handleDrag(sourceViewId: string, sourceTreeItemHandles: string[], operationUuid: string, token: vscode.CancellationToken): Promise<DataTransferDTO | undefined>;
$createTreeView(handle: number, componentId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }, extension: IExtensionDescription): azdata.TreeComponentView<any>;
@@ -16,7 +16,7 @@ import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -30,13 +30,14 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { IModelService } from 'vs/editor/common/services/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { IFileService } from 'vs/platform/files/common/files';
class DesignerCodeEditor extends CodeEditorWidget {
}
let DesignerScriptEditorInstanceId = 0;
export class DesignerScriptEditor extends BaseTextEditor<editorCommon.ICodeEditorViewState> implements DesignerTextEditor {
export class DesignerScriptEditor extends AbstractTextCodeEditor<editorCommon.ICodeEditorViewState> implements DesignerTextEditor {
private _content: string;
private _contentChangeEventEmitter: Emitter<string> = new Emitter<string>();
readonly onDidContentChange: Event<string> = this._contentChangeEventEmitter.event;
@@ -55,9 +56,10 @@ export class DesignerScriptEditor extends BaseTextEditor<editorCommon.ICodeEdito
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IFileService fileService: IFileService
) {
super(DesignerScriptEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService);
super(DesignerScriptEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, fileService);
this.create(this._container);
this.setVisible(true);
this._untitledTextEditorModel = this.instantiationService.createInstance(UntitledTextEditorModel, URI.from({ scheme: Schemas.untitled, path: `DesignerScriptEditor-${DesignerScriptEditorInstanceId++}` }), false, undefined, 'sql', undefined);
@@ -70,7 +72,9 @@ export class DesignerScriptEditor extends BaseTextEditor<editorCommon.ICodeEdito
}
public override createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(DesignerCodeEditor, parent, configuration, {});
this.editorControl = this.instantiationService.createInstance(DesignerCodeEditor, parent, configuration, {});
return this.editorControl;
}
protected override getConfigurationOverrides(): IEditorOptions {
@@ -405,7 +405,7 @@ export default class DeclarativeTableComponent extends ContainerBase<any, azdata
private createMenuItem(commandId: string): MenuItemAction {
const command = MenuRegistry.getCommand(commandId);
return this.instantiationService.createInstance(MenuItemAction, command, undefined, { shouldForwardArgs: true });
return this.instantiationService.createInstance(MenuItemAction, command, undefined, { shouldForwardArgs: true }, undefined);
}
public onKey(e: KeyboardEvent, row: number) {
@@ -9,7 +9,7 @@ import * as DOM from 'vs/base/browser/dom';
import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -24,11 +24,13 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { IFileService } from 'vs/platform/files/common/files';
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/
export class QueryTextEditor extends BaseTextEditor<editorCommon.ICodeEditorViewState> {
export class QueryTextEditor extends AbstractTextCodeEditor<editorCommon.ICodeEditorViewState> {
public static ID = 'modelview.editors.textEditor';
private _dimension: DOM.Dimension;
@@ -47,15 +49,18 @@ export class QueryTextEditor extends BaseTextEditor<editorCommon.ICodeEditorView
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService editorService: IEditorService
@IEditorService editorService: IEditorService,
@IFileService fileService: IFileService
) {
super(
QueryTextEditor.ID, telemetryService, instantiationService, storageService,
configurationService, themeService, editorService, editorGroupService);
configurationService, themeService, editorService, editorGroupService, fileService);
}
public override createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {});
this.editorControl = this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {});
return this.editorControl;
}
protected override getConfigurationOverrides(): IEditorOptions {
@@ -584,7 +584,7 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
private createMenuItem(commandId: string): MenuItemAction {
const command = MenuRegistry.getCommand(commandId);
return this.instantiationService.createInstance(MenuItemAction, command, undefined, { shouldForwardArgs: true });
return this.instantiationService.createInstance(MenuItemAction, command, undefined, { shouldForwardArgs: true }, undefined);
}
@@ -21,14 +21,14 @@ export function getChartMaxRowCount(configurationService: IConfigurationService)
*/
export function notifyMaxRowCountExceeded(storageService: IStorageService, notificationService: INotificationService, configurationService: IConfigurationService): void {
const storageKey = 'charts/ignoreMaxRowCountExceededNotification';
if (!storageService.getBoolean(storageKey, StorageScope.GLOBAL, false)) {
if (!storageService.getBoolean(storageKey, StorageScope.APPLICATION, false)) {
notificationService.prompt(Severity.Info,
nls.localize('charts.maxAllowedRowsExceeded', "Maximum row count for built-in charts has been exceeded, only the first {0} rows are used. To configure the value, you can open user settings and search for: 'builtinCharts.maxRowCount'.", getChartMaxRowCount(configurationService)),
[{
label: nls.localize('charts.neverShowAgain', "Don't Show Again"),
isSecondary: true,
run: () => {
storageService.store(storageKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
storageService.store(storageKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
}
}]);
}
@@ -31,7 +31,7 @@ const settingsToMove: { [key: string]: string } = deepFreeze({
export class ConfigurationUpgraderContribution implements IWorkbenchContribution {
private static readonly STORAGE_KEY = 'configurationUpgrader';
private readonly globalStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.GLOBAL, '{}'));
private readonly globalStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.APPLICATION, '{}'));
private readonly workspaceStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.WORKSPACE, '{}'));
public readonly processingPromise: Promise<void>;
@@ -43,7 +43,7 @@ export class ConfigurationUpgraderContribution implements IWorkbenchContribution
) {
this.processingPromise = (async () => {
await this.processSettings();
this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.globalStorage), StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.globalStorage), StorageScope.APPLICATION, StorageTarget.MACHINE);
this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.workspaceStorage), StorageScope.WORKSPACE, StorageTarget.MACHINE);
})();
}
@@ -98,11 +98,14 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo
this._onMessageDisposable.dispose();
}
this._webview = this.webviewService.createWebviewElement(this.id,
{},
{
allowScripts: true
}, undefined);
this._webview = this.webviewService.createWebviewElement({
id: this.id,
contentOptions: {
allowScripts: true,
},
options: {},
extension: undefined
});
this._webview.mountTo(this._el.nativeElement);
@@ -190,14 +190,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
};
this.lastUpdated = nls.localize('insights.lastUpdated', "Last Updated: {0} {1}", currentTime.toLocaleTimeString(), currentTime.toLocaleDateString());
this._changeRef.detectChanges();
this.storageService.store(this._getStorageKey(), JSON.stringify(store), StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(this._getStorageKey(), JSON.stringify(store), StorageScope.APPLICATION, StorageTarget.MACHINE);
}
return result;
}
private _checkStorage(): boolean {
if (this.insightConfig.cacheId) {
const storage = this.storageService.get(this._getStorageKey(), StorageScope.GLOBAL);
const storage = this.storageService.get(this._getStorageKey(), StorageScope.APPLICATION);
if (storage) {
const storedResult: IStorageResult = JSON.parse(storage);
const date = new Date(storedResult.date);
@@ -99,11 +99,14 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
this._onMessageDisposable.dispose();
}
this._webview = this.webviewService.createWebviewElement(this.id,
{},
{
this._webview = this.webviewService.createWebviewElement({
id: this.id,
contentOptions: {
allowScripts: true,
}, undefined);
},
options: {},
extension: undefined
});
this._webview.mountTo(this._el.nativeElement);
this._onMessageDisposable = this._webview.onMessage(e => {
@@ -41,6 +41,7 @@ import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditorViewState } from 'vs/editor/common/editorCommon';
/**
* Editor that hosts an action bar and a resultSetInput for an edit data session
@@ -583,7 +584,7 @@ export class EditDataEditor extends EditorPane {
newInput.results.onRestoreViewStateEmitter.fire();
}
if (newInput.savedViewState) {
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
this._sqlEditor.getControl().restoreViewState(<ICodeEditorViewState>newInput.savedViewState);
}
});
}
@@ -798,11 +798,10 @@ class SearchNodeAction extends Action {
public static LABEL_FOR_ADDED_PLAN = localize('epCompare.searchNodeActionAddedPlan', 'Find Node - Added Plan');
constructor(private readonly _planIdentifier: PlanIdentifier, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IAdsTelemetryService private readonly _telemetryService: IAdsTelemetryService) {
const getLabelForAction = () => {
super(SearchNodeAction.ID, undefined, searchIconClassNames, true, () => {
return _planIdentifier === PlanIdentifier.Added ? SearchNodeAction.LABEL_FOR_ADDED_PLAN : SearchNodeAction.LABEL;
};
});
super(SearchNodeAction.ID, getLabelForAction(), searchIconClassNames);
this.enabled = false;
}
@@ -70,14 +70,16 @@ export class ExecutionPlanEditorOverrideContribution extends Disposable implemen
priority: RegisteredEditorPriority.builtin
},
{},
(editorInput, group) => {
const executionPlanGraphInfo = {
graphFileContent: undefined,
graphFileType: undefined
};
const executionPlanInput = this._register(this._instantiationService.createInstance(ExecutionPlanInput, editorInput.resource, executionPlanGraphInfo));
{
createEditorInput: (editorInput, group) => {
const executionPlanGraphInfo = {
graphFileContent: undefined,
graphFileType: undefined
};
const executionPlanInput = this._register(this._instantiationService.createInstance(ExecutionPlanInput, editorInput.resource, executionPlanGraphInfo));
return { editor: executionPlanInput, options: editorInput.options, group: group };
return { editor: executionPlanInput, options: editorInput.options, group: group };
}
}
));
}
@@ -449,7 +449,7 @@ export class SavePlanFile extends Action {
const fileExtension = 'sqlplan'; //TODO: Get this extension from provider
let defaultUri: URI;
const lastUsedSavePath = this.storageService.get(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, StorageScope.GLOBAL);
const lastUsedSavePath = this.storageService.get(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, StorageScope.APPLICATION);
if (lastUsedSavePath) {
defaultUri = joinPath(URI.file(lastUsedSavePath), `${defaultFileName}.${fileExtension}`);
@@ -479,7 +479,7 @@ export class SavePlanFile extends Action {
if (destination) {
// Remember as last used save folder
this.storageService.store(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, dirname(destination).fsPath, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, dirname(destination).fsPath, StorageScope.APPLICATION, StorageTarget.MACHINE);
// Perform save
await context.fileService.writeFile(destination, VSBuffer.fromString(context.model.graphFile.graphFileContent));
@@ -181,7 +181,7 @@ export class HighlightExpensiveOperationWidget extends ExecutionPlanWidgetBase {
public showStoreDefaultMetricPrompt(): void {
const currentDefaultExpensiveOperationMetric = this.getDefaultExpensiveOperationMetric();
if (this._selectedExpensiveOperationType === currentDefaultExpensiveOperationMetric || !this._storageService.getBoolean('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', StorageScope.GLOBAL, true)) {
if (this._selectedExpensiveOperationType === currentDefaultExpensiveOperationMetric || !this._storageService.getBoolean('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', StorageScope.APPLICATION, true)) {
return;
}
@@ -197,7 +197,7 @@ export class HighlightExpensiveOperationWidget extends ExecutionPlanWidgetBase {
},
{
label: localize('qp.expensiveOperationMetric.dontShowAgain', "Don't Show Again"),
run: () => this._storageService.store('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', false, StorageScope.GLOBAL, StorageTarget.USER)
run: () => this._storageService.store('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', false, StorageScope.APPLICATION, StorageTarget.USER)
}
];
@@ -52,7 +52,7 @@ export class ScenarioRecommendations extends ExtensionRecommendations {
promptRecommendedExtensionsByScenario(scenarioType: string): void {
const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType;
if (this.storageService.getBoolean(storageKey, StorageScope.GLOBAL, false) || this.ignoreRecommendations()) {
if (this.storageService.getBoolean(storageKey, StorageScope.APPLICATION, false) || this.ignoreRecommendations()) {
return;
}
@@ -104,7 +104,7 @@ export class ScenarioRecommendations extends ExtensionRecommendations {
'NeverShowAgainButton',
visualizerExtensionNotificationService
);
this.storageService.store(storageKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(storageKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
}
}],
{
@@ -73,11 +73,14 @@ export default class WebViewComponent extends ComponentBase<WebViewProperties> i
}
private _createWebview(): void {
this._webview = this.webviewService.createWebviewElement(this.id,
{},
{
allowScripts: true
}, undefined);
this._webview = this.webviewService.createWebviewElement({
id: this.id,
contentOptions: {
allowScripts: true,
},
options: {},
extension: undefined
});
this._webview.mountTo(this._el.nativeElement);
@@ -10,7 +10,7 @@ import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { ICellEditorProvider, INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { nb } from 'azdata';
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
@@ -34,7 +34,7 @@ export abstract class CellView extends AngularDisposable implements OnDestroy, I
public abstract layout(): void;
public getEditor(): BaseTextEditor<ICodeEditorViewState> | undefined {
public getEditor(): AbstractTextCodeEditor<ICodeEditorViewState> | undefined {
return undefined;
}
@@ -803,18 +803,19 @@ export class NotebookEditorOverrideContribution extends Disposable implements IW
priority: RegisteredEditorPriority.builtin
},
{},
async (editorInput, group) => {
const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput;
// Try to convert the input, falling back to just a plain file input if we're unable to
const newInput = this.convertInput(fileInput);
return { editor: newInput, options: editorInput.options, group: group };
},
undefined,
async (diffEditorInput, group) => {
const diffEditorInputImpl = await this._editorService.createEditorInput(diffEditorInput) as DiffEditorInput;
// Try to convert the input, falling back to the original input if we're unable to
const newInput = this.convertInput(diffEditorInputImpl);
return { editor: newInput, options: diffEditorInput.options, group: group };
{
createEditorInput: async (editorInput, group) => {
const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput;
// Try to convert the input, falling back to just a plain file input if we're unable to
const newInput = this.convertInput(fileInput);
return { editor: newInput, options: editorInput.options, group: group };
},
createDiffEditorInput: async (diffEditorInput, group) => {
const diffEditorInputImpl = await this._editorService.createEditorInput(diffEditorInput) as DiffEditorInput;
// Try to convert the input, falling back to the original input if we're unable to
const newInput = this.convertInput(diffEditorInputImpl);
return { editor: newInput, options: diffEditorInput.options, group: group };
}
}
));
}
@@ -32,7 +32,7 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model';
import { NotebookFindDecorations } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations';
import { TimeoutTimer } from 'vs/base/common/async';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState';
@@ -94,7 +94,7 @@ export class NotebookEditor extends EditorPane implements IFindNotebookControlle
public getLastPosition(): NotebookRange {
return this._previousMatch;
}
public getCellEditor(cellGuid: string): BaseTextEditor<ICodeEditorViewState> | undefined {
public getCellEditor(cellGuid: string): AbstractTextCodeEditor<ICodeEditorViewState> | undefined {
let editorImpl = this._notebookService.findNotebookEditor(this.notebookInput.notebookUri);
if (editorImpl) {
let cellEditorProvider = editorImpl.cellEditors.filter(c => c.cellGuid() === cellGuid)[0];
@@ -33,7 +33,7 @@ import { NotebookEditorStub } from 'sql/workbench/contrib/notebook/test/testComm
import { Range } from 'vs/editor/common/core/range';
import { IProductService } from 'vs/platform/product/common/productService';
import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService';
import { LanguageId } from 'vs/editor/common/languages';
import { LanguageId } from 'vs/editor/common/encodedTokenAttributes';
suite.skip('MarkdownTextTransformer', () => {
let markdownTextTransformer: MarkdownTextTransformer;
@@ -730,7 +730,8 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
instantiationService.get(ITextResourceConfigurationService),
instantiationService.get(IThemeService),
instantiationService.get(IEditorGroupsService),
instantiationService.get(IEditorService)
instantiationService.get(IEditorService),
instantiationService.get(IFileService)
);
const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub(), notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(notebookEditorStub);
@@ -11,7 +11,7 @@ import * as dom from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { TestEditorGroupsService, TestEditorService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestEditorGroupsService, TestEditorService, TestFileService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
@@ -48,7 +48,8 @@ class CellEditorProviderStub extends stubs.CellEditorProviderStub {
new TestTextResourceConfigurationService(),
new TestThemeService(),
new TestEditorGroupsService(),
new TestEditorService()
new TestEditorService(),
new TestFileService()
);
}
if (this._editor) {
@@ -44,6 +44,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { workbenchTreeDataPreamble } from 'vs/platform/list/browser/listService';
suite('SQL Connection Tree Action tests', () => {
let errorMessageService: TypeMoq.Mock<TestErrorMessageService>;
@@ -706,7 +707,16 @@ suite('SQL Connection Tree Action tests', () => {
});
});
test('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', () => {
/*
TypeError: instantiationService.invokeFunction is not a function
at new WorkbenchAsyncDataTree (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/vs/platform/list/browser/listService.js:643:102)
at new AsyncServerTree (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/sql/workbench/services/objectExplorer/browser/asyncServerTree.js:9:5)
at Utils.conthunktor (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:227:23)
at Mock.ofType2 (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:1248:48)
at Mock.ofType (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:1243:29)
at Context.<anonymous> (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.js:612:
*/
test.skip('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', (done) => { // {{SQL CARBON TODO}} 3/17/23 Not sure why this is failing after mokcing the instantiation service's invoke function.
let isConnectedReturnValue: boolean = false;
let sqlProvider = {
providerId: mssqlProviderName,
@@ -770,6 +780,24 @@ suite('SQL Connection Tree Action tests', () => {
objectExplorerService.callBase = true;
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode);
objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node]));
let testDisposable = {
dispose() {
//
}
};
let testInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose);
testInstantiationService.setup(x => x.invokeFunction(workbenchTreeDataPreamble, TypeMoq.It.isAny())).returns((): any => {
return {
getTypeNavigationMode: undefined,
disposable: testDisposable,
treeOptions: {}
};
});
let mockContextKeyService = new MockContextKeyService();
let testListService = new TestListService();
let testConfigurationService = new TestConfigurationService();
let tree = TypeMoq.Mock.ofType<AsyncServerTree>(AsyncServerTree, TypeMoq.MockBehavior.Loose,
'ConnectionTreeActionsTest', // user
$('div'), // container
@@ -777,12 +805,11 @@ suite('SQL Connection Tree Action tests', () => {
[], // renderers
{}, // data source
{}, // options
new MockContextKeyService(), // IContextKeyService
new TestListService(), // IListService,
testInstantiationService.object,
mockContextKeyService, // IContextKeyService
testListService, // IListService,
undefined, // IThemeService,
new TestConfigurationService(), // IConfigurationService,
undefined, // IKeybindingService,
new TestAccessibilityService()); // IAccessibilityService
testConfigurationService); // IConfigurationService,
tree.callBase = true;
tree.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve());
@@ -802,6 +829,7 @@ suite('SQL Connection Tree Action tests', () => {
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
tree.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
done();
});
});
@@ -9,7 +9,7 @@ import * as DOM from 'vs/base/browser/dom';
import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -23,6 +23,7 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { IFileService } from 'vs/platform/files/common/files';
class ProfilerResourceCodeEditor extends CodeEditorWidget {
@@ -38,7 +39,7 @@ class ProfilerResourceCodeEditor extends CodeEditorWidget {
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/
export class ProfilerResourceEditor extends BaseTextEditor<editorCommon.ICodeEditorViewState> {
export class ProfilerResourceEditor extends AbstractTextCodeEditor<editorCommon.ICodeEditorViewState> {
public static ID = 'profiler.editors.textEditor';
constructor(
@@ -48,14 +49,16 @@ export class ProfilerResourceEditor extends BaseTextEditor<editorCommon.ICodeEdi
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IFileService fileService: IFileService
) {
super(ProfilerResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService);
super(ProfilerResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, fileService);
}
public override createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(ProfilerResourceCodeEditor, parent, configuration, {});
this.editorControl = this.instantiationService.createInstance(ProfilerResourceCodeEditor, parent, configuration, {});
return this.editorControl;
}
protected override getConfigurationOverrides(): IEditorOptions {
@@ -83,7 +83,7 @@ export class SaveResultAction extends Action {
this.notificationService.notify({
severity: Severity.Info,
message: localize('jsonEncoding', "Results encoding will not be saved when exporting to JSON, remember to save with desired encoding once file is created."),
neverShowAgain: { id: 'ignoreJsonEncoding', scope: NeverShowAgainScope.GLOBAL }
neverShowAgain: { id: 'ignoreJsonEncoding', scope: NeverShowAgainScope.APPLICATION }
});
}
@@ -578,17 +578,19 @@ export class QueryEditorOverrideContribution extends Disposable implements IWork
},
{
// Fall back to using the normal text based diff editor - we don't want the query bar and related items showing up in the diff editor
canHandleDiff: () => false
// canHandleDiff: () => false
},
async (editorInput, group) => {
const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput;
const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang);
const queryEditorInput = langAssociation?.syncConvertInput?.(fileInput);
if (!queryEditorInput) {
this._logService.warn('Unable to create input for resolving editor ', editorInput.resource);
return undefined;
{
createEditorInput: async (editorInput, group) => {
const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput;
const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang);
const queryEditorInput = langAssociation?.syncConvertInput?.(fileInput);
if (!queryEditorInput) {
this._logService.warn('Unable to create input for resolving editor ', editorInput.resource);
return undefined;
}
return { editor: queryEditorInput, options: editorInput.options, group: group };
}
return { editor: queryEditorInput, options: editorInput.options, group: group };
}
));
});
@@ -27,7 +27,7 @@ import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { FileEditorInput } from 'vs/workbench/contrib/files/browser/editors/fileEditorInput';
import { URI } from 'vs/base/common/uri';
import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files';
@@ -71,7 +71,7 @@ export class QueryEditor extends EditorPane {
private textResourceEditor: TextResourceEditor;
private textFileEditor: TextFileEditor;
private currentTextEditor: BaseTextEditor<ICodeEditorViewState>;
private currentTextEditor: AbstractTextCodeEditor<ICodeEditorViewState>;
private textResourceEditorContainer: HTMLElement;
private textFileEditorContainer: HTMLElement;
@@ -87,11 +87,14 @@ export class WebViewDialog extends Modal {
protected renderBody(container: HTMLElement) {
this._body = DOM.append(container, DOM.$('div.webview-dialog'));
this._webview = this.webviewService.createWebviewElement(this.id,
{},
{
allowScripts: true
}, undefined);
this._webview = this.webviewService.createWebviewElement({
id: this.id,
contentOptions: {
allowScripts: true,
},
options: {},
extension: undefined
});
this._webview.mountTo(this._body);
@@ -25,7 +25,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib
protected handlePreviewFeatures(): void {
let previewFeaturesEnabled = this.configurationService.getValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES);
if (previewFeaturesEnabled || this.storageService.get(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.GLOBAL)) {
if (previewFeaturesEnabled || this.storageService.get(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.APPLICATION)) {
return;
}
Promise.all([
@@ -45,7 +45,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib
label: localize('enablePreviewFeatures.yes', "Yes (recommended)"),
run: () => {
this.configurationService.updateValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, true).catch(e => onUnexpectedError(e));
this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
}
}, {
label: localize('enablePreviewFeatures.no', "No"),
@@ -56,7 +56,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib
label: localize('enablePreviewFeatures.never', "No, don't show again"),
run: () => {
this.configurationService.updateValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, false).catch(e => onUnexpectedError(e));
this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
},
isSecondary: true
}]
@@ -43,11 +43,11 @@ export class NotifyEncryptionDialog extends ErrorMessageDialog {
}
public override open(): void {
if (this._storageService.get(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, StorageScope.GLOBAL)) {
if (this._storageService.get(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, StorageScope.APPLICATION)) {
return;
}
this._storageService.store(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
this._storageService.store(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
if (!this._connectionManagementService.getConnections()?.some(conn => conn.providerName === mssqlProviderName)) {
return;
@@ -382,7 +382,7 @@ class WelcomePage extends Disposable {
newButton.onDidClick(() => {
this.contextMenuService.showContextMenu({
getAnchor: () => newButtonHtmlElement,
getActions: () => NewActionItems.map(command => new MenuItemAction(command, undefined, {}, this.contextKeyService, this.commandService))
getActions: () => NewActionItems.map(command => new MenuItemAction(command, undefined, {}, undefined, this.contextKeyService, this.commandService))
});
});
@@ -67,7 +67,7 @@ export class AccountManagementService implements IAccountManagementService {
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService
) {
this._mementoContext = new Memento(AccountManagementService.ACCOUNT_MEMENTO, this._storageService);
const mementoObj = this._mementoContext.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
const mementoObj = this._mementoContext.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE);
this._accountStore = this._instantiationService.createInstance(AccountStore, mementoObj);
// Setup the event emitters
@@ -123,7 +123,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this._connectionStatusManager = _instantiationService.createInstance(ConnectionStatusManager);
if (this._storageService) {
this._mementoContext = new Memento(ConnectionManagementService.CONNECTION_MEMENTO, this._storageService);
this._mementoObj = this._mementoContext.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
this._mementoObj = this._mementoContext.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE);
}
this.initializeConnectionProvidersMap();
@@ -12,6 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
/* eslint-disable */
/**
* Resolves the given file path using the VS ConfigurationResolver service, replacing macros such as
* ${workspaceRoot} with their expected values and then testing each path to see if it exists. It will
@@ -58,3 +59,4 @@ export async function resolveQueryFilePath(services: ServicesAccessor, filePath?
throw Error(localize('insightsDidNotFindResolvedFile', "Could not find query file at any of the following paths :\n {0}", resolvedFileUris.map(uri => uri.fsPath).join('\n')));
}
/* eslint-enable */
@@ -15,7 +15,7 @@ import { INotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes
import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
import { NotebookChangeType, CellType } from 'sql/workbench/services/notebook/common/contracts';
import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor';
import { Range } from 'vs/editor/common/core/range';
import { IEditorPane } from 'vs/workbench/common/editor';
import { INotebookInput } from 'sql/workbench/services/notebook/browser/interface';
@@ -196,7 +196,7 @@ export interface INotebookSection {
export interface ICellEditorProvider {
isCellOutput: boolean;
cellGuid(): string;
getEditor(): BaseTextEditor<ICodeEditorViewState> | undefined;
getEditor(): AbstractTextCodeEditor<ICodeEditorViewState> | undefined;
deltaDecorations(newDecorationsRange: NotebookRange | NotebookRange[], oldDecorationsRange: NotebookRange | NotebookRange[]): void;
}
@@ -866,11 +866,11 @@ export class NotebookService extends Disposable implements INotebookService {
}
private get providersMemento(): NotebookProvidersMemento {
return this._providersMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE) as NotebookProvidersMemento;
return this._providersMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE) as NotebookProvidersMemento;
}
private get trustedNotebooksMemento(): TrustedNotebooksMemento {
let cache = this._trustedNotebooksMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE) as TrustedNotebooksMemento;
let cache = this._trustedNotebooksMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE) as TrustedNotebooksMemento;
if (!cache.trustedNotebooksCache) {
cache.trustedNotebooksCache = {};
}
@@ -12,12 +12,11 @@ import { IAsyncDataTreeNode, IAsyncDataTreeUpdateChildrenOptions } from 'vs/base
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IAsyncDataSource, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export class AsyncServerTree extends WorkbenchAsyncDataTree<ConnectionProfileGroup, ServerTreeElement, FuzzyScore> {
@@ -32,14 +31,13 @@ export class AsyncServerTree extends WorkbenchAsyncDataTree<ConnectionProfileGro
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(
user, container, delegate,
renderers, dataSource, options,
contextKeyService, listService,
themeService, configurationService, keybindingService, accessibilityService);
instantiationService, contextKeyService, listService,
themeService, configurationService);
// Adding support for expand/collapse on enter/space
this.onKeyDown(e => {
@@ -72,7 +72,7 @@ export class ProfilerService implements IProfilerService {
@IStorageService private _storageService: IStorageService
) {
this._context = new Memento('ProfilerEditor', this._storageService);
this._memento = this._context.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
this._memento = this._context.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE);
}
public registerProvider(providerId: string, provider: azdata.ProfilerProvider): void {
+6 -3
View File
@@ -2,7 +2,10 @@
"extends": "./tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"types": ["trusted-types"],
"types": [
"trusted-types",
"wicg-file-system-access"
],
"paths": {},
"module": "amd",
"moduleResolution": "classic",
@@ -15,9 +18,8 @@
"include": [
"typings/require.d.ts",
"typings/thenable.d.ts",
"vs/css.d.ts",
"vs/loader.d.ts",
"vs/monaco.d.ts",
"vs/nls.d.ts",
"vs/editor/*",
"vs/base/common/*",
"vs/base/browser/*",
@@ -28,6 +30,7 @@
"node_modules/*",
"vs/platform/files/browser/htmlFileSystemProvider.ts",
"vs/platform/files/browser/webFileSystemAccess.ts",
"vs/platform/telemetry/*",
"vs/platform/assignment/*"
]
}
+1
View File
@@ -14,6 +14,7 @@
"types": [],
"lib": [
"es5",
"ES2015.Iterable"
],
},
"include": [
+2 -1
View File
@@ -7,7 +7,7 @@
"vs/workbench/api/worker/extHostExtensionService.ts",
"vs/base/worker/workerMain",
"vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts",
"vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils.ts"
"vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts"
],
"ban-trustedtypes-createpolicy": [
"vs/base/browser/dom.ts",
@@ -15,6 +15,7 @@
"vs/base/browser/defaultWorkerFactory.ts",
"vs/base/worker/workerMain.ts",
"vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts",
"vs/editor/contrib/stickyScroll/browser/stickyScroll.ts",
"vs/editor/browser/view/domLineBreaksComputer.ts",
"vs/editor/browser/view/viewLayer.ts",
"vs/editor/browser/widget/diffEditorWidget.ts",
+69
View File
@@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getErrorMessage } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
export class BroadcastDataChannel<T> extends Disposable {
private broadcastChannel: BroadcastChannel | undefined;
private readonly _onDidReceiveData = this._register(new Emitter<T>());
readonly onDidReceiveData = this._onDidReceiveData.event;
constructor(private readonly channelName: string) {
super();
// Use BroadcastChannel
if ('BroadcastChannel' in window) {
try {
this.broadcastChannel = new BroadcastChannel(channelName);
const listener = (event: MessageEvent) => {
this._onDidReceiveData.fire(event.data);
};
this.broadcastChannel.addEventListener('message', listener);
this._register(toDisposable(() => {
if (this.broadcastChannel) {
this.broadcastChannel.removeEventListener('message', listener);
this.broadcastChannel.close();
}
}));
} catch (error) {
console.warn('Error while creating broadcast channel. Falling back to localStorage.', getErrorMessage(error));
}
}
// BroadcastChannel is not supported. Use storage.
if (!this.broadcastChannel) {
this.channelName = `BroadcastDataChannel.${channelName}`;
this.createBroadcastChannel();
}
}
private createBroadcastChannel(): void {
const listener = (event: StorageEvent) => {
if (event.key === this.channelName && event.newValue) {
this._onDidReceiveData.fire(JSON.parse(event.newValue));
}
};
window.addEventListener('storage', listener);
this._register(toDisposable(() => window.removeEventListener('storage', listener)));
}
/**
* Sends the data to other BroadcastChannel objects set up for this channel. Data can be structured objects, e.g. nested objects and arrays.
* @param data data to broadcast
*/
postData(data: T): void {
if (this.broadcastChannel) {
this.broadcastChannel.postMessage(data);
} else {
// remove previous changes so that event is triggered even if new changes are same as old changes
window.localStorage.removeItem(this.channelName);
window.localStorage.setItem(this.channelName, JSON.stringify(data));
}
}
}
+1 -3
View File
@@ -71,9 +71,7 @@ class DevicePixelRatioMonitor extends Disposable {
}
private _handleChange(fireEvent: boolean): void {
if (this._mediaQueryList) {
this._mediaQueryList.removeEventListener('change', this._listener);
}
this._mediaQueryList?.removeEventListener('change', this._listener);
this._mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
this._mediaQueryList.addEventListener('change', this._listener);
+3 -7
View File
@@ -86,15 +86,11 @@ class WebWorker implements IWorker {
}
public postMessage(message: any, transfer: Transferable[]): void {
if (this.worker) {
this.worker.then(w => w.postMessage(message, transfer));
}
this.worker?.then(w => w.postMessage(message, transfer));
}
public dispose(): void {
if (this.worker) {
this.worker.then(w => w.terminate());
}
this.worker?.then(w => w.terminate());
this.worker = null;
}
}
@@ -112,7 +108,7 @@ export class DefaultWorkerFactory implements IWorkerFactory {
}
public create(moduleId: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker {
let workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
if (this._webWorkerFailedBeforeError) {
throw this._webWorkerFailedBeforeError;
+108
View File
@@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// https://wicg.github.io/webusb/
export interface UsbDeviceData {
readonly deviceClass: number;
readonly deviceProtocol: number;
readonly deviceSubclass: number;
readonly deviceVersionMajor: number;
readonly deviceVersionMinor: number;
readonly deviceVersionSubminor: number;
readonly manufacturerName?: string;
readonly productId: number;
readonly productName?: string;
readonly serialNumber?: string;
readonly usbVersionMajor: number;
readonly usbVersionMinor: number;
readonly usbVersionSubminor: number;
readonly vendorId: number;
}
export async function requestUsbDevice(options?: { filters?: unknown[] }): Promise<UsbDeviceData | undefined> {
const usb = (navigator as any).usb;
if (!usb) {
return undefined;
}
const device = await usb.requestDevice({ filters: options?.filters ?? [] });
if (!device) {
return undefined;
}
return {
deviceClass: device.deviceClass,
deviceProtocol: device.deviceProtocol,
deviceSubclass: device.deviceSubclass,
deviceVersionMajor: device.deviceVersionMajor,
deviceVersionMinor: device.deviceVersionMinor,
deviceVersionSubminor: device.deviceVersionSubminor,
manufacturerName: device.manufacturerName,
productId: device.productId,
productName: device.productName,
serialNumber: device.serialNumber,
usbVersionMajor: device.usbVersionMajor,
usbVersionMinor: device.usbVersionMinor,
usbVersionSubminor: device.usbVersionSubminor,
vendorId: device.vendorId,
};
}
// https://wicg.github.io/serial/
export interface SerialPortData {
readonly usbVendorId?: number | undefined;
readonly usbProductId?: number | undefined;
}
export async function requestSerialPort(options?: { filters?: unknown[] }): Promise<SerialPortData | undefined> {
const serial = (navigator as any).serial;
if (!serial) {
return undefined;
}
const port = await serial.requestPort({ filters: options?.filters ?? [] });
if (!port) {
return undefined;
}
const info = port.getInfo();
return {
usbVendorId: info.usbVendorId,
usbProductId: info.usbProductId
};
}
// https://wicg.github.io/webhid/
export interface HidDeviceData {
readonly opened: boolean;
readonly vendorId: number;
readonly productId: number;
readonly productName: string;
readonly collections: [];
}
export async function requestHidDevice(options?: { filters?: unknown[] }): Promise<HidDeviceData | undefined> {
const hid = (navigator as any).hid;
if (!hid) {
return undefined;
}
const devices = await hid.requestDevice({ filters: options?.filters ?? [] });
if (!devices.length) {
return undefined;
}
const device = devices[0];
return {
opened: device.opened,
vendorId: device.vendorId,
productId: device.productId,
productName: device.productName,
collections: device.collections
};
}
+232 -84
View File
@@ -88,7 +88,7 @@ function _wrapAsStandardKeyboardEvent(handler: (e: IKeyboardEvent) => void): (e:
return handler(new StandardKeyboardEvent(e));
};
}
export let addStandardDisposableListener: IAddStandardDisposableListenerSignature = function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
export const addStandardDisposableListener: IAddStandardDisposableListenerSignature = function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
let wrapHandler = handler;
if (type === 'click' || type === 'mousedown') {
@@ -100,14 +100,14 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur
return addDisposableListener(node, type, wrapHandler, useCapture);
};
export let addStandardDisposableGenericMouseDownListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
let wrapHandler = _wrapAsStandardMouseEvent(handler);
export const addStandardDisposableGenericMouseDownListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
const wrapHandler = _wrapAsStandardMouseEvent(handler);
return addDisposableGenericMouseDownListener(node, wrapHandler, useCapture);
};
export let addStandardDisposableGenericMouseUpListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
let wrapHandler = _wrapAsStandardMouseEvent(handler);
export const addStandardDisposableGenericMouseUpListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
const wrapHandler = _wrapAsStandardMouseEvent(handler);
return addDisposableGenericMouseUpListener(node, wrapHandler, useCapture);
};
@@ -122,35 +122,6 @@ export function addDisposableGenericMouseMoveListener(node: EventTarget, handler
export function addDisposableGenericMouseUpListener(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable {
return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_UP : EventType.MOUSE_UP, handler, useCapture);
}
export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
return addDisposableListener(node, 'mouseout', (e: MouseEvent) => {
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
let toElement: Node | null = <Node>(e.relatedTarget);
while (toElement && toElement !== node) {
toElement = toElement.parentNode;
}
if (toElement === node) {
return;
}
handler(e);
});
}
export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
return addDisposableListener(node, 'pointerout', (e: MouseEvent) => {
// Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
let toElement: Node | null = <Node>(e.relatedTarget);
while (toElement && toElement !== node) {
toElement = toElement.parentNode;
}
if (toElement === node) {
return;
}
handler(e);
});
}
export function createEventEmitter<K extends keyof HTMLElementEventMap>(target: HTMLElement, type: K, options?: boolean | AddEventListenerOptions): event.Emitter<HTMLElementEventMap[K]> {
let domListener: DomListener | null = null;
@@ -258,7 +229,7 @@ class AnimationFrameQueueItem implements IDisposable {
*/
let inAnimationFrameRunner = false;
let animationFrameRunner = () => {
const animationFrameRunner = () => {
animFrameRequested = false;
CURRENT_QUEUE = NEXT_QUEUE;
@@ -267,14 +238,14 @@ class AnimationFrameQueueItem implements IDisposable {
inAnimationFrameRunner = true;
while (CURRENT_QUEUE.length > 0) {
CURRENT_QUEUE.sort(AnimationFrameQueueItem.sort);
let top = CURRENT_QUEUE.shift()!;
const top = CURRENT_QUEUE.shift()!;
top.execute();
}
inAnimationFrameRunner = false;
};
scheduleAtNextAnimationFrame = (runner: () => void, priority: number = 0) => {
let item = new AnimationFrameQueueItem(runner, priority);
const item = new AnimationFrameQueueItem(runner, priority);
NEXT_QUEUE.push(item);
if (!animFrameRequested) {
@@ -287,7 +258,7 @@ class AnimationFrameQueueItem implements IDisposable {
runAtThisOrScheduleAtNextAnimationFrame = (runner: () => void, priority?: number) => {
if (inAnimationFrameRunner) {
let item = new AnimationFrameQueueItem(runner, priority);
const item = new AnimationFrameQueueItem(runner, priority);
CURRENT_QUEUE!.push(item);
return item;
} else {
@@ -323,9 +294,9 @@ class TimeoutThrottledDomListener<R, E extends Event> extends Disposable {
let lastEvent: R | null = null;
let lastHandlerTime = 0;
let timeout = this._register(new TimeoutTimer());
const timeout = this._register(new TimeoutTimer());
let invokeHandler = () => {
const invokeHandler = () => {
lastHandlerTime = (new Date()).getTime();
handler(<R>lastEvent);
lastEvent = null;
@@ -334,7 +305,7 @@ class TimeoutThrottledDomListener<R, E extends Event> extends Disposable {
this._register(addDisposableListener(node, type, (e) => {
lastEvent = eventMerger(lastEvent, e);
let elapsedTime = (new Date()).getTime() - lastHandlerTime;
const elapsedTime = (new Date()).getTime() - lastHandlerTime;
if (elapsedTime >= minimumTimeMs) {
timeout.cancel();
@@ -392,7 +363,7 @@ class SizeUtils {
}
private static getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number {
let computedStyle: CSSStyleDeclaration = getComputedStyle(element);
const computedStyle: CSSStyleDeclaration = getComputedStyle(element);
let value = '0';
if (computedStyle) {
if (computedStyle.getPropertyValue) {
@@ -568,7 +539,7 @@ export function position(element: HTMLElement, top: number, right?: number, bott
* Returns the position of a dom node relative to the entire page.
*/
export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePosition {
let bb = domNode.getBoundingClientRect();
const bb = domNode.getBoundingClientRect();
return {
left: bb.left + StandardWindow.scrollX,
top: bb.top + StandardWindow.scrollY,
@@ -577,6 +548,24 @@ export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePositi
};
}
/**
* Returns the effective zoom on a given element before window zoom level is applied
*/
export function getDomNodeZoomLevel(domNode: HTMLElement): number {
let testElement: HTMLElement | null = domNode;
let zoom = 1.0;
do {
const elementZoomLevel = (getComputedStyle(testElement) as any).zoom;
if (elementZoomLevel !== null && elementZoomLevel !== undefined && elementZoomLevel !== '1') {
zoom *= elementZoomLevel;
}
testElement = testElement.parentElement;
} while (testElement !== null && testElement !== document.documentElement);
return zoom;
}
export interface IStandardWindow {
readonly scrollX: number;
readonly scrollY: number;
@@ -605,33 +594,33 @@ export const StandardWindow: IStandardWindow = new class implements IStandardWin
// Adapted from WinJS
// Gets the width of the element, including margins.
export function getTotalWidth(element: HTMLElement): number {
let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
const margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
return element.offsetWidth + margin;
}
export function getContentWidth(element: HTMLElement): number {
let border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element);
let padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element);
const border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element);
const padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element);
return element.offsetWidth - border - padding;
}
export function getTotalScrollWidth(element: HTMLElement): number {
let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
const margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
return element.scrollWidth + margin;
}
// Adapted from WinJS
// Gets the height of the content of the specified element. The content height does not include borders or padding.
export function getContentHeight(element: HTMLElement): number {
let border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element);
let padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element);
const border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element);
const padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element);
return element.offsetHeight - border - padding;
}
// Adapted from WinJS
// Gets the height of the element, including its margins.
export function getTotalHeight(element: HTMLElement): number {
let margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element);
const margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element);
return element.offsetHeight + margin;
}
@@ -641,16 +630,16 @@ function getRelativeLeft(element: HTMLElement, parent: HTMLElement): number {
return 0;
}
let elementPosition = getTopLeftOffset(element);
let parentPosition = getTopLeftOffset(parent);
const elementPosition = getTopLeftOffset(element);
const parentPosition = getTopLeftOffset(parent);
return elementPosition.left - parentPosition.left;
}
export function getLargestChildWidth(parent: HTMLElement, children: HTMLElement[]): number {
let childWidths = children.map((child) => {
const childWidths = children.map((child) => {
return Math.max(getTotalScrollWidth(child), getTotalWidth(child)) + getRelativeLeft(child, parent) || 0;
});
let maxWidth = Math.max(...childWidths);
const maxWidth = Math.max(...childWidths);
return maxWidth;
}
@@ -769,7 +758,7 @@ export function getActiveElement(): Element | null {
}
export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement {
let style = document.createElement('style');
const style = document.createElement('style');
style.type = 'text/css';
style.media = 'screen';
container.appendChild(style);
@@ -777,7 +766,7 @@ export function createStyleSheet(container: HTMLElement = document.getElementsBy
}
export function createMetaElement(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLMetaElement {
let meta = document.createElement('meta');
const meta = document.createElement('meta');
container.appendChild(meta);
return meta;
}
@@ -815,10 +804,10 @@ export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLSt
return;
}
let rules = getDynamicStyleSheetRules(style);
let toDelete: number[] = [];
const rules = getDynamicStyleSheetRules(style);
const toDelete: number[] = [];
for (let i = 0; i < rules.length; i++) {
let rule = rules[i];
const rule = rules[i];
if (rule.selectorText.indexOf(ruleName) !== -1) {
toDelete.push(i);
}
@@ -852,6 +841,7 @@ export const EventType = {
POINTER_UP: 'pointerup',
POINTER_DOWN: 'pointerdown',
POINTER_MOVE: 'pointermove',
POINTER_LEAVE: 'pointerleave',
CONTEXT_MENU: 'contextmenu',
WHEEL: 'wheel',
// Keyboard
@@ -928,7 +918,7 @@ export interface IFocusTracker extends Disposable {
}
export function saveParentsScrollTop(node: Element): number[] {
let r: number[] = [];
const r: number[] = [];
for (let i = 0; node && node.nodeType === node.ELEMENT_NODE; i++) {
r[i] = node.scrollTop;
node = <Element>node.parentNode;
@@ -988,7 +978,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
};
this._refreshStateHandler = () => {
let currentNodeHasFocus = FocusTracker.hasFocusWithin(<HTMLElement>element);
const currentNodeHasFocus = FocusTracker.hasFocusWithin(<HTMLElement>element);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();
@@ -1048,7 +1038,7 @@ export enum Namespace {
}
function _$<T extends Element>(namespace: Namespace, description: string, attrs?: { [key: string]: any }, ...children: Array<Node | string>): T {
let match = SELECTOR_REGEX.exec(description);
const match = SELECTOR_REGEX.exec(description);
if (!match) {
throw new Error('Bad use of emmet');
@@ -1056,7 +1046,7 @@ function _$<T extends Element>(namespace: Namespace, description: string, attrs?
attrs = { ...(attrs || {}) };
let tagName = match[1] || 'div';
const tagName = match[1] || 'div';
let result: T;
if (namespace !== Namespace.HTML) {
@@ -1123,14 +1113,14 @@ export function join(nodes: Node[], separator: Node | string): Node[] {
}
export function show(...elements: HTMLElement[]): void {
for (let element of elements) {
for (const element of elements) {
element.style.display = '';
element.removeAttribute('aria-hidden');
}
}
export function hide(...elements: HTMLElement[]): void {
for (let element of elements) {
for (const element of elements) {
element.style.display = 'none';
element.setAttribute('aria-hidden', 'true');
}
@@ -1158,7 +1148,7 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void {
// typically never want that, rather put focus to the closest element
// in the hierarchy of the parent DOM nodes.
if (document.activeElement === node) {
let parentFocusable = findParentWithAttribute(node.parentElement, 'tabIndex');
const parentFocusable = findParentWithAttribute(node.parentElement, 'tabIndex');
if (parentFocusable) {
parentFocusable.focus();
}
@@ -1289,7 +1279,7 @@ RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? '
/**
* returns url('...')
*/
export function asCSSUrl(uri: URI): string {
export function asCSSUrl(uri: URI | null | undefined): string {
if (!uri) {
return `url('')`;
}
@@ -1669,25 +1659,12 @@ export function getCookieValue(name: string): string | undefined {
return match ? match.pop() : undefined;
}
export const enum ZIndex {
SASH = 35,
SuggestWidget = 40,
Hover = 50,
DragImage = 1000,
MenubarMenuItemsHolder = 2000, // quick-input-widget
ContextView = 2500,
ModalDialog = 2600,
PaneDropOverlay = 10000
}
export interface IDragAndDropObserverCallbacks {
readonly onDragEnter: (e: DragEvent) => void;
readonly onDragLeave: (e: DragEvent) => void;
readonly onDrop: (e: DragEvent) => void;
readonly onDragEnd: (e: DragEvent) => void;
readonly onDragOver?: (e: DragEvent) => void;
readonly onDragOver?: (e: DragEvent, dragDuration: number) => void;
}
export class DragAndDropObserver extends Disposable {
@@ -1698,6 +1675,9 @@ export class DragAndDropObserver extends Disposable {
// repeadedly.
private counter: number = 0;
// Allows to measure the duration of the drag operation.
private dragStartTime = 0;
constructor(private readonly element: HTMLElement, private readonly callbacks: IDragAndDropObserverCallbacks) {
super();
@@ -1707,6 +1687,7 @@ export class DragAndDropObserver extends Disposable {
private registerListeners(): void {
this._register(addDisposableListener(this.element, EventType.DRAG_ENTER, (e: DragEvent) => {
this.counter++;
this.dragStartTime = e.timeStamp;
this.callbacks.onDragEnter(e);
}));
@@ -1714,27 +1695,194 @@ export class DragAndDropObserver extends Disposable {
this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => {
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
if (this.callbacks.onDragOver) {
this.callbacks.onDragOver(e);
}
this.callbacks.onDragOver?.(e, e.timeStamp - this.dragStartTime);
}));
this._register(addDisposableListener(this.element, EventType.DRAG_LEAVE, (e: DragEvent) => {
this.counter--;
if (this.counter === 0) {
this.dragStartTime = 0;
this.callbacks.onDragLeave(e);
}
}));
this._register(addDisposableListener(this.element, EventType.DRAG_END, (e: DragEvent) => {
this.counter = 0;
this.dragStartTime = 0;
this.callbacks.onDragEnd(e);
}));
this._register(addDisposableListener(this.element, EventType.DROP, (e: DragEvent) => {
this.counter = 0;
this.dragStartTime = 0;
this.callbacks.onDrop(e);
}));
}
}
export function computeClippingRect(elementOrRect: HTMLElement | DOMRectReadOnly, clipper: HTMLElement) {
const frameRect = (elementOrRect instanceof HTMLElement ? elementOrRect.getBoundingClientRect() : elementOrRect);
const rootRect = clipper.getBoundingClientRect();
const top = Math.max(rootRect.top - frameRect.top, 0);
const right = Math.max(frameRect.width - (frameRect.right - rootRect.right), 0);
const bottom = Math.max(frameRect.height - (frameRect.bottom - rootRect.bottom), 0);
const left = Math.max(rootRect.left - frameRect.left, 0);
return { top, right, bottom, left };
}
type HTMLElementAttributeKeys<T> = Partial<{ [K in keyof T]: T[K] extends Function ? never : T[K] extends object ? HTMLElementAttributeKeys<T[K]> : T[K] }>;
type ElementAttributes<T> = HTMLElementAttributeKeys<T> & Record<string, any>;
type RemoveHTMLElement<T> = T extends HTMLElement ? never : T;
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type ArrayToObj<T extends readonly any[]> = UnionToIntersection<RemoveHTMLElement<T[number]>>;
type HHTMLElementTagNameMap = HTMLElementTagNameMap & { '': HTMLDivElement };
type TagToElement<T> = T extends `${infer TStart}#${string}`
? TStart extends keyof HHTMLElementTagNameMap
? HHTMLElementTagNameMap[TStart]
: HTMLElement
: T extends `${infer TStart}.${string}`
? TStart extends keyof HHTMLElementTagNameMap
? HHTMLElementTagNameMap[TStart]
: HTMLElement
: T extends keyof HTMLElementTagNameMap
? HTMLElementTagNameMap[T]
: HTMLElement;
type TagToElementAndId<TTag> = TTag extends `${infer TTag}@${infer TId}`
? { element: TagToElement<TTag>; id: TId }
: { element: TagToElement<TTag>; id: 'root' };
type TagToRecord<TTag> = TagToElementAndId<TTag> extends { element: infer TElement; id: infer TId }
? Record<(TId extends string ? TId : never) | 'root', TElement>
: never;
type Child = HTMLElement | string | Record<string, HTMLElement>;
type Children = []
| [Child]
| [Child, Child]
| [Child, Child, Child]
| [Child, Child, Child, Child]
| [Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child];
const H_REGEX = /(?<tag>[\w\-]+)?(?:#(?<id>[\w\-]+))?(?<class>(?:\.(?:[\w\-]+))*)(?:@(?<name>(?:[\w\_])+))?/;
/**
* A helper function to create nested dom nodes.
*
*
* ```ts
* const elements = h('div.code-view', [
* h('div.title@title'),
* h('div.container', [
* h('div.gutter@gutterDiv'),
* h('div@editor'),
* ]),
* ]);
* const editor = createEditor(elements.editor);
* ```
*/
export function h<TTag extends string>
(tag: TTag):
TagToRecord<TTag> extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
export function h<TTag extends string, T extends Children>
(tag: TTag, children: T):
(ArrayToObj<T> & TagToRecord<TTag>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
export function h<TTag extends string>
(tag: TTag, attributes: Partial<ElementAttributes<TagToElement<TTag>>>):
TagToRecord<TTag> extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
export function h<TTag extends string, T extends Children>
(tag: TTag, attributes: Partial<ElementAttributes<TagToElement<TTag>>>, children: T):
(ArrayToObj<T> & TagToRecord<TTag>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
export function h(tag: string, ...args: [] | [attributes: { $: string } & Partial<ElementAttributes<HTMLElement>> | Record<string, any>, children?: any[]] | [children: any[]]): Record<string, HTMLElement> {
let attributes: { $?: string } & Partial<ElementAttributes<HTMLElement>>;
let children: (Record<string, HTMLElement> | HTMLElement)[] | undefined;
if (Array.isArray(args[0])) {
attributes = {};
children = args[0];
} else {
attributes = args[0] as any || {};
children = args[1];
}
const match = H_REGEX.exec(tag);
if (!match || !match.groups) {
throw new Error('Bad use of h');
}
const tagName = match.groups['tag'] || 'div';
const el = document.createElement(tagName);
if (match.groups['id']) {
el.id = match.groups['id'];
}
if (match.groups['class']) {
el.className = match.groups['class'].replace(/\./g, ' ').trim();
}
const result: Record<string, HTMLElement> = {};
if (match.groups['name']) {
result[match.groups['name']] = el;
}
if (children) {
for (const c of children) {
if (c instanceof HTMLElement) {
el.appendChild(c);
} else if (typeof c === 'string') {
el.append(c);
} else {
Object.assign(result, c);
el.appendChild(c.root);
}
}
}
for (const [key, value] of Object.entries(attributes)) {
if (key === 'style') {
for (const [cssKey, cssValue] of Object.entries(value)) {
el.style.setProperty(
camelCaseToHyphenCase(cssKey),
typeof cssValue === 'number' ? cssValue + 'px' : '' + cssValue
);
}
} else if (key === 'tabIndex') {
el.tabIndex = value;
} else {
el.setAttribute(camelCaseToHyphenCase(key), value.toString());
}
}
result['root'] = el;
return result;
}
function camelCaseToHyphenCase(str: string) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
+1 -1
View File
@@ -8,7 +8,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { DisposableStore } from 'vs/base/common/lifecycle';
export interface IContentActionHandler {
callback: (content: string, event?: IMouseEvent) => void;
callback: (content: string, event: IMouseEvent) => void;
readonly disposables: DisposableStore;
}
+13 -37
View File
@@ -6,40 +6,18 @@
import * as dom from 'vs/base/browser/dom';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
export interface IPointerMoveEventData {
leftButton: boolean;
buttons: number;
pageX: number;
pageY: number;
}
export interface IEventMerger<R> {
(lastEvent: R | null, currentEvent: PointerEvent): R;
}
export interface IPointerMoveCallback<R> {
(pointerMoveData: R): void;
export interface IPointerMoveCallback {
(event: PointerEvent): void;
}
export interface IOnStopCallback {
(browserEvent?: PointerEvent | KeyboardEvent): void;
}
export function standardPointerMoveMerger(lastEvent: IPointerMoveEventData | null, currentEvent: PointerEvent): IPointerMoveEventData {
currentEvent.preventDefault();
return {
leftButton: (currentEvent.button === 0),
buttons: currentEvent.buttons,
pageX: currentEvent.pageX,
pageY: currentEvent.pageY
};
}
export class GlobalPointerMoveMonitor<R extends { buttons: number } = IPointerMoveEventData> implements IDisposable {
export class GlobalPointerMoveMonitor implements IDisposable {
private readonly _hooks = new DisposableStore();
private _pointerMoveEventMerger: IEventMerger<R> | null = null;
private _pointerMoveCallback: IPointerMoveCallback<R> | null = null;
private _pointerMoveCallback: IPointerMoveCallback | null = null;
private _onStopCallback: IOnStopCallback | null = null;
public dispose(): void {
@@ -55,7 +33,6 @@ export class GlobalPointerMoveMonitor<R extends { buttons: number } = IPointerMo
// Unhook
this._hooks.clear();
this._pointerMoveEventMerger = null;
this._pointerMoveCallback = null;
const onStopCallback = this._onStopCallback;
this._onStopCallback = null;
@@ -66,21 +43,19 @@ export class GlobalPointerMoveMonitor<R extends { buttons: number } = IPointerMo
}
public isMonitoring(): boolean {
return !!this._pointerMoveEventMerger;
return !!this._pointerMoveCallback;
}
public startMonitoring(
initialElement: Element,
pointerId: number,
initialButtons: number,
pointerMoveEventMerger: IEventMerger<R>,
pointerMoveCallback: IPointerMoveCallback<R>,
pointerMoveCallback: IPointerMoveCallback,
onStopCallback: IOnStopCallback
): void {
if (this.isMonitoring()) {
this.stopMonitoring(false);
}
this._pointerMoveEventMerger = pointerMoveEventMerger;
this._pointerMoveCallback = pointerMoveCallback;
this._onStopCallback = onStopCallback;
@@ -103,18 +78,19 @@ export class GlobalPointerMoveMonitor<R extends { buttons: number } = IPointerMo
eventSource = window;
}
this._hooks.add(dom.addDisposableThrottledListener<R, PointerEvent>(
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_MOVE,
(data: R) => {
if (data.buttons !== initialButtons) {
(e) => {
if (e.buttons !== initialButtons) {
// Buttons state has changed in the meantime
this.stopMonitoring(true);
return;
}
this._pointerMoveCallback!(data);
},
(lastEvent: R | null, currentEvent) => this._pointerMoveEventMerger!(lastEvent, currentEvent)
e.preventDefault();
this._pointerMoveCallback!(e);
}
));
this._hooks.add(dom.addDisposableListener(
+8
View File
@@ -3,10 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
export interface IHistoryNavigationWidget {
readonly element: HTMLElement;
showPreviousValue(): void;
showNextValue(): void;
onDidFocus: Event<void>;
onDidBlur: Event<void>;
}
+4 -4
View File
@@ -27,8 +27,8 @@ function getParentWindowIfSameOrigin(w: Window): Window | null {
// Cannot really tell if we have access to the parent window unless we try to access something in it
try {
let location = w.location;
let parentLocation = w.parent.location;
const location = w.location;
const parentLocation = w.parent.location;
if (location.origin !== 'null' && parentLocation.origin !== 'null' && location.origin !== parentLocation.origin) {
hasDifferentOriginAncestorFlag = true;
return null;
@@ -97,7 +97,7 @@ export class IframeUtils {
let top = 0, left = 0;
let windowChain = this.getSameOriginWindowChain();
const windowChain = this.getSameOriginWindowChain();
for (const windowChainEl of windowChain) {
@@ -112,7 +112,7 @@ export class IframeUtils {
break;
}
let boundingRect = windowChainEl.iframeElement.getBoundingClientRect();
const boundingRect = windowChainEl.iframeElement.getBoundingClientRect();
top += boundingRect.top;
left += boundingRect.left;
}
+10 -3
View File
@@ -14,6 +14,13 @@ class MissingStoresError extends Error {
}
}
export class DBClosedError extends Error {
readonly code = 'DBClosed';
constructor(dbName: string) {
super(`IndexedDB database '${dbName}' is closed.`);
}
}
export class IndexedDB {
static async create(name: string, version: number | undefined, stores: string[]): Promise<IndexedDB> {
@@ -21,7 +28,7 @@ export class IndexedDB {
return new IndexedDB(database, name);
}
static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
mark(`code/willOpenDatabase/${name}`);
try {
return await IndexedDB.doOpenDatabase(name, version, stores);
@@ -109,7 +116,7 @@ export class IndexedDB {
runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T>): Promise<T>;
async runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T> | IDBRequest<T>[]): Promise<T | T[]> {
if (!this.database) {
throw new Error(`IndexedDB database '${this.name}' is not opened.`);
throw new DBClosedError(this.name);
}
const transaction = this.database.transaction(store, transactionMode);
this.pendingTransactions.push(transaction);
@@ -128,7 +135,7 @@ export class IndexedDB {
async getKeyValues<V>(store: string, isValid: (value: unknown) => value is V): Promise<Map<string, V>> {
if (!this.database) {
throw new Error(`IndexedDB database '${this.name}' is not opened.`);
throw new DBClosedError(this.name);
}
const transaction = this.database.transaction(store, 'readonly');
this.pendingTransactions.push(transaction);
+4 -4
View File
@@ -13,7 +13,7 @@ import * as platform from 'vs/base/common/platform';
function extractKeyCode(e: KeyboardEvent): KeyCode {
if (e.charCode) {
// "keypress" events mostly
let char = String.fromCharCode(e.charCode).toUpperCase();
const char = String.fromCharCode(e.charCode).toUpperCase();
return KeyCodeUtils.fromString(char);
}
@@ -77,7 +77,7 @@ const shiftKeyMod = KeyMod.Shift;
const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl);
export function printKeyboardEvent(e: KeyboardEvent): string {
let modifiers: string[] = [];
const modifiers: string[] = [];
if (e.ctrlKey) {
modifiers.push(`ctrl`);
}
@@ -94,7 +94,7 @@ export function printKeyboardEvent(e: KeyboardEvent): string {
}
export function printStandardKeyboardEvent(e: StandardKeyboardEvent): string {
let modifiers: string[] = [];
const modifiers: string[] = [];
if (e.ctrlKey) {
modifiers.push(`ctrl`);
}
@@ -128,7 +128,7 @@ export class StandardKeyboardEvent implements IKeyboardEvent {
private _asRuntimeKeybinding: SimpleKeybinding;
constructor(source: KeyboardEvent) {
let e = source;
const e = source;
this.browserEvent = e;
this.target = <HTMLElement>e.target;
+56 -54
View File
@@ -9,11 +9,9 @@ import { DomEmitter } from 'vs/base/browser/event';
import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
import { IMarkdownString, escapeDoubleQuotes, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
import { markdownEscapeEscapedIcons } from 'vs/base/common/iconLabels';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -44,8 +42,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
const disposables = new DisposableStore();
let isDisposed = false;
const cts = disposables.add(new CancellationTokenSource());
const element = createElement(options);
const _uriMassage = function (part: string): string {
@@ -96,11 +92,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
return uri.toString();
};
// signal to code-block render that the
// element has been created
let signalInnerHTML: () => void;
const withInnerHTML = new Promise<void>(c => signalInnerHTML = c);
const renderer = new marked.Renderer();
renderer.image = (href: string, title: string, text: string) => {
@@ -108,13 +99,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
let attributes: string[] = [];
if (href) {
({ href, dimensions } = parseHrefAndDimensions(href));
attributes.push(`src="${href}"`);
attributes.push(`src="${escapeDoubleQuotes(href)}"`);
}
if (text) {
attributes.push(`alt="${text}"`);
attributes.push(`alt="${escapeDoubleQuotes(text)}"`);
}
if (title) {
attributes.push(`title="${title}"`);
attributes.push(`title="${escapeDoubleQuotes(title)}"`);
}
if (dimensions.length) {
attributes = attributes.concat(dimensions);
@@ -130,53 +121,30 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
if (href === text) { // raw link case
text = removeMarkdownEscapes(text);
}
href = _href(href, false);
if (markdown.baseUri) {
href = resolveWithBaseUri(URI.from(markdown.baseUri), href);
}
title = typeof title === 'string' ? removeMarkdownEscapes(title) : '';
href = removeMarkdownEscapes(href);
if (
!href
|| /^data:|javascript:/i.test(href)
|| (/^command:/i.test(href) && !markdown.isTrusted)
|| /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href)
) {
// drop the link
return text;
} else {
// HTML Encode href
href = href.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
return `<a data-href="${href}" title="${title || href}">${text}</a>`;
}
title = typeof title === 'string' ? escapeDoubleQuotes(removeMarkdownEscapes(title)) : '';
href = removeMarkdownEscapes(href);
// HTML Encode href
href = href.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
return `<a href="${href}" title="${title || href}">${text}</a>`;
};
renderer.paragraph = (text): string => {
return `<p>${text}</p>`;
};
// Will collect [id, renderedElement] tuples
const codeBlocks: Promise<[string, HTMLElement]>[] = [];
if (options.codeBlockRenderer) {
renderer.code = (code, lang) => {
const value = options.codeBlockRenderer!(lang ?? '', code);
// when code-block rendering is async we return sync
// but update the node with the real result later.
const id = defaultGenerator.nextId();
raceCancellation(Promise.all([value, withInnerHTML]), cts.token).then(values => {
if (!isDisposed && values) {
const span = element.querySelector<HTMLDivElement>(`div[data-code="${id}"]`);
if (span) {
DOM.reset(span, values[0]);
}
options.asyncRenderCallback?.();
}
}).catch(() => {
// ignore
});
const value = options.codeBlockRenderer!(lang ?? '', code);
codeBlocks.push(value.then(element => [id, element]));
return `<div class="code" data-code="${id}">${escape(code)}</div>`;
};
}
@@ -268,10 +236,45 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
}
});
markdownHtmlDoc.body.querySelectorAll('a')
.forEach(a => {
const href = a.getAttribute('href'); // Get the raw 'href' attribute value as text, not the resolved 'href'
a.setAttribute('href', ''); // Clear out href. We use the `data-href` for handling clicks instead
if (
!href
|| /^data:|javascript:/i.test(href)
|| (/^command:/i.test(href) && !markdown.isTrusted)
|| /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href)
) {
// drop the link
a.replaceWith(...a.childNodes);
} else {
let resolvedHref = _href(href, false);
if (markdown.baseUri) {
resolvedHref = resolveWithBaseUri(URI.from(markdown.baseUri), href);
}
a.dataset.href = resolvedHref;
}
});
element.innerHTML = sanitizeRenderedMarkdown(markdown, markdownHtmlDoc.body.innerHTML) as unknown as string;
// signal that async code blocks can be now be inserted
signalInnerHTML!();
if (codeBlocks.length > 0) {
Promise.all(codeBlocks).then((tuples) => {
if (isDisposed) {
return;
}
const renderedElements = new Map(tuples);
const placeholderElements = element.querySelectorAll<HTMLDivElement>(`div[data-code]`);
for (const placeholderElement of placeholderElements) {
const renderedElement = renderedElements.get(placeholderElement.dataset['code'] ?? '');
if (renderedElement) {
DOM.reset(placeholderElement, renderedElement);
}
}
options.asyncRenderCallback?.();
});
}
// signal size changes for image tags
if (options.asyncRenderCallback) {
@@ -287,7 +290,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
element,
dispose: () => {
isDisposed = true;
cts.cancel();
disposables.dispose();
}
};
+3 -3
View File
@@ -74,7 +74,7 @@ export class StandardMouseEvent implements IMouseEvent {
}
// Find the position of the iframe this code is executing in relative to the iframe where the event was captured.
let iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(self, e.view);
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(self, e.view);
this.posx -= iframeOffsets.left;
this.posy -= iframeOffsets.top;
}
@@ -152,8 +152,8 @@ export class StandardWheelEvent {
if (e) {
// Old (deprecated) wheel events
let e1 = <IWebKitMouseWheelEvent><any>e;
let e2 = <IGeckoMouseWheelEvent><any>e;
const e1 = <IWebKitMouseWheelEvent><any>e;
const e2 = <IGeckoMouseWheelEvent><any>e;
// vertical delta scroll
if (typeof e1.wheelDeltaY !== 'undefined') {
+24 -24
View File
@@ -146,7 +146,7 @@ export class Gesture extends Disposable {
}
private onTouchStart(e: TouchEvent): void {
let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
if (this.handle) {
this.handle.dispose();
@@ -154,7 +154,7 @@ export class Gesture extends Disposable {
}
for (let i = 0, len = e.targetTouches.length; i < len; i++) {
let touch = e.targetTouches.item(i);
const touch = e.targetTouches.item(i);
this.activeTouches[touch.identifier] = {
id: touch.identifier,
@@ -167,7 +167,7 @@ export class Gesture extends Disposable {
rollingPageY: [touch.pageY]
};
let evt = this.newGestureEvent(EventType.Start, touch.target);
const evt = this.newGestureEvent(EventType.Start, touch.target);
evt.pageX = touch.pageX;
evt.pageY = touch.pageY;
this.dispatchEvent(evt);
@@ -181,27 +181,27 @@ export class Gesture extends Disposable {
}
private onTouchEnd(e: TouchEvent): void {
let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
let activeTouchCount = Object.keys(this.activeTouches).length;
const activeTouchCount = Object.keys(this.activeTouches).length;
for (let i = 0, len = e.changedTouches.length; i < len; i++) {
let touch = e.changedTouches.item(i);
const touch = e.changedTouches.item(i);
if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) {
console.warn('move of an UNKNOWN touch', touch);
continue;
}
let data = this.activeTouches[touch.identifier],
const data = this.activeTouches[touch.identifier],
holdTime = Date.now() - data.initialTimeStamp;
if (holdTime < Gesture.HOLD_DELAY
&& Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30
&& Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) {
let evt = this.newGestureEvent(EventType.Tap, data.initialTarget);
const evt = this.newGestureEvent(EventType.Tap, data.initialTarget);
evt.pageX = arrays.tail(data.rollingPageX);
evt.pageY = arrays.tail(data.rollingPageY);
this.dispatchEvent(evt);
@@ -210,18 +210,18 @@ export class Gesture extends Disposable {
&& Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30
&& Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) {
let evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget);
const evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget);
evt.pageX = arrays.tail(data.rollingPageX);
evt.pageY = arrays.tail(data.rollingPageY);
this.dispatchEvent(evt);
} else if (activeTouchCount === 1) {
let finalX = arrays.tail(data.rollingPageX);
let finalY = arrays.tail(data.rollingPageY);
const finalX = arrays.tail(data.rollingPageX);
const finalY = arrays.tail(data.rollingPageY);
let deltaT = arrays.tail(data.rollingTimestamps) - data.rollingTimestamps[0];
let deltaX = finalX - data.rollingPageX[0];
let deltaY = finalY - data.rollingPageY[0];
const deltaT = arrays.tail(data.rollingTimestamps) - data.rollingTimestamps[0];
const deltaX = finalX - data.rollingPageX[0];
const deltaY = finalY - data.rollingPageY[0];
// We need to get all the dispatch targets on the start of the inertia event
const dispatchTo = this.targets.filter(t => data.initialTarget instanceof Node && t.contains(data.initialTarget));
@@ -249,7 +249,7 @@ export class Gesture extends Disposable {
}
private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent {
let event = document.createEvent('CustomEvent') as unknown as GestureEvent;
const event = document.createEvent('CustomEvent') as unknown as GestureEvent;
event.initEvent(type, false, true);
event.initialTarget = initialTarget;
event.tapCount = 0;
@@ -289,12 +289,12 @@ export class Gesture extends Disposable {
private inertia(dispatchTo: EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void {
this.handle = DomUtils.scheduleAtNextAnimationFrame(() => {
let now = Date.now();
const now = Date.now();
// velocity: old speed + accel_over_time
let deltaT = now - t1,
delta_pos_x = 0, delta_pos_y = 0,
stopped = true;
const deltaT = now - t1;
let delta_pos_x = 0, delta_pos_y = 0;
let stopped = true;
vX += Gesture.SCROLL_FRICTION * deltaT;
vY += Gesture.SCROLL_FRICTION * deltaT;
@@ -310,7 +310,7 @@ export class Gesture extends Disposable {
}
// dispatch translation event
let evt = this.newGestureEvent(EventType.Change);
const evt = this.newGestureEvent(EventType.Change);
evt.translationX = delta_pos_x;
evt.translationY = delta_pos_y;
dispatchTo.forEach(d => d.dispatchEvent(evt));
@@ -322,20 +322,20 @@ export class Gesture extends Disposable {
}
private onTouchMove(e: TouchEvent): void {
let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
for (let i = 0, len = e.changedTouches.length; i < len; i++) {
let touch = e.changedTouches.item(i);
const touch = e.changedTouches.item(i);
if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) {
console.warn('end of an UNKNOWN touch', touch);
continue;
}
let data = this.activeTouches[touch.identifier];
const data = this.activeTouches[touch.identifier];
let evt = this.newGestureEvent(EventType.Change, data.initialTarget);
const evt = this.newGestureEvent(EventType.Change, data.initialTarget);
evt.translationX = touch.pageX - arrays.tail(data.rollingPageX);
evt.translationY = touch.pageY - arrays.tail(data.rollingPageY);
evt.pageX = touch.pageX;
@@ -9,6 +9,8 @@ import { $, addDisposableListener, append, EventHelper, EventLike, EventType } f
import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch';
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover';
import { ISelectBoxOptions, ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import { Action, ActionRunner, IAction, IActionChangeEvent, IActionRunner, Separator } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -21,6 +23,7 @@ export interface IBaseActionViewItemOptions {
draggable?: boolean;
isMenu?: boolean;
useEventAsContext?: boolean;
hoverDelegate?: IHoverDelegate;
}
export class BaseActionViewItem extends Disposable implements IActionViewItem {
@@ -28,7 +31,9 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
element: HTMLElement | undefined;
_context: unknown;
_action: IAction;
readonly _action: IAction;
private customHover?: ICustomHover;
get action() {
return this._action;
@@ -213,8 +218,27 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
// implement in subclass
}
protected getTooltip(): string | undefined {
return this.getAction().tooltip;
}
protected updateTooltip(): void {
// implement in subclass
if (!this.element) {
return;
}
const title = this.getTooltip() ?? '';
this.element.setAttribute('aria-label', title);
if (!this.options.hoverDelegate) {
this.element.title = title;
} else {
this.element.title = '';
if (!this.customHover) {
this.customHover = setupCustomHover(this.options.hoverDelegate, this.element, title);
this._store.add(this.customHover);
} else {
this.customHover.update(title);
}
}
}
protected updateClass(): void {
@@ -323,7 +347,7 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
override updateTooltip(): void {
override getTooltip() {
let title: string | null = null;
if (this.getAction().tooltip) {
@@ -336,11 +360,7 @@ export class ActionViewItem extends BaseActionViewItem {
title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding);
}
}
if (title && this.label) {
this.label.title = title;
this.label.setAttribute('aria-label', title);
}
return title ?? undefined;
}
override updateClass(): void {
@@ -372,9 +392,7 @@ export class ActionViewItem extends BaseActionViewItem {
this.updateEnabled();
} else {
if (this.label) {
this.label.classList.remove('codicon');
}
this.label?.classList.remove('codicon');
}
}
@@ -385,18 +403,14 @@ export class ActionViewItem extends BaseActionViewItem {
this.label.classList.remove('disabled');
}
if (this.element) {
this.element.classList.remove('disabled');
}
this.element?.classList.remove('disabled');
} else {
if (this.label) {
this.label.setAttribute('aria-disabled', 'true');
this.label.classList.add('disabled');
}
if (this.element) {
this.element.classList.add('disabled');
}
this.element?.classList.add('disabled');
}
}
@@ -6,6 +6,7 @@
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { ActionRunner, IAction, IActionRunner, IRunEvent, Separator } from 'vs/base/common/actions';
import { Emitter } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
@@ -49,6 +50,7 @@ export interface IActionBarOptions {
readonly allowContextMenu?: boolean;
readonly preventLoopNavigation?: boolean;
readonly focusOnlyEnabledItems?: boolean;
readonly hoverDelegate?: IHoverDelegate;
}
export interface IActionOptions extends IActionViewItemOptions {
@@ -327,7 +329,7 @@ export class ActionBar extends Disposable implements IActionRunner {
}
if (!item) {
item = new ActionViewItem(this.context, action, options);
item = new ActionViewItem(this.context, action, { hoverDelegate: this.options.hoverDelegate, ...options });
}
// Prevent native context menu on actions
@@ -170,7 +170,7 @@ export class BreadcrumbsWidget {
}
domFocus(): void {
let idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1;
const idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1;
if (idx >= 0 && idx < this._items.length) {
this._focus(idx, undefined);
} else {
@@ -226,7 +226,7 @@ export class BreadcrumbsWidget {
}
reveal(item: BreadcrumbsItem): void {
let idx = this._items.indexOf(item);
const idx = this._items.indexOf(item);
if (idx >= 0) {
this._reveal(idx, false);
}
@@ -281,7 +281,7 @@ export class BreadcrumbsWidget {
dispose(removed);
this._focus(-1, undefined);
} catch (e) {
let newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`);
const newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`);
newError.name = e.name;
newError.stack = e.stack;
throw newError;
@@ -291,8 +291,8 @@ export class BreadcrumbsWidget {
private _render(start: number): void {
let didChange = false;
for (; start < this._items.length && start < this._nodes.length; start++) {
let item = this._items[start];
let node = this._nodes[start];
const item = this._items[start];
const node = this._nodes[start];
this._renderItem(item, node);
didChange = true;
}
@@ -308,8 +308,8 @@ export class BreadcrumbsWidget {
// case b: more items -> render them
for (; start < this._items.length; start++) {
let item = this._items[start];
let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div');
const item = this._items[start];
const node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div');
if (node) {
this._renderItem(item, node);
this._domNode.appendChild(node);
@@ -343,7 +343,7 @@ export class BreadcrumbsWidget {
return;
}
for (let el: HTMLElement | null = event.target; el; el = el.parentElement) {
let idx = this._nodes.indexOf(el as HTMLDivElement);
const idx = this._nodes.indexOf(el as HTMLDivElement);
if (idx >= 0) {
this._focus(idx, event);
this._select(idx, event);
+30 -2
View File
@@ -38,8 +38,36 @@
cursor: pointer;
}
.monaco-button-dropdown > .monaco-dropdown-button {
margin-left: 1px;
.monaco-button-dropdown.disabled {
cursor: default;
}
.monaco-button-dropdown > .monaco-button:focus {
outline-offset: -1px !important;
}
.monaco-button-dropdown.disabled > .monaco-button.disabled,
.monaco-button-dropdown.disabled > .monaco-button.disabled:focus,
.monaco-button-dropdown.disabled > .monaco-button-dropdown-separator {
opacity: 0.4 !important;
}
.monaco-button-dropdown > .monaco-button.monaco-text-button {
border-right-width: 0 !important;
}
.monaco-button-dropdown .monaco-button-dropdown-separator {
padding: 4px 0;
cursor: default;
}
.monaco-button-dropdown .monaco-button-dropdown-separator > div {
height: 100%;
width: 1px;
}
.monaco-button-dropdown > .monaco-button.monaco-dropdown-button {
border-left-width: 0 !important;
}
.monaco-description-button {
+35 -5
View File
@@ -15,6 +15,7 @@ import { Emitter, Event as BaseEvent } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { mixin } from 'vs/base/common/objects';
import { localize } from 'vs/nls';
import 'vs/css!./button';
export interface IButtonOptions extends IButtonStyles {
@@ -27,6 +28,7 @@ export interface IButtonStyles {
buttonBackground?: Color;
buttonHoverBackground?: Color;
buttonForeground?: Color;
buttonSeparator?: Color;
buttonSecondaryBackground?: Color;
buttonSecondaryHoverBackground?: Color;
buttonSecondaryForeground?: Color;
@@ -41,6 +43,7 @@ export interface IButtonStyles {
const defaultOptions: IButtonStyles = {
buttonBackground: Color.fromHex('#0E639C'),
buttonHoverBackground: Color.fromHex('#006BB3'),
buttonSeparator: Color.white,
buttonForeground: Color.white
};
@@ -154,8 +157,8 @@ export class Button extends Disposable implements IButton {
// Also set hover background when button is focused for feedback
this.focusTracker = this._register(trackFocus(this._element));
this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground()));
this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles
this._register(this.focusTracker.onDidFocus(() => { if (this.enabled) { this.setHoverBackground(); } }));
this._register(this.focusTracker.onDidBlur(() => { if (this.enabled) { this.applyStyles(); } }));
this.applyStyles();
}
@@ -317,6 +320,7 @@ export interface IButtonWithDropdownOptions extends IButtonOptions {
readonly contextMenuProvider: IContextMenuProvider;
readonly actions: IAction[];
readonly actionRunner?: IActionRunner;
readonly addPrimaryActionToDropdown?: boolean;
}
export class ButtonWithDropdown extends Disposable implements IButton {
@@ -324,6 +328,8 @@ export class ButtonWithDropdown extends Disposable implements IButton {
private readonly button: Button;
private readonly action: Action;
private readonly dropdownButton: Button;
private readonly separatorContainer: HTMLDivElement;
private readonly separator: HTMLDivElement;
readonly element: HTMLElement;
private readonly _onDidClick = this._register(new Emitter<Event | undefined>());
@@ -340,13 +346,23 @@ export class ButtonWithDropdown extends Disposable implements IButton {
this._register(this.button.onDidClick(e => this._onDidClick.fire(e)));
this.action = this._register(new Action('primaryAction', this.button.label, undefined, true, async () => this._onDidClick.fire(undefined)));
this.separatorContainer = document.createElement('div');
this.separatorContainer.classList.add('monaco-button-dropdown-separator');
this.separator = document.createElement('div');
this.separatorContainer.appendChild(this.separator);
this.element.appendChild(this.separatorContainer);
this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportIcons: true }));
this.dropdownButton.element.title = localize("button dropdown more actions", 'More Actions...');
this.dropdownButton.element.setAttribute('aria-haspopup', 'true');
this.dropdownButton.element.setAttribute('aria-expanded', 'false');
this.dropdownButton.element.classList.add('monaco-dropdown-button');
this.dropdownButton.icon = Codicon.dropDownButton;
this._register(this.dropdownButton.onDidClick(e => {
options.contextMenuProvider.showContextMenu({
getAnchor: () => this.dropdownButton.element,
getActions: () => [this.action, ...options.actions],
getActions: () => options.addPrimaryActionToDropdown === false ? [...options.actions] : [this.action, ...options.actions],
actionRunner: options.actionRunner,
onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false')
});
@@ -366,6 +382,8 @@ export class ButtonWithDropdown extends Disposable implements IButton {
set enabled(enabled: boolean) {
this.button.enabled = enabled;
this.dropdownButton.enabled = enabled;
this.element.classList.toggle('disabled', !enabled);
}
get enabled(): boolean {
@@ -375,6 +393,20 @@ export class ButtonWithDropdown extends Disposable implements IButton {
style(styles: IButtonStyles): void {
this.button.style(styles);
this.dropdownButton.style(styles);
// Separator
const border = styles.buttonBorder ? styles.buttonBorder.toString() : '';
this.separatorContainer.style.borderTopWidth = border ? '1px' : '';
this.separatorContainer.style.borderTopStyle = border ? 'solid' : '';
this.separatorContainer.style.borderTopColor = border;
this.separatorContainer.style.borderBottomWidth = border ? '1px' : '';
this.separatorContainer.style.borderBottomStyle = border ? 'solid' : '';
this.separatorContainer.style.borderBottomColor = border;
this.separatorContainer.style.backgroundColor = styles.buttonBackground?.toString() ?? '';
this.separator.style.backgroundColor = styles.buttonSeparator?.toString() ?? '';
}
focus(): void {
@@ -398,12 +430,10 @@ export class ButtonWithDescription extends Button implements IButtonWithDescript
this._labelElement = document.createElement('div');
this._labelElement.classList.add('monaco-button-label');
this._labelElement.tabIndex = -1;
this._element.appendChild(this._labelElement);
this._descriptionElement = document.createElement('div');
this._descriptionElement.classList.add('monaco-button-description');
this._descriptionElement.tabIndex = -1;
this._element.appendChild(this._descriptionElement);
}
Binary file not shown.
@@ -5,7 +5,6 @@
.context-view {
position: absolute;
z-index: 2500;
}
.context-view.fixed {
@@ -13,6 +12,5 @@
font-family: inherit;
font-size: 13px;
position: fixed;
z-index: 2500;
color: inherit;
}
@@ -206,7 +206,7 @@ export class ContextView extends Disposable {
this.view.className = 'context-view';
this.view.style.top = '0px';
this.view.style.left = '0px';
this.view.style.zIndex = '2500';
this.view.style.zIndex = '2575';
this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute';
DOM.show(this.view);
@@ -220,9 +220,7 @@ export class ContextView extends Disposable {
this.doLayout();
// Focus
if (this.delegate.focus) {
this.delegate.focus();
}
this.delegate.focus?.();
}
getViewElement(): HTMLElement {
@@ -253,20 +251,25 @@ export class ContextView extends Disposable {
}
// Get anchor
let anchor = this.delegate!.getAnchor();
const anchor = this.delegate!.getAnchor();
// Compute around
let around: IView;
// Get the element's position and size (to anchor the view)
if (DOM.isHTMLElement(anchor)) {
let elementPosition = DOM.getDomNodePagePosition(anchor);
const elementPosition = DOM.getDomNodePagePosition(anchor);
// In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element
// e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level.
// Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Size Multiplier: 1.5
const zoom = DOM.getDomNodeZoomLevel(anchor);
around = {
top: elementPosition.top,
left: elementPosition.left,
width: elementPosition.width,
height: elementPosition.height
top: elementPosition.top * zoom,
left: elementPosition.left * zoom,
width: elementPosition.width * zoom,
height: elementPosition.height * zoom
};
} else {
around = {
@@ -359,7 +362,7 @@ export class ContextView extends Disposable {
}
}
let SHADOW_ROOT_CSS = /* css */ `
const SHADOW_ROOT_CSS = /* css */ `
:host {
all: initial; /* 1st rule so subsequent properties are reset. */
}
+3 -7
View File
@@ -165,7 +165,7 @@ export class Dialog extends Disposable {
}
private getIconAriaLabel(): string {
let typeLabel = nls.localize('dialogInfoMessage', 'Info');
const typeLabel = nls.localize('dialogInfoMessage', 'Info');
switch (this.options.type) {
case 'error':
nls.localize('dialogErrorMessage', 'Error');
@@ -427,13 +427,9 @@ export class Dialog extends Disposable {
this.element.style.backgroundColor = bgColor?.toString() ?? '';
this.element.style.border = border;
if (this.buttonBar) {
this.buttonBar.buttons.forEach(button => button.style(style));
}
this.buttonBar?.buttons.forEach(button => button.style(style));
if (this.checkbox) {
this.checkbox.style(style);
}
this.checkbox?.style(style);
if (fgColor && bgColor) {
const messageDetailColor = fgColor.transparent(.9);
+4 -2
View File
@@ -56,8 +56,10 @@ export class BaseDropdown extends ActionRunner {
for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) {
this._register(addDisposableListener(this._label, event, e => {
if (e instanceof MouseEvent && e.detail > 1) {
return; // prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363)
if (e instanceof MouseEvent && (e.detail > 1 || e.button !== 0)) {
// prevent right click trigger to allow separate context menu (https://github.com/microsoft/vscode/issues/151064)
// prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363)
return;
}
if (this.visible) {
@@ -126,9 +126,22 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
};
}
this.updateTooltip();
this.updateEnabled();
}
override getTooltip(): string | undefined {
let title: string | null = null;
if (this.getAction().tooltip) {
title = this.getAction().tooltip;
} else if (this.getAction().label) {
title = this.getAction().label;
}
return title ?? undefined;
}
override setActionContext(newContext: unknown): void {
super.setActionContext(newContext);
+46 -10
View File
@@ -6,7 +6,7 @@
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle';
import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { CaseSensitiveToggle, RegexToggle, WholeWordsToggle } from 'vs/base/browser/ui/findinput/findInputToggles';
import { HistoryInputBox, IInputBoxStyles, IInputValidator, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
@@ -31,6 +31,7 @@ export interface IFindInputOptions extends IFindInputStyles {
readonly appendWholeWordsLabel?: string;
readonly appendRegexLabel?: string;
readonly history?: string[];
readonly additionalToggles?: Toggle[];
readonly showHistoryHint?: () => boolean;
}
@@ -74,6 +75,7 @@ export class FindInput extends Widget {
protected regex: RegexToggle;
protected wholeWords: WholeWordsToggle;
protected caseSensitive: CaseSensitiveToggle;
protected additionalToggles: Toggle[] = [];
public domNode: HTMLElement;
public inputBox: HistoryInputBox;
@@ -209,15 +211,11 @@ export class FindInput extends Widget {
this._onCaseSensitiveKeyDown.fire(e);
}));
if (this._showOptionButtons) {
this.inputBox.paddingRight = this.caseSensitive.width() + this.wholeWords.width() + this.regex.width();
}
// Arrow-Key support to navigate between options
let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode];
const indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode];
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
let index = indexes.indexOf(<HTMLElement>document.activeElement);
const index = indexes.indexOf(<HTMLElement>document.activeElement);
if (index >= 0) {
let newIndex: number = -1;
if (event.equals(KeyCode.RightArrow)) {
@@ -250,11 +248,37 @@ export class FindInput extends Widget {
this.controls.appendChild(this.wholeWords.domNode);
this.controls.appendChild(this.regex.domNode);
if (!this._showOptionButtons) {
this.caseSensitive.domNode.style.display = 'none';
this.wholeWords.domNode.style.display = 'none';
this.regex.domNode.style.display = 'none';
}
for (const toggle of options?.additionalToggles ?? []) {
this._register(toggle);
this.controls.appendChild(toggle.domNode);
this._register(toggle.onChange(viaKeyboard => {
this._onDidOptionChange.fire(viaKeyboard);
if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {
this.inputBox.focus();
}
}));
this.additionalToggles.push(toggle);
}
if (this.additionalToggles.length > 0) {
this.controls.style.display = 'block';
}
this.inputBox.paddingRight =
(this._showOptionButtons ? this.caseSensitive.width() + this.wholeWords.width() + this.regex.width() : 0)
+ this.additionalToggles.reduce((r, t) => r + t.width(), 0);
this.domNode.appendChild(this.controls);
if (parent) {
parent.appendChild(this.domNode);
}
parent?.appendChild(this.domNode);
this._register(dom.addDisposableListener(this.inputBox.inputElement, 'compositionstart', (e: CompositionEvent) => {
this.imeSessionInProgress = true;
@@ -284,6 +308,10 @@ export class FindInput extends Widget {
this.regex.enable();
this.wholeWords.enable();
this.caseSensitive.enable();
for (const toggle of this.additionalToggles) {
toggle.enable();
}
}
public disable(): void {
@@ -292,6 +320,10 @@ export class FindInput extends Widget {
this.regex.disable();
this.wholeWords.disable();
this.caseSensitive.disable();
for (const toggle of this.additionalToggles) {
toggle.disable();
}
}
public setFocusInputOnOptionClick(value: boolean): void {
@@ -358,6 +390,10 @@ export class FindInput extends Widget {
this.wholeWords.style(toggleStyles);
this.caseSensitive.style(toggleStyles);
for (const toggle of this.additionalToggles) {
toggle.style(toggleStyles);
}
const inputBoxStyles: IInputBoxStyles = {
inputBackground: this.inputBackground,
inputForeground: this.inputForeground,
@@ -189,10 +189,10 @@ export class ReplaceInput extends Widget {
}
// Arrow-Key support to navigate between options
let indexes = [this.preserveCase.domNode];
const indexes = [this.preserveCase.domNode];
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
let index = indexes.indexOf(<HTMLElement>document.activeElement);
const index = indexes.indexOf(<HTMLElement>document.activeElement);
if (index >= 0) {
let newIndex: number = -1;
if (event.equals(KeyCode.RightArrow)) {
@@ -218,16 +218,14 @@ export class ReplaceInput extends Widget {
});
let controls = document.createElement('div');
const controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = this._showOptionButtons ? 'block' : 'none';
controls.appendChild(this.preserveCase.domNode);
this.domNode.appendChild(controls);
if (parent) {
parent.appendChild(this.domNode);
}
parent?.appendChild(this.domNode);
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
@@ -361,9 +359,7 @@ export class ReplaceInput extends Widget {
}
public showMessage(message: InputBoxMessage): void {
if (this.inputBox) {
this.inputBox.showMessage(message);
}
this.inputBox?.showMessage(message);
}
public clearMessage(): void {
+36 -10
View File
@@ -527,6 +527,16 @@ export class Grid<T extends IView = IView> extends Disposable {
return this.gridview.resizeView(location, size);
}
/**
* Returns whether all other {@link IView views} are at their minimum size.
*
* @param view The reference {@link IView view}.
*/
isViewSizeMaximized(view: T): boolean {
const location = this.getViewLocation(view);
return this.gridview.isViewSizeMaximized(location);
}
/**
* Get the size of a {@link IView view}.
*
@@ -748,6 +758,16 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
return result;
}
/**
* Construct a new {@link SerializableGrid} from a grid descriptor.
*
* @param gridDescriptor A grid descriptor in which leaf nodes point to actual views.
* @returns A new {@link SerializableGrid} instance.
*/
static from<T extends ISerializableView>(gridDescriptor: GridDescriptor<T>, options: IGridOptions = {}): SerializableGrid<T> {
return SerializableGrid.deserialize(createSerializedGrid(gridDescriptor), { fromJSON: view => view }, options);
}
/**
* Useful information in order to proportionally restore view sizes
* upon the very first layout call.
@@ -776,15 +796,21 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
}
}
export type GridNodeDescriptor = { size?: number; groups?: GridNodeDescriptor[] };
export type GridDescriptor = { orientation: Orientation; groups?: GridNodeDescriptor[] };
export type GridLeafNodeDescriptor<T> = { size?: number; data?: any };
export type GridBranchNodeDescriptor<T> = { size?: number; groups: GridNodeDescriptor<T>[] };
export type GridNodeDescriptor<T> = GridBranchNodeDescriptor<T> | GridLeafNodeDescriptor<T>;
export type GridDescriptor<T> = { orientation: Orientation } & GridBranchNodeDescriptor<T>;
export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, rootNode: boolean): void {
if (!rootNode && nodeDescriptor.groups && nodeDescriptor.groups.length <= 1) {
nodeDescriptor.groups = undefined;
function isGridBranchNodeDescriptor<T>(nodeDescriptor: GridNodeDescriptor<T>): nodeDescriptor is GridBranchNodeDescriptor<T> {
return !!(nodeDescriptor as GridBranchNodeDescriptor<T>).groups;
}
export function sanitizeGridNodeDescriptor<T>(nodeDescriptor: GridNodeDescriptor<T>, rootNode: boolean): void {
if (!rootNode && (nodeDescriptor as any).groups && (nodeDescriptor as any).groups.length <= 1) {
(nodeDescriptor as any).groups = undefined;
}
if (!nodeDescriptor.groups) {
if (!isGridBranchNodeDescriptor(nodeDescriptor)) {
return;
}
@@ -811,11 +837,11 @@ export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, r
}
}
function createSerializedNode(nodeDescriptor: GridNodeDescriptor): ISerializedNode {
if (nodeDescriptor.groups) {
function createSerializedNode<T>(nodeDescriptor: GridNodeDescriptor<T>): ISerializedNode {
if (isGridBranchNodeDescriptor(nodeDescriptor)) {
return { type: 'branch', data: nodeDescriptor.groups.map(c => createSerializedNode(c)), size: nodeDescriptor.size! };
} else {
return { type: 'leaf', data: null, size: nodeDescriptor.size! };
return { type: 'leaf', data: nodeDescriptor.data, size: nodeDescriptor.size! };
}
}
@@ -843,7 +869,7 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width
* Creates a new JSON object from a {@link GridDescriptor}, which can
* be deserialized by {@link SerializableGrid.deserialize}.
*/
export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid {
export function createSerializedGrid<T>(gridDescriptor: GridDescriptor<T>): ISerializedGrid {
sanitizeGridNodeDescriptor(gridDescriptor, true);
const root = createSerializedNode(gridDescriptor);
+27 -6
View File
@@ -592,6 +592,10 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
this.splitview.resizeView(index, size);
}
isChildSizeMaximized(index: number): boolean {
return this.splitview.isViewSizeMaximized(index);
}
distributeViewSizes(recursive = false): void {
this.splitview.distributeViewSizes();
@@ -857,9 +861,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
set boundarySashes(boundarySashes: IRelativeBoundarySashes) {
this._boundarySashes = boundarySashes;
if (this.view.setBoundarySashes) {
this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation));
}
this.view.setBoundarySashes?.(toAbsoluteBoundarySashes(boundarySashes, this.orientation));
}
layout(size: number, offset: number, ctx: ILayoutContext | undefined): void {
@@ -897,9 +899,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
}
setVisible(visible: boolean): void {
if (this.view.setVisible) {
this.view.setVisible(visible);
}
this.view.setVisible?.(visible);
}
dispose(): void {
@@ -1435,6 +1435,27 @@ export class GridView implements IDisposable {
}
}
/**
* Returns whether all other {@link IView views} are at their minimum size.
*
* @param location The {@link GridLocation location} of the view.
*/
isViewSizeMaximized(location: GridLocation): boolean {
const [ancestors, node] = this.getNode(location);
if (!(node instanceof LeafNode)) {
throw new Error('Invalid location');
}
for (let i = 0; i < ancestors.length; i++) {
if (!ancestors[i].isChildSizeMaximized(location[i])) {
return false;
}
}
return true;
}
/**
* Distribute the size among all {@link IView views} within the entire
* grid or within a single {@link SplitView}.
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { IUpdatableHoverOptions } from 'vs/base/browser/ui/iconLabel/iconLabelHover';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
@@ -12,7 +13,7 @@ export interface IHoverDelegateTarget extends IDisposable {
x?: number;
}
export interface IHoverDelegateOptions {
export interface IHoverDelegateOptions extends IUpdatableHoverOptions {
content: IMarkdownString | string | HTMLElement;
target: IHoverDelegateTarget | HTMLElement;
hoverPosition?: HoverPosition;
@@ -33,6 +33,21 @@ export function setupNativeHover(htmlElement: HTMLElement, tooltip: string | ITo
export type IHoverContent = string | ITooltipMarkdownString | HTMLElement | undefined;
type IResolvedHoverContent = IMarkdownString | string | HTMLElement | undefined;
/**
* Copied from src\vs\workbench\services\hover\browser\hover.ts
* @deprecated Use IHoverService
*/
export interface IHoverAction {
label: string;
commandId: string;
iconClass?: string;
run(target: HTMLElement): void;
}
export interface IUpdatableHoverOptions {
actions?: IHoverAction[];
linkHandler?(url: string): void;
}
export interface ICustomHover extends IDisposable {
@@ -49,7 +64,7 @@ export interface ICustomHover extends IDisposable {
/**
* Updates the contents of the hover.
*/
update(tooltip: IHoverContent): void;
update(tooltip: IHoverContent, options?: IUpdatableHoverOptions): void;
}
@@ -61,7 +76,7 @@ class UpdatableHoverWidget implements IDisposable {
constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) {
}
async update(content: IHoverContent, focus?: boolean): Promise<void> {
async update(content: IHoverContent, focus?: boolean, options?: IUpdatableHoverOptions): Promise<void> {
if (this._cancellationTokenSource) {
// there's an computation ongoing, cancel it
this._cancellationTokenSource.dispose(true);
@@ -99,10 +114,10 @@ class UpdatableHoverWidget implements IDisposable {
}
}
this.show(resolvedContent, focus);
this.show(resolvedContent, focus, options);
}
private show(content: IResolvedHoverContent, focus?: boolean): void {
private show(content: IResolvedHoverContent, focus?: boolean, options?: IUpdatableHoverOptions): void {
const oldHoverWidget = this._hoverWidget;
if (this.hasContent(content)) {
@@ -111,7 +126,8 @@ class UpdatableHoverWidget implements IDisposable {
target: this.target,
showPointer: this.hoverDelegate.placement === 'element',
hoverPosition: HoverPosition.BELOW,
skipFadeInAnimation: !this.fadeInAnimation || !!oldHoverWidget // do not fade in if the hover is already showing
skipFadeInAnimation: !this.fadeInAnimation || !!oldHoverWidget, // do not fade in if the hover is already showing
...options
};
this._hoverWidget = this.hoverDelegate.showHover(hoverOptions, focus);
@@ -142,7 +158,7 @@ class UpdatableHoverWidget implements IDisposable {
}
}
export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContent): ICustomHover {
export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContent, options?: IUpdatableHoverOptions): ICustomHover {
let hoverPreparation: IDisposable | undefined;
let hoverWidget: UpdatableHoverWidget | undefined;
@@ -163,7 +179,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
return new TimeoutTimer(async () => {
if (!hoverWidget || hoverWidget.isDisposed) {
hoverWidget = new UpdatableHoverWidget(hoverDelegate, target || htmlElement, delay > 0);
await hoverWidget.update(content, focus);
await hoverWidget.update(content, focus, options);
}
}, delay);
};
@@ -208,9 +224,9 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
hide: () => {
hideHover(true, true);
},
update: async newContent => {
update: async (newContent, hoverOptions) => {
content = newContent;
await hoverWidget?.update(content);
await hoverWidget?.update(content, undefined, hoverOptions);
},
dispose: () => {
mouseOverDomEmitter.dispose();
+24 -6
View File
@@ -171,9 +171,9 @@ export class InputBox extends Widget {
this.element = dom.append(container, $('.monaco-inputbox.idle'));
let tagName = this.options.flexibleHeight ? 'textarea' : 'input';
const tagName = this.options.flexibleHeight ? 'textarea' : 'input';
let wrapper = dom.append(this.element, $('.ibwrapper'));
const wrapper = dom.append(this.element, $('.ibwrapper'));
this.input = dom.append(wrapper, $(tagName + '.input.empty'));
this.input.setAttribute('autocorrect', 'off');
this.input.setAttribute('autocapitalize', 'off');
@@ -257,14 +257,14 @@ export class InputBox extends Widget {
this.applyStyles();
}
private onBlur(): void {
protected onBlur(): void {
this._hideMessage();
if (this.options.showPlaceholderOnFocus) {
this.input.setAttribute('placeholder', '');
}
}
private onFocus(): void {
protected onFocus(): void {
this._showMessage();
if (this.options.showPlaceholderOnFocus) {
this.input.setAttribute('placeholder', this.placeholder || '');
@@ -491,7 +491,7 @@ export class InputBox extends Widget {
}
let div: HTMLElement;
let layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px';
const layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px';
this.contextViewProvider.showContextView({
getAnchor: () => this.element,
@@ -672,11 +672,19 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge
private readonly history: HistoryNavigator<string>;
private observer: MutationObserver | undefined;
private readonly _onDidFocus = this._register(new Emitter<void>());
readonly onDidFocus = this._onDidFocus.event;
private readonly _onDidBlur = this._register(new Emitter<void>());
readonly onDidBlur = this._onDidBlur.event;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) {
super(container, contextViewProvider, options);
const NLS_PLACEHOLDER_HISTORY_HINT = nls.localize({ key: 'history.inputbox.hint', comment: ['Text will be prefixed with \u21C5 plus a single space, then used as a hint where input field keeps history'] }, "for history");
const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX = ` or \u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT}`;
const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS = ` (\u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT})`;
super(container, contextViewProvider, options);
this.history = new HistoryNavigator<string>(options.history, 100);
// Function to append the history suffix to the placeholder if necessary
@@ -781,6 +789,16 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge
this.history.clear();
}
protected override onBlur(): void {
super.onBlur();
this._onDidBlur.fire();
}
protected override onFocus(): void {
super.onFocus();
this._onDidFocus.fire();
}
private getCurrentValue(): string | null {
let currentValue = this.history.current();
if (!currentValue) {
@@ -89,7 +89,7 @@ export class KeybindingLabel implements IThemable {
this.clear();
if (this.keybinding) {
let [firstPart, chordPart] = this.keybinding.getParts();
const [firstPart, chordPart] = this.keybinding.getParts();
if (firstPart) {
this.renderPart(this.domNode, firstPart, this.matches ? this.matches.firstPart : null);
}
+1 -76
View File
@@ -65,72 +65,7 @@
z-index: 1000;
}
/* Type filter */
.monaco-list-type-filter {
display: flex;
align-items: center;
position: absolute;
border-radius: 2px;
padding: 0px 3px;
max-width: calc(100% - 10px);
text-overflow: ellipsis;
overflow: hidden;
text-align: right;
box-sizing: border-box;
cursor: all-scroll;
font-size: 13px;
line-height: 18px;
height: 20px;
z-index: 1;
top: 4px;
}
.monaco-list-type-filter.dragging {
transition: top 0.2s, left 0.2s;
}
.monaco-list-type-filter.ne {
right: 4px;
}
.monaco-list-type-filter.nw {
left: 4px;
}
.monaco-list-type-filter > .controls {
display: flex;
align-items: center;
box-sizing: border-box;
transition: width 0.2s;
width: 0;
}
.monaco-list-type-filter.dragging > .controls,
.monaco-list-type-filter:hover > .controls {
width: 36px;
}
.monaco-list-type-filter > .controls > * {
border: none;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
background: none;
width: 16px;
height: 16px;
flex-shrink: 0;
margin: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.monaco-list-type-filter > .controls > .filter {
margin-left: 4px;
}
/* Filter */
.monaco-list-type-filter-message {
position: absolute;
@@ -149,13 +84,3 @@
.monaco-list-type-filter-message:empty {
display: none;
}
/* Electron */
.monaco-list-type-filter {
cursor: grab;
}
.monaco-list-type-filter.dragging {
cursor: grabbing;
}
+5 -5
View File
@@ -12,7 +12,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IThemable } from 'vs/base/common/styler';
import 'vs/css!./list';
import { IListContextMenuEvent, IListEvent, IListMouseEvent, IListRenderer, IListVirtualDelegate } from './list';
import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List } from './listWidget';
import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List, TypeNavigationMode } from './listWidget';
export interface IPagedRenderer<TElement, TTemplateData> extends IListRenderer<TElement, TTemplateData> {
renderPlaceholder(index: number, templateData: TTemplateData): void;
@@ -95,8 +95,8 @@ class PagedAccessibilityProvider<T> implements IListAccessibilityProvider<number
}
export interface IPagedListOptions<T> {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
readonly typeNavigationEnabled?: boolean;
readonly typeNavigationMode?: TypeNavigationMode;
readonly ariaLabel?: string;
readonly keyboardSupport?: boolean;
readonly multipleSelectionSupport?: boolean;
@@ -282,8 +282,8 @@ export class PagedList<T> implements IThemable, IDisposable {
this.list.layout(height, width);
}
toggleKeyboardNavigation(): void {
this.list.toggleKeyboardNavigation();
triggerTypeNavigation(): void {
this.list.triggerTypeNavigation();
}
reveal(index: number, relativeTop?: number): void {
+32 -49
View File
@@ -15,7 +15,6 @@ import { Delayer, disposableTimeout } from 'vs/base/common/async';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { getOrDefault } from 'vs/base/common/objects';
import { IRange, Range } from 'vs/base/common/range';
import { INewScrollDimensions, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
import { ISpliceable } from 'vs/base/common/sequence';
@@ -257,7 +256,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private readonly disposables: DisposableStore = new DisposableStore();
private readonly _onDidChangeContentHeight = new Emitter<number>();
readonly onDidChangeContentHeight: Event<number> = Event.latch(this._onDidChangeContentHeight.event);
readonly onDidChangeContentHeight: Event<number> = Event.latch(this._onDidChangeContentHeight.event, undefined, this.disposables);
get contentHeight(): number { return this.rangeMap.size; }
get onDidScroll(): Event<ScrollEvent> { return this.scrollableElement.onScroll; }
@@ -325,7 +324,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.domNode.classList.toggle('mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true);
this._horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling);
this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling;
this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling);
this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight;
@@ -335,7 +334,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization);
const transformOptimization = options.transformOptimization ?? DefaultOptions.transformOptimization;
if (transformOptimization) {
this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)';
}
@@ -344,14 +343,14 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.scrollable = new Scrollable({
forceIntegerValues: true,
smoothScrollDuration: getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0,
smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0,
scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb)
});
this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, {
alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel),
alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel,
horizontal: ScrollbarVisibility.Auto,
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
vertical: options.verticalScrollMode ?? DefaultOptions.verticalScrollMode,
useShadows: options.useShadows ?? DefaultOptions.useShadows,
mouseWheelScrollSensitivity: options.mouseWheelScrollSensitivity,
fastScrollSensitivity: options.fastScrollSensitivity
}, this.scrollable));
@@ -371,10 +370,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.disposables.add(addDisposableListener(this.domNode, 'dragleave', e => this.onDragLeave(this.toDragEvent(e))));
this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e)));
this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight);
this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight);
this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights);
this.dnd = getOrDefault<IListViewOptions<T>, IListViewDragAndDrop<T>>(options, o => o.dnd, DefaultOptions.dnd);
this.setRowLineHeight = options.setRowLineHeight ?? DefaultOptions.setRowLineHeight;
this.setRowHeight = options.setRowHeight ?? DefaultOptions.setRowHeight;
this.supportDynamicHeights = options.supportDynamicHeights ?? DefaultOptions.supportDynamicHeights;
this.dnd = options.dnd ?? DefaultOptions.dnd;
this.layout();
}
@@ -705,7 +704,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
layout(height?: number, width?: number): void {
let scrollDimensions: INewScrollDimensions = {
const scrollDimensions: INewScrollDimensions = {
height: typeof height === 'number' ? height : getContentHeight(this.domNode)
};
@@ -813,9 +812,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
throw new Error(`No renderer found for template id ${item.templateId}`);
}
if (renderer) {
renderer.renderElement(item.element, index, item.row.templateData, item.size);
}
renderer?.renderElement(item.element, index, item.row.templateData, item.size);
const uri = this.dnd.getDragURI(item.element);
item.dragStartDisposable.dispose();
@@ -938,17 +935,17 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
// Events
@memoize get onMouseClick(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'click')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseDblClick(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'dblclick')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseMiddleClick(): Event<IListMouseEvent<T>> { return Event.filter(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'auxclick')).event, e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); }
@memoize get onMouseUp(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseup')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseDown(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousedown')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseOver(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseover')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseMove(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousemove')).event, e => this.toMouseEvent(e)); }
@memoize get onMouseOut(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseout')).event, e => this.toMouseEvent(e)); }
@memoize get onContextMenu(): Event<IListMouseEvent<T> | IListGestureEvent<T>> { return Event.any(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'contextmenu')).event, e => this.toMouseEvent(e)), Event.map(this.disposables.add(new DomEmitter(this.domNode, TouchEventType.Contextmenu)).event as Event<GestureEvent>, e => this.toGestureEvent(e))); }
@memoize get onTouchStart(): Event<IListTouchEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'touchstart')).event, e => this.toTouchEvent(e)); }
@memoize get onTap(): Event<IListGestureEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.rowsContainer, TouchEventType.Tap)).event, e => this.toGestureEvent(e as GestureEvent)); }
@memoize get onMouseClick(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'click')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseDblClick(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'dblclick')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseMiddleClick(): Event<IListMouseEvent<T>> { return Event.filter(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'auxclick')).event, e => this.toMouseEvent(e as MouseEvent), this.disposables), e => e.browserEvent.button === 1, this.disposables); }
@memoize get onMouseUp(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseup')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseDown(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousedown')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseOver(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseover')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseMove(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousemove')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onMouseOut(): Event<IListMouseEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseout')).event, e => this.toMouseEvent(e), this.disposables); }
@memoize get onContextMenu(): Event<IListMouseEvent<T> | IListGestureEvent<T>> { return Event.any(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'contextmenu')).event, e => this.toMouseEvent(e), this.disposables), Event.map(this.disposables.add(new DomEmitter(this.domNode, TouchEventType.Contextmenu)).event as Event<GestureEvent>, e => this.toGestureEvent(e), this.disposables)); }
@memoize get onTouchStart(): Event<IListTouchEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'touchstart')).event, e => this.toTouchEvent(e), this.disposables); }
@memoize get onTap(): Event<IListGestureEvent<T>> { return Event.map(this.disposables.add(new DomEmitter(this.rowsContainer, TouchEventType.Tap)).event, e => this.toGestureEvent(e as GestureEvent), this.disposables); }
private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent<T> {
const index = this.getItemIndexFromEventTarget(browserEvent.target || null);
@@ -1032,9 +1029,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.currentDragData = new ElementsDragAndDropData(elements);
StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements);
if (this.dnd.onDragStart) {
this.dnd.onDragStart(this.currentDragData, event);
}
this.dnd.onDragStart?.(this.currentDragData, event);
}
private onDragOver(event: IListDragEvent<T>): boolean {
@@ -1114,9 +1109,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const item = this.items[index]!;
item.dropTarget = true;
if (item.row) {
item.row.domNode.classList.add('drop-target');
}
item.row?.domNode.classList.add('drop-target');
}
this.currentDragFeedbackDisposable = toDisposable(() => {
@@ -1124,9 +1117,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const item = this.items[index]!;
item.dropTarget = false;
if (item.row) {
item.row.domNode.classList.remove('drop-target');
}
item.row?.domNode.classList.remove('drop-target');
}
});
}
@@ -1169,9 +1160,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.currentDragData = undefined;
StaticDND.CurrentDragAndDropData = undefined;
if (this.dnd.onDragEnd) {
this.dnd.onDragEnd(event);
}
this.dnd.onDragEnd?.(event);
}
private clearDragOverFeedback(): void {
@@ -1364,7 +1353,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const size = item.size;
if (!this.setRowHeight && item.row) {
let newSize = item.row.domNode.offsetHeight;
const newSize = item.row.domNode.offsetHeight;
item.size = newSize;
item.lastDynamicHeightWidth = this.renderWidth;
return newSize - size;
@@ -1379,16 +1368,12 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (renderer) {
renderer.renderElement(item.element, index, row.templateData, undefined);
if (renderer.disposeElement) {
renderer.disposeElement(item.element, index, row.templateData, undefined);
}
renderer.disposeElement?.(item.element, index, row.templateData, undefined);
}
item.size = row.domNode.offsetHeight;
if (this.virtualDelegate.setDynamicHeight) {
this.virtualDelegate.setDynamicHeight(item.element, item.size);
}
this.virtualDelegate.setDynamicHeight?.(item.element, item.size);
item.lastDynamicHeightWidth = this.renderWidth;
this.rowsContainer.removeChild(row.domNode);
@@ -1429,9 +1414,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (item.row) {
const renderer = this.renderers.get(item.row.templateId);
if (renderer) {
if (renderer.disposeElement) {
renderer.disposeElement(item.element, -1, item.row.templateData, undefined);
}
renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined);
renderer.disposeTemplate(item.row.templateData);
}
}
+97 -77
View File
@@ -9,6 +9,7 @@ import { DomEmitter, stopEvent } from 'vs/base/browser/event';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Gesture } from 'vs/base/browser/touch';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
import { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
import { binarySearch, firstOrDefault, range } from 'vs/base/common/arrays';
@@ -258,6 +259,23 @@ export function isMonacoEditor(e: HTMLElement): boolean {
return isMonacoEditor(e.parentElement);
}
export function isButton(e: HTMLElement): boolean {
if ((e.tagName === 'A' && e.classList.contains('monaco-button')) ||
(e.tagName === 'DIV' && e.classList.contains('monaco-button-dropdown'))) {
return true;
}
if (e.classList.contains('monaco-list')) {
return false;
}
if (!e.parentElement) {
return false;
}
return isButton(e.parentElement);
}
class KeyboardController<T> implements IDisposable {
private readonly disposables = new DisposableStore();
@@ -265,9 +283,9 @@ class KeyboardController<T> implements IDisposable {
@memoize
private get onKeyDown(): Event.IChainableEvent<StandardKeyboardEvent> {
return Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)
return this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)
.filter(e => !isInputElement(e.target as HTMLElement))
.map(e => new StandardKeyboardEvent(e));
.map(e => new StandardKeyboardEvent(e)));
}
constructor(
@@ -367,7 +385,12 @@ class KeyboardController<T> implements IDisposable {
}
}
enum TypeLabelControllerState {
export enum TypeNavigationMode {
Automatic,
Trigger
}
enum TypeNavigationControllerState {
Idle,
Typing
}
@@ -385,12 +408,12 @@ export const DefaultKeyboardNavigationDelegate = new class implements IKeyboardN
}
};
class TypeLabelController<T> implements IDisposable {
class TypeNavigationController<T> implements IDisposable {
private enabled = false;
private state: TypeLabelControllerState = TypeLabelControllerState.Idle;
private state: TypeNavigationControllerState = TypeNavigationControllerState.Idle;
private automaticKeyboardNavigation = true;
private mode = TypeNavigationMode.Automatic;
private triggered = false;
private previouslyFocused = -1;
@@ -401,26 +424,23 @@ class TypeLabelController<T> implements IDisposable {
private list: List<T>,
private view: ListView<T>,
private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider<T>,
private keyboardNavigationEventFilter: IKeyboardNavigationEventFilter,
private delegate: IKeyboardNavigationDelegate
) {
this.updateOptions(list.options);
}
updateOptions(options: IListOptions<T>): void {
const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation;
if (enableKeyboardNavigation) {
if (options.typeNavigationEnabled ?? true) {
this.enable();
} else {
this.disable();
}
if (typeof options.automaticKeyboardNavigation !== 'undefined') {
this.automaticKeyboardNavigation = options.automaticKeyboardNavigation;
}
this.mode = options.typeNavigationMode ?? TypeNavigationMode.Automatic;
}
toggle(): void {
trigger(): void {
this.triggered = !this.triggered;
}
@@ -429,21 +449,27 @@ class TypeLabelController<T> implements IDisposable {
return;
}
const onChar = Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)
let typing = false;
const onChar = this.enabledDisposables.add(Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event))
.filter(e => !isInputElement(e.target as HTMLElement))
.filter(() => this.automaticKeyboardNavigation || this.triggered)
.filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered)
.map(event => new StandardKeyboardEvent(event))
.filter(e => typing || this.keyboardNavigationEventFilter(e))
.filter(e => this.delegate.mightProducePrintableCharacter(e))
.forEach(e => e.preventDefault())
.forEach(stopEvent)
.map(event => event.browserEvent.key)
.event;
const onClear = Event.debounce<string, null>(onChar, () => null, 800);
const onInput = Event.reduce<string | null, string | null>(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i));
const onClear = Event.debounce<string, null>(onChar, () => null, 800, undefined, undefined, this.enabledDisposables);
const onInput = Event.reduce<string | null, string | null>(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i), undefined, this.enabledDisposables);
onInput(this.onInput, this, this.enabledDisposables);
onClear(this.onClear, this, this.enabledDisposables);
onChar(() => typing = true, undefined, this.enabledDisposables);
onClear(() => typing = false, undefined, this.enabledDisposables);
this.enabled = true;
this.triggered = false;
}
@@ -473,15 +499,15 @@ class TypeLabelController<T> implements IDisposable {
private onInput(word: string | null): void {
if (!word) {
this.state = TypeLabelControllerState.Idle;
this.state = TypeNavigationControllerState.Idle;
this.triggered = false;
return;
}
const focus = this.list.getFocus();
const start = focus.length > 0 ? focus[0] : 0;
const delta = this.state === TypeLabelControllerState.Idle ? 1 : 0;
this.state = TypeLabelControllerState.Typing;
const delta = this.state === TypeNavigationControllerState.Idle ? 1 : 0;
this.state = TypeNavigationControllerState.Typing;
for (let i = 0; i < this.list.length; i++) {
const index = (start + i + delta) % this.list.length;
@@ -512,7 +538,7 @@ class DOMFocusController<T> implements IDisposable {
private list: List<T>,
private view: ListView<T>
) {
const onKeyDown = Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event)
const onKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event))
.filter(e => !isInputElement(e.target as HTMLElement))
.map(e => new StandardKeyboardEvent(e));
@@ -801,6 +827,10 @@ export class DefaultStyleController implements IStyleController {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected .codicon { color: ${styles.listActiveSelectionIconForeground}; }`);
}
if (styles.listFocusAndSelectionOutline) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { outline-color: ${styles.listFocusAndSelectionOutline} !important; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`
.monaco-drag-image,
@@ -874,22 +904,6 @@ export class DefaultStyleController implements IStyleController {
`);
}
if (styles.listFilterWidgetBackground) {
content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`);
}
if (styles.listFilterWidgetOutline) {
content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`);
}
if (styles.listFilterWidgetNoMatchesOutline) {
content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`);
}
if (styles.listMatchesShadow) {
content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`);
}
if (styles.tableColumnsBorder) {
content.push(`
.monaco-table:hover > .monaco-split-view2,
@@ -912,9 +926,13 @@ export class DefaultStyleController implements IStyleController {
}
}
export interface IKeyboardNavigationEventFilter {
(e: StandardKeyboardEvent): boolean;
}
export interface IListOptionsUpdate extends IListViewOptionsUpdate {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
readonly typeNavigationEnabled?: boolean;
readonly typeNavigationMode?: TypeNavigationMode;
readonly multipleSelectionSupport?: boolean;
}
@@ -927,6 +945,7 @@ export interface IListOptions<T> extends IListOptionsUpdate {
readonly multipleSelectionController?: IMultipleSelectionController<T>;
readonly styleController?: (suffix: string) => IStyleController;
readonly accessibilityProvider?: IListAccessibilityProvider<T>;
readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter;
// list view options
readonly useShadows?: boolean;
@@ -943,13 +962,14 @@ export interface IListOptions<T> extends IListOptionsUpdate {
readonly alwaysConsumeMouseWheel?: boolean;
}
export interface IListStyles {
export interface IListStyles extends IFindInputStyles {
listBackground?: Color;
listFocusBackground?: Color;
listFocusForeground?: Color;
listActiveSelectionBackground?: Color;
listActiveSelectionForeground?: Color;
listActiveSelectionIconForeground?: Color;
listFocusAndSelectionOutline?: Color;
listFocusAndSelectionBackground?: Color;
listFocusAndSelectionForeground?: Color;
listInactiveSelectionBackground?: Color;
@@ -967,7 +987,8 @@ export interface IListStyles {
listFilterWidgetBackground?: Color;
listFilterWidgetOutline?: Color;
listFilterWidgetNoMatchesOutline?: Color;
listMatchesShadow?: Color;
listFilterWidgetShadow?: Color;
listMatchesShadow?: Color; // {{SQL CARBON EDIT}}
treeIndentGuidesStroke?: Color;
tableColumnsBorder?: Color;
tableOddRowsBackgroundColor?: Color;
@@ -978,6 +999,7 @@ const defaultStyles: IListStyles = {
listActiveSelectionBackground: Color.fromHex('#0E639C'),
listActiveSelectionForeground: Color.fromHex('#FFFFFF'),
listActiveSelectionIconForeground: Color.fromHex('#FFFFFF'),
listFocusAndSelectionOutline: Color.fromHex('#90C2F9'),
listFocusAndSelectionBackground: Color.fromHex('#094771'),
listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'),
listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
@@ -1109,9 +1131,7 @@ class PipelineRenderer<T> implements IListRenderer<T, any> {
let i = 0;
for (const renderer of this.renderers) {
if (renderer.disposeElement) {
renderer.disposeElement(element, index, templateData[i], height);
}
renderer.disposeElement?.(element, index, templateData[i], height);
i += 1;
}
@@ -1182,9 +1202,7 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
}
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
if (this.dnd.onDragStart) {
this.dnd.onDragStart(data, originalEvent);
}
this.dnd.onDragStart?.(data, originalEvent);
}
onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction {
@@ -1196,9 +1214,7 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
}
onDragEnd(originalEvent: DragEvent): void {
if (this.dnd.onDragEnd) {
this.dnd.onDragEnd(originalEvent);
}
this.dnd.onDragEnd?.(originalEvent);
}
drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void {
@@ -1230,7 +1246,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
protected view: ListView<T>;
private spliceable: ISpliceable<T>;
private styleController: IStyleController;
private typeLabelController?: TypeLabelController<T>;
private typeNavigationController?: TypeNavigationController<T>;
private accessibilityProvider?: IListAccessibilityProvider<T>;
private keyboardController: KeyboardController<T> | undefined;
private mouseController: MouseController<T>;
@@ -1239,11 +1255,11 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
protected readonly disposables = new DisposableStore();
@memoize get onDidChangeFocus(): Event<IListEvent<T>> {
return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e));
return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e), this.disposables);
}
@memoize get onDidChangeSelection(): Event<IListEvent<T>> {
return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e));
return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e), this.disposables);
}
get domId(): string { return this.view.domId; }
@@ -1270,14 +1286,14 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
@memoize get onContextMenu(): Event<IListContextMenuEvent<T>> {
let didJustPressContextMenuKey = false;
const fromKeyDown = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)
const fromKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event))
.map(e => new StandardKeyboardEvent(e))
.filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
.map(stopEvent)
.filter(() => false)
.event as Event<any>;
const fromKeyUp = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event)
const fromKeyUp = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event))
.forEach(() => didJustPressContextMenuKey = false)
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
@@ -1291,7 +1307,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
})
.event;
const fromMouse = Event.chain(this.view.onContextMenu)
const fromMouse = this.disposables.add(Event.chain(this.view.onContextMenu))
.filter(_ => !didJustPressContextMenuKey)
.map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.pageX + 1, y: browserEvent.pageY }, browserEvent }))
.event;
@@ -1329,9 +1345,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
if (this.accessibilityProvider) {
baseRenderers.push(new AccessibiltyRenderer<T>(this.accessibilityProvider));
if (this.accessibilityProvider.onDidChangeActiveDescendant) {
this.accessibilityProvider.onDidChangeActiveDescendant(this.onDidChangeActiveDescendant, this, this.disposables);
}
this.accessibilityProvider.onDidChangeActiveDescendant?.(this.onDidChangeActiveDescendant, this, this.disposables);
}
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r]));
@@ -1373,8 +1387,8 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
if (_options.keyboardNavigationLabelProvider) {
const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate;
this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider, delegate);
this.disposables.add(this.typeLabelController);
this.typeNavigationController = new TypeNavigationController(this, this.view, _options.keyboardNavigationLabelProvider, _options.keyboardNavigationEventFilter ?? (() => true), delegate);
this.disposables.add(this.typeNavigationController);
}
this.mouseController = this.createMouseController(_options);
@@ -1399,9 +1413,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
updateOptions(optionsUpdate: IListOptionsUpdate = {}): void {
this._options = { ...this._options, ...optionsUpdate };
if (this.typeLabelController) {
this.typeLabelController.updateOptions(this._options);
}
this.typeNavigationController?.updateOptions(this._options);
if (this._options.multipleSelectionController !== undefined) {
if (this._options.multipleSelectionSupport) {
@@ -1517,9 +1529,9 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
this.view.layout(height, width);
}
toggleKeyboardNavigation(): void {
if (this.typeLabelController) {
this.typeLabelController.toggle();
triggerTypeNavigation(): void {
if (this.typeNavigationController) {
this.typeNavigationController.trigger();
}
}
@@ -1598,20 +1610,25 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
async focusNextPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): Promise<void> {
let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight);
lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1;
const lastPageElement = this.view.element(lastPageIndex);
const currentlyFocusedElement = this.getFocusedElements()[0];
const currentlyFocusedElementIndex = this.getFocus()[0];
if (currentlyFocusedElement !== lastPageElement) {
if (currentlyFocusedElementIndex !== lastPageIndex && (currentlyFocusedElementIndex === undefined || lastPageIndex > currentlyFocusedElementIndex)) {
const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter);
if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) {
if (lastGoodPageIndex > -1 && currentlyFocusedElementIndex !== lastGoodPageIndex) {
this.setFocus([lastGoodPageIndex], browserEvent);
} else {
this.setFocus([lastPageIndex], browserEvent);
}
} else {
const previousScrollTop = this.view.getScrollTop();
this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex));
let nextpageScrollTop = previousScrollTop + this.view.renderHeight;
if (lastPageIndex > currentlyFocusedElementIndex) {
// scroll last page element to the top only if the last page element is below the focused element
nextpageScrollTop -= this.view.elementHeight(lastPageIndex);
}
this.view.setScrollTop(nextpageScrollTop);
if (this.view.getScrollTop() !== previousScrollTop) {
this.setFocus([]);
@@ -1633,13 +1650,12 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
firstPageIndex = this.view.indexAfter(scrollTop - 1);
}
const firstPageElement = this.view.element(firstPageIndex);
const currentlyFocusedElement = this.getFocusedElements()[0];
const currentlyFocusedElementIndex = this.getFocus()[0];
if (currentlyFocusedElement !== firstPageElement) {
if (currentlyFocusedElementIndex !== firstPageIndex && (currentlyFocusedElementIndex === undefined || currentlyFocusedElementIndex >= firstPageIndex)) {
const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter);
if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) {
if (firstGoodPageIndex > -1 && currentlyFocusedElementIndex !== firstGoodPageIndex) {
this.setFocus([firstGoodPageIndex], browserEvent);
} else {
this.setFocus([firstPageIndex], browserEvent);
@@ -1783,6 +1799,10 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
return this.view.domNode;
}
getElementID(index: number): string {
return this.view.getElementDomId(index);
}
style(styles: IListStyles): void {
this.styleController.style(styles);
}
+4 -4
View File
@@ -21,7 +21,7 @@ export interface IRangedGroup {
export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGroup[] {
const result: IRangedGroup[] = [];
for (let r of groups) {
for (const r of groups) {
if (range.start >= r.range.end) {
continue;
}
@@ -62,7 +62,7 @@ export function consolidate(groups: IRangedGroup[]): IRangedGroup[] {
const result: IRangedGroup[] = [];
let previousGroup: IRangedGroup | null = null;
for (let group of groups) {
for (const group of groups) {
const start = group.range.start;
const end = group.range.end;
const size = group.size;
@@ -138,7 +138,7 @@ export class RangeMap {
let index = 0;
let size = 0;
for (let group of this.groups) {
for (const group of this.groups) {
const count = group.range.end - group.range.start;
const newSize = size + (count * group.size);
@@ -172,7 +172,7 @@ export class RangeMap {
let position = 0;
let count = 0;
for (let group of this.groups) {
for (const group of this.groups) {
const groupCount = group.range.end - group.range.start;
const newCount = count + groupCount;
+1 -3
View File
@@ -15,9 +15,7 @@ export interface IRow {
function removeFromParent(element: HTMLElement): void {
try {
if (element.parentElement) {
element.parentElement.removeChild(element);
}
element.parentElement?.removeChild(element);
} catch (e) {
// this will throw if this happens due to a blur event, nasty business
}
+43 -44
View File
@@ -90,14 +90,13 @@ export class Menu extends ActionBar {
context: options.context,
actionRunner: options.actionRunner,
ariaLabel: options.ariaLabel,
ariaRole: 'menu',
focusOnlyEnabledItems: true,
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true }
});
this.menuElement = menuElement;
this.actionsList.setAttribute('role', 'menu');
this.actionsList.tabIndex = 0;
this.menuDisposables = this._register(new DisposableStore());
@@ -160,7 +159,7 @@ export class Menu extends ActionBar {
}
this._register(addDisposableListener(this.domNode, EventType.MOUSE_OUT, e => {
let relatedTarget = e.relatedTarget as HTMLElement;
const relatedTarget = e.relatedTarget as HTMLElement;
if (!isAncestor(relatedTarget, this.domNode)) {
this.focusedItem = undefined;
this.updateFocus();
@@ -211,7 +210,7 @@ export class Menu extends ActionBar {
}));
let parentData: ISubMenuData = {
const parentData: ISubMenuData = {
parent: this
};
@@ -287,11 +286,13 @@ export class Menu extends ActionBar {
const fgColor = style.foregroundColor ? `${style.foregroundColor}` : '';
const bgColor = style.backgroundColor ? `${style.backgroundColor}` : '';
const border = style.borderColor ? `1px solid ${style.borderColor}` : '';
const shadow = style.shadowColor ? `0 2px 4px ${style.shadowColor}` : '';
const borderRadius = '5px';
const shadow = style.shadowColor ? `0 2px 8px ${style.shadowColor}` : '';
container.style.border = border;
this.domNode.style.color = fgColor;
this.domNode.style.backgroundColor = bgColor;
container.style.outline = border;
container.style.borderRadius = borderRadius;
container.style.color = fgColor;
container.style.backgroundColor = bgColor;
container.style.boxShadow = shadow;
if (this.viewItems) {
@@ -340,7 +341,7 @@ export class Menu extends ActionBar {
private setFocusedItem(element: HTMLElement): void {
for (let i = 0; i < this.actionsList.children.length; i++) {
let elem = this.actionsList.children[i];
const elem = this.actionsList.children[i];
if (element === elem) {
this.focusedItem = i;
break;
@@ -445,9 +446,9 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
// Set mnemonic
if (this.options.label && options.enableMnemonics) {
let label = this.getAction().label;
const label = this.getAction().label;
if (label) {
let matches = MENU_MNEMONIC_REGEX.exec(label);
const matches = MENU_MNEMONIC_REGEX.exec(label);
if (matches) {
this.mnemonic = (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase();
}
@@ -608,9 +609,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
this.label.innerText = replaceDoubleEscapes(label).trim();
}
if (this.item) {
this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase());
}
this.item?.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase());
} else {
this.label.innerText = label.replace(/&&/g, '&').trim();
}
@@ -691,20 +690,19 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
const isSelected = this.element && this.element.classList.contains('focused');
const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor;
const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined;
const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : '';
const outline = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : '';
const outlineOffset = isSelected && this.menuStyle.selectionBorderColor ? `-1px` : '';
if (this.item) {
this.item.style.color = fgColor ? fgColor.toString() : '';
this.item.style.backgroundColor = bgColor ? bgColor.toString() : '';
this.item.style.outline = outline;
this.item.style.outlineOffset = outlineOffset;
}
if (this.check) {
this.check.style.color = fgColor ? fgColor.toString() : '';
}
if (this.container) {
this.container.style.border = border;
}
}
style(style: IMenuStyles): void {
@@ -765,7 +763,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}
this._register(addDisposableListener(this.element, EventType.KEY_UP, e => {
let event = new StandardKeyboardEvent(e);
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) {
EventHelper.stop(e, true);
@@ -774,7 +772,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}));
this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => {
let event = new StandardKeyboardEvent(e);
const event = new StandardKeyboardEvent(e);
if (getActiveElement() === this.item) {
if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) {
@@ -914,7 +912,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
this.submenuContainer.style.top = `${top - viewBox.top}px`;
this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => {
let event = new StandardKeyboardEvent(e);
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.LeftArrow)) {
EventHelper.stop(e, true);
@@ -925,7 +923,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}));
this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => {
let event = new StandardKeyboardEvent(e);
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.LeftArrow)) {
EventHelper.stop(e, true);
}
@@ -966,9 +964,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
this.submenuIndicator.style.color = fgColor ? `${fgColor}` : '';
}
if (this.parentData.submenu) {
this.parentData.submenu.style(this.menuStyle);
}
this.parentData.submenu?.style(this.menuStyle);
}
override dispose(): void {
@@ -1012,7 +1008,8 @@ function getMenuWidgetCSS(style: IMenuStyles, isForShadowDom: boolean): string {
let result = /* css */`
.monaco-menu {
font-size: 13px;
border-radius: 5px;
min-width: 160px;
}
${formatRule(Codicon.menuSelection)}
@@ -1087,10 +1084,9 @@ ${formatRule(Codicon.menuSubmenu)}
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
display: block;
border-bottom: 1px solid #bbb;
border-bottom: 1px solid var(--vscode-menu-separatorBackground);
padding-top: 1px;
margin-left: .8em;
margin-right: .8em;
padding: 30px;
}
.monaco-menu .secondary-actions .monaco-action-bar .action-label {
@@ -1136,6 +1132,11 @@ ${formatRule(Codicon.menuSubmenu)}
position: relative;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .keybinding,
.monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .keybinding {
opacity: unset;
}
.monaco-menu .monaco-action-bar.vertical .action-label {
flex: 1 1 auto;
text-decoration: none;
@@ -1191,12 +1192,9 @@ ${formatRule(Codicon.menuSubmenu)}
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
padding: 0.5em 0 0 0;
margin-bottom: 0.5em;
width: 100%;
height: 0px !important;
margin-left: .8em !important;
margin-right: .8em !important;
opacity: 1;
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
@@ -1238,17 +1236,15 @@ ${formatRule(Codicon.menuSubmenu)}
outline: 0;
}
.monaco-menu .monaco-action-bar.vertical .action-item {
border: thin solid transparent; /* prevents jumping behaviour on hover or focus */
}
/* High Contrast Theming */
.hc-black .context-view.monaco-menu-container,
.hc-light .context-view.monaco-menu-container,
:host-context(.hc-black) .context-view.monaco-menu-container,
:host-context(.hc-light) .context-view.monaco-menu-container {
box-shadow: none;
}
.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused,
.hc-light .monaco-menu .monaco-action-bar.vertical .action-item.focused,
:host-context(.hc-black) .monaco-menu .monaco-action-bar.vertical .action-item.focused,
:host-context(.hc-light) .monaco-menu .monaco-action-bar.vertical .action-item.focused {
background: none;
@@ -1257,11 +1253,11 @@ ${formatRule(Codicon.menuSubmenu)}
/* Vertical Action Bar Styles */
.monaco-menu .monaco-action-bar.vertical {
padding: .5em 0;
padding: .6em 0;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item {
height: 1.8em;
height: 2em;
}
.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator),
@@ -1277,10 +1273,12 @@ ${formatRule(Codicon.menuSubmenu)}
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
font-size: inherit;
padding: 0.2em 0 0 0;
margin-bottom: 0.2em;
margin: 5px 0 !important;
padding: 0;
border-radius: 0;
}
.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator,
:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .action-label.separator {
margin-left: 0;
margin-right: 0;
@@ -1291,6 +1289,7 @@ ${formatRule(Codicon.menuSubmenu)}
padding: 0 1.8em;
}
.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
height: 100%;
mask-size: 10px 10px;
+31 -15
View File
@@ -9,25 +9,35 @@
display: flex;
flex-shrink: 1;
box-sizing: border-box;
height: 30px;
height: 100%;
overflow: hidden;
flex-wrap: wrap;
}
.menubar.overflow-menu-only {
width: 38px;
}
.fullscreen .menubar:not(.compact) {
margin: 0px;
padding: 0px 5px;
padding: 4px 5px;
}
.menubar > .menubar-menu-button {
display: flex;
align-items: center;
box-sizing: border-box;
padding: 0px 8px;
cursor: default;
-webkit-app-region: no-drag;
zoom: 1;
white-space: nowrap;
outline: 0;
outline: 0 !important;
}
.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title {
outline-width: 1px;
outline-style: solid;
outline-offset: -1px;
outline-color: var(--vscode-focusBorder);
}
.menubar.compact {
@@ -41,6 +51,11 @@
padding: 0px;
}
.menubar-menu-title {
padding: 0px 8px;
border-radius: 5px;
}
.menubar .menubar-menu-items-holder {
position: fixed;
left: 0px;
@@ -62,8 +77,13 @@
}
.menubar .toolbar-toggle-more {
width: 20px;
height: 100%;
width: 22px;
height: 22px;
padding: 0 8px;
display: flex;
align-items: center;
justify-content: center;
vertical-align: sub;
}
.menubar.compact .toolbar-toggle-more {
@@ -77,19 +97,15 @@
justify-content: center;
}
.menubar .toolbar-toggle-more {
padding: 0;
vertical-align: sub;
}
.menubar:not(.compact) .menubar-menu-button:first-child .toolbar-toggle-more::before,
.menubar.compact .toolbar-toggle-more::before {
content: "\eb94" !important;
}
/* Match behavior of outline for activity bar icons */
.menubar.compact > .menubar-menu-button.open,
.menubar.compact > .menubar-menu-button:focus,
.menubar.compact > .menubar-menu-button:hover {
.menubar.compact > .menubar-menu-button.open .menubar-menu-title,
.menubar.compact > .menubar-menu-button:focus .menubar-menu-title,
.menubar.compact > .menubar-menu-button:hover .menubar-menu-title{
outline-width: 1px !important;
outline-offset: -8px !important;
}
+51 -28
View File
@@ -116,7 +116,7 @@ export class MenuBar extends Disposable {
this._register(DOM.ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this));
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
const event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true;
const key = !!e.key ? e.key.toLocaleLowerCase() : '';
@@ -154,7 +154,7 @@ export class MenuBar extends Disposable {
}));
this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, (e) => {
let event = e as FocusEvent;
const event = e as FocusEvent;
if (event.relatedTarget) {
if (!this.container.contains(event.relatedTarget as HTMLElement)) {
@@ -164,7 +164,7 @@ export class MenuBar extends Disposable {
}));
this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_OUT, (e) => {
let event = e as FocusEvent;
const event = e as FocusEvent;
// We are losing focus and there is no related target, e.g. webview case
if (!event.relatedTarget) {
@@ -204,11 +204,11 @@ export class MenuBar extends Disposable {
const menuIndex = this.menus.length;
const cleanMenuLabel = cleanMnemonic(menuBarMenu.label);
let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(menuBarMenu.label);
const mnemonicMatches = MENU_MNEMONIC_REGEX.exec(menuBarMenu.label);
// Register mnemonics
if (mnemonicMatches) {
let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3];
const mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3];
this.registerMnemonic(this.menus.length, mnemonic);
}
@@ -225,7 +225,7 @@ export class MenuBar extends Disposable {
this.updateLabels(titleElement, buttonElement, menuBarMenu.label);
this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
const event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true;
if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) {
@@ -313,8 +313,7 @@ export class MenuBar extends Disposable {
createOverflowMenu(): void {
const label = this.isCompact ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More');
const title = this.isCompact ? label : undefined;
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.isCompact ? 0 : -1, 'aria-label': label, 'title': title, 'aria-haspopup': true });
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.isCompact ? 0 : -1, 'aria-label': label, 'aria-haspopup': true });
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + Codicon.menuBarMore.cssSelector, { 'role': 'none', 'aria-hidden': true });
buttonElement.appendChild(titleElement);
@@ -322,7 +321,7 @@ export class MenuBar extends Disposable {
buttonElement.style.visibility = 'hidden';
this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
const event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true;
const triggerKeys = [KeyCode.Enter];
@@ -330,7 +329,12 @@ export class MenuBar extends Disposable {
triggerKeys.push(KeyCode.DownArrow);
} else {
triggerKeys.push(KeyCode.Space);
triggerKeys.push(this.options.compactMode === Direction.Right ? KeyCode.RightArrow : KeyCode.LeftArrow);
if (this.options.compactMode === Direction.Right) {
triggerKeys.push(KeyCode.RightArrow);
} else if (this.options.compactMode === Direction.Left) {
triggerKeys.push(KeyCode.LeftArrow);
}
}
if ((triggerKeys.some(k => event.equals(k)) && !this.isOpen)) {
@@ -469,6 +473,11 @@ export class MenuBar extends Disposable {
return;
}
const overflowMenuOnlyClass = 'overflow-menu-only';
// Remove overflow only restriction to allow the most space
this.container.classList.toggle(overflowMenuOnlyClass, false);
const sizeAvailable = this.container.offsetWidth;
let currentSize = 0;
let full = this.isCompact;
@@ -476,7 +485,7 @@ export class MenuBar extends Disposable {
this.numMenusShown = 0;
const showableMenus = this.menus.filter(menu => menu.buttonElement !== undefined && menu.titleElement !== undefined) as (MenuBarMenuWithElements & { titleElement: HTMLElement; buttonElement: HTMLElement })[];
for (let menuBarMenu of showableMenus) {
for (const menuBarMenu of showableMenus) {
if (!full) {
const size = menuBarMenu.buttonElement.offsetWidth;
if (currentSize + size > sizeAvailable) {
@@ -495,6 +504,18 @@ export class MenuBar extends Disposable {
}
}
// If below minimium menu threshold, show the overflow menu only as hamburger menu
if (this.numMenusShown - 1 <= showableMenus.length / 2) {
for (const menuBarMenu of showableMenus) {
menuBarMenu.buttonElement.style.visibility = 'hidden';
}
full = true;
this.numMenusShown = 0;
currentSize = 0;
}
// Overflow
if (this.isCompact) {
this.overflowMenu.actions = [];
@@ -534,6 +555,9 @@ export class MenuBar extends Disposable {
this.container.appendChild(this.overflowMenu.buttonElement);
this.overflowMenu.buttonElement.style.visibility = 'hidden';
}
// If we are only showing the overflow, add this class to avoid taking up space
this.container.classList.toggle(overflowMenuOnlyClass, this.numMenusShown === 0);
}
private updateLabels(titleElement: HTMLElement, buttonElement: HTMLElement, label: string): void {
@@ -542,7 +566,7 @@ export class MenuBar extends Disposable {
// Update the button label to reflect mnemonics
if (this.options.enableMnemonics) {
let cleanLabel = strings.escape(label);
const cleanLabel = strings.escape(label);
// This is global so reset it
MENU_ESCAPED_MNEMONIC_REGEX.lastIndex = 0;
@@ -569,11 +593,11 @@ export class MenuBar extends Disposable {
titleElement.innerText = cleanMenuLabel.replace(/&&/g, '&');
}
let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label);
const mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label);
// Register mnemonics
if (mnemonicMatches) {
let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3];
const mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3];
if (this.options.enableMnemonics) {
buttonElement.setAttribute('aria-keyshortcuts', 'Alt+' + mnemonic.toLocaleLowerCase());
@@ -740,7 +764,7 @@ export class MenuBar extends Disposable {
this._onFocusStateChange.fire(this.focusState >= MenubarState.FOCUSED);
}
private get isVisible(): boolean {
get isVisible(): boolean {
return this.focusState >= MenubarState.VISIBLE;
}
@@ -838,7 +862,7 @@ export class MenuBar extends Disposable {
if (this.menus) {
this.menus.forEach(menuBarMenu => {
if (menuBarMenu.titleElement && menuBarMenu.titleElement.children.length) {
let child = menuBarMenu.titleElement.children.item(0) as HTMLElement;
const child = menuBarMenu.titleElement.children.item(0) as HTMLElement;
if (child) {
child.style.textDecoration = (this.options.alwaysOnMnemonics || visible) ? 'underline' : '';
}
@@ -956,9 +980,7 @@ export class MenuBar extends Disposable {
}
if (this.focusedMenu.holder) {
if (this.focusedMenu.holder.parentElement) {
this.focusedMenu.holder.parentElement.classList.remove('open');
}
this.focusedMenu.holder.parentElement?.classList.remove('open');
this.focusedMenu.holder.remove();
}
@@ -975,7 +997,7 @@ export class MenuBar extends Disposable {
const actualMenuIndex = menuIndex >= this.numMenusShown ? MenuBar.OVERFLOW_INDEX : menuIndex;
const customMenu = actualMenuIndex === MenuBar.OVERFLOW_INDEX ? this.overflowMenu : this.menus[actualMenuIndex];
if (!customMenu.actions || !customMenu.buttonElement) {
if (!customMenu.actions || !customMenu.buttonElement || !customMenu.titleElement) {
return;
}
@@ -983,23 +1005,24 @@ export class MenuBar extends Disposable {
customMenu.buttonElement.classList.add('open');
const buttonBoundingRect = customMenu.buttonElement.getBoundingClientRect();
const titleBoundingRect = customMenu.titleElement.getBoundingClientRect();
const titleBoundingRectZoom = DOM.getDomNodeZoomLevel(customMenu.titleElement);
if (this.options.compactMode === Direction.Right) {
menuHolder.style.top = `${buttonBoundingRect.top}px`;
menuHolder.style.left = `${buttonBoundingRect.left + this.container.clientWidth}px`;
menuHolder.style.top = `${titleBoundingRect.top}px`;
menuHolder.style.left = `${titleBoundingRect.left + this.container.clientWidth}px`;
} else if (this.options.compactMode === Direction.Left) {
menuHolder.style.top = `${buttonBoundingRect.top}px`;
menuHolder.style.top = `${titleBoundingRect.top}px`;
menuHolder.style.right = `${this.container.clientWidth}px`;
menuHolder.style.left = 'auto';
} else {
menuHolder.style.top = `${buttonBoundingRect.bottom}px`;
menuHolder.style.left = `${buttonBoundingRect.left}px`;
menuHolder.style.top = `${titleBoundingRect.bottom * titleBoundingRectZoom}px`;
menuHolder.style.left = `${titleBoundingRect.left * titleBoundingRectZoom}px`;
}
customMenu.buttonElement.appendChild(menuHolder);
let menuOptions: IMenuOptions = {
const menuOptions: IMenuOptions = {
getKeyBinding: this.options.getKeybinding,
actionRunner: this.actionRunner,
enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics),
@@ -1008,7 +1031,7 @@ export class MenuBar extends Disposable {
useEventAsContext: true
};
let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions));
const menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions));
if (this.menuStyle) {
menuWidget.style(this.menuStyle);
}
+1 -1
View File
@@ -17,7 +17,7 @@ import 'vs/css!./sash';
* Allow the sashes to be visible at runtime.
* @remark Use for development purposes only.
*/
let DEBUG = false;
const DEBUG = false;
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
/**
@@ -5,7 +5,7 @@
import * as dom from 'vs/base/browser/dom';
import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode';
import { GlobalPointerMoveMonitor, IPointerMoveEventData, standardPointerMoveMerger } from 'vs/base/browser/globalPointerMoveMonitor';
import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor';
import { StandardWheelEvent } from 'vs/base/browser/mouseEvent';
import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
@@ -245,8 +245,7 @@ export abstract class AbstractScrollbar extends Widget {
e.target,
e.pointerId,
e.buttons,
standardPointerMoveMerger,
(pointerMoveData: IPointerMoveEventData) => {
(pointerMoveData: PointerEvent) => {
const pointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(pointerMoveData);
const pointerOrthogonalDelta = Math.abs(pointerOrthogonalPosition - initialPointerOrthogonalPosition);
@@ -232,7 +232,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._setListeningToMouseWheel(this._options.handleMouseWheel);
this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e));
this.onnonbubblingmouseout(this._listenOnDomNode, (e) => this._onMouseOut(e));
this.onmouseleave(this._listenOnDomNode, (e) => this._onMouseLeave(e));
this._hideTimeout = this._register(new TimeoutTimer());
this._isDragging = false;
@@ -525,7 +525,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._hide();
}
private _onMouseOut(e: IMouseEvent): void {
private _onMouseLeave(e: IMouseEvent): void {
this._mouseIsOver = false;
this._hide();
}

Some files were not shown because too many files have changed in this diff Show More