Archived
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:
Vendored
+18
-54
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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>} */
|
||||
|
||||
Vendored
+5
@@ -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 {
|
||||
/**
|
||||
|
||||
Vendored
+3
-3
@@ -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);
|
||||
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+7
-4
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+2
-3
@@ -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));
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+34
-6
@@ -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);
|
||||
|
||||
|
||||
+3
-3
@@ -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 {
|
||||
|
||||
@@ -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/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"types": [],
|
||||
"lib": [
|
||||
"es5",
|
||||
"ES2015.Iterable"
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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. */
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user