mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 11:08:31 -05:00
Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy * Fix yarn build * Fix numerous build breaks * Next batch of build break fixes * More build break fixes * Runtime breaks * Additional post merge fixes * Fix windows setup file * Fix test failures. * Update license header blocks to refer to source eula
This commit is contained in:
@@ -11,16 +11,15 @@ import lifecycle = require('vs/base/common/lifecycle');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner } from 'vs/base/common/actions';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { EventType as CommonEventType } from 'vs/base/common/events';
|
||||
import types = require('vs/base/common/types');
|
||||
import { IEventEmitter, EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { Gesture, EventType } from 'vs/base/browser/touch';
|
||||
import { EventType, Gesture } from 'vs/base/browser/touch';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface IActionItem extends IEventEmitter {
|
||||
export interface IActionItem {
|
||||
actionRunner: IActionRunner;
|
||||
setActionContext(context: any): void;
|
||||
render(element: HTMLElement): void;
|
||||
@@ -35,19 +34,16 @@ export interface IBaseActionItemOptions {
|
||||
isMenu?: boolean;
|
||||
}
|
||||
|
||||
export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
export class BaseActionItem implements IActionItem {
|
||||
|
||||
public builder: Builder;
|
||||
public _callOnDispose: lifecycle.IDisposable[];
|
||||
public _context: any;
|
||||
public _action: IAction;
|
||||
|
||||
private gesture: Gesture;
|
||||
private _actionRunner: IActionRunner;
|
||||
|
||||
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
|
||||
super();
|
||||
|
||||
this._callOnDispose = [];
|
||||
this._context = context || this;
|
||||
this._action = action;
|
||||
@@ -109,7 +105,7 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.builder = $(container);
|
||||
this.gesture = new Gesture(container);
|
||||
Gesture.addTarget(container);
|
||||
|
||||
const enableDragging = this.options && this.options.draggable;
|
||||
if (enableDragging) {
|
||||
@@ -118,17 +114,18 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
|
||||
this.builder.on(EventType.Tap, e => this.onClick(e));
|
||||
|
||||
this.builder.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
this.builder.on(DOM.EventType.MOUSE_DOWN, (e) => {
|
||||
if (!enableDragging) {
|
||||
DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it
|
||||
}
|
||||
|
||||
if (this._action.enabled && e.button === 0) {
|
||||
const mouseEvent = e as MouseEvent;
|
||||
if (this._action.enabled && mouseEvent.button === 0) {
|
||||
this.builder.addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.builder.on(DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
this.builder.on(DOM.EventType.CLICK, (e) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
// See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
|
||||
// > Writing to the clipboard
|
||||
@@ -145,13 +142,13 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
}
|
||||
});
|
||||
|
||||
this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e: MouseEvent) => {
|
||||
this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
this.builder.removeClass('active');
|
||||
});
|
||||
}
|
||||
|
||||
public onClick(event: Event): void {
|
||||
public onClick(event: DOM.EventLike): void {
|
||||
DOM.EventHelper.stop(event, true);
|
||||
|
||||
let context: any;
|
||||
@@ -198,25 +195,18 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (this.builder) {
|
||||
this.builder.destroy();
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
if (this.gesture) {
|
||||
this.gesture.dispose();
|
||||
this.gesture = null;
|
||||
}
|
||||
|
||||
this._callOnDispose = lifecycle.dispose(this._callOnDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export class Separator extends Action {
|
||||
|
||||
public static ID = 'vs.actions.separator';
|
||||
public static readonly ID = 'vs.actions.separator';
|
||||
|
||||
constructor(label?: string, order?: number) {
|
||||
super(Separator.ID, label, label ? 'separator text' : 'separator');
|
||||
@@ -339,14 +329,6 @@ export class ActionItem extends BaseActionItem {
|
||||
this.$e.removeClass('checked');
|
||||
}
|
||||
}
|
||||
|
||||
public _updateRadio(): void {
|
||||
if (this.getAction().radio) {
|
||||
this.$e.addClass('radio');
|
||||
} else {
|
||||
this.$e.removeClass('radio');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum ActionsOrientation {
|
||||
@@ -379,7 +361,7 @@ export interface IActionOptions extends IActionItemOptions {
|
||||
index?: number;
|
||||
}
|
||||
|
||||
export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
export class ActionBar implements IActionRunner {
|
||||
|
||||
public options: IActionBarOptions;
|
||||
|
||||
@@ -398,8 +380,12 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
|
||||
private _onDidBlur = new Emitter<void>();
|
||||
private _onDidCancel = new Emitter<void>();
|
||||
private _onDidRun = new Emitter<IRunEvent>();
|
||||
private _onDidBeforeRun = new Emitter<IRunEvent>();
|
||||
|
||||
constructor(container: HTMLElement | Builder, options: IActionBarOptions = defaultOptions) {
|
||||
super();
|
||||
this.options = options;
|
||||
this._context = options.context;
|
||||
this.toDispose = [];
|
||||
@@ -410,7 +396,8 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
this.toDispose.push(this._actionRunner);
|
||||
}
|
||||
|
||||
this.toDispose.push(this.addEmitter(this._actionRunner));
|
||||
this.toDispose.push(this._actionRunner.onDidRun(e => this._onDidRun.fire(e)));
|
||||
this.toDispose.push(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e)));
|
||||
|
||||
this.items = [];
|
||||
this.focusedItem = undefined;
|
||||
@@ -447,8 +434,8 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
break;
|
||||
}
|
||||
|
||||
$(this.domNode).on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
$(this.domNode).on(DOM.EventType.KEY_DOWN, (e) => {
|
||||
let event = new StandardKeyboardEvent(e as KeyboardEvent);
|
||||
let eventHandled = true;
|
||||
|
||||
if (event.equals(previousKey)) {
|
||||
@@ -469,8 +456,8 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
}
|
||||
});
|
||||
|
||||
$(this.domNode).on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
$(this.domNode).on(DOM.EventType.KEY_UP, (e) => {
|
||||
let event = new StandardKeyboardEvent(e as KeyboardEvent);
|
||||
|
||||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
@@ -486,14 +473,14 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
});
|
||||
|
||||
this.focusTracker = DOM.trackFocus(this.domNode);
|
||||
this.focusTracker.addBlurListener(() => {
|
||||
this.toDispose.push(this.focusTracker.onDidBlur(() => {
|
||||
if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) {
|
||||
this.emit(DOM.EventType.BLUR, {});
|
||||
this._onDidBlur.fire();
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this.focusTracker.addFocusListener(() => this.updateFocusedItem());
|
||||
this.toDispose.push(this.focusTracker.onDidFocus(() => this.updateFocusedItem()));
|
||||
|
||||
this.actionsList = document.createElement('ul');
|
||||
this.actionsList.className = 'actions-container';
|
||||
@@ -511,6 +498,22 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
((container instanceof Builder) ? container.getHTMLElement() : container).appendChild(this.domNode);
|
||||
}
|
||||
|
||||
public get onDidBlur(): Event<void> {
|
||||
return this._onDidBlur.event;
|
||||
}
|
||||
|
||||
public get onDidCancel(): Event<void> {
|
||||
return this._onDidCancel.event;
|
||||
}
|
||||
|
||||
public get onDidRun(): Event<IRunEvent> {
|
||||
return this._onDidRun.event;
|
||||
}
|
||||
|
||||
public get onDidBeforeRun(): Event<IRunEvent> {
|
||||
return this._onDidBeforeRun.event;
|
||||
}
|
||||
|
||||
public setAriaLabel(label: string): void {
|
||||
if (label) {
|
||||
this.actionsList.setAttribute('aria-label', label);
|
||||
@@ -565,7 +568,7 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
actionItemElement.setAttribute('role', 'presentation');
|
||||
|
||||
// Prevent native context menu on actions
|
||||
$(actionItemElement).on(DOM.EventType.CONTEXT_MENU, (e: Event) => {
|
||||
$(actionItemElement).on(DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
@@ -582,7 +585,6 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
|
||||
item.actionRunner = this._actionRunner;
|
||||
item.setActionContext(this.context);
|
||||
this.addEmitter(item);
|
||||
item.render(actionItemElement);
|
||||
|
||||
if (index === null || index < 0 || index >= this.actionsList.children.length) {
|
||||
@@ -725,7 +727,7 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
(<HTMLElement>document.activeElement).blur(); // remove focus from focused action
|
||||
}
|
||||
|
||||
this.emit(CommonEventType.CANCEL);
|
||||
this._onDidCancel.fire();
|
||||
}
|
||||
|
||||
public run(action: IAction, context?: any): TPromise<void> {
|
||||
@@ -746,8 +748,6 @@ export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
this.getContainer().destroy();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./button';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface IButtonOptions extends IButtonStyles {
|
||||
}
|
||||
@@ -30,7 +30,8 @@ const defaultOptions: IButtonStyles = {
|
||||
buttonForeground: Color.white
|
||||
};
|
||||
|
||||
export class Button extends EventEmitter {
|
||||
export class Button {
|
||||
|
||||
// {{SQL CARBON EDIT}} -- changed access modifier to protected
|
||||
protected $el: Builder;
|
||||
private options: IButtonOptions;
|
||||
@@ -40,11 +41,12 @@ export class Button extends EventEmitter {
|
||||
private buttonForeground: Color;
|
||||
private buttonBorder: Color;
|
||||
|
||||
private _onDidClick = new Emitter<any>();
|
||||
readonly onDidClick: Event<any> = this._onDidClick.event;
|
||||
|
||||
constructor(container: Builder, options?: IButtonOptions);
|
||||
constructor(container: HTMLElement, options?: IButtonOptions);
|
||||
constructor(container: any, options?: IButtonOptions) {
|
||||
super();
|
||||
|
||||
this.options = options || Object.create(null);
|
||||
mixin(this.options, defaultOptions, false);
|
||||
|
||||
@@ -64,14 +66,14 @@ export class Button extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit(DOM.EventType.CLICK, e);
|
||||
this._onDidClick.fire(e);
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
this.$el.on(DOM.EventType.KEY_DOWN, (e) => {
|
||||
let event = new StandardKeyboardEvent(e as KeyboardEvent);
|
||||
let eventHandled = false;
|
||||
if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
this.emit(DOM.EventType.CLICK, e);
|
||||
this._onDidClick.fire(e);
|
||||
eventHandled = true;
|
||||
} else if (event.equals(KeyCode.Escape)) {
|
||||
this.$el.domBlur();
|
||||
@@ -83,7 +85,7 @@ export class Button extends EventEmitter {
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.MOUSE_OVER, (e: MouseEvent) => {
|
||||
this.$el.on(DOM.EventType.MOUSE_OVER, (e) => {
|
||||
if (!this.$el.hasClass('disabled')) {
|
||||
const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null;
|
||||
if (hoverBackground) {
|
||||
@@ -92,7 +94,7 @@ export class Button extends EventEmitter {
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.MOUSE_OUT, (e: MouseEvent) => {
|
||||
this.$el.on(DOM.EventType.MOUSE_OUT, (e) => {
|
||||
this.applyStyles(); // restore standard styles
|
||||
});
|
||||
|
||||
@@ -167,6 +169,6 @@ export class Button extends EventEmitter {
|
||||
this.$el = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
this._onDidClick.dispose();
|
||||
}
|
||||
}
|
||||
@@ -39,13 +39,13 @@ export class Checkbox extends Widget {
|
||||
|
||||
constructor(opts: ICheckboxOpts) {
|
||||
super();
|
||||
this._opts = objects.clone(opts);
|
||||
this._opts = objects.deepClone(opts);
|
||||
objects.mixin(this._opts, defaultOpts, false);
|
||||
this._checked = this._opts.isChecked;
|
||||
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.title = this._opts.title;
|
||||
this.domNode.className = this._className();
|
||||
this.domNode.className = 'custom-checkbox ' + this._opts.actionClassName + ' ' + (this._checked ? 'checked' : 'unchecked');
|
||||
this.domNode.tabIndex = 0;
|
||||
this.domNode.setAttribute('role', 'checkbox');
|
||||
this.domNode.setAttribute('aria-checked', String(this._checked));
|
||||
@@ -88,12 +88,13 @@ export class Checkbox extends Widget {
|
||||
public set checked(newIsChecked: boolean) {
|
||||
this._checked = newIsChecked;
|
||||
this.domNode.setAttribute('aria-checked', String(this._checked));
|
||||
this.domNode.className = this._className();
|
||||
this.applyStyles();
|
||||
}
|
||||
if (this._checked) {
|
||||
this.domNode.classList.add('checked');
|
||||
} else {
|
||||
this.domNode.classList.remove('checked');
|
||||
}
|
||||
|
||||
private _className(): string {
|
||||
return 'custom-checkbox ' + this._opts.actionClassName + ' ' + (this._checked ? 'checked' : 'unchecked');
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public width(): number {
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'vs/css!./contextview';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
|
||||
export interface IAnchor {
|
||||
x: number;
|
||||
@@ -103,10 +102,10 @@ function layout(view: ISize, around: IView, viewport: IView, anchorPosition: Anc
|
||||
return { top: top, left: left };
|
||||
}
|
||||
|
||||
export class ContextView extends EventEmitter {
|
||||
export class ContextView {
|
||||
|
||||
private static BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur'];
|
||||
private static BUBBLE_DOWN_EVENTS = ['click'];
|
||||
private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur'];
|
||||
private static readonly BUBBLE_DOWN_EVENTS = ['click'];
|
||||
|
||||
private $container: Builder;
|
||||
private $view: Builder;
|
||||
@@ -115,7 +114,6 @@ export class ContextView extends EventEmitter {
|
||||
private toDisposeOnClean: IDisposable;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.$view = $('.context-view').hide();
|
||||
this.setContainer(container);
|
||||
|
||||
@@ -265,7 +263,6 @@ export class ContextView extends EventEmitter {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.hide();
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
|
||||
@@ -11,7 +11,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
|
||||
import { ActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
|
||||
@@ -68,7 +67,7 @@ export class BaseDropdown extends ActionRunner {
|
||||
this._toDispose.push(cleanupFn);
|
||||
}
|
||||
|
||||
this._toDispose.push(new Gesture(this.$label.getHTMLElement()));
|
||||
Gesture.addTarget(this.$label.getHTMLElement());
|
||||
}
|
||||
|
||||
public get toDispose(): IDisposable[] {
|
||||
@@ -244,22 +243,4 @@ export class DropdownMenu extends BaseDropdown {
|
||||
public hide(): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export class DropdownGroup extends EventEmitter {
|
||||
|
||||
private el: HTMLElement;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
|
||||
this.el = document.createElement('div');
|
||||
this.el.className = 'dropdown-group';
|
||||
|
||||
container.appendChild(this.el);
|
||||
}
|
||||
|
||||
public get element(): HTMLElement {
|
||||
return this.el;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isFunction } from 'vs/base/common/types';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { DropdownMenu, IDropdownMenuOptions } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
|
||||
export interface ILinksDropdownMenuOptions extends IDropdownMenuOptions {
|
||||
tooltip: string;
|
||||
}
|
||||
|
||||
export class LinksDropdownMenu extends DropdownMenu {
|
||||
|
||||
constructor(container: HTMLElement, options: ILinksDropdownMenuOptions) {
|
||||
super(container, options);
|
||||
|
||||
this.tooltip = options.tooltip;
|
||||
}
|
||||
|
||||
protected onEvent(e: Event, activeElement: HTMLElement): void {
|
||||
if (e instanceof KeyboardEvent && ((<KeyboardEvent>e).ctrlKey || (isMacintosh && (<KeyboardEvent>e).metaKey))) {
|
||||
return; // allow to use Ctrl/Meta in workspace dropdown menu
|
||||
}
|
||||
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkDropdownAction extends Action {
|
||||
|
||||
constructor(id: string, name: string, clazz: string, url: () => string, forceOpenInNewTab?: boolean);
|
||||
constructor(id: string, name: string, clazz: string, url: string, forceOpenInNewTab?: boolean);
|
||||
constructor(id: string, name: string, clazz: string, url: any, forceOpenInNewTab?: boolean) {
|
||||
super(id, name, clazz, true, (e: Event) => {
|
||||
let urlString = url;
|
||||
|
||||
if (isFunction(url)) {
|
||||
urlString = url();
|
||||
}
|
||||
|
||||
if (forceOpenInNewTab || (e instanceof MouseEvent && ((<MouseEvent>e).ctrlKey || (isMacintosh && (<MouseEvent>e).metaKey)))) {
|
||||
window.open(urlString, '_blank');
|
||||
} else {
|
||||
window.location.href = urlString;
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -268,8 +268,7 @@ export class FindInput extends Widget {
|
||||
placeholder: this.placeholder || '',
|
||||
ariaLabel: this.label || '',
|
||||
validationOptions: {
|
||||
validation: this.validation || null,
|
||||
showMessage: true
|
||||
validation: this.validation || null
|
||||
},
|
||||
inputBackground: this.inputBackground,
|
||||
inputForeground: this.inputForeground,
|
||||
|
||||
@@ -11,18 +11,13 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider } from 'vs/base/common/labels';
|
||||
import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider, getBaseLabel } from 'vs/base/common/labels';
|
||||
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IIconLabelCreationOptions {
|
||||
supportHighlights?: boolean;
|
||||
}
|
||||
|
||||
export interface ILabelBadgeOptions {
|
||||
title: string;
|
||||
className: string;
|
||||
}
|
||||
|
||||
export interface IIconLabelOptions {
|
||||
title?: string;
|
||||
extraClasses?: string[];
|
||||
@@ -168,6 +163,6 @@ export class FileLabel extends IconLabel {
|
||||
public setFile(file: uri, provider: IWorkspaceFolderProvider, userHome: IUserHomeProvider): void {
|
||||
const parent = paths.dirname(file.fsPath);
|
||||
|
||||
this.setValue(paths.basename(file.fsPath), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath });
|
||||
this.setValue(getBaseLabel(file), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ export interface IMessage {
|
||||
|
||||
export interface IInputValidationOptions {
|
||||
validation: IInputValidator;
|
||||
showMessage?: boolean;
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
@@ -94,11 +93,11 @@ export class InputBox extends Widget {
|
||||
private placeholder: string;
|
||||
private ariaLabel: string;
|
||||
private validation: IInputValidator;
|
||||
private showValidationMessage: boolean;
|
||||
private state = 'idle';
|
||||
private cachedHeight: number;
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected showValidationMessage: boolean;
|
||||
protected inputBackground: Color;
|
||||
protected inputForeground: Color;
|
||||
protected inputBorder: Color;
|
||||
@@ -141,7 +140,7 @@ export class InputBox extends Widget {
|
||||
if (this.options.validationOptions) {
|
||||
this.validation = this.options.validationOptions.validation;
|
||||
// {{SQL CARBON EDIT}} Canidate for addition to vscode
|
||||
this.showValidationMessage = this.options.validationOptions.showMessage || true;
|
||||
this.showValidationMessage = true;
|
||||
}
|
||||
|
||||
this.element = dom.append(container, $('.monaco-inputbox.idle'));
|
||||
@@ -235,10 +234,6 @@ export class InputBox extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
public setContextViewProvider(contextViewProvider: IContextViewProvider): void {
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
}
|
||||
|
||||
public get inputElement(): HTMLInputElement {
|
||||
return this.input;
|
||||
}
|
||||
@@ -405,9 +400,9 @@ export class InputBox extends Widget {
|
||||
className: 'monaco-inputbox-message'
|
||||
};
|
||||
|
||||
let spanElement: HTMLElement = (this.message.formatContent
|
||||
const spanElement = (this.message.formatContent
|
||||
? renderFormattedText(this.message.content, renderOptions)
|
||||
: renderText(this.message.content, renderOptions)) as any;
|
||||
: renderText(this.message.content, renderOptions));
|
||||
dom.addClass(spanElement, this.classForType(this.message.type));
|
||||
|
||||
const styles = this.stylesForType(this.message.type);
|
||||
@@ -511,7 +506,6 @@ export class InputBox extends Widget {
|
||||
this.placeholder = null;
|
||||
this.ariaLabel = null;
|
||||
this.validation = null;
|
||||
this.showValidationMessage = null;
|
||||
this.state = null;
|
||||
this.actionbar = null;
|
||||
|
||||
|
||||
@@ -43,4 +43,6 @@
|
||||
}
|
||||
|
||||
/* Focus */
|
||||
.monaco-list.element-focused { outline: 0 !important; }
|
||||
.monaco-list.element-focused, .monaco-list.selection-single, .monaco-list.selection-multiple {
|
||||
outline: 0 !important;
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { GestureEvent } from 'vs/base/browser/touch';
|
||||
|
||||
export interface IDelegate<T> {
|
||||
getHeight(element: T): number;
|
||||
getTemplateId(element: T): string;
|
||||
@@ -15,19 +17,26 @@ export interface IRenderer<TElement, TTemplateData> {
|
||||
disposeTemplate(templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
export interface IListElementEvent<T, E> {
|
||||
element: T;
|
||||
index: number;
|
||||
event: E;
|
||||
}
|
||||
|
||||
export interface IListEvent<T> {
|
||||
elements: T[];
|
||||
indexes: number[];
|
||||
}
|
||||
|
||||
export interface IListMouseEvent<T> extends MouseEvent {
|
||||
element: T;
|
||||
export interface IListMouseEvent<T> {
|
||||
browserEvent: MouseEvent;
|
||||
element: T | undefined;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface IListTouchEvent<T> {
|
||||
browserEvent: TouchEvent;
|
||||
element: T | undefined;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface IListGestureEvent<T> {
|
||||
browserEvent: GestureEvent;
|
||||
element: T | undefined;
|
||||
index: number;
|
||||
}
|
||||
|
||||
@@ -35,4 +44,4 @@ export interface IListContextMenuEvent<T> {
|
||||
element: T;
|
||||
index: number;
|
||||
anchor: HTMLElement | { x: number; y: number; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'vs/css!./list';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { IDelegate, IRenderer, IListEvent } from './list';
|
||||
import { List, IListOptions } from './listWidget';
|
||||
import { List, IListOptions, IListStyles } from './listWidget';
|
||||
import { IPagedModel } from 'vs/base/common/paging';
|
||||
import Event, { mapEvent } from 'vs/base/common/event';
|
||||
|
||||
@@ -73,6 +73,22 @@ export class PagedList<T> {
|
||||
this.list = new List(container, delegate, pagedRenderers, options);
|
||||
}
|
||||
|
||||
getHTMLElement(): HTMLElement {
|
||||
return this.list.getHTMLElement();
|
||||
}
|
||||
|
||||
isDOMFocused(): boolean {
|
||||
return this.list.getHTMLElement() === document.activeElement;
|
||||
}
|
||||
|
||||
get onDidFocus(): Event<void> {
|
||||
return this.list.onDidFocus;
|
||||
}
|
||||
|
||||
get onDidBlur(): Event<void> {
|
||||
return this.list.onDidBlur;
|
||||
}
|
||||
|
||||
get widget(): List<number> {
|
||||
return this.list;
|
||||
}
|
||||
@@ -110,6 +126,14 @@ export class PagedList<T> {
|
||||
this.list.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
open(indexes: number[]): void {
|
||||
this.list.open(indexes);
|
||||
}
|
||||
|
||||
setFocus(indexes: number[]): void {
|
||||
this.list.setFocus(indexes);
|
||||
}
|
||||
|
||||
focusNext(n?: number, loop?: boolean): void {
|
||||
this.list.focusNext(n, loop);
|
||||
}
|
||||
@@ -149,4 +173,8 @@ export class PagedList<T> {
|
||||
reveal(index: number, relativeTop?: number): void {
|
||||
this.list.reveal(index, relativeTop);
|
||||
}
|
||||
|
||||
style(styles: IListStyles): void {
|
||||
this.list.style(styles);
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,22 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toObject, assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { getOrDefault } from 'vs/base/common/objects';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import Event, { mapEvent, filterEvent } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { RangeMap, IRange, relativeComplement, each } from './rangeMap';
|
||||
import { IDelegate, IRenderer } from './list';
|
||||
import { RangeMap, IRange, relativeComplement, intersect, shift } from './rangeMap';
|
||||
import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list';
|
||||
import { RowCache, IRow } from './rowCache';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
function canUseTranslate3d(): boolean {
|
||||
if (browser.isFirefox) {
|
||||
@@ -46,18 +50,6 @@ interface IItem<T> {
|
||||
row: IRow;
|
||||
}
|
||||
|
||||
const MouseEventTypes = [
|
||||
'click',
|
||||
'dblclick',
|
||||
'mouseup',
|
||||
'mousedown',
|
||||
'mouseover',
|
||||
'mousemove',
|
||||
'mouseout',
|
||||
'contextmenu',
|
||||
'touchstart'
|
||||
];
|
||||
|
||||
export interface IListViewOptions {
|
||||
useShadows?: boolean;
|
||||
}
|
||||
@@ -66,19 +58,23 @@ const DefaultOptions: IListViewOptions = {
|
||||
useShadows: true
|
||||
};
|
||||
|
||||
export class ListView<T> implements IDisposable {
|
||||
export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
private items: IItem<T>[];
|
||||
private itemId: number;
|
||||
private rangeMap: RangeMap;
|
||||
private cache: RowCache<T>;
|
||||
private renderers: { [templateId: string]: IRenderer<T, any>; };
|
||||
private renderers = new Map<string, IRenderer<T, any>>();
|
||||
private lastRenderTop: number;
|
||||
private lastRenderHeight: number;
|
||||
private _domNode: HTMLElement;
|
||||
private gesture: Gesture;
|
||||
private rowsContainer: HTMLElement;
|
||||
private scrollableElement: ScrollableElement;
|
||||
private splicing = false;
|
||||
private dragAndDropScrollInterval: number;
|
||||
private dragAndDropScrollTimeout: number;
|
||||
private dragAndDropMouseY: number;
|
||||
private disposables: IDisposable[];
|
||||
|
||||
constructor(
|
||||
@@ -90,7 +86,11 @@ export class ListView<T> implements IDisposable {
|
||||
this.items = [];
|
||||
this.itemId = 0;
|
||||
this.rangeMap = new RangeMap();
|
||||
this.renderers = toObject<IRenderer<T, any>>(renderers, r => r.templateId);
|
||||
|
||||
for (const renderer of renderers) {
|
||||
this.renderers.set(renderer.templateId, renderer);
|
||||
}
|
||||
|
||||
this.cache = new RowCache(this.renderers);
|
||||
|
||||
this.lastRenderTop = 0;
|
||||
@@ -101,7 +101,7 @@ export class ListView<T> implements IDisposable {
|
||||
|
||||
this.rowsContainer = document.createElement('div');
|
||||
this.rowsContainer.className = 'monaco-list-rows';
|
||||
this.gesture = new Gesture(this.rowsContainer);
|
||||
Gesture.addTarget(this.rowsContainer);
|
||||
|
||||
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
|
||||
alwaysConsumeMouseWheel: true,
|
||||
@@ -118,6 +118,9 @@ export class ListView<T> implements IDisposable {
|
||||
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
|
||||
domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables);
|
||||
|
||||
const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e));
|
||||
onDragOver(this.onDragOver, this, this.disposables);
|
||||
|
||||
this.layout();
|
||||
}
|
||||
|
||||
@@ -126,8 +129,31 @@ export class ListView<T> implements IDisposable {
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): T[] {
|
||||
if (this.splicing) {
|
||||
throw new Error('Can\'t run recursive splices.');
|
||||
}
|
||||
|
||||
this.splicing = true;
|
||||
|
||||
try {
|
||||
return this._splice(start, deleteCount, elements);
|
||||
} finally {
|
||||
this.splicing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _splice(start: number, deleteCount: number, elements: T[] = []): T[] {
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(previousRenderRange, i => this.removeItemFromDOM(this.items[i]));
|
||||
const deleteRange = { start, end: start + deleteCount };
|
||||
const removeRange = intersect(previousRenderRange, deleteRange);
|
||||
|
||||
for (let i = removeRange.start; i < removeRange.end; i++) {
|
||||
this.removeItemFromDOM(this.items[i]);
|
||||
}
|
||||
|
||||
const previousRestRange: IRange = { start: start + deleteCount, end: this.items.length };
|
||||
const previousRenderedRestRange = intersect(previousRestRange, previousRenderRange);
|
||||
const previousUnrenderedRestRanges = relativeComplement(previousRestRange, previousRenderRange);
|
||||
|
||||
const inserted = elements.map<IItem<T>>(element => ({
|
||||
id: String(this.itemId++),
|
||||
@@ -138,11 +164,38 @@ export class ListView<T> implements IDisposable {
|
||||
}));
|
||||
|
||||
this.rangeMap.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const deleted = this.items.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const delta = elements.length - deleteCount;
|
||||
const renderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(renderRange, i => this.insertItemInDOM(this.items[i], i));
|
||||
const renderedRestRange = shift(previousRenderedRestRange, delta);
|
||||
const updateRange = intersect(renderRange, renderedRestRange);
|
||||
|
||||
for (let i = updateRange.start; i < updateRange.end; i++) {
|
||||
this.updateItemInDOM(this.items[i], i);
|
||||
}
|
||||
|
||||
const removeRanges = relativeComplement(renderedRestRange, renderRange);
|
||||
|
||||
for (let r = 0; r < removeRanges.length; r++) {
|
||||
const removeRange = removeRanges[r];
|
||||
|
||||
for (let i = removeRange.start; i < removeRange.end; i++) {
|
||||
this.removeItemFromDOM(this.items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const unrenderedRestRanges = previousUnrenderedRestRanges.map(r => shift(r, delta));
|
||||
const elementsRange = { start, end: start + elements.length };
|
||||
const insertRanges = [elementsRange, ...unrenderedRestRanges].map(r => intersect(renderRange, r));
|
||||
|
||||
for (let r = 0; r < insertRanges.length; r++) {
|
||||
const insertRange = insertRanges[r];
|
||||
|
||||
for (let i = insertRange.start; i < insertRange.end; i++) {
|
||||
this.insertItemInDOM(this.items[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
const scrollHeight = this.getContentHeight();
|
||||
this.rowsContainer.style.height = `${scrollHeight}px`;
|
||||
@@ -200,8 +253,17 @@ export class ListView<T> implements IDisposable {
|
||||
const rangesToInsert = relativeComplement(renderRange, previousRenderRange);
|
||||
const rangesToRemove = relativeComplement(previousRenderRange, renderRange);
|
||||
|
||||
rangesToInsert.forEach(range => each(range, i => this.insertItemInDOM(this.items[i], i)));
|
||||
rangesToRemove.forEach(range => each(range, i => this.removeItemFromDOM(this.items[i])));
|
||||
for (const range of rangesToInsert) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.insertItemInDOM(this.items[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
for (const range of rangesToRemove) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
this.removeItemFromDOM(this.items[i], );
|
||||
}
|
||||
}
|
||||
|
||||
if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) {
|
||||
const transform = `translate3d(0px, -${renderTop}px, 0px)`;
|
||||
@@ -226,13 +288,18 @@ export class ListView<T> implements IDisposable {
|
||||
this.rowsContainer.appendChild(item.row.domNode);
|
||||
}
|
||||
|
||||
const renderer = this.renderers[item.templateId];
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
item.row.domNode.style.top = `${this.elementTop(index)}px`;
|
||||
item.row.domNode.style.height = `${item.size}px`;
|
||||
item.row.domNode.setAttribute('data-index', `${index}`);
|
||||
renderer.renderElement(item.element, index, item.row.templateData);
|
||||
}
|
||||
|
||||
private updateItemInDOM(item: IItem<T>, index: number): void {
|
||||
item.row.domNode.style.top = `${this.elementTop(index)}px`;
|
||||
item.row.domNode.setAttribute('data-index', `${index}`);
|
||||
}
|
||||
|
||||
private removeItemFromDOM(item: IItem<T>): void {
|
||||
this.cache.release(item.row);
|
||||
item.row = null;
|
||||
@@ -261,31 +328,33 @@ export class ListView<T> implements IDisposable {
|
||||
|
||||
// Events
|
||||
|
||||
addListener(type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
const userHandler = handler;
|
||||
let domNode = this.domNode;
|
||||
@memoize get onMouseClick(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseDblClick(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseUp(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseDown(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseOver(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseMove(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onMouseOut(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onContextMenu(): Event<IListMouseEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onTouchStart(): Event<IListTouchEvent<T>> { return filterEvent(mapEvent(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)), e => e.index >= 0); }
|
||||
@memoize get onTap(): Event<IListGestureEvent<T>> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); }
|
||||
|
||||
if (MouseEventTypes.indexOf(type) > -1) {
|
||||
handler = e => this.fireScopedEvent(e, userHandler, this.getItemIndexFromMouseEvent(e));
|
||||
} else if (type === TouchEventType.Tap) {
|
||||
domNode = this.rowsContainer;
|
||||
handler = e => this.fireScopedEvent(e, userHandler, this.getItemIndexFromGestureEvent(e));
|
||||
}
|
||||
|
||||
return DOM.addDisposableListener(domNode, type, handler, useCapture);
|
||||
private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent<T> {
|
||||
const index = this.getItemIndexFromEventTarget(browserEvent.target);
|
||||
const element = index < 0 ? undefined : this.items[index].element;
|
||||
return { browserEvent, index, element };
|
||||
}
|
||||
|
||||
private fireScopedEvent(
|
||||
event: any,
|
||||
handler: (event: any) => void,
|
||||
index: number
|
||||
) {
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent<T> {
|
||||
const index = this.getItemIndexFromEventTarget(browserEvent.target);
|
||||
const element = index < 0 ? undefined : this.items[index].element;
|
||||
return { browserEvent, index, element };
|
||||
}
|
||||
|
||||
const element = this.items[index].element;
|
||||
handler(assign(event, { element, index }));
|
||||
private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent<T> {
|
||||
const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget);
|
||||
const element = index < 0 ? undefined : this.items[index].element;
|
||||
return { browserEvent, index, element };
|
||||
}
|
||||
|
||||
private onScroll(e: ScrollEvent): void {
|
||||
@@ -299,16 +368,60 @@ export class ListView<T> implements IDisposable {
|
||||
this.scrollTop -= event.translationY;
|
||||
}
|
||||
|
||||
private onDragOver(event: DragMouseEvent): void {
|
||||
this.setupDragAndDropScrollInterval();
|
||||
this.dragAndDropMouseY = event.posy;
|
||||
}
|
||||
|
||||
private setupDragAndDropScrollInterval(): void {
|
||||
var viewTop = DOM.getTopLeftOffset(this._domNode).top;
|
||||
|
||||
if (!this.dragAndDropScrollInterval) {
|
||||
this.dragAndDropScrollInterval = window.setInterval(() => {
|
||||
if (this.dragAndDropMouseY === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var diff = this.dragAndDropMouseY - viewTop;
|
||||
var scrollDiff = 0;
|
||||
var upperLimit = this.renderHeight - 35;
|
||||
|
||||
if (diff < 35) {
|
||||
scrollDiff = Math.max(-14, 0.2 * (diff - 35));
|
||||
} else if (diff > upperLimit) {
|
||||
scrollDiff = Math.min(14, 0.2 * (diff - upperLimit));
|
||||
}
|
||||
|
||||
this.scrollTop += scrollDiff;
|
||||
}, 10);
|
||||
|
||||
this.cancelDragAndDropScrollTimeout();
|
||||
|
||||
this.dragAndDropScrollTimeout = window.setTimeout(() => {
|
||||
this.cancelDragAndDropScrollInterval();
|
||||
this.dragAndDropScrollTimeout = null;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private cancelDragAndDropScrollInterval(): void {
|
||||
if (this.dragAndDropScrollInterval) {
|
||||
window.clearInterval(this.dragAndDropScrollInterval);
|
||||
this.dragAndDropScrollInterval = null;
|
||||
}
|
||||
|
||||
this.cancelDragAndDropScrollTimeout();
|
||||
}
|
||||
|
||||
private cancelDragAndDropScrollTimeout(): void {
|
||||
if (this.dragAndDropScrollTimeout) {
|
||||
window.clearTimeout(this.dragAndDropScrollTimeout);
|
||||
this.dragAndDropScrollTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Util
|
||||
|
||||
private getItemIndexFromMouseEvent(event: MouseEvent): number {
|
||||
return this.getItemIndexFromEventTarget(event.target);
|
||||
}
|
||||
|
||||
private getItemIndexFromGestureEvent(event: GestureEvent): number {
|
||||
return this.getItemIndexFromEventTarget(event.initialTarget);
|
||||
}
|
||||
|
||||
private getItemIndexFromEventTarget(target: EventTarget): number {
|
||||
while (target instanceof HTMLElement && target !== this.rowsContainer) {
|
||||
const element = target as HTMLElement;
|
||||
|
||||
@@ -4,37 +4,35 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./list';
|
||||
import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { range, firstIndex } from 'vs/base/common/arrays';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { EventType as TouchEventType } from 'vs/base/browser/touch';
|
||||
import { Gesture } from 'vs/base/browser/touch';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import Event, { Emitter, EventBufferer, chain, mapEvent, fromCallback, anyEvent } from 'vs/base/common/event';
|
||||
import Event, { Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IDelegate, IRenderer, IListEvent, IListMouseEvent, IListContextMenuEvent } from './list';
|
||||
import { IDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list';
|
||||
import { ListView, IListViewOptions } from './listView';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
|
||||
export interface IIdentityProvider<T> {
|
||||
(element: T): string;
|
||||
}
|
||||
|
||||
export interface ISpliceable<T> {
|
||||
splice(start: number, deleteCount: number, elements: T[]): void;
|
||||
}
|
||||
|
||||
class CombinedSpliceable<T> implements ISpliceable<T> {
|
||||
|
||||
constructor(private spliceables: ISpliceable<T>[]) { }
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
this.spliceables.forEach(s => s.splice(start, deleteCount, elements));
|
||||
for (const spliceable of this.spliceables) {
|
||||
spliceable.splice(start, deleteCount, elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,19 +40,16 @@ interface ITraitChangeEvent {
|
||||
indexes: number[];
|
||||
}
|
||||
|
||||
interface ITraitTemplateData {
|
||||
container: HTMLElement;
|
||||
elementDisposable: IDisposable;
|
||||
}
|
||||
type ITraitTemplateData = HTMLElement;
|
||||
|
||||
interface IRenderedElement {
|
||||
interface IRenderedContainer {
|
||||
templateData: ITraitTemplateData;
|
||||
index: number;
|
||||
}
|
||||
|
||||
class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData>
|
||||
class TraitRenderer<T> implements IRenderer<T, ITraitTemplateData>
|
||||
{
|
||||
private rendered: IRenderedElement[] = [];
|
||||
private renderedElements: IRenderedContainer[] = [];
|
||||
|
||||
constructor(private trait: Trait<T>) { }
|
||||
|
||||
@@ -63,34 +58,59 @@ class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData>
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): ITraitTemplateData {
|
||||
const elementDisposable = EmptyDisposable;
|
||||
return { container, elementDisposable };
|
||||
return container;
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: ITraitTemplateData): void {
|
||||
templateData.elementDisposable.dispose();
|
||||
const renderedElementIndex = firstIndex(this.renderedElements, el => el.templateData === templateData);
|
||||
|
||||
const rendered = { index, templateData };
|
||||
this.rendered.push(rendered);
|
||||
templateData.elementDisposable = toDisposable(once(() => this.rendered.splice(this.rendered.indexOf(rendered), 1)));
|
||||
if (renderedElementIndex >= 0) {
|
||||
const rendered = this.renderedElements[renderedElementIndex];
|
||||
this.trait.unrender(templateData);
|
||||
rendered.index = index;
|
||||
} else {
|
||||
const rendered = { index, templateData };
|
||||
this.renderedElements.push(rendered);
|
||||
}
|
||||
|
||||
this.trait.renderIndex(index, templateData.container);
|
||||
this.trait.renderIndex(index, templateData);
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, insertCount: number): void {
|
||||
const rendered: IRenderedContainer[] = [];
|
||||
|
||||
for (let i = 0; i < this.renderedElements.length; i++) {
|
||||
const renderedElement = this.renderedElements[i];
|
||||
|
||||
if (renderedElement.index < start) {
|
||||
rendered.push(renderedElement);
|
||||
} else if (renderedElement.index >= start + deleteCount) {
|
||||
rendered.push({
|
||||
index: renderedElement.index + insertCount - deleteCount,
|
||||
templateData: renderedElement.templateData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.renderedElements = rendered;
|
||||
}
|
||||
|
||||
renderIndexes(indexes: number[]): void {
|
||||
this.rendered
|
||||
.filter(({ index }) => indexes.indexOf(index) > -1)
|
||||
.forEach(({ index, templateData }) => this.trait.renderIndex(index, templateData.container));
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number): void {
|
||||
this.rendered
|
||||
.filter(({ index }) => index >= start && index < start + deleteCount)
|
||||
.forEach(({ templateData }) => templateData.elementDisposable.dispose());
|
||||
for (const { index, templateData } of this.renderedElements) {
|
||||
if (indexes.indexOf(index) > -1) {
|
||||
this.trait.renderIndex(index, templateData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ITraitTemplateData): void {
|
||||
templateData.elementDisposable.dispose();
|
||||
const index = firstIndex(this.renderedElements, el => el.templateData === templateData);
|
||||
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderedElements.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +127,8 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
|
||||
get trait(): string { return this._trait; }
|
||||
|
||||
@memoize
|
||||
get renderer(): TraitRenderer<T, any> {
|
||||
return new TraitRenderer<T, any>(this);
|
||||
get renderer(): TraitRenderer<T> {
|
||||
return new TraitRenderer<T>(this);
|
||||
}
|
||||
|
||||
constructor(private _trait: string) {
|
||||
@@ -124,7 +144,7 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
|
||||
...this.indexes.filter(i => i >= end).map(i => i + diff)
|
||||
];
|
||||
|
||||
this.renderer.splice(start, deleteCount);
|
||||
this.renderer.splice(start, deleteCount, elements.length);
|
||||
this.set(indexes);
|
||||
}
|
||||
|
||||
@@ -132,6 +152,10 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
|
||||
DOM.toggleClass(container, this._trait, this.contains(index));
|
||||
}
|
||||
|
||||
unrender(container: HTMLElement): void {
|
||||
DOM.removeClass(container, this._trait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indexes which should have this trait.
|
||||
*
|
||||
@@ -229,17 +253,24 @@ class TraitSpliceable<T> implements ISpliceable<T> {
|
||||
}
|
||||
}
|
||||
|
||||
function isInputElement(e: HTMLElement): boolean {
|
||||
return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA';
|
||||
}
|
||||
|
||||
class KeyboardController<T> implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[];
|
||||
|
||||
constructor(
|
||||
private list: List<T>,
|
||||
private view: ListView<T>
|
||||
private view: ListView<T>,
|
||||
options: IListOptions<T>
|
||||
) {
|
||||
const multipleSelectionSupport = !(options.multipleSelectionSupport === false);
|
||||
this.disposables = [];
|
||||
|
||||
const onKeyDown = chain(domEvent(view.domNode, 'keydown'))
|
||||
.filter(e => !isInputElement(e.target as HTMLElement))
|
||||
.map(e => new StandardKeyboardEvent(e));
|
||||
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables);
|
||||
@@ -247,8 +278,11 @@ class KeyboardController<T> implements IDisposable {
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables);
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables);
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables);
|
||||
onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === KeyCode.KEY_A).on(this.onCtrlA, this, this.disposables);
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables);
|
||||
|
||||
if (multipleSelectionSupport) {
|
||||
onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === KeyCode.KEY_A).on(this.onCtrlA, this, this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
private onEnter(e: StandardKeyboardEvent): void {
|
||||
@@ -309,32 +343,39 @@ class KeyboardController<T> implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
function isSelectionSingleChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
return platform.isMacintosh ? event.metaKey : event.ctrlKey;
|
||||
function isSelectionSingleChangeEvent(event: IListMouseEvent<any> | IListTouchEvent<any>): boolean {
|
||||
return platform.isMacintosh ? event.browserEvent.metaKey : event.browserEvent.ctrlKey;
|
||||
}
|
||||
|
||||
function isSelectionRangeChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
return event.shiftKey;
|
||||
function isSelectionRangeChangeEvent(event: IListMouseEvent<any> | IListTouchEvent<any>): boolean {
|
||||
return event.browserEvent.shiftKey;
|
||||
}
|
||||
|
||||
function isSelectionChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
function isSelectionChangeEvent(event: IListMouseEvent<any> | IListTouchEvent<any>): boolean {
|
||||
return isSelectionSingleChangeEvent(event) || isSelectionRangeChangeEvent(event);
|
||||
}
|
||||
|
||||
export interface IMouseControllerOptions {
|
||||
selectOnMouseDown?: boolean;
|
||||
}
|
||||
|
||||
class MouseController<T> implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[];
|
||||
private multipleSelectionSupport: boolean;
|
||||
private didJustPressContextMenuKey: boolean = false;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@memoize get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
const fromKeyboard = chain(domEvent(this.view.domNode, 'keydown'))
|
||||
const fromKeydown = chain(domEvent(this.view.domNode, 'keydown'))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(e => this.list.getFocus().length > 0)
|
||||
.filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.map(e => {
|
||||
.filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.filter(e => { e.preventDefault(); e.stopPropagation(); return false; })
|
||||
.event as Event<any>;
|
||||
|
||||
const fromKeyup = chain(domEvent(this.view.domNode, 'keyup'))
|
||||
.filter(() => {
|
||||
const didJustPressContextMenuKey = this.didJustPressContextMenuKey;
|
||||
this.didJustPressContextMenuKey = false;
|
||||
return didJustPressContextMenuKey;
|
||||
})
|
||||
.filter(() => this.list.getFocus().length > 0)
|
||||
.map(() => {
|
||||
const index = this.list.getFocus()[0];
|
||||
const element = this.view.element(index);
|
||||
const anchor = this.view.domElement(index);
|
||||
@@ -343,40 +384,48 @@ class MouseController<T> implements IDisposable {
|
||||
.filter(({ anchor }) => !!anchor)
|
||||
.event;
|
||||
|
||||
const fromMouse = chain(fromCallback(handler => this.view.addListener('contextmenu', handler)))
|
||||
.map(({ element, index, clientX, clientY }) => ({ element, index, anchor: { x: clientX + 1, y: clientY } }))
|
||||
const fromMouse = chain(this.view.onContextMenu)
|
||||
.filter(() => !this.didJustPressContextMenuKey)
|
||||
.map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY } }))
|
||||
.event;
|
||||
|
||||
return anyEvent<IListContextMenuEvent<T>>(fromKeyboard, fromMouse);
|
||||
return anyEvent<IListContextMenuEvent<T>>(fromKeydown, fromKeyup, fromMouse);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private list: List<T>,
|
||||
private view: ListView<T>,
|
||||
private options: IMouseControllerOptions = {}
|
||||
private options: IListOptions<T> = {}
|
||||
) {
|
||||
this.disposables = [];
|
||||
this.disposables.push(view.addListener('mousedown', e => this.onMouseDown(e)));
|
||||
this.disposables.push(view.addListener('click', e => this.onPointer(e)));
|
||||
this.disposables.push(view.addListener('dblclick', e => this.onDoubleClick(e)));
|
||||
this.disposables.push(view.addListener('touchstart', e => this.onMouseDown(e)));
|
||||
this.disposables.push(view.addListener(TouchEventType.Tap, e => this.onPointer(e)));
|
||||
this.multipleSelectionSupport = options.multipleSelectionSupport !== false;
|
||||
|
||||
view.onMouseDown(this.onMouseDown, this, this.disposables);
|
||||
view.onMouseClick(this.onPointer, this, this.disposables);
|
||||
view.onMouseDblClick(this.onDoubleClick, this, this.disposables);
|
||||
view.onTouchStart(this.onMouseDown, this, this.disposables);
|
||||
view.onTap(this.onPointer, this, this.disposables);
|
||||
Gesture.addTarget(view.domNode);
|
||||
}
|
||||
|
||||
private onMouseDown(e: IListMouseEvent<T>): void {
|
||||
this.view.domNode.focus();
|
||||
private onMouseDown(e: IListMouseEvent<T> | IListTouchEvent<T>): void {
|
||||
if (this.options.focusOnMouseDown === false) {
|
||||
e.browserEvent.preventDefault();
|
||||
e.browserEvent.stopPropagation();
|
||||
} else if (document.activeElement !== e.browserEvent.target) {
|
||||
this.view.domNode.focus();
|
||||
}
|
||||
|
||||
let reference = this.list.getFocus()[0];
|
||||
reference = reference === undefined ? this.list.getSelection()[0] : reference;
|
||||
|
||||
if (isSelectionRangeChangeEvent(e)) {
|
||||
if (this.multipleSelectionSupport && isSelectionRangeChangeEvent(e)) {
|
||||
return this.changeSelection(e, reference);
|
||||
}
|
||||
|
||||
const focus = e.index;
|
||||
this.list.setFocus([focus]);
|
||||
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
if (this.multipleSelectionSupport && isSelectionChangeEvent(e)) {
|
||||
return this.changeSelection(e, reference);
|
||||
}
|
||||
|
||||
@@ -387,7 +436,7 @@ class MouseController<T> implements IDisposable {
|
||||
}
|
||||
|
||||
private onPointer(e: IListMouseEvent<T>): void {
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
if (this.multipleSelectionSupport && isSelectionChangeEvent(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -399,7 +448,7 @@ class MouseController<T> implements IDisposable {
|
||||
}
|
||||
|
||||
private onDoubleClick(e: IListMouseEvent<T>): void {
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
if (this.multipleSelectionSupport && isSelectionChangeEvent(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -408,7 +457,7 @@ class MouseController<T> implements IDisposable {
|
||||
this.list.pin(focus);
|
||||
}
|
||||
|
||||
private changeSelection(e: IListMouseEvent<T>, reference: number | undefined): void {
|
||||
private changeSelection(e: IListMouseEvent<T> | IListTouchEvent<T>, reference: number | undefined): void {
|
||||
const focus = e.index;
|
||||
|
||||
if (isSelectionRangeChangeEvent(e) && reference !== undefined) {
|
||||
@@ -442,11 +491,14 @@ class MouseController<T> implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IListOptions<T> extends IListViewOptions, IMouseControllerOptions, IListStyles {
|
||||
export interface IListOptions<T> extends IListViewOptions, IListStyles {
|
||||
identityProvider?: IIdentityProvider<T>;
|
||||
ariaLabel?: string;
|
||||
mouseSupport?: boolean;
|
||||
selectOnMouseDown?: boolean;
|
||||
focusOnMouseDown?: boolean;
|
||||
keyboardSupport?: boolean;
|
||||
multipleSelectionSupport?: boolean;
|
||||
}
|
||||
|
||||
export interface IListStyles {
|
||||
@@ -481,7 +533,8 @@ const defaultStyles: IListStyles = {
|
||||
|
||||
const DefaultOptions: IListOptions<any> = {
|
||||
keyboardSupport: true,
|
||||
mouseSupport: true
|
||||
mouseSupport: true,
|
||||
multipleSelectionSupport: true
|
||||
};
|
||||
|
||||
// TODO@Joao: move these utils into a SortedArray class
|
||||
@@ -581,11 +634,19 @@ class PipelineRenderer<T> implements IRenderer<T, any> {
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.renderElement(element, index, templateData[i]));
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
renderer.renderElement(element, index, templateData[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.disposeTemplate(templateData[i]));
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
renderer.disposeTemplate(templateData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,7 +657,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
private focus: Trait<T>;
|
||||
private selection: Trait<T>;
|
||||
private eventBufferer: EventBufferer;
|
||||
private eventBufferer = new EventBufferer();
|
||||
private view: ListView<T>;
|
||||
private spliceable: ISpliceable<T>;
|
||||
private disposables: IDisposable[];
|
||||
@@ -610,10 +671,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
return mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e));
|
||||
}
|
||||
|
||||
private _onContextMenu: Event<IListContextMenuEvent<T>> = Event.None;
|
||||
get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
return this._onContextMenu;
|
||||
}
|
||||
readonly onContextMenu: Event<IListContextMenuEvent<T>> = Event.None;
|
||||
|
||||
private _onOpen = new Emitter<number[]>();
|
||||
@memoize get onOpen(): Event<IListEvent<T>> {
|
||||
@@ -625,11 +683,25 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
return mapEvent(this._onPin.event, indexes => this.toListEvent({ indexes }));
|
||||
}
|
||||
|
||||
readonly onDOMFocus: Event<void>;
|
||||
readonly onDOMBlur: Event<void>;
|
||||
get onMouseClick(): Event<IListMouseEvent<T>> { return this.view.onMouseClick; }
|
||||
get onMouseDblClick(): Event<IListMouseEvent<T>> { return this.view.onMouseDblClick; }
|
||||
get onMouseUp(): Event<IListMouseEvent<T>> { return this.view.onMouseUp; }
|
||||
get onMouseDown(): Event<IListMouseEvent<T>> { return this.view.onMouseDown; }
|
||||
get onMouseOver(): Event<IListMouseEvent<T>> { return this.view.onMouseOver; }
|
||||
get onMouseMove(): Event<IListMouseEvent<T>> { return this.view.onMouseMove; }
|
||||
get onMouseOut(): Event<IListMouseEvent<T>> { return this.view.onMouseOut; }
|
||||
get onTouchStart(): Event<IListTouchEvent<T>> { return this.view.onTouchStart; }
|
||||
get onTap(): Event<IListGestureEvent<T>> { return this.view.onTap; }
|
||||
|
||||
private _onDispose = new Emitter<void>();
|
||||
get onDispose(): Event<void> { return this._onDispose.event; }
|
||||
get onKeyDown(): Event<KeyboardEvent> { return domEvent(this.view.domNode, 'keydown'); }
|
||||
get onKeyUp(): Event<KeyboardEvent> { return domEvent(this.view.domNode, 'keyup'); }
|
||||
get onKeyPress(): Event<KeyboardEvent> { return domEvent(this.view.domNode, 'keypress'); }
|
||||
|
||||
readonly onDidFocus: Event<void>;
|
||||
readonly onDidBlur: Event<void>;
|
||||
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
get onDidDispose(): Event<void> { return this._onDidDispose.event; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@@ -641,7 +713,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
this.focus = new FocusTrait(i => this.getElementDomId(i));
|
||||
this.selection = new Trait('selected');
|
||||
|
||||
this.eventBufferer = new EventBufferer();
|
||||
mixin(options, defaultStyles, false);
|
||||
|
||||
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [aria, this.focus.renderer, this.selection.renderer, r]));
|
||||
@@ -660,20 +731,20 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
this.view
|
||||
]);
|
||||
|
||||
this.disposables = [this.focus, this.selection, this.view, this._onDispose];
|
||||
this.disposables = [this.focus, this.selection, this.view, this._onDidDispose];
|
||||
|
||||
this.onDOMFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null);
|
||||
this.onDOMBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null);
|
||||
this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null);
|
||||
this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null);
|
||||
|
||||
if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) {
|
||||
const controller = new KeyboardController(this, this.view);
|
||||
const controller = new KeyboardController(this, this.view, options);
|
||||
this.disposables.push(controller);
|
||||
}
|
||||
|
||||
if (typeof options.mouseSupport !== 'boolean' || options.mouseSupport) {
|
||||
const controller = new MouseController(this, this.view, options);
|
||||
this.disposables.push(controller);
|
||||
this._onContextMenu = controller.onContextMenu;
|
||||
this.onContextMenu = controller.onContextMenu;
|
||||
}
|
||||
|
||||
this.onFocusChange(this._onFocusChange, this, this.disposables);
|
||||
@@ -687,6 +758,10 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): void {
|
||||
if (deleteCount === 0 && elements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
|
||||
}
|
||||
|
||||
@@ -966,7 +1041,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._onDispose.fire();
|
||||
this._onDidDispose.fire();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@ export interface IRangedGroup {
|
||||
|
||||
/**
|
||||
* Returns the intersection between two ranges as a range itself.
|
||||
* Returns `null` if the intersection is empty.
|
||||
* Returns `{ start: 0, end: 0 }` if the intersection is empty.
|
||||
*/
|
||||
export function intersect(one: IRange, other: IRange): IRange {
|
||||
if (one.start >= other.end || other.start >= one.end) {
|
||||
return null;
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
|
||||
const start = Math.max(one.start, other.start);
|
||||
const end = Math.min(one.end, other.end);
|
||||
|
||||
if (end - start <= 0) {
|
||||
return null;
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
|
||||
return { start, end };
|
||||
@@ -56,12 +56,6 @@ export function relativeComplement(one: IRange, other: IRange): IRange[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function each(range: IRange, fn: (index: number) => void): void {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intersection between a ranged group and a range.
|
||||
* Returns `[]` if the intersection is empty.
|
||||
@@ -80,7 +74,7 @@ export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGr
|
||||
|
||||
const intersection = intersect(range, r.range);
|
||||
|
||||
if (!intersection) {
|
||||
if (isEmpty(intersection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -96,7 +90,7 @@ export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGr
|
||||
/**
|
||||
* Shifts a range by that `much`.
|
||||
*/
|
||||
function shift({ start, end }: IRange, much: number): IRange {
|
||||
export function shift({ start, end }: IRange, much: number): IRange {
|
||||
return { start: start + much, end: end + much };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,9 @@ function removeFromParent(element: HTMLElement): void {
|
||||
|
||||
export class RowCache<T> implements IDisposable {
|
||||
|
||||
private cache: { [templateId: string]: IRow[]; };
|
||||
private cache = new Map<string, IRow[]>();
|
||||
|
||||
constructor(private renderers: { [templateId: string]: IRenderer<T, any>; }) {
|
||||
this.cache = Object.create(null);
|
||||
}
|
||||
constructor(private renderers: Map<string, IRenderer<T, any>>) { }
|
||||
|
||||
/**
|
||||
* Returns a row either by creating a new one or reusing
|
||||
@@ -38,7 +36,7 @@ export class RowCache<T> implements IDisposable {
|
||||
|
||||
if (!result) {
|
||||
const domNode = $('.monaco-list-row');
|
||||
const renderer = this.renderers[templateId];
|
||||
const renderer = this.renderers.get(templateId);
|
||||
const templateData = renderer.renderTemplate(domNode);
|
||||
result = { domNode, templateId, templateData };
|
||||
}
|
||||
@@ -67,27 +65,36 @@ export class RowCache<T> implements IDisposable {
|
||||
}
|
||||
|
||||
private getTemplateCache(templateId: string): IRow[] {
|
||||
return this.cache[templateId] || (this.cache[templateId] = []);
|
||||
let result = this.cache.get(templateId);
|
||||
|
||||
if (!result) {
|
||||
result = [];
|
||||
this.cache.set(templateId, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private garbageCollect(): void {
|
||||
if (this.cache) {
|
||||
Object.keys(this.cache).forEach(templateId => {
|
||||
this.cache[templateId].forEach(cachedRow => {
|
||||
const renderer = this.renderers[templateId];
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
});
|
||||
|
||||
delete this.cache[templateId];
|
||||
});
|
||||
if (!this.renderers) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cache.forEach((cachedRows, templateId) => {
|
||||
for (const cachedRow of cachedRows) {
|
||||
const renderer = this.renderers[templateId];
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.garbageCollect();
|
||||
this.cache = null;
|
||||
this.cache.clear();
|
||||
this.renderers = null;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { IActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, IActionItemProvider, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import Event from 'vs/base/common/event';
|
||||
|
||||
export interface IMenuOptions {
|
||||
context?: any;
|
||||
@@ -20,14 +20,12 @@ export interface IMenuOptions {
|
||||
getKeyBinding?: (action: IAction) => ResolvedKeybinding;
|
||||
}
|
||||
|
||||
export class Menu extends EventEmitter {
|
||||
export class Menu {
|
||||
|
||||
private actionBar: ActionBar;
|
||||
private listener: IDisposable;
|
||||
|
||||
constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) {
|
||||
super();
|
||||
|
||||
$(container).addClass('monaco-menu-container');
|
||||
|
||||
let $menu = $('.monaco-menu').appendTo(container);
|
||||
@@ -40,18 +38,22 @@ export class Menu extends EventEmitter {
|
||||
isMenu: true
|
||||
});
|
||||
|
||||
this.listener = this.addEmitter(this.actionBar);
|
||||
|
||||
this.actionBar.push(actions, { icon: true, label: true });
|
||||
}
|
||||
|
||||
public get onDidCancel(): Event<void> {
|
||||
return this.actionBar.onDidCancel;
|
||||
}
|
||||
|
||||
public get onDidBlur(): Event<void> {
|
||||
return this.actionBar.onDidBlur;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.actionBar.focus(true);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
|
||||
if (this.actionBar) {
|
||||
this.actionBar.dispose();
|
||||
this.actionBar = null;
|
||||
|
||||
@@ -40,7 +40,6 @@ export class ProgressBar {
|
||||
private toUnbind: IDisposable[];
|
||||
private workedVal: number;
|
||||
private element: Builder;
|
||||
private animationRunning: boolean;
|
||||
private bit: HTMLElement;
|
||||
private totalWork: number;
|
||||
private animationStopToken: ValueCallback;
|
||||
@@ -64,11 +63,6 @@ export class ProgressBar {
|
||||
|
||||
builder.div({ 'class': css_progress_bit }).on([DOM.EventType.ANIMATION_START, DOM.EventType.ANIMATION_END, DOM.EventType.ANIMATION_ITERATION], (e: Event) => {
|
||||
switch (e.type) {
|
||||
case DOM.EventType.ANIMATION_START:
|
||||
case DOM.EventType.ANIMATION_END:
|
||||
this.animationRunning = e.type === DOM.EventType.ANIMATION_START;
|
||||
break;
|
||||
|
||||
case DOM.EventType.ANIMATION_ITERATION:
|
||||
if (this.animationStopToken) {
|
||||
this.animationStopToken(null);
|
||||
|
||||
@@ -13,8 +13,8 @@ import paths = require('vs/base/common/paths');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
[index: string]: string;
|
||||
@@ -75,14 +75,19 @@ export interface IResourceDescriptor {
|
||||
name: string;
|
||||
size: number;
|
||||
etag: string;
|
||||
mime: string;
|
||||
}
|
||||
|
||||
// Chrome is caching images very aggressively and so we use the ETag information to find out if
|
||||
// we need to bypass the cache or not. We could always bypass the cache everytime we show the image
|
||||
// however that has very bad impact on memory consumption because each time the image gets shown,
|
||||
// memory grows (see also https://github.com/electron/electron/issues/6275)
|
||||
const IMAGE_RESOURCE_ETAG_CACHE = new BoundedMap<{ etag: string, src: string }>(100);
|
||||
const IMAGE_RESOURCE_ETAG_CACHE = new LRUCache<string, { etag: string, src: string }>(100);
|
||||
function imageSrc(descriptor: IResourceDescriptor): string {
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
return descriptor.resource.toString(true /* skip encoding */);
|
||||
}
|
||||
|
||||
const src = descriptor.resource.toString();
|
||||
|
||||
let cached = IMAGE_RESOURCE_ETAG_CACHE.get(src);
|
||||
@@ -105,12 +110,12 @@ function imageSrc(descriptor: IResourceDescriptor): string {
|
||||
*/
|
||||
export class ResourceViewer {
|
||||
|
||||
private static KB = 1024;
|
||||
private static MB = ResourceViewer.KB * ResourceViewer.KB;
|
||||
private static GB = ResourceViewer.MB * ResourceViewer.KB;
|
||||
private static TB = ResourceViewer.GB * ResourceViewer.KB;
|
||||
private static readonly KB = 1024;
|
||||
private static readonly MB = ResourceViewer.KB * ResourceViewer.KB;
|
||||
private static readonly GB = ResourceViewer.MB * ResourceViewer.KB;
|
||||
private static readonly TB = ResourceViewer.GB * ResourceViewer.KB;
|
||||
|
||||
private static MAX_IMAGE_SIZE = ResourceViewer.MB; // showing images inline is memory intense, so we have a limit
|
||||
private static readonly MAX_IMAGE_SIZE = ResourceViewer.MB; // showing images inline is memory intense, so we have a limit
|
||||
|
||||
public static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
@@ -119,23 +124,26 @@ export class ResourceViewer {
|
||||
openExternal: (uri: URI) => void,
|
||||
metadataClb?: (meta: string) => void
|
||||
): void {
|
||||
|
||||
// Ensure CSS class
|
||||
$(container).setClass('monaco-resource-viewer');
|
||||
|
||||
// Lookup media mime if any
|
||||
let mime: string;
|
||||
const ext = paths.extname(descriptor.resource.toString());
|
||||
if (ext) {
|
||||
mime = mapExtToMediaMimes[ext.toLowerCase()];
|
||||
let mime = descriptor.mime;
|
||||
if (!mime && descriptor.resource.scheme === Schemas.file) {
|
||||
const ext = paths.extname(descriptor.resource.toString());
|
||||
if (ext) {
|
||||
mime = mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
}
|
||||
|
||||
if (!mime) {
|
||||
mime = mimes.MIME_BINARY;
|
||||
}
|
||||
|
||||
// Show Image inline
|
||||
// Show Image inline unless they are large
|
||||
if (mime.indexOf('image/') >= 0) {
|
||||
if (descriptor.size <= ResourceViewer.MAX_IMAGE_SIZE) {
|
||||
if (ResourceViewer.inlineImage(descriptor)) {
|
||||
$(container)
|
||||
.empty()
|
||||
.addClass('image')
|
||||
@@ -159,18 +167,21 @@ export class ResourceViewer {
|
||||
scrollbar.scanDomNode();
|
||||
});
|
||||
} else {
|
||||
$(container)
|
||||
const imageContainer = $(container)
|
||||
.empty()
|
||||
.p({
|
||||
text: nls.localize('largeImageError', "The image is too large to display in the editor. ")
|
||||
})
|
||||
.append($('a', {
|
||||
});
|
||||
|
||||
if (descriptor.resource.scheme !== Schemas.data) {
|
||||
imageContainer.append($('a', {
|
||||
role: 'button',
|
||||
class: 'open-external',
|
||||
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
|
||||
}).on(DOM.EventType.CLICK, (e) => {
|
||||
openExternal(descriptor.resource);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +201,26 @@ export class ResourceViewer {
|
||||
}
|
||||
}
|
||||
|
||||
private static inlineImage(descriptor: IResourceDescriptor): boolean {
|
||||
let skipInlineImage: boolean;
|
||||
|
||||
// Data URI
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
const BASE64_MARKER = 'base64,';
|
||||
const base64MarkerIndex = descriptor.resource.path.indexOf(BASE64_MARKER);
|
||||
const hasData = base64MarkerIndex >= 0 && descriptor.resource.path.substring(base64MarkerIndex + BASE64_MARKER.length).length > 0;
|
||||
|
||||
skipInlineImage = !hasData || descriptor.size > ResourceViewer.MAX_IMAGE_SIZE || descriptor.resource.path.length > ResourceViewer.MAX_IMAGE_SIZE;
|
||||
}
|
||||
|
||||
// File URI
|
||||
else {
|
||||
skipInlineImage = typeof descriptor.size !== 'number' || descriptor.size > ResourceViewer.MAX_IMAGE_SIZE;
|
||||
}
|
||||
|
||||
return !skipInlineImage;
|
||||
}
|
||||
|
||||
private static formatSize(size: number): string {
|
||||
if (size < ResourceViewer.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
|
||||
@@ -12,8 +12,7 @@ import { isIPad } from 'vs/base/browser/browser';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import types = require('vs/base/common/types');
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { Gesture, EventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { EventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
@@ -48,18 +47,21 @@ export enum Orientation {
|
||||
HORIZONTAL
|
||||
}
|
||||
|
||||
export class Sash extends EventEmitter {
|
||||
export class Sash {
|
||||
|
||||
private $e: Builder;
|
||||
private gesture: Gesture;
|
||||
private layoutProvider: ISashLayoutProvider;
|
||||
private isDisabled: boolean;
|
||||
private hidden: boolean;
|
||||
private orientation: Orientation;
|
||||
private size: number;
|
||||
|
||||
private _onDidStart = new Emitter<ISashEvent>();
|
||||
private _onDidChange = new Emitter<ISashEvent>();
|
||||
private _onDidReset = new Emitter<void>();
|
||||
private _onDidEnd = new Emitter<void>();
|
||||
|
||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions = {}) {
|
||||
super();
|
||||
|
||||
this.$e = $('.monaco-sash').appendTo(container);
|
||||
|
||||
@@ -67,11 +69,10 @@ export class Sash extends EventEmitter {
|
||||
this.$e.addClass('mac');
|
||||
}
|
||||
|
||||
this.gesture = new Gesture(this.$e.getHTMLElement());
|
||||
|
||||
this.$e.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { this.onMouseDown(e); });
|
||||
this.$e.on(DOM.EventType.DBLCLICK, (e: MouseEvent) => { this.emit('reset', e); });
|
||||
this.$e.on(EventType.Start, (e: GestureEvent) => { this.onTouchStart(e); });
|
||||
this.$e.on(DOM.EventType.MOUSE_DOWN, (e) => { this.onMouseDown(e as MouseEvent); });
|
||||
this.$e.on(DOM.EventType.DBLCLICK, (e) => this._onDidReset.fire());
|
||||
Gesture.addTarget(this.$e.getHTMLElement());
|
||||
this.$e.on(EventType.Start, (e) => { this.onTouchStart(e as GestureEvent); });
|
||||
|
||||
this.size = options.baseSize || 5;
|
||||
|
||||
@@ -87,8 +88,20 @@ export class Sash extends EventEmitter {
|
||||
this.layoutProvider = layoutProvider;
|
||||
}
|
||||
|
||||
public getHTMLElement(): HTMLElement {
|
||||
return this.$e.getHTMLElement();
|
||||
public get onDidStart(): Event<ISashEvent> {
|
||||
return this._onDidStart.event;
|
||||
}
|
||||
|
||||
public get onDidChange(): Event<ISashEvent> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
public get onDidReset(): Event<void> {
|
||||
return this._onDidReset.event;
|
||||
}
|
||||
|
||||
public get onDidEnd(): Event<void> {
|
||||
return this._onDidEnd.event;
|
||||
}
|
||||
|
||||
public setOrientation(orientation: Orientation): void {
|
||||
@@ -136,17 +149,14 @@ export class Sash extends EventEmitter {
|
||||
};
|
||||
|
||||
this.$e.addClass('active');
|
||||
this.emit('start', startEvent);
|
||||
this._onDidStart.fire(startEvent);
|
||||
|
||||
let $window = $(window);
|
||||
let containerCSSClass = `${this.getOrientation()}-cursor-container${isMacintosh ? '-mac' : ''}`;
|
||||
|
||||
let lastCurrentX = startX;
|
||||
let lastCurrentY = startY;
|
||||
|
||||
$window.on('mousemove', (e: MouseEvent) => {
|
||||
$window.on('mousemove', (e) => {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
let mouseMoveEvent = new StandardMouseEvent(e);
|
||||
let mouseMoveEvent = new StandardMouseEvent(e as MouseEvent);
|
||||
|
||||
let event: ISashEvent = {
|
||||
startX: startX,
|
||||
@@ -155,14 +165,11 @@ export class Sash extends EventEmitter {
|
||||
currentY: mouseMoveEvent.posy
|
||||
};
|
||||
|
||||
lastCurrentX = mouseMoveEvent.posx;
|
||||
lastCurrentY = mouseMoveEvent.posy;
|
||||
|
||||
this.emit('change', event);
|
||||
}).once('mouseup', (e: MouseEvent) => {
|
||||
this._onDidChange.fire(event);
|
||||
}).once('mouseup', (e) => {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
this.$e.removeClass('active');
|
||||
this.emit('end');
|
||||
this._onDidEnd.fire();
|
||||
|
||||
$window.off('mousemove');
|
||||
document.body.classList.remove(containerCSSClass);
|
||||
@@ -184,32 +191,26 @@ export class Sash extends EventEmitter {
|
||||
let startX = event.pageX;
|
||||
let startY = event.pageY;
|
||||
|
||||
this.emit('start', {
|
||||
this._onDidStart.fire({
|
||||
startX: startX,
|
||||
currentX: startX,
|
||||
startY: startY,
|
||||
currentY: startY
|
||||
});
|
||||
|
||||
let lastCurrentX = startX;
|
||||
let lastCurrentY = startY;
|
||||
|
||||
listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.Change, (event: GestureEvent) => {
|
||||
if (types.isNumber(event.pageX) && types.isNumber(event.pageY)) {
|
||||
this.emit('change', {
|
||||
this._onDidChange.fire({
|
||||
startX: startX,
|
||||
currentX: event.pageX,
|
||||
startY: startY,
|
||||
currentY: event.pageY
|
||||
});
|
||||
|
||||
lastCurrentX = event.pageX;
|
||||
lastCurrentY = event.pageY;
|
||||
}
|
||||
}));
|
||||
|
||||
listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.End, (event: GestureEvent) => {
|
||||
this.emit('end');
|
||||
this._onDidEnd.fire();
|
||||
dispose(listeners);
|
||||
}));
|
||||
}
|
||||
@@ -277,8 +278,6 @@ export class Sash extends EventEmitter {
|
||||
this.$e.destroy();
|
||||
this.$e = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,10 +301,10 @@ export class VSash extends Disposable implements IVerticalSashLayoutProvider {
|
||||
this.ratio = 0.5;
|
||||
this.sash = new Sash(container, this);
|
||||
|
||||
this._register(this.sash.addListener('start', () => this.onSashDragStart()));
|
||||
this._register(this.sash.addListener('change', (e: ISashEvent) => this.onSashDrag(e)));
|
||||
this._register(this.sash.addListener('end', () => this.onSashDragEnd()));
|
||||
this._register(this.sash.addListener('reset', () => this.onSashReset()));
|
||||
this._register(this.sash.onDidStart(() => this.onSashDragStart()));
|
||||
this._register(this.sash.onDidChange((e: ISashEvent) => this.onSashDrag(e)));
|
||||
this._register(this.sash.onDidEnd(() => this.onSashDragEnd()));
|
||||
this._register(this.sash.onDidReset(() => this.onSashReset()));
|
||||
}
|
||||
|
||||
public getVerticalSashTop(): number {
|
||||
@@ -344,7 +343,7 @@ export class VSash extends Disposable implements IVerticalSashLayoutProvider {
|
||||
}
|
||||
|
||||
private onSashReset(): void {
|
||||
this.ratio = 0.5;
|
||||
this.compute(0.5);
|
||||
this._onPositionChange.fire(this.position);
|
||||
this.sash.layout();
|
||||
}
|
||||
|
||||
@@ -187,10 +187,6 @@ export abstract class AbstractScrollbar extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
public delegateSliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
|
||||
this._sliderMouseDown(e, onDragFinished);
|
||||
}
|
||||
|
||||
private _onMouseDown(e: IMouseEvent): void {
|
||||
let offsetX: number;
|
||||
let offsetY: number;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollDimensions, ISc
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import { ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
const HIDE_TIMEOUT = 500;
|
||||
@@ -45,7 +45,7 @@ class MouseWheelClassifierItem {
|
||||
|
||||
export class MouseWheelClassifier {
|
||||
|
||||
public static INSTANCE = new MouseWheelClassifier();
|
||||
public static readonly INSTANCE = new MouseWheelClassifier();
|
||||
|
||||
private readonly _capacity: number;
|
||||
private _memory: MouseWheelClassifierItem[];
|
||||
@@ -250,14 +250,6 @@ export abstract class AbstractScrollableElement extends Widget {
|
||||
this._verticalScrollbar.delegateMouseDown(browserEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate a mouse down event to the vertical scrollbar (directly to the slider!).
|
||||
* This is to help with clicking somewhere else and having the scrollbar react.
|
||||
*/
|
||||
public delegateSliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
|
||||
this._verticalScrollbar.delegateSliderMouseDown(e, onDragFinished);
|
||||
}
|
||||
|
||||
public getScrollDimensions(): IScrollDimensions {
|
||||
return this._scrollable.getScrollDimensions();
|
||||
}
|
||||
|
||||
@@ -189,10 +189,6 @@ export class ScrollbarState {
|
||||
return this._computedSliderPosition;
|
||||
}
|
||||
|
||||
public getSliderCenter(): number {
|
||||
return (this._computedSliderPosition + this._computedSliderSize / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a desired `scrollPosition` such that `offset` ends up in the center of the slider.
|
||||
* `offset` is based on the same coordinate system as the `sliderPosition`.
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { clone } from 'vs/base/common/objects';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
export interface ISelectBoxStyles {
|
||||
selectBackground?: Color;
|
||||
@@ -36,18 +36,15 @@ export class SelectBox extends Widget {
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected selectElement: HTMLSelectElement;
|
||||
protected options: string[];
|
||||
|
||||
private selected: number;
|
||||
private container: HTMLElement;
|
||||
private _onDidSelect: Emitter<ISelectData>;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected selectBackground: Color;
|
||||
protected selectForeground: Color;
|
||||
protected selectBorder: Color;
|
||||
|
||||
constructor(options: string[], selected: number, styles: ISelectBoxStyles = clone(defaultStyles)) {
|
||||
constructor(options: string[], selected: number, styles: ISelectBoxStyles = deepClone(defaultStyles)) {
|
||||
super();
|
||||
|
||||
this.selectElement = document.createElement('select');
|
||||
@@ -117,7 +114,6 @@ export class SelectBox extends Widget {
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.container = container;
|
||||
dom.addClass(container, 'select-container');
|
||||
container.appendChild(this.selectElement);
|
||||
this.setOptions(this.options, this.selected);
|
||||
@@ -159,4 +155,4 @@ export class SelectBox extends Widget {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export interface IPanelStyles {
|
||||
|
||||
export abstract class Panel implements IView {
|
||||
|
||||
private static HEADER_SIZE = 22;
|
||||
private static readonly HEADER_SIZE = 22;
|
||||
|
||||
protected _expanded: boolean;
|
||||
private expandedSize: number | undefined = undefined;
|
||||
@@ -146,8 +146,8 @@ export abstract class Panel implements IView {
|
||||
this.renderHeader(this.header);
|
||||
|
||||
const focusTracker = trackFocus(this.header);
|
||||
focusTracker.addFocusListener(() => addClass(this.header, 'focused'));
|
||||
focusTracker.addBlurListener(() => removeClass(this.header, 'focused'));
|
||||
focusTracker.onDidFocus(() => addClass(this.header, 'focused'));
|
||||
focusTracker.onDidBlur(() => removeClass(this.header, 'focused'));
|
||||
|
||||
this.updateHeader();
|
||||
|
||||
@@ -226,7 +226,7 @@ interface IDndContext {
|
||||
|
||||
class PanelDraggable implements IDisposable {
|
||||
|
||||
private static DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5));
|
||||
private static readonly DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5));
|
||||
|
||||
// see https://github.com/Microsoft/vscode/issues/14470
|
||||
private dragOverCounter = 0;
|
||||
@@ -338,7 +338,7 @@ export class PanelView implements IDisposable {
|
||||
|
||||
readonly onDidSashChange: Event<void>;
|
||||
|
||||
constructor(private container: HTMLElement, options: IPanelViewOptions = {}) {
|
||||
constructor(container: HTMLElement, options: IPanelViewOptions = {}) {
|
||||
this.dnd = !!options.dnd;
|
||||
this.el = append(container, $('.monaco-panel-view'));
|
||||
this.splitview = new SplitView(this.el);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import 'vs/css!./splitview';
|
||||
import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { fromEventEmitter, mapEvent, Emitter } from 'vs/base/common/event';
|
||||
import Event, { mapEvent, Emitter } from 'vs/base/common/event';
|
||||
import types = require('vs/base/common/types');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
@@ -59,6 +59,25 @@ enum State {
|
||||
Busy
|
||||
}
|
||||
|
||||
function pushToEnd<T>(arr: T[], value: T): T[] {
|
||||
let didFindValue = false;
|
||||
|
||||
const result = arr.filter(v => {
|
||||
if (v === value) {
|
||||
didFindValue = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (didFindValue) {
|
||||
result.push(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export class SplitView implements IDisposable {
|
||||
|
||||
private orientation: Orientation;
|
||||
@@ -76,8 +95,7 @@ export class SplitView implements IDisposable {
|
||||
get length(): number {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
|
||||
constructor(private container: HTMLElement, options: ISplitViewOptions = {}) {
|
||||
constructor(container: HTMLElement, options: ISplitViewOptions = {}) {
|
||||
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
|
||||
|
||||
this.el = document.createElement('div');
|
||||
@@ -128,11 +146,11 @@ export class SplitView implements IDisposable {
|
||||
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
|
||||
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
|
||||
|
||||
const onStart = mapEvent(fromEventEmitter<IBaseSashEvent>(sash, 'start'), sashEventMapper);
|
||||
const onStart = mapEvent(sash.onDidStart, sashEventMapper);
|
||||
const onStartDisposable = onStart(this.onSashStart, this);
|
||||
const onChange = mapEvent(fromEventEmitter<IBaseSashEvent>(sash, 'change'), sashEventMapper);
|
||||
const onChange = mapEvent(sash.onDidChange, sashEventMapper);
|
||||
const onSashChangeDisposable = onChange(this.onSashChange, this);
|
||||
const onEnd = mapEvent<IBaseSashEvent, void>(fromEventEmitter<IBaseSashEvent>(sash, 'end'), () => null);
|
||||
const onEnd = mapEvent<void, void>(sash.onDidEnd, () => null);
|
||||
const onEndDisposable = onEnd(() => this._onDidSashChange.fire());
|
||||
|
||||
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, onEndDisposable, sash]);
|
||||
@@ -204,9 +222,9 @@ export class SplitView implements IDisposable {
|
||||
this.state = State.Idle;
|
||||
}
|
||||
|
||||
private relayout(): void {
|
||||
private relayout(lowPriorityIndex?: number): void {
|
||||
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this.resize(this.viewItems.length - 1, this.contentSize - contentSize);
|
||||
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
@@ -250,7 +268,7 @@ export class SplitView implements IDisposable {
|
||||
size = typeof size === 'number' ? size : item.size;
|
||||
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
|
||||
item.size = size;
|
||||
this.relayout();
|
||||
this.relayout(index);
|
||||
}
|
||||
|
||||
resizeView(index: number, size: number): void {
|
||||
@@ -299,21 +317,28 @@ export class SplitView implements IDisposable {
|
||||
return this.viewItems[index].size;
|
||||
}
|
||||
|
||||
private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size)): void {
|
||||
private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size), lowPriorityIndex?: number): void {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta !== 0) {
|
||||
const upIndexes = range(index, -1);
|
||||
const up = upIndexes.map(i => this.viewItems[i]);
|
||||
let upIndexes = range(index, -1);
|
||||
let downIndexes = range(index + 1, this.viewItems.length);
|
||||
|
||||
if (typeof lowPriorityIndex === 'number') {
|
||||
upIndexes = pushToEnd(upIndexes, lowPriorityIndex);
|
||||
downIndexes = pushToEnd(downIndexes, lowPriorityIndex);
|
||||
}
|
||||
|
||||
const upItems = upIndexes.map(i => this.viewItems[i]);
|
||||
const upSizes = upIndexes.map(i => sizes[i]);
|
||||
const downIndexes = range(index + 1, this.viewItems.length);
|
||||
const down = downIndexes.map(i => this.viewItems[i]);
|
||||
|
||||
const downItems = downIndexes.map(i => this.viewItems[i]);
|
||||
const downSizes = downIndexes.map(i => sizes[i]);
|
||||
|
||||
for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < up.length; i++) {
|
||||
const item = up[i];
|
||||
for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < upItems.length; i++) {
|
||||
const item = upItems[i];
|
||||
const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize);
|
||||
const viewDelta = size - upSizes[i];
|
||||
|
||||
@@ -321,8 +346,8 @@ export class SplitView implements IDisposable {
|
||||
item.size = size;
|
||||
}
|
||||
|
||||
for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < down.length; i++) {
|
||||
const item = down[i];
|
||||
for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < downItems.length; i++) {
|
||||
const item = downItems[i];
|
||||
const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize);
|
||||
const viewDelta = size - downSizes[i];
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ export class ToolBar {
|
||||
|
||||
class ToggleMenuAction extends Action {
|
||||
|
||||
public static ID = 'toolbar.toggle.more';
|
||||
public static readonly ID = 'toolbar.toggle.more';
|
||||
|
||||
private _menuActions: IAction[];
|
||||
private toggleDropdownMenu: () => void;
|
||||
@@ -186,7 +186,6 @@ class ToggleMenuAction extends Action {
|
||||
export class DropdownMenuActionItem extends BaseActionItem {
|
||||
private menuActionsOrProvider: any;
|
||||
private dropdownMenu: DropdownMenu;
|
||||
private toUnbind: IDisposable;
|
||||
private contextMenuProvider: IContextMenuProvider;
|
||||
private actionItemProvider: IActionItemProvider;
|
||||
private keybindings: (action: IAction) => ResolvedKeybinding;
|
||||
@@ -240,9 +239,6 @@ export class DropdownMenuActionItem extends BaseActionItem {
|
||||
getKeyBinding: this.keybindings,
|
||||
context: this._context
|
||||
};
|
||||
|
||||
// Reemit events for running actions
|
||||
this.toUnbind = this.addEmitter(this.dropdownMenu);
|
||||
}
|
||||
|
||||
public setActionContext(newContext: any): void {
|
||||
@@ -260,7 +256,6 @@ export class DropdownMenuActionItem extends BaseActionItem {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind.dispose();
|
||||
this.dropdownMenu.dispose();
|
||||
|
||||
super.dispose();
|
||||
|
||||
Reference in New Issue
Block a user