diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index e7403fe12fa01ebaeb3672178514245e36736332..df8b0554090aa82337797ef805d65a9948b9cd38 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -11,7 +11,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; @@ -22,9 +22,9 @@ import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/pla import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -export interface IExtensionWithFocus extends IExtension { - onDidFocusChange?: Event; - onFocusChangeEventEmitter?: Emitter; +export interface IExtensionsViewState { + onFocus: Event; + onBlur: Event; } export interface ITemplateData { @@ -52,6 +52,7 @@ const actionOptions = { icon: true, label: true, tabOnlyOnFocus: true }; export class Renderer implements IPagedRenderer { constructor( + private extensionViewState: IExtensionsViewState, @IInstantiationService private instantiationService: IInstantiationService, @INotificationService private notificationService: INotificationService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -141,7 +142,7 @@ export class Renderer implements IPagedRenderer { data.extension = null; } - renderElement(extension: IExtensionWithFocus, index: number, data: ITemplateData): void { + renderElement(extension: IExtension, index: number, data: ITemplateData): void { removeClass(data.element, 'loading'); data.extensionDisposables = dispose(data.extensionDisposables); @@ -195,13 +196,17 @@ export class Renderer implements IPagedRenderer { data.description.textContent = extension.gallery.properties.localizedLanguages.map(name => name[0].toLocaleUpperCase() + name.slice(1)).join(', '); } - if (extension.onDidFocusChange) { - data.extensionDisposables.push(extension.onDidFocusChange(hasFocus => { - data.actionbar.items.forEach(item => { - (item).setFocus(hasFocus); - }); - })); - } + this.extensionViewState.onFocus(e => { + if (areSameExtensions(extension, e)) { + data.actionbar.items.forEach(item => (item).setFocus(true)); + } + }, this, data.extensionDisposables); + + this.extensionViewState.onBlur(e => { + if (areSameExtensions(extension, e)) { + data.actionbar.items.forEach(item => (item).setFocus(false)); + } + }, this, data.extensionDisposables); } private updateRecommendationStatus(extension: IExtension, data: ITemplateData) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index af79332717111c792867a62513877c2b89e16b83..bd98fc553e04e18caf9701007d0ec7b6be3f57b5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { dispose } from 'vs/base/common/lifecycle'; +import { dispose, Disposable } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -15,8 +15,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { append, $, toggleClass } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Delegate, Renderer, IExtensionWithFocus as IExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsList'; -import { IExtension as IExtensionBase, IExtensionsWorkbenchService } from '../common/extensions'; +import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/parts/extensions/electron-browser/extensionsList'; +import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; import { Query } from '../common/extensionQuery'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -37,12 +37,29 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { distinct } from 'vs/base/common/arrays'; import { IExperimentService, IExperiment, ExperimentActionType } from 'vs/workbench/parts/experiments/node/experimentService'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; import { IAction } from 'vs/base/common/actions'; +class ExtensionsViewState extends Disposable implements IExtensionsViewState { + + private readonly _onFocus: Emitter = this._register(new Emitter()); + readonly onFocus: Event = this._onFocus.event; + + private readonly _onBlur: Emitter = this._register(new Emitter()); + readonly onBlur: Event = this._onBlur.event; + + private currentlyFocusedItems: IExtension[] = []; + + onFocusChange(extensions: IExtension[]): void { + this.currentlyFocusedItems.forEach(extension => this._onBlur.fire(extension)); + this.currentlyFocusedItems = extensions; + this.currentlyFocusedItems.forEach(extension => this._onFocus.fire(extension)); + } +} + export class ExtensionsListView extends ViewletPanel { private messageBox: HTMLElement; @@ -87,15 +104,17 @@ export class ExtensionsListView extends ViewletPanel { this.extensionsList = append(container, $('.extensions-list')); this.messageBox = append(container, $('.message')); const delegate = new Delegate(); - const renderer = this.instantiationService.createInstance(Renderer); + const extensionsViewState = new ExtensionsViewState(); + const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState); this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false, setRowLineHeight: false }) as WorkbenchPagedList; this.list.onContextMenu(e => this.onContextMenu(e), this, this.disposables); - this.list.onFocusChange(e => this.onFocusChange(e), this, this.disposables); + this.list.onFocusChange(e => extensionsViewState.onFocusChange(e.elements), this, this.disposables); this.disposables.push(this.list); + this.disposables.push(extensionsViewState); Event.chain(this.list.onOpen) .map(e => e.elements[0]) @@ -154,13 +173,6 @@ export class ExtensionsListView extends ViewletPanel { return Promise.resolve(emptyModel); } - private currentlyFocusedItems: IExtension[] = []; - private onFocusChange(e: IListEvent): void { - this.currentlyFocusedItems.forEach(item => item.onFocusChangeEventEmitter.fire(false)); - this.currentlyFocusedItems = e.elements; - this.currentlyFocusedItems.forEach(item => item.onFocusChangeEventEmitter.fire(true)); - } - private onContextMenu(e: IListContextMenuEvent): void { if (e.element) { this.extensionService.getExtensions() @@ -660,28 +672,15 @@ export class ExtensionsListView extends ViewletPanel { this.notificationService.error(err); } - private getPagedModel(arg: IPager | IExtensionBase[]): IPagedModel { - const convert = (ext: IExtension): IExtension => { - const eventEmitter = new Emitter(); - const extFocus: IExtension = ext; - extFocus.onFocusChangeEventEmitter = eventEmitter; - extFocus.onDidFocusChange = eventEmitter.event; - return extFocus; - }; - + private getPagedModel(arg: IPager | IExtension[]): IPagedModel { if (Array.isArray(arg)) { - return new PagedModel(arg.map(ext => convert(ext))); + return new PagedModel(arg); } - const pager = { total: arg.total, pageSize: arg.pageSize, - firstPage: arg.firstPage.map(ext => convert(ext)), - getPage: (pageIndex: number, cancellationToken: CancellationToken) => { - return arg.getPage(pageIndex, cancellationToken).then(extensions => { - return extensions.map(ext => convert(ext)); - }); - } + firstPage: arg.firstPage, + getPage: (pageIndex: number, cancellationToken: CancellationToken) => arg.getPage(pageIndex, cancellationToken) }; return new PagedModel(pager); }