debt - more builder cleanup

This commit is contained in:
Benjamin Pasero 2018-08-27 07:35:28 +02:00
parent c4aa2b5da3
commit dbd3e70395
17 changed files with 276 additions and 301 deletions

View file

@ -5,7 +5,7 @@
'use strict'; 'use strict';
import * as arrays from 'vs/base/common/arrays'; import * as arrays from 'vs/base/common/arrays';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import * as DomUtils from 'vs/base/browser/dom'; import * as DomUtils from 'vs/base/browser/dom';
import { memoize } from 'vs/base/common/decorators'; import { memoize } from 'vs/base/common/decorators';
@ -64,7 +64,7 @@ interface TouchEvent extends Event {
changedTouches: TouchList; changedTouches: TouchList;
} }
export class Gesture implements IDisposable { export class Gesture extends Disposable {
private static readonly SCROLL_FRICTION = -0.005; private static readonly SCROLL_FRICTION = -0.005;
private static INSTANCE: Gesture; private static INSTANCE: Gesture;
@ -72,19 +72,19 @@ export class Gesture implements IDisposable {
private dispatched: boolean; private dispatched: boolean;
private targets: HTMLElement[]; private targets: HTMLElement[];
private toDispose: IDisposable[];
private handle: IDisposable; private handle: IDisposable;
private activeTouches: { [id: number]: TouchData; }; private activeTouches: { [id: number]: TouchData; };
private constructor() { private constructor() {
this.toDispose = []; super();
this.activeTouches = {}; this.activeTouches = {};
this.handle = null; this.handle = null;
this.targets = []; this.targets = [];
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchstart', (e) => this.onTouchStart(e))); this._register(DomUtils.addDisposableListener(document, 'touchstart', (e) => this.onTouchStart(e)));
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchend', (e) => this.onTouchEnd(e))); this._register(DomUtils.addDisposableListener(document, 'touchend', (e) => this.onTouchEnd(e)));
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchmove', (e) => this.onTouchMove(e))); this._register(DomUtils.addDisposableListener(document, 'touchmove', (e) => this.onTouchMove(e)));
} }
public static addTarget(element: HTMLElement): void { public static addTarget(element: HTMLElement): void {
@ -106,9 +106,10 @@ export class Gesture implements IDisposable {
public dispose(): void { public dispose(): void {
if (this.handle) { if (this.handle) {
this.handle.dispose(); this.handle.dispose();
dispose(this.toDispose);
this.handle = null; this.handle = null;
} }
super.dispose();
} }
private onTouchStart(e: TouchEvent): void { private onTouchStart(e: TouchEvent): void {

View file

@ -35,23 +35,23 @@ export interface IBaseActionItemOptions {
isMenu?: boolean; isMenu?: boolean;
} }
export class BaseActionItem implements IActionItem { export class BaseActionItem extends lifecycle.Disposable implements IActionItem {
public builder: HTMLElement; public element: HTMLElement;
public _callOnDispose: lifecycle.IDisposable[];
public _context: any; public _context: any;
public _action: IAction; public _action: IAction;
private _actionRunner: IActionRunner; private _actionRunner: IActionRunner;
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) { constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
this._callOnDispose = []; super();
this._context = context || this; this._context = context || this;
this._action = action; this._action = action;
if (action instanceof Action) { if (action instanceof Action) {
this._callOnDispose.push(action.onDidChange(event => { this._register(action.onDidChange(event => {
if (!this.builder) { if (!this.element) {
// we have not been rendered yet, so there // we have not been rendered yet, so there
// is no point in updating the UI // is no point in updating the UI
return; return;
@ -80,10 +80,6 @@ export class BaseActionItem implements IActionItem {
} }
} }
public get callOnDispose() {
return this._callOnDispose;
}
public set actionRunner(actionRunner: IActionRunner) { public set actionRunner(actionRunner: IActionRunner) {
this._actionRunner = actionRunner; this._actionRunner = actionRunner;
} }
@ -105,7 +101,7 @@ export class BaseActionItem implements IActionItem {
} }
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
this.builder = container; this.element = container;
Gesture.addTarget(container); Gesture.addTarget(container);
const enableDragging = this.options && this.options.draggable; const enableDragging = this.options && this.options.draggable;
@ -113,20 +109,20 @@ export class BaseActionItem implements IActionItem {
container.draggable = true; container.draggable = true;
} }
this._callOnDispose.push(DOM.addDisposableListener(this.builder, EventType.Tap, e => this.onClick(e))); this._register(DOM.addDisposableListener(this.element, EventType.Tap, e => this.onClick(e)));
this._callOnDispose.push(DOM.addDisposableListener(this.builder, DOM.EventType.MOUSE_DOWN, e => { this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_DOWN, e => {
if (!enableDragging) { if (!enableDragging) {
DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it
} }
const mouseEvent = e as MouseEvent; const mouseEvent = e as MouseEvent;
if (this._action.enabled && mouseEvent.button === 0) { if (this._action.enabled && mouseEvent.button === 0) {
DOM.addClass(this.builder, 'active'); DOM.addClass(this.element, 'active');
} }
})); }));
this._callOnDispose.push(DOM.addDisposableListener(this.builder, DOM.EventType.CLICK, e => { this._register(DOM.addDisposableListener(this.element, DOM.EventType.CLICK, e => {
DOM.EventHelper.stop(e, true); DOM.EventHelper.stop(e, true);
// See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
// > Writing to the clipboard // > Writing to the clipboard
@ -144,9 +140,9 @@ export class BaseActionItem implements IActionItem {
})); }));
[DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => {
this._callOnDispose.push(DOM.addDisposableListener(this.builder, event, e => { this._register(DOM.addDisposableListener(this.element, event, e => {
DOM.EventHelper.stop(e); DOM.EventHelper.stop(e);
DOM.removeClass(this.builder, 'active'); DOM.removeClass(this.element, 'active');
})); }));
}); });
} }
@ -169,16 +165,16 @@ export class BaseActionItem implements IActionItem {
} }
public focus(): void { public focus(): void {
if (this.builder) { if (this.element) {
this.builder.focus(); this.element.focus();
DOM.addClass(this.builder, 'focused'); DOM.addClass(this.element, 'focused');
} }
} }
public blur(): void { public blur(): void {
if (this.builder) { if (this.element) {
this.builder.blur(); this.element.blur();
DOM.removeClass(this.builder, 'focused'); DOM.removeClass(this.element, 'focused');
} }
} }
@ -203,12 +199,12 @@ export class BaseActionItem implements IActionItem {
} }
public dispose(): void { public dispose(): void {
if (this.builder) { if (this.element) {
DOM.removeNode(this.builder); DOM.removeNode(this.element);
this.builder = null; this.element = null;
} }
this._callOnDispose = lifecycle.dispose(this._callOnDispose); super.dispose();
} }
} }
@ -233,8 +229,9 @@ export interface IActionItemOptions extends IBaseActionItemOptions {
export class ActionItem extends BaseActionItem { export class ActionItem extends BaseActionItem {
protected $e: HTMLElement; protected label: HTMLElement;
protected options: IActionItemOptions; protected options: IActionItemOptions;
private cssClass: string; private cssClass: string;
constructor(context: any, action: IAction, options: IActionItemOptions = {}) { constructor(context: any, action: IAction, options: IActionItemOptions = {}) {
@ -249,20 +246,20 @@ export class ActionItem extends BaseActionItem {
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
super.render(container); super.render(container);
this.$e = DOM.append(this.builder, DOM.$('a.action-label')); this.label = DOM.append(this.element, DOM.$('a.action-label'));
if (this._action.id === Separator.ID) { if (this._action.id === Separator.ID) {
// A separator is a presentation item // A separator is a presentation item
this.$e.setAttribute('role', 'presentation'); this.label.setAttribute('role', 'presentation');
} else { } else {
if (this.options.isMenu) { if (this.options.isMenu) {
this.$e.setAttribute('role', 'menuitem'); this.label.setAttribute('role', 'menuitem');
} else { } else {
this.$e.setAttribute('role', 'button'); this.label.setAttribute('role', 'button');
} }
} }
if (this.options.label && this.options.keybinding) { if (this.options.label && this.options.keybinding) {
DOM.append(this.builder, DOM.$('span.keybinding')).textContent = this.options.keybinding; DOM.append(this.element, DOM.$('span.keybinding')).textContent = this.options.keybinding;
} }
this._updateClass(); this._updateClass();
@ -274,12 +271,12 @@ export class ActionItem extends BaseActionItem {
public focus(): void { public focus(): void {
super.focus(); super.focus();
this.$e.focus(); this.label.focus();
} }
public _updateLabel(): void { public _updateLabel(): void {
if (this.options.label) { if (this.options.label) {
this.$e.textContent = this.getAction().label; this.label.textContent = this.getAction().label;
} }
} }
@ -298,43 +295,43 @@ export class ActionItem extends BaseActionItem {
} }
if (title) { if (title) {
this.$e.title = title; this.label.title = title;
} }
} }
public _updateClass(): void { public _updateClass(): void {
if (this.cssClass) { if (this.cssClass) {
DOM.removeClasses(this.$e, this.cssClass); DOM.removeClasses(this.label, this.cssClass);
} }
if (this.options.icon) { if (this.options.icon) {
this.cssClass = this.getAction().class; this.cssClass = this.getAction().class;
DOM.addClass(this.$e, 'icon'); DOM.addClass(this.label, 'icon');
if (this.cssClass) { if (this.cssClass) {
DOM.addClasses(this.$e, this.cssClass); DOM.addClasses(this.label, this.cssClass);
} }
this._updateEnabled(); this._updateEnabled();
} else { } else {
DOM.removeClass(this.$e, 'icon'); DOM.removeClass(this.label, 'icon');
} }
} }
public _updateEnabled(): void { public _updateEnabled(): void {
if (this.getAction().enabled) { if (this.getAction().enabled) {
DOM.removeClass(this.builder, 'disabled'); DOM.removeClass(this.element, 'disabled');
DOM.removeClass(this.$e, 'disabled'); DOM.removeClass(this.label, 'disabled');
this.$e.tabIndex = 0; this.label.tabIndex = 0;
} else { } else {
DOM.addClass(this.builder, 'disabled'); DOM.addClass(this.element, 'disabled');
DOM.addClass(this.$e, 'disabled'); DOM.addClass(this.label, 'disabled');
DOM.removeTabIndexAndUpdateFocus(this.$e); DOM.removeTabIndexAndUpdateFocus(this.label);
} }
} }
public _updateChecked(): void { public _updateChecked(): void {
if (this.getAction().checked) { if (this.getAction().checked) {
DOM.addClass(this.$e, 'checked'); DOM.addClass(this.label, 'checked');
} else { } else {
DOM.removeClass(this.$e, 'checked'); DOM.removeClass(this.label, 'checked');
} }
} }
} }
@ -368,7 +365,7 @@ export interface IActionOptions extends IActionItemOptions {
index?: number; index?: number;
} }
export class ActionBar implements IActionRunner { export class ActionBar extends lifecycle.Disposable implements IActionRunner {
public options: IActionBarOptions; public options: IActionBarOptions;
@ -384,26 +381,25 @@ export class ActionBar implements IActionRunner {
public domNode: HTMLElement; public domNode: HTMLElement;
protected actionsList: HTMLElement; protected actionsList: HTMLElement;
private toDispose: lifecycle.IDisposable[];
private _onDidBlur = new Emitter<void>(); private _onDidBlur = new Emitter<void>();
private _onDidCancel = new Emitter<void>(); private _onDidCancel = new Emitter<void>();
private _onDidRun = new Emitter<IRunEvent>(); private _onDidRun = new Emitter<IRunEvent>();
private _onDidBeforeRun = new Emitter<IRunEvent>(); private _onDidBeforeRun = new Emitter<IRunEvent>();
constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) {
super();
this.options = options; this.options = options;
this._context = options.context; this._context = options.context;
this.toDispose = [];
this._actionRunner = this.options.actionRunner; this._actionRunner = this.options.actionRunner;
if (!this._actionRunner) { if (!this._actionRunner) {
this._actionRunner = new ActionRunner(); this._actionRunner = new ActionRunner();
this.toDispose.push(this._actionRunner); this._register(this._actionRunner);
} }
this.toDispose.push(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e)));
this.toDispose.push(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e)));
this.items = []; this.items = [];
this.focusedItem = undefined; this.focusedItem = undefined;
@ -440,7 +436,7 @@ export class ActionBar implements IActionRunner {
break; break;
} }
this.toDispose.push(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => { this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => {
let event = new StandardKeyboardEvent(e as KeyboardEvent); let event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true; let eventHandled = true;
@ -462,7 +458,7 @@ export class ActionBar implements IActionRunner {
} }
})); }));
this.toDispose.push(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => { this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => {
let event = new StandardKeyboardEvent(e as KeyboardEvent); let event = new StandardKeyboardEvent(e as KeyboardEvent);
// Run action on Enter/Space // Run action on Enter/Space
@ -478,15 +474,15 @@ export class ActionBar implements IActionRunner {
} }
})); }));
this.focusTracker = DOM.trackFocus(this.domNode); this.focusTracker = this._register(DOM.trackFocus(this.domNode));
this.toDispose.push(this.focusTracker.onDidBlur(() => { this._register(this.focusTracker.onDidBlur(() => {
if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) { if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) {
this._onDidBlur.fire(); this._onDidBlur.fire();
this.focusedItem = undefined; this.focusedItem = undefined;
} }
})); }));
this.toDispose.push(this.focusTracker.onDidFocus(() => this.updateFocusedItem())); this._register(this.focusTracker.onDidFocus(() => this.updateFocusedItem()));
this.actionsList = document.createElement('ul'); this.actionsList = document.createElement('ul');
this.actionsList.className = 'actions-container'; this.actionsList.className = 'actions-container';
@ -571,7 +567,7 @@ export class ActionBar implements IActionRunner {
actionItemElement.setAttribute('role', 'presentation'); actionItemElement.setAttribute('role', 'presentation');
// Prevent native context menu on actions // Prevent native context menu on actions
this.toDispose.push(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { this._register(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
})); }));
@ -750,28 +746,22 @@ export class ActionBar implements IActionRunner {
} }
this.items = null; this.items = null;
if (this.focusTracker) {
this.focusTracker.dispose();
this.focusTracker = null;
}
this.toDispose = lifecycle.dispose(this.toDispose);
DOM.removeNode(this.getContainer()); DOM.removeNode(this.getContainer());
super.dispose();
} }
} }
export class SelectActionItem extends BaseActionItem { export class SelectActionItem extends BaseActionItem {
protected selectBox: SelectBox; protected selectBox: SelectBox;
protected toDispose: lifecycle.IDisposable[];
constructor(ctx: any, action: IAction, options: string[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions constructor(ctx: any, action: IAction, options: string[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions
) { ) {
super(ctx, action); super(ctx, action);
this.selectBox = new SelectBox(options, selected, contextViewProvider, null, selectBoxOptions); this.selectBox = new SelectBox(options, selected, contextViewProvider, null, selectBoxOptions);
this.toDispose = []; this._register(this.selectBox);
this.toDispose.push(this.selectBox);
this.registerListeners(); this.registerListeners();
} }
@ -784,7 +774,7 @@ export class SelectActionItem extends BaseActionItem {
} }
private registerListeners(): void { private registerListeners(): void {
this.toDispose.push(this.selectBox.onDidSelect(e => { this._register(this.selectBox.onDidSelect(e => {
this.actionRunner.run(this._action, this.getActionContext(e.selected)).done(); this.actionRunner.run(this._action, this.getActionContext(e.selected)).done();
})); }));
} }
@ -808,10 +798,4 @@ export class SelectActionItem extends BaseActionItem {
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
this.selectBox.render(container); this.selectBox.render(container);
} }
public dispose(): void {
this.toDispose = lifecycle.dispose(this.toDispose);
super.dispose();
}
} }

View file

@ -34,7 +34,7 @@ const defaultOptions: IButtonStyles = {
export class Button extends Disposable { export class Button extends Disposable {
private $el: HTMLElement; private _element: HTMLElement;
private options: IButtonOptions; private options: IButtonOptions;
private buttonBackground: Color; private buttonBackground: Color;
@ -58,16 +58,16 @@ export class Button extends Disposable {
this.buttonForeground = this.options.buttonForeground; this.buttonForeground = this.options.buttonForeground;
this.buttonBorder = this.options.buttonBorder; this.buttonBorder = this.options.buttonBorder;
this.$el = document.createElement('a'); this._element = document.createElement('a');
DOM.addClass(this.$el, 'monaco-button'); DOM.addClass(this._element, 'monaco-button');
this.$el.tabIndex = 0; this._element.tabIndex = 0;
this.$el.setAttribute('role', 'button'); this._element.setAttribute('role', 'button');
container.appendChild(this.$el); container.appendChild(this._element);
Gesture.addTarget(this.$el); Gesture.addTarget(this._element);
this._register(DOM.addDisposableListener(this.$el, DOM.EventType.CLICK, e => { this._register(DOM.addDisposableListener(this._element, DOM.EventType.CLICK, e => {
if (!this.enabled) { if (!this.enabled) {
DOM.EventHelper.stop(e); DOM.EventHelper.stop(e);
return; return;
@ -76,14 +76,14 @@ export class Button extends Disposable {
this._onDidClick.fire(e); this._onDidClick.fire(e);
})); }));
this._register(DOM.addDisposableListener(this.$el, DOM.EventType.KEY_DOWN, e => { this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e as KeyboardEvent); const event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = false; let eventHandled = false;
if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
this._onDidClick.fire(e); this._onDidClick.fire(e);
eventHandled = true; eventHandled = true;
} else if (event.equals(KeyCode.Escape)) { } else if (event.equals(KeyCode.Escape)) {
this.$el.blur(); this._element.blur();
eventHandled = true; eventHandled = true;
} }
@ -92,18 +92,18 @@ export class Button extends Disposable {
} }
})); }));
this._register(DOM.addDisposableListener(this.$el, DOM.EventType.MOUSE_OVER, e => { this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OVER, e => {
if (!DOM.hasClass(this.$el, 'disabled')) { if (!DOM.hasClass(this._element, 'disabled')) {
this.setHoverBackground(); this.setHoverBackground();
} }
})); }));
this._register(DOM.addDisposableListener(this.$el, DOM.EventType.MOUSE_OUT, e => { this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OUT, e => {
this.applyStyles(); // restore standard styles this.applyStyles(); // restore standard styles
})); }));
// Also set hover background when button is focused for feedback // Also set hover background when button is focused for feedback
this.focusTracker = this._register(DOM.trackFocus(this.$el)); this.focusTracker = this._register(DOM.trackFocus(this._element));
this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground())); this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground()));
this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles
@ -113,7 +113,7 @@ export class Button extends Disposable {
private setHoverBackground(): void { private setHoverBackground(): void {
const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null; const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null;
if (hoverBackground) { if (hoverBackground) {
this.$el.style.backgroundColor = hoverBackground; this._element.style.backgroundColor = hoverBackground;
} }
} }
@ -127,56 +127,56 @@ export class Button extends Disposable {
} }
private applyStyles(): void { private applyStyles(): void {
if (this.$el) { if (this._element) {
const background = this.buttonBackground ? this.buttonBackground.toString() : null; const background = this.buttonBackground ? this.buttonBackground.toString() : null;
const foreground = this.buttonForeground ? this.buttonForeground.toString() : null; const foreground = this.buttonForeground ? this.buttonForeground.toString() : null;
const border = this.buttonBorder ? this.buttonBorder.toString() : null; const border = this.buttonBorder ? this.buttonBorder.toString() : null;
this.$el.style.color = foreground; this._element.style.color = foreground;
this.$el.style.backgroundColor = background; this._element.style.backgroundColor = background;
this.$el.style.borderWidth = border ? '1px' : null; this._element.style.borderWidth = border ? '1px' : null;
this.$el.style.borderStyle = border ? 'solid' : null; this._element.style.borderStyle = border ? 'solid' : null;
this.$el.style.borderColor = border; this._element.style.borderColor = border;
} }
} }
get element(): HTMLElement { get element(): HTMLElement {
return this.$el; return this._element;
} }
set label(value: string) { set label(value: string) {
if (!DOM.hasClass(this.$el, 'monaco-text-button')) { if (!DOM.hasClass(this._element, 'monaco-text-button')) {
DOM.addClass(this.$el, 'monaco-text-button'); DOM.addClass(this._element, 'monaco-text-button');
} }
this.$el.innerText = value; this._element.textContent = value;
if (this.options.title) { if (this.options.title) {
this.$el.title = value; this._element.title = value;
} }
} }
set icon(iconClassName: string) { set icon(iconClassName: string) {
DOM.addClass(this.$el, iconClassName); DOM.addClass(this._element, iconClassName);
} }
set enabled(value: boolean) { set enabled(value: boolean) {
if (value) { if (value) {
DOM.removeClass(this.$el, 'disabled'); DOM.removeClass(this._element, 'disabled');
this.$el.setAttribute('aria-disabled', String(false)); this._element.setAttribute('aria-disabled', String(false));
this.$el.tabIndex = 0; this._element.tabIndex = 0;
} else { } else {
DOM.addClass(this.$el, 'disabled'); DOM.addClass(this._element, 'disabled');
this.$el.setAttribute('aria-disabled', String(true)); this._element.setAttribute('aria-disabled', String(true));
DOM.removeTabIndexAndUpdateFocus(this.$el); DOM.removeTabIndexAndUpdateFocus(this._element);
} }
} }
get enabled() { get enabled() {
return !DOM.hasClass(this.$el, 'disabled'); return !DOM.hasClass(this._element, 'disabled');
} }
focus(): void { focus(): void {
this.$el.focus(); this._element.focus();
} }
} }

View file

@ -7,7 +7,7 @@
import 'vs/css!./contextview'; import 'vs/css!./contextview';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { IDisposable, dispose, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { IDisposable, dispose, toDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle';
export interface IAnchor { export interface IAnchor {
x: number; x: number;
@ -95,55 +95,54 @@ export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAn
} }
} }
export class ContextView { export class ContextView extends Disposable {
private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur']; private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur'];
private static readonly BUBBLE_DOWN_EVENTS = ['click']; private static readonly BUBBLE_DOWN_EVENTS = ['click'];
private $container: HTMLElement; private container: HTMLElement;
private $view: HTMLElement; private view: HTMLElement;
private delegate: IDelegate; private delegate: IDelegate;
private toDispose: IDisposable[];
private toDisposeOnClean: IDisposable; private toDisposeOnClean: IDisposable;
private toDisposeOnSetContainer: IDisposable; private toDisposeOnSetContainer: IDisposable;
constructor(container: HTMLElement) { constructor(container: HTMLElement) {
this.$view = DOM.$('.context-view'); super();
DOM.hide(this.$view); this.view = DOM.$('.context-view');
DOM.hide(this.view);
this.setContainer(container); this.setContainer(container);
this.toDispose = [toDisposable(() => { this._register(toDisposable(() => this.setContainer(null)));
this.setContainer(null);
})];
} }
public setContainer(container: HTMLElement): void { public setContainer(container: HTMLElement): void {
if (this.$container) { if (this.container) {
this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer); this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer);
this.$container.removeChild(this.$view); this.container.removeChild(this.view);
this.$container = null; this.container = null;
} }
if (container) { if (container) {
this.$container = container; this.container = container;
this.$container.appendChild(this.$view); this.container.appendChild(this.view);
const toDispose: IDisposable[] = []; const toDisposeOnSetContainer: IDisposable[] = [];
ContextView.BUBBLE_UP_EVENTS.forEach(event => { ContextView.BUBBLE_UP_EVENTS.forEach(event => {
toDispose.push(DOM.addStandardDisposableListener(this.$container, event, (e: Event) => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container, event, (e: Event) => {
this.onDOMEvent(e, <HTMLElement>document.activeElement, false); this.onDOMEvent(e, <HTMLElement>document.activeElement, false);
})); }));
}); });
ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { ContextView.BUBBLE_DOWN_EVENTS.forEach(event => {
toDispose.push(DOM.addStandardDisposableListener(this.$container, event, (e: Event) => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container, event, (e: Event) => {
this.onDOMEvent(e, <HTMLElement>document.activeElement, true); this.onDOMEvent(e, <HTMLElement>document.activeElement, true);
}, true)); }, true));
}); });
this.toDisposeOnSetContainer = combinedDisposable(toDispose); this.toDisposeOnSetContainer = combinedDisposable(toDisposeOnSetContainer);
} }
} }
@ -153,14 +152,14 @@ export class ContextView {
} }
// Show static box // Show static box
DOM.clearNode(this.$view); DOM.clearNode(this.view);
this.$view.className = 'context-view'; this.view.className = 'context-view';
this.$view.style.top = '0px'; this.view.style.top = '0px';
this.$view.style.left = '0px'; this.view.style.left = '0px';
DOM.show(this.$view); DOM.show(this.view);
// Render content // Render content
this.toDisposeOnClean = delegate.render(this.$view); this.toDisposeOnClean = delegate.render(this.view);
// Set active delegate // Set active delegate
this.delegate = delegate; this.delegate = delegate;
@ -219,8 +218,8 @@ export class ContextView {
}; };
} }
const viewSizeWidth = DOM.getTotalWidth(this.$view); const viewSizeWidth = DOM.getTotalWidth(this.view);
const viewSizeHeight = DOM.getTotalHeight(this.$view); const viewSizeHeight = DOM.getTotalHeight(this.view);
const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;
@ -235,17 +234,17 @@ export class ContextView {
horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After };
} }
const containerPosition = DOM.getDomNodePagePosition(this.$container); const containerPosition = DOM.getDomNodePagePosition(this.container);
const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) - containerPosition.top; const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) - containerPosition.top;
const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor) - containerPosition.left; const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor) - containerPosition.left;
DOM.removeClasses(this.$view, 'top', 'bottom', 'left', 'right'); DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right');
DOM.addClass(this.$view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
DOM.addClass(this.$view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
this.$view.style.top = `${top}px`; this.view.style.top = `${top}px`;
this.$view.style.left = `${left}px`; this.view.style.left = `${left}px`;
this.$view.style.width = 'initial'; this.view.style.width = 'initial';
} }
public hide(data?: any): void { public hide(data?: any): void {
@ -260,7 +259,7 @@ export class ContextView {
this.toDisposeOnClean = null; this.toDisposeOnClean = null;
} }
DOM.hide(this.$view); DOM.hide(this.view);
} }
private isVisible(): boolean { private isVisible(): boolean {
@ -271,7 +270,7 @@ export class ContextView {
if (this.delegate) { if (this.delegate) {
if (this.delegate.onDOMEvent) { if (this.delegate.onDOMEvent) {
this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement); this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement);
} else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.$container)) { } else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.container)) {
this.hide(); this.hide();
} }
} }
@ -280,6 +279,6 @@ export class ContextView {
public dispose(): void { public dispose(): void {
this.hide(); this.hide();
this.toDispose = dispose(this.toDispose); super.dispose();
} }
} }

View file

@ -29,18 +29,18 @@ export interface IBaseDropdownOptions {
export class BaseDropdown extends ActionRunner { export class BaseDropdown extends ActionRunner {
private _toDispose: IDisposable[] = []; private _toDispose: IDisposable[] = [];
private $el: HTMLElement; private _element: HTMLElement;
private $boxContainer: HTMLElement; private boxContainer: HTMLElement;
private $label: HTMLElement; private _label: HTMLElement;
private $contents: HTMLElement; private contents: HTMLElement;
private visible: boolean; private visible: boolean;
constructor(container: HTMLElement, options: IBaseDropdownOptions) { constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super(); super();
this.$el = append(container, $('.monaco-dropdown')); this._element = append(container, $('.monaco-dropdown'));
this.$label = append(this.$el, $('.dropdown-label')); this._label = append(this._element, $('.dropdown-label'));
let labelRenderer = options.labelRenderer; let labelRenderer = options.labelRenderer;
if (!labelRenderer) { if (!labelRenderer) {
@ -52,11 +52,11 @@ export class BaseDropdown extends ActionRunner {
} }
[EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => {
this._toDispose.push(addDisposableListener(this.$label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger this._toDispose.push(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
}); });
[EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { [EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => {
this._toDispose.push(addDisposableListener(this.$label, event, e => { this._toDispose.push(addDisposableListener(this._label, event, e => {
if (e instanceof MouseEvent && e.detail > 1) { if (e instanceof MouseEvent && e.detail > 1) {
return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363)
} }
@ -69,12 +69,12 @@ export class BaseDropdown extends ActionRunner {
})); }));
}); });
const cleanupFn = labelRenderer(this.$label); const cleanupFn = labelRenderer(this._label);
if (cleanupFn) { if (cleanupFn) {
this._toDispose.push(cleanupFn); this._toDispose.push(cleanupFn);
} }
Gesture.addTarget(this.$label); Gesture.addTarget(this._label);
} }
get toDispose(): IDisposable[] { get toDispose(): IDisposable[] {
@ -82,15 +82,15 @@ export class BaseDropdown extends ActionRunner {
} }
get element(): HTMLElement { get element(): HTMLElement {
return this.$el; return this._element;
} }
get label(): HTMLElement { get label(): HTMLElement {
return this.$label; return this._label;
} }
set tooltip(tooltip: string) { set tooltip(tooltip: string) {
this.$label.title = tooltip; this._label.title = tooltip;
} }
show(): void { show(): void {
@ -111,19 +111,19 @@ export class BaseDropdown extends ActionRunner {
this._toDispose = dispose(this.toDispose); this._toDispose = dispose(this.toDispose);
if (this.$boxContainer) { if (this.boxContainer) {
removeNode(this.$boxContainer); removeNode(this.boxContainer);
this.$boxContainer = null; this.boxContainer = null;
} }
if (this.$contents) { if (this.contents) {
removeNode(this.$contents); removeNode(this.contents);
this.$contents = null; this.contents = null;
} }
if (this.$label) { if (this._label) {
removeNode(this.$label); removeNode(this._label);
this.$label = null; this._label = null;
} }
} }
} }
@ -283,13 +283,13 @@ export class DropdownMenuActionItem extends BaseActionItem {
render(container: HTMLElement): void { render(container: HTMLElement): void {
const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => {
this.builder = append(el, $('a.action-label')); this.element = append(el, $('a.action-label'));
addClasses(this.builder, this.clazz); addClasses(this.element, this.clazz);
this.builder.tabIndex = 0; this.element.tabIndex = 0;
this.builder.setAttribute('role', 'button'); this.element.setAttribute('role', 'button');
this.builder.setAttribute('aria-haspopup', 'true'); this.element.setAttribute('aria-haspopup', 'true');
this.builder.title = this._action.label || ''; this.element.title = this._action.label || '';
return null; return null;
}; };

View file

@ -245,7 +245,7 @@ class MenuActionItem extends BaseActionItem {
this.container = container; this.container = container;
this.$e = $('a.action-menu-item').appendTo(this.builder); this.$e = $('a.action-menu-item').appendTo(this.element);
if (this._action.id === Separator.ID) { if (this._action.id === Separator.ID) {
// A separator is a presentation item // A separator is a presentation item
this.$e.attr({ role: 'presentation' }); this.$e.attr({ role: 'presentation' });
@ -335,11 +335,11 @@ class MenuActionItem extends BaseActionItem {
_updateEnabled(): void { _updateEnabled(): void {
if (this.getAction().enabled) { if (this.getAction().enabled) {
removeClass(this.builder, 'disabled'); removeClass(this.element, 'disabled');
this.$e.removeClass('disabled'); this.$e.removeClass('disabled');
this.$e.attr({ tabindex: 0 }); this.$e.attr({ tabindex: 0 });
} else { } else {
addClass(this.builder, 'disabled'); addClass(this.element, 'disabled');
this.$e.addClass('disabled'); this.$e.addClass('disabled');
removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); removeTabIndexAndUpdateFocus(this.$e.getHTMLElement());
} }
@ -383,7 +383,7 @@ class SubmenuActionItem extends MenuActionItem {
}, 250); }, 250);
this.hideScheduler = new RunOnceScheduler(() => { this.hideScheduler = new RunOnceScheduler(() => {
if ((!isAncestor(document.activeElement, this.builder) && this.parentData.submenu === this.mysubmenu)) { if ((!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) {
this.parentData.parent.focus(false); this.parentData.parent.focus(false);
this.cleanupExistingSubmenu(true); this.cleanupExistingSubmenu(true);
} }
@ -397,7 +397,7 @@ class SubmenuActionItem extends MenuActionItem {
this.$e.attr('aria-haspopup', 'true'); this.$e.attr('aria-haspopup', 'true');
$('span.submenu-indicator').attr('aria-hidden', 'true').text('\u25B6').appendTo(this.$e); $('span.submenu-indicator').attr('aria-hidden', 'true').text('\u25B6').appendTo(this.$e);
$(this.builder).on(EventType.KEY_UP, (e) => { $(this.element).on(EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent); let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) {
EventHelper.stop(e, true); EventHelper.stop(e, true);
@ -406,14 +406,14 @@ class SubmenuActionItem extends MenuActionItem {
} }
}); });
$(this.builder).on(EventType.KEY_DOWN, (e) => { $(this.element).on(EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent); let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) {
EventHelper.stop(e, true); EventHelper.stop(e, true);
} }
}); });
$(this.builder).on(EventType.MOUSE_OVER, (e) => { $(this.element).on(EventType.MOUSE_OVER, (e) => {
if (!this.mouseOver) { if (!this.mouseOver) {
this.mouseOver = true; this.mouseOver = true;
@ -421,12 +421,12 @@ class SubmenuActionItem extends MenuActionItem {
} }
}); });
$(this.builder).on(EventType.MOUSE_LEAVE, (e) => { $(this.element).on(EventType.MOUSE_LEAVE, (e) => {
this.mouseOver = false; this.mouseOver = false;
}); });
$(this.builder).on(EventType.FOCUS_OUT, (e) => { $(this.element).on(EventType.FOCUS_OUT, (e) => {
if (!isAncestor(document.activeElement, this.builder)) { if (!isAncestor(document.activeElement, this.element)) {
this.hideScheduler.schedule(); this.hideScheduler.schedule();
} }
}); });
@ -454,10 +454,10 @@ class SubmenuActionItem extends MenuActionItem {
private createSubmenu(selectFirstItem = true) { private createSubmenu(selectFirstItem = true) {
if (!this.parentData.submenu) { if (!this.parentData.submenu) {
this.submenuContainer = $(this.builder).div({ class: 'monaco-submenu menubar-menu-items-holder context-view' }); this.submenuContainer = $(this.element).div({ class: 'monaco-submenu menubar-menu-items-holder context-view' });
$(this.submenuContainer).style({ $(this.submenuContainer).style({
'left': `${$(this.builder).getClientArea().width}px` 'left': `${$(this.element).getClientArea().width}px`
}); });
$(this.submenuContainer).on(EventType.KEY_UP, (e) => { $(this.submenuContainer).on(EventType.KEY_UP, (e) => {

View file

@ -199,17 +199,17 @@ export class MenuItemActionItem extends ActionItem {
} }
}; };
this._callOnDispose.push(alternativeKeyEmitter.event(value => { this._register(alternativeKeyEmitter.event(value => {
alternativeKeyDown = value; alternativeKeyDown = value;
updateAltState(); updateAltState();
})); }));
this._callOnDispose.push(domEvent(container, 'mouseleave')(_ => { this._register(domEvent(container, 'mouseleave')(_ => {
mouseOver = false; mouseOver = false;
updateAltState(); updateAltState();
})); }));
this._callOnDispose.push(domEvent(container, 'mouseenter')(e => { this._register(domEvent(container, 'mouseenter')(e => {
mouseOver = true; mouseOver = true;
updateAltState(); updateAltState();
})); }));
@ -217,12 +217,12 @@ export class MenuItemActionItem extends ActionItem {
_updateLabel(): void { _updateLabel(): void {
if (this.options.label) { if (this.options.label) {
this.$e.textContent = this._commandAction.label; this.label.textContent = this._commandAction.label;
} }
} }
_updateTooltip(): void { _updateTooltip(): void {
const element = this.$e; const element = this.label;
const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id);
const keybindingLabel = keybinding && keybinding.getLabel(); const keybindingLabel = keybinding && keybinding.getLabel();
@ -259,8 +259,8 @@ export class MenuItemActionItem extends ActionItem {
MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
} }
addClasses(this.$e, 'icon', iconClass); addClasses(this.label, 'icon', iconClass);
this._itemClassDispose = toDisposable(() => removeClasses(this.$e, 'icon', iconClass)); this._itemClassDispose = toDisposable(() => removeClasses(this.label, 'icon', iconClass));
} }
} }

View file

@ -23,8 +23,8 @@ export class ContextMenuHandler {
private notificationService: INotificationService; private notificationService: INotificationService;
private telemetryService: ITelemetryService; private telemetryService: ITelemetryService;
private $el: HTMLElement; private element: HTMLElement;
private $elDisposable: IDisposable; private elementDisposable: IDisposable;
private menuContainerElement: HTMLElement; private menuContainerElement: HTMLElement;
private focusToReturn: HTMLElement; private focusToReturn: HTMLElement;
@ -39,13 +39,13 @@ export class ContextMenuHandler {
} }
public setContainer(container: HTMLElement): void { public setContainer(container: HTMLElement): void {
if (this.$el) { if (this.element) {
this.$elDisposable = dispose(this.$elDisposable); this.elementDisposable = dispose(this.elementDisposable);
this.$el = null; this.element = null;
} }
if (container) { if (container) {
this.$el = container; this.element = container;
this.$elDisposable = addDisposableListener(this.$el, 'mousedown', (e) => this.onMouseDown(e as MouseEvent)); this.elementDisposable = addDisposableListener(this.element, 'mousedown', (e) => this.onMouseDown(e as MouseEvent));
} }
} }

View file

@ -123,23 +123,23 @@ export class GlobalActivityActionItem extends ActivityActionItem {
// Context menus are triggered on mouse down so that an item can be picked // Context menus are triggered on mouse down so that an item can be picked
// and executed with releasing the mouse over it // and executed with releasing the mouse over it
this.callOnDispose.push(DOM.addDisposableListener(this.$container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
DOM.EventHelper.stop(e, true); DOM.EventHelper.stop(e, true);
const event = new StandardMouseEvent(e); const event = new StandardMouseEvent(e);
this.showContextMenu({ x: event.posx, y: event.posy }); this.showContextMenu({ x: event.posx, y: event.posy });
})); }));
this.callOnDispose.push(DOM.addDisposableListener(this.$container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e); let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
DOM.EventHelper.stop(e, true); DOM.EventHelper.stop(e, true);
this.showContextMenu(this.$container); this.showContextMenu(this.container);
} }
})); }));
this.callOnDispose.push(DOM.addDisposableListener(this.$container, TouchEventType.Tap, (e: GestureEvent) => { this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {
DOM.EventHelper.stop(e, true); DOM.EventHelper.stop(e, true);
const event = new StandardMouseEvent(e); const event = new StandardMouseEvent(e);

View file

@ -124,12 +124,12 @@ export interface IActivityActionItemOptions extends IBaseActionItemOptions {
} }
export class ActivityActionItem extends BaseActionItem { export class ActivityActionItem extends BaseActionItem {
protected $container: HTMLElement; protected container: HTMLElement;
protected $label: HTMLElement; protected label: HTMLElement;
protected $badge: HTMLElement; protected badge: HTMLElement;
protected options: IActivityActionItemOptions; protected options: IActivityActionItemOptions;
private $badgeContent: HTMLElement; private badgeContent: HTMLElement;
private badgeDisposable: IDisposable = Disposable.None; private badgeDisposable: IDisposable = Disposable.None;
private mouseUpTimeout: number; private mouseUpTimeout: number;
@ -140,9 +140,9 @@ export class ActivityActionItem extends BaseActionItem {
) { ) {
super(null, action, options); super(null, action, options);
this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose); this._register(this.themeService.onThemeChange(this.onThemeChange, this));
action.onDidChangeActivity(this.updateActivity, this, this._callOnDispose); this._register(action.onDidChangeActivity(this.updateActivity, this));
action.onDidChangeBadge(this.updateBadge, this, this._callOnDispose); this._register(action.onDidChangeBadge(this.updateBadge, this));
} }
protected get activity(): IActivity { protected get activity(): IActivity {
@ -153,59 +153,59 @@ export class ActivityActionItem extends BaseActionItem {
const theme = this.themeService.getTheme(); const theme = this.themeService.getTheme();
// Label // Label
if (this.$label && this.options.icon) { if (this.label && this.options.icon) {
const background = theme.getColor(this.options.colors.backgroundColor); const background = theme.getColor(this.options.colors.backgroundColor);
this.$label.style.backgroundColor = background ? background.toString() : null; this.label.style.backgroundColor = background ? background.toString() : null;
} }
// Badge // Badge
if (this.$badgeContent) { if (this.badgeContent) {
const badgeForeground = theme.getColor(this.options.colors.badgeForeground); const badgeForeground = theme.getColor(this.options.colors.badgeForeground);
const badgeBackground = theme.getColor(this.options.colors.badgeBackground); const badgeBackground = theme.getColor(this.options.colors.badgeBackground);
const contrastBorderColor = theme.getColor(contrastBorder); const contrastBorderColor = theme.getColor(contrastBorder);
this.$badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null; this.badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null;
this.$badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null; this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null;
this.$badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null; this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null;
this.$badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null; this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null;
this.$badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null; this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null;
} }
} }
render(container: HTMLElement): void { render(container: HTMLElement): void {
super.render(container); super.render(container);
this.$container = container; this.container = container;
// Make the container tab-able for keyboard navigation // Make the container tab-able for keyboard navigation
this.$container.tabIndex = 0; this.container.tabIndex = 0;
this.$container.setAttribute('role', 'button'); this.container.setAttribute('role', 'button');
// Try hard to prevent keyboard only focus feedback when using mouse // Try hard to prevent keyboard only focus feedback when using mouse
this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.MOUSE_DOWN, () => { this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_DOWN, () => {
dom.addClass(this.$container, 'clicked'); dom.addClass(this.container, 'clicked');
})); }));
this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.MOUSE_UP, () => { this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_UP, () => {
if (this.mouseUpTimeout) { if (this.mouseUpTimeout) {
clearTimeout(this.mouseUpTimeout); clearTimeout(this.mouseUpTimeout);
} }
this.mouseUpTimeout = setTimeout(() => { this.mouseUpTimeout = setTimeout(() => {
dom.removeClass(this.$container, 'clicked'); dom.removeClass(this.container, 'clicked');
}, 800); // delayed to prevent focus feedback from showing on mouse up }, 800); // delayed to prevent focus feedback from showing on mouse up
})); }));
// Label // Label
this.$label = dom.append(this.builder, dom.$('a.action-label')); this.label = dom.append(this.element, dom.$('a.action-label'));
// Badge // Badge
this.$badge = dom.append(this.builder, dom.$('.badge')); this.badge = dom.append(this.element, dom.$('.badge'));
this.$badgeContent = dom.append(this.$badge, dom.$('.badge-content')); this.badgeContent = dom.append(this.badge, dom.$('.badge-content'));
dom.hide(this.$badge); dom.hide(this.badge);
this.updateActivity(); this.updateActivity();
this.updateStyles(); this.updateStyles();
@ -223,7 +223,7 @@ export class ActivityActionItem extends BaseActionItem {
protected updateBadge(): void { protected updateBadge(): void {
const action = this.getAction(); const action = this.getAction();
if (!this.$badge || !this.$badgeContent || !(action instanceof ActivityAction)) { if (!this.badge || !this.badgeContent || !(action instanceof ActivityAction)) {
return; return;
} }
@ -233,8 +233,8 @@ export class ActivityActionItem extends BaseActionItem {
this.badgeDisposable.dispose(); this.badgeDisposable.dispose();
this.badgeDisposable = Disposable.None; this.badgeDisposable = Disposable.None;
dom.clearNode(this.$badgeContent); dom.clearNode(this.badgeContent);
dom.hide(this.$badge); dom.hide(this.badge);
if (badge) { if (badge) {
@ -247,30 +247,30 @@ export class ActivityActionItem extends BaseActionItem {
} else if (badge.number > 999) { } else if (badge.number > 999) {
number = number.charAt(0) + 'k'; number = number.charAt(0) + 'k';
} }
this.$badgeContent.textContent = number; this.badgeContent.textContent = number;
dom.show(this.$badge); dom.show(this.badge);
} }
} }
// Text // Text
else if (badge instanceof TextBadge) { else if (badge instanceof TextBadge) {
this.$badgeContent.textContent = badge.text; this.badgeContent.textContent = badge.text;
dom.show(this.$badge); dom.show(this.badge);
} }
// Text // Text
else if (badge instanceof IconBadge) { else if (badge instanceof IconBadge) {
dom.show(this.$badge); dom.show(this.badge);
} }
// Progress // Progress
else if (badge instanceof ProgressBadge) { else if (badge instanceof ProgressBadge) {
dom.show(this.$badge); dom.show(this.badge);
} }
if (clazz) { if (clazz) {
dom.addClasses(this.$badge, clazz); dom.addClasses(this.badge, clazz);
this.badgeDisposable = toDisposable(() => dom.removeClasses(this.$badge, clazz)); this.badgeDisposable = toDisposable(() => dom.removeClasses(this.badge, clazz));
} }
} }
@ -290,15 +290,15 @@ export class ActivityActionItem extends BaseActionItem {
private updateLabel(): void { private updateLabel(): void {
if (this.activity.cssClass) { if (this.activity.cssClass) {
dom.addClasses(this.$label, this.activity.cssClass); dom.addClasses(this.label, this.activity.cssClass);
} }
if (!this.options.icon) { if (!this.options.icon) {
this.$label.textContent = this.getAction().label; this.label.textContent = this.getAction().label;
} }
} }
private updateTitle(title: string): void { private updateTitle(title: string): void {
[this.$label, this.$badge, this.$container].forEach(element => { [this.label, this.badge, this.container].forEach(element => {
if (element) { if (element) {
element.setAttribute('aria-label', title); element.setAttribute('aria-label', title);
element.title = title; element.title = title;
@ -313,7 +313,7 @@ export class ActivityActionItem extends BaseActionItem {
clearTimeout(this.mouseUpTimeout); clearTimeout(this.mouseUpTimeout);
} }
dom.removeNode(this.$badge); dom.removeNode(this.badge);
} }
} }
@ -360,7 +360,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
this.actions = this.getActions(); this.actions = this.getActions();
this.contextMenuService.showContextMenu({ this.contextMenuService.showContextMenu({
getAnchor: () => this.builder, getAnchor: () => this.element,
getActions: () => TPromise.as(this.actions), getActions: () => TPromise.as(this.actions),
onHide: () => dispose(this.actions) onHide: () => dispose(this.actions)
}); });
@ -445,7 +445,7 @@ export class CompositeActionItem extends ActivityActionItem {
CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
} }
compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this, this._callOnDispose); this._register(compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this));
} }
protected get activity(): IActivity { protected get activity(): IActivity {
@ -483,14 +483,14 @@ export class CompositeActionItem extends ActivityActionItem {
this._updateChecked(); this._updateChecked();
this._updateEnabled(); this._updateEnabled();
this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.CONTEXT_MENU, e => { this._register(dom.addDisposableListener(this.container, dom.EventType.CONTEXT_MENU, e => {
dom.EventHelper.stop(e, true); dom.EventHelper.stop(e, true);
this.showContextMenu(container); this.showContextMenu(container);
})); }));
// Allow to drag // Allow to drag
this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.DRAG_START, (e: DragEvent) => { this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => {
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = 'move';
// Registe as dragged to local transfer // Registe as dragged to local transfer
@ -502,7 +502,7 @@ export class CompositeActionItem extends ActivityActionItem {
} }
})); }));
this.callOnDispose.push(new DragAndDropObserver(this.$container, { this._register(new DragAndDropObserver(this.container, {
onDragEnter: e => { onDragEnter: e => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) { if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) {
this.updateFromDragging(container, true); this.updateFromDragging(container, true);
@ -539,7 +539,7 @@ export class CompositeActionItem extends ActivityActionItem {
})); }));
// Activate on drag over to reveal targets // Activate on drag over to reveal targets
[this.$badge, this.$label].forEach(b => new DelayedDragHandler(b, () => { [this.badge, this.label].forEach(b => new DelayedDragHandler(b, () => {
if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) { if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) {
this.getAction().run(); this.getAction().run();
} }
@ -578,35 +578,35 @@ export class CompositeActionItem extends ActivityActionItem {
} }
focus(): void { focus(): void {
this.$container.focus(); this.container.focus();
} }
protected _updateClass(): void { protected _updateClass(): void {
if (this.cssClass) { if (this.cssClass) {
dom.removeClasses(this.$label, this.cssClass); dom.removeClasses(this.label, this.cssClass);
} }
this.cssClass = this.getAction().class; this.cssClass = this.getAction().class;
if (this.cssClass) { if (this.cssClass) {
dom.addClasses(this.$label, this.cssClass); dom.addClasses(this.label, this.cssClass);
} }
} }
protected _updateChecked(): void { protected _updateChecked(): void {
if (this.getAction().checked) { if (this.getAction().checked) {
dom.addClass(this.$container, 'checked'); dom.addClass(this.container, 'checked');
this.$container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.$container.title)); this.container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.container.title));
} else { } else {
dom.removeClass(this.$container, 'checked'); dom.removeClass(this.container, 'checked');
this.$container.setAttribute('aria-label', this.$container.title); this.container.setAttribute('aria-label', this.container.title);
} }
} }
protected _updateEnabled(): void { protected _updateEnabled(): void {
if (this.getAction().enabled) { if (this.getAction().enabled) {
dom.removeClass(this.builder, 'disabled'); dom.removeClass(this.element, 'disabled');
} else { } else {
dom.addClass(this.builder, 'disabled'); dom.addClass(this.element, 'disabled');
} }
} }
@ -615,7 +615,7 @@ export class CompositeActionItem extends ActivityActionItem {
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
dom.removeNode(this.$label); dom.removeNode(this.label);
} }
} }

View file

@ -106,9 +106,9 @@ export class NotificationsCenter extends Themable {
private updateTitle(): void { private updateTitle(): void {
if (this.model.notifications.length === 0) { if (this.model.notifications.length === 0) {
this.notificationsCenterTitle.innerText = localize('notificationsEmpty', "No new notifications"); this.notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications");
} else { } else {
this.notificationsCenterTitle.innerText = localize('notifications', "Notifications"); this.notificationsCenterTitle.textContent = localize('notifications', "Notifications");
} }
} }

View file

@ -426,10 +426,10 @@ export class NotificationTemplateRenderer {
private renderSource(notification): void { private renderSource(notification): void {
if (notification.expanded && notification.source) { if (notification.expanded && notification.source) {
this.template.source.innerText = localize('notificationSource', "Source: {0}", notification.source); this.template.source.textContent = localize('notificationSource', "Source: {0}", notification.source);
this.template.source.title = notification.source; this.template.source.title = notification.source;
} else { } else {
this.template.source.innerText = ''; this.template.source.textContent = '';
this.template.source.removeAttribute('title'); this.template.source.removeAttribute('title');
} }
} }

View file

@ -10,10 +10,10 @@ import * as paths from 'vs/base/common/paths';
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IModeService } from 'vs/editor/common/services/modeService'; import { IModeService } from 'vs/editor/common/services/modeService';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
export class ResourceContextKey implements IContextKey<URI>, IDisposable { export class ResourceContextKey extends Disposable implements IContextKey<URI> {
static Scheme = new RawContextKey<string>('resourceScheme', undefined); static Scheme = new RawContextKey<string>('resourceScheme', undefined);
static Filename = new RawContextKey<string>('resourceFilename', undefined); static Filename = new RawContextKey<string>('resourceFilename', undefined);
@ -32,13 +32,14 @@ export class ResourceContextKey implements IContextKey<URI>, IDisposable {
private _hasResource: IContextKey<boolean>; private _hasResource: IContextKey<boolean>;
private _isfileSystemResource: IContextKey<boolean>; private _isfileSystemResource: IContextKey<boolean>;
private _isFileSystemResourceOrUntitled: IContextKey<boolean>; private _isFileSystemResourceOrUntitled: IContextKey<boolean>;
private toDispose: IDisposable[] = [];
constructor( constructor(
@IContextKeyService contextKeyService: IContextKeyService, @IContextKeyService contextKeyService: IContextKeyService,
@IFileService private readonly _fileService: IFileService, @IFileService private readonly _fileService: IFileService,
@IModeService private readonly _modeService: IModeService @IModeService private readonly _modeService: IModeService
) { ) {
super();
this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService); this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService);
this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService); this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService);
this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService); this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService);
@ -47,7 +48,8 @@ export class ResourceContextKey implements IContextKey<URI>, IDisposable {
this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService); this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService);
this._isfileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(contextKeyService); this._isfileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(contextKeyService);
this._isFileSystemResourceOrUntitled = ResourceContextKey.IsFileSystemResourceOrUntitled.bindTo(contextKeyService); this._isFileSystemResourceOrUntitled = ResourceContextKey.IsFileSystemResourceOrUntitled.bindTo(contextKeyService);
this.toDispose.push(_fileService.onDidChangeFileSystemProviderRegistrations(() => {
this._register(_fileService.onDidChangeFileSystemProviderRegistrations(() => {
const resource = this._resourceKey.get(); const resource = this._resourceKey.get();
this._isfileSystemResource.set(resource && _fileService.canHandleResource(resource)); this._isfileSystemResource.set(resource && _fileService.canHandleResource(resource));
this._isFileSystemResourceOrUntitled.set(this._isfileSystemResource.get() || this._schemeKey.get() === Schemas.untitled); this._isFileSystemResourceOrUntitled.set(this._isfileSystemResource.get() || this._schemeKey.get() === Schemas.untitled);
@ -77,10 +79,6 @@ export class ResourceContextKey implements IContextKey<URI>, IDisposable {
get(): URI { get(): URI {
return this._resourceKey.get(); return this._resourceKey.get();
} }
dispose(): void {
this.toDispose = dispose(this.toDispose);
}
} }
/** /**

View file

@ -377,7 +377,7 @@ export class DropDownMenuActionItem extends ActionItem {
public showMenu(): void { public showMenu(): void {
const actions = this.getActions(); const actions = this.getActions();
let elementPosition = DOM.getDomNodePagePosition(this.builder); let elementPosition = DOM.getDomNodePagePosition(this.element);
const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 };
this.contextMenuService.showContextMenu({ this.contextMenuService.showContextMenu({
getAnchor: () => anchor, getAnchor: () => anchor,

View file

@ -93,14 +93,12 @@ export interface IMarkersFilterActionItemOptions {
export class MarkersFilterActionItem extends BaseActionItem { export class MarkersFilterActionItem extends BaseActionItem {
private _toDispose: IDisposable[] = [];
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>()); private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event; readonly onDidChange: Event<void> = this._onDidChange.event;
private delayedFilterUpdate: Delayer<void>; private delayedFilterUpdate: Delayer<void>;
private container: HTMLElement; private container: HTMLElement;
private element: HTMLElement; private filterContainer: HTMLElement;
private filterInputBox: HistoryInputBox; private filterInputBox: HistoryInputBox;
private controlsContainer: HTMLInputElement; private controlsContainer: HTMLInputElement;
private filterBadge: HTMLInputElement; private filterBadge: HTMLInputElement;
@ -123,7 +121,7 @@ export class MarkersFilterActionItem extends BaseActionItem {
render(container: HTMLElement): void { render(container: HTMLElement): void {
this.container = container; this.container = container;
DOM.addClass(this.container, 'markers-panel-action-filter-container'); DOM.addClass(this.container, 'markers-panel-action-filter-container');
DOM.append(this.container, this.element); DOM.append(this.container, this.filterContainer);
this.adjustInputBox(); this.adjustInputBox();
} }
@ -157,9 +155,9 @@ export class MarkersFilterActionItem extends BaseActionItem {
} }
private create(itemOptions: IMarkersFilterActionItemOptions): void { private create(itemOptions: IMarkersFilterActionItemOptions): void {
this.element = DOM.$('.markers-panel-action-filter'); this.filterContainer = DOM.$('.markers-panel-action-filter');
this.createInput(this.element, itemOptions); this.createInput(this.filterContainer, itemOptions);
this.createControls(this.element, itemOptions); this.createControls(this.filterContainer, itemOptions);
} }
private createInput(container: HTMLElement, itemOptions: IMarkersFilterActionItemOptions): void { private createInput(container: HTMLElement, itemOptions: IMarkersFilterActionItemOptions): void {
@ -271,11 +269,6 @@ export class MarkersFilterActionItem extends BaseActionItem {
*/ */
this.telemetryService.publicLog('problems.filter', data); this.telemetryService.publicLog('problems.filter', data);
} }
private _register<T extends IDisposable>(t: T): T {
this._toDispose.push(t);
return t;
}
} }
export class QuickFixAction extends Action { export class QuickFixAction extends Action {
@ -354,7 +347,7 @@ export class QuickFixActionItem extends ActionItem {
public onClick(event: DOM.EventLike): void { public onClick(event: DOM.EventLike): void {
DOM.EventHelper.stop(event, true); DOM.EventHelper.stop(event, true);
const elementPosition = DOM.getDomNodePagePosition(this.builder); const elementPosition = DOM.getDomNodePagePosition(this.element);
this.contextMenuService.showContextMenu({ this.contextMenuService.showContextMenu({
getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }),
getActions: () => TPromise.wrap((<QuickFixAction>this.getAction()).getQuickFixActions()), getActions: () => TPromise.wrap((<QuickFixAction>this.getAction()).getQuickFixActions()),

View file

@ -317,7 +317,7 @@ export class FolderSettingsActionItem extends BaseActionItem {
} }
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
this.builder = container; this.element = container;
this.container = container; this.container = container;
this.labelElement = DOM.$('.action-title'); this.labelElement = DOM.$('.action-title');

View file

@ -109,7 +109,7 @@ class StatusBarActionItem extends ActionItem {
_updateLabel(): void { _updateLabel(): void {
if (this.options.label) { if (this.options.label) {
this.$e.innerHTML = renderOcticons(this.getAction().label); this.label.innerHTML = renderOcticons(this.getAction().label);
} }
} }
} }