From beda943d920c331417ea61822f310d72fa58cc26 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 28 Feb 2023 13:04:17 +0100 Subject: [PATCH] [vscode] Support ViewBadge and tree/web view badge property Extend BadgeWidget with tooltip support Provide support for badge on tree based views and webview views Contributed on behalf of STMicroelectronics Signed-off-by: Remi SCHNEKENBURGER --- packages/core/src/browser/view-container.ts | 13 ++++- .../plugin-ext/src/common/plugin-api-rpc.ts | 3 ++ .../main/browser/view/plugin-view-registry.ts | 8 +++ .../main/browser/view/plugin-view-widget.ts | 54 +++++++++++++++++-- .../src/main/browser/view/tree-views-main.ts | 9 ++++ .../webview-views/webview-views-main.ts | 9 ++++ .../browser/webview-views/webview-views.ts | 4 ++ .../src/main/browser/webview/webview.ts | 38 ++++++++++++- .../src/main/browser/webviews-main.ts | 10 +++- .../plugin-ext/src/plugin/tree/tree-views.ts | 17 +++++- .../plugin-ext/src/plugin/webview-views.ts | 16 +++++- packages/plugin/src/theia.d.ts | 28 ++++++++++ .../src/browser/vsx-extensions-widget.tsx | 16 ++++++ 13 files changed, 216 insertions(+), 9 deletions(-) diff --git a/packages/core/src/browser/view-container.ts b/packages/core/src/browser/view-container.ts index ba6faff5badea..7939644123e14 100644 --- a/packages/core/src/browser/view-container.ts +++ b/packages/core/src/browser/view-container.ts @@ -59,7 +59,9 @@ export interface DescriptionWidget { export interface BadgeWidget { badge?: number; + badgeTooltip?: string; onDidChangeBadge: CommonEvent; + onDidChangeBadgeTooltip: CommonEvent; } export namespace DescriptionWidget { @@ -70,7 +72,7 @@ export namespace DescriptionWidget { export namespace BadgeWidget { export function is(arg: unknown): arg is BadgeWidget { - return isObject(arg) && 'onDidChangeBadge' in arg; + return isObject(arg) && ('onDidChangeBadge' in arg || 'onDidChangeBadgeTooltip' in arg); } } @@ -927,6 +929,8 @@ export class ViewContainerPart extends BaseWidget { readonly onDidChangeDescription = this.onDidChangeDescriptionEmitter.event; protected readonly onDidChangeBadgeEmitter = new Emitter(); readonly onDidChangeBadge = this.onDidChangeBadgeEmitter.event; + protected readonly onDidChangeBadgeTooltipEmitter = new Emitter(); + readonly onDidChangeBadgeTooltip = this.onDidChangeBadgeTooltipEmitter.event; protected readonly toolbar: TabBarToolbar; @@ -963,6 +967,7 @@ export class ViewContainerPart extends BaseWidget { if (BadgeWidget.is(this.wrapped)) { this.wrapped?.onDidChangeBadge(() => this.onDidChangeBadgeEmitter.fire(), undefined, this.toDispose); + this.wrapped?.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire(), undefined, this.toDispose); } const { header, body, disposable } = this.createContent(); @@ -982,6 +987,7 @@ export class ViewContainerPart extends BaseWidget { this.onTitleChangedEmitter, this.onDidChangeDescriptionEmitter, this.onDidChangeBadgeEmitter, + this.onDidChangeBadgeTooltipEmitter, this.registerContextMenu(), this.onDidFocusEmitter, // focus event does not bubble, capture it @@ -1172,8 +1178,12 @@ export class ViewContainerPart extends BaseWidget { const updateBadge = () => { const visibleToolBarItems = this.toolbarRegistry.visibleItems(this.wrapped).length > 0; const badge = BadgeWidget.is(this.wrapped) && this.wrapped.badge; + const badgeTooltip = BadgeWidget.is(this.wrapped) && this.wrapped.badgeTooltip; if (typeof badge === 'number' && !visibleToolBarItems) { badgeSpan.innerText = badge.toString(); + if (typeof badgeTooltip === 'string') { + badgeSpan.title = badgeTooltip; + } badgeContainer.style.display = badgeContainerDisplay; } else { badgeContainer.style.display = 'none'; @@ -1191,6 +1201,7 @@ export class ViewContainerPart extends BaseWidget { this.onDidMove(updateTitle), this.onDidChangeDescription(updateDescription), this.onDidChangeBadge(updateBadge), + this.onDidChangeBadgeTooltip(updateBadge), this.onCollapsed(updateDescription) ]); header.appendChild(title); diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index a53f2c32451a8..ad369c5363b56 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -743,6 +743,7 @@ export interface TreeViewsMain { $setMessage(treeViewId: string, message: string): void; $setTitle(treeViewId: string, title: string): void; $setDescription(treeViewId: string, description: string): void; + $setBadge(treeViewId: string, badge: theia.ViewBadge | undefined): void; } export class DataTransferFileDTO { constructor(readonly name: string, readonly contentId: string, readonly uri?: UriComponents) { } @@ -1709,6 +1710,7 @@ export interface WebviewsMain { $reveal(handle: string, showOptions: theia.WebviewPanelShowOptions): void; $setTitle(handle: string, value: string): void; $setIconPath(handle: string, value: IconUrl | undefined): void; + $setBadge(handle: string, badge: theia.ViewBadge | undefined): void; $setHtml(handle: string, value: string): void; $setOptions(handle: string, options: theia.WebviewOptions): void; $postMessage(handle: string, value: any): Thenable; @@ -1734,6 +1736,7 @@ export interface WebviewViewsMain extends Disposable { $setWebviewViewTitle(handle: string, value: string | undefined): void; $setWebviewViewDescription(handle: string, value: string | undefined): void; + $setBadge(handle: string, badge: theia.ViewBadge | undefined): void; $show(handle: string, preserveFocus: boolean): void; } diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index ec9e3f44a2fa6..4d7a04b66fb87 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -397,6 +397,14 @@ export class PluginViewRegistry implements FrontendApplicationContribution { get description(): string | undefined { return _description; }, set description(value: string | undefined) { _description = value; }, + get badge(): number | undefined { return webview.badge; }, + set badge(badge: number | undefined) { webview.badge = badge; }, + + get badgeTooltip(): string | undefined { return webview.badgeTooltip; }, + set badgeTooltip(badgeTooltip: string | undefined) { webview.badgeTooltip = badgeTooltip; }, + onDidChangeBadge: webview.onDidChangeBadge, + onDidChangeBadgeTooltip: webview.onDidChangeBadgeTooltip, + dispose: webview.dispose, show: webview.show }; diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts index a5c668d158e2e..069575dd00098 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts @@ -21,7 +21,7 @@ import { CommandRegistry } from '@theia/core/lib/common/command'; import { StatefulWidget } from '@theia/core/lib/browser/shell/shell-layout-restorer'; import { Message } from '@theia/core/shared/@phosphor/messaging'; import { TreeViewWidget } from './tree-view-widget'; -import { DescriptionWidget } from '@theia/core/lib/browser/view-container'; +import { BadgeWidget, DescriptionWidget } from '@theia/core/lib/browser/view-container'; import { DisposableCollection, Emitter, Event } from '@theia/core/lib/common'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; @@ -32,16 +32,20 @@ export class PluginViewWidgetIdentifier { } @injectable() -export class PluginViewWidget extends Panel implements StatefulWidget, DescriptionWidget { +export class PluginViewWidget extends Panel implements StatefulWidget, DescriptionWidget, BadgeWidget { currentViewContainerId?: string; protected _message?: string; protected _description: string = ''; + protected _badge?: number | undefined; + protected _badgeTooltip?: string | undefined; protected _suppressUpdateViewVisibility = false; protected updatingViewVisibility = false; protected onDidChangeDescriptionEmitter = new Emitter(); - protected toDispose = new DisposableCollection(this.onDidChangeDescriptionEmitter); + protected onDidChangeBadgeEmitter = new Emitter(); + protected onDidChangeBadgeTooltipEmitter = new Emitter(); + protected toDispose = new DisposableCollection(this.onDidChangeDescriptionEmitter, this.onDidChangeBadgeEmitter, this.onDidChangeBadgeTooltipEmitter); @inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry; @@ -73,6 +77,14 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti return this.onDidChangeDescriptionEmitter.event; } + get onDidChangeBadge(): Event { + return this.onDidChangeBadgeEmitter.event; + } + + get onDidChangeBadgeTooltip(): Event { + return this.onDidChangeBadgeTooltipEmitter.event; + } + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); const widget = this.widgets[0]; @@ -138,6 +150,38 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti this.onDidChangeDescriptionEmitter.fire(); } + get badge(): number | undefined { + if (this._badge === undefined) { + for (const w of this.widgets) { + if (BadgeWidget.is(w)) { + return w.badge; + } + }; + } + return this._badge; + } + + set badge(badge: number | undefined) { + this._badge = badge; + this.onDidChangeBadgeEmitter.fire(); + } + + get badgeTooltip(): string | undefined { + if (this._badgeTooltip === undefined) { + for (const w of this.widgets) { + if (BadgeWidget.is(w)) { + return w.badgeTooltip; + } + }; + } + return this._badgeTooltip; + } + + set badgeTooltip(badgeTooltip: string | undefined) { + this._badgeTooltip = badgeTooltip; + this.onDidChangeBadgeTooltipEmitter.fire(); + } + private updateWidgetMessage(): void { const widget = this.widgets[0]; if (widget) { @@ -149,6 +193,10 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti override addWidget(widget: Widget): void { super.addWidget(widget); + if (BadgeWidget.is(widget)) { + widget?.onDidChangeBadge(() => this.onDidChangeBadgeEmitter.fire()); + widget?.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire()); + } this.updateWidgetMessage(); } diff --git a/packages/plugin-ext/src/main/browser/view/tree-views-main.ts b/packages/plugin-ext/src/main/browser/view/tree-views-main.ts index 7342d553b0592..eddd6d559af23 100644 --- a/packages/plugin-ext/src/main/browser/view/tree-views-main.ts +++ b/packages/plugin-ext/src/main/browser/view/tree-views-main.ts @@ -30,6 +30,7 @@ import { TreeViewWidget, TreeViewNode, PluginTreeModel, TreeViewWidgetOptions } import { PluginViewWidget } from './plugin-view-widget'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { DnDFileContentStore } from './dnd-file-content-store'; +import { ViewBadge } from '@theia/plugin'; export class TreeViewsMainImpl implements TreeViewsMain, Disposable { @@ -173,6 +174,14 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable { } } + async $setBadge(treeViewId: string, badge: ViewBadge | undefined): Promise { + const viewPanel = await this.viewRegistry.getView(treeViewId); + if (viewPanel) { + viewPanel.badge = badge?.value; + viewPanel.badgeTooltip = badge?.tooltip; + } + } + protected handleTreeEvents(treeViewId: string, treeViewWidget: TreeViewWidget): void { this.toDispose.push(treeViewWidget.model.onExpansionChanged(event => { this.proxy.$setExpanded(treeViewId, event.id, event.expanded); diff --git a/packages/plugin-ext/src/main/browser/webview-views/webview-views-main.ts b/packages/plugin-ext/src/main/browser/webview-views/webview-views-main.ts index 5e164cce7a7e6..0a155d4d4e4e8 100644 --- a/packages/plugin-ext/src/main/browser/webview-views/webview-views-main.ts +++ b/packages/plugin-ext/src/main/browser/webview-views/webview-views-main.ts @@ -28,6 +28,7 @@ import { CancellationToken } from '@theia/core/lib/common/cancellation'; import { WebviewsMainImpl } from '../webviews-main'; import { Widget, WidgetManager } from '@theia/core/lib/browser'; import { PluginViewRegistry } from '../view/plugin-view-registry'; +import { ViewBadge } from '@theia/plugin'; export class WebviewViewsMainImpl implements WebviewViewsMain, Disposable { @@ -126,6 +127,14 @@ export class WebviewViewsMainImpl implements WebviewViewsMain, Disposable { webviewView.description = value; } + async $setBadge(handle: string, badge: ViewBadge | undefined): Promise { + const webviewView = this.getWebviewView(handle); + if (webviewView) { + webviewView.badge = badge?.value; + webviewView.badgeTooltip = badge?.tooltip; + } + } + $show(handle: string, preserveFocus: boolean): void { const webviewView = this.getWebviewView(handle); webviewView.show(preserveFocus); diff --git a/packages/plugin-ext/src/main/browser/webview-views/webview-views.ts b/packages/plugin-ext/src/main/browser/webview-views/webview-views.ts index b15182d248b65..fa6a3d7a35abd 100644 --- a/packages/plugin-ext/src/main/browser/webview-views/webview-views.ts +++ b/packages/plugin-ext/src/main/browser/webview-views/webview-views.ts @@ -25,9 +25,13 @@ import { WebviewWidget } from '../webview/webview'; export interface WebviewView { title?: string; description?: string; + badge?: number | undefined; + badgeTooltip?: string | undefined; readonly webview: WebviewWidget; readonly onDidChangeVisibility: Event; readonly onDidDispose: Event; + readonly onDidChangeBadge: Event; + readonly onDidChangeBadgeTooltip: Event; dispose(): void; show(preserveFocus: boolean): void; diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts index 1cd19c66177a5..3e87673a1d223 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview.ts @@ -33,7 +33,7 @@ import { IconUrl } from '../../../common/plugin-protocol'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { WebviewEnvironment } from './webview-environment'; import URI from '@theia/core/lib/common/uri'; -import { Emitter } from '@theia/core/lib/common/event'; +import { Emitter, Event } from '@theia/core/lib/common/event'; import { open, OpenerService } from '@theia/core/lib/browser/opener-service'; import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding'; import { Schemes } from '../../../common/uri-components'; @@ -50,6 +50,7 @@ import { FileOperationError, FileOperationResult } from '@theia/filesystem/lib/c import { BinaryBufferReadableStream } from '@theia/core/lib/common/buffer'; import { ViewColumn } from '../../../plugin/types-impl'; import { ExtractableWidget } from '@theia/core/lib/browser/widgets/extractable-widget'; +import { BadgeWidget } from '@theia/core/lib/browser/view-container'; // Style from core const TRANSPARENT_OVERLAY_STYLE = 'theia-transparent-overlay'; @@ -87,7 +88,7 @@ export class WebviewWidgetIdentifier { export const WebviewWidgetExternalEndpoint = Symbol('WebviewWidgetExternalEndpoint'); @injectable() -export class WebviewWidget extends BaseWidget implements StatefulWidget, ExtractableWidget { +export class WebviewWidget extends BaseWidget implements StatefulWidget, ExtractableWidget, BadgeWidget { private static readonly standardSupportedLinkSchemes = new Set([ Schemes.http, @@ -178,6 +179,11 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract isExtractable: boolean = true; secondaryWindow: Window | undefined = undefined; + protected _badge?: number | undefined; + protected _badgeTooltip?: string | undefined; + protected onDidChangeBadgeEmitter = new Emitter(); + protected onDidChangeBadgeTooltipEmitter = new Emitter(); + @postConstruct() protected init(): void { this.node.tabIndex = 0; @@ -186,6 +192,8 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract this.addClass(WebviewWidget.Styles.WEBVIEW); this.toDispose.push(this.onMessageEmitter); + this.toDispose.push(this.onDidChangeBadgeEmitter); + this.toDispose.push(this.onDidChangeBadgeTooltipEmitter); this.transparentOverlay = document.createElement('div'); this.transparentOverlay.classList.add(TRANSPARENT_OVERLAY_STYLE); @@ -204,6 +212,32 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract })); } + get onDidChangeBadge(): Event { + return this.onDidChangeBadgeEmitter.event; + } + + get onDidChangeBadgeTooltip(): Event { + return this.onDidChangeBadgeTooltipEmitter.event; + } + + get badge(): number | undefined { + return this._badge; + } + + set badge(badge: number | undefined) { + this._badge = badge; + this.onDidChangeBadgeEmitter.fire(); + } + + get badgeTooltip(): string | undefined { + return this._badgeTooltip; + } + + set badgeTooltip(badgeTooltip: string | undefined) { + this._badgeTooltip = badgeTooltip; + this.onDidChangeBadgeTooltipEmitter.fire(); + } + protected override onBeforeAttach(msg: Message): void { super.onBeforeAttach(msg); this.doShow(); diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index 904d5efc4fcf7..f40cddc79f9bc 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -19,7 +19,7 @@ import { URI } from '@theia/core/shared/vscode-uri'; import { interfaces } from '@theia/core/shared/inversify'; import { WebviewsMain, MAIN_RPC_CONTEXT, WebviewsExt, WebviewPanelViewState } from '../../common/plugin-api-rpc'; import { RPCProtocol } from '../../common/rpc-protocol'; -import { WebviewOptions, WebviewPanelOptions, WebviewPanelShowOptions } from '@theia/plugin'; +import { ViewBadge, WebviewOptions, WebviewPanelOptions, WebviewPanelShowOptions } from '@theia/plugin'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { WebviewWidget, WebviewWidgetIdentifier } from './webview/webview'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; @@ -160,6 +160,14 @@ export class WebviewsMainImpl implements WebviewsMain, Disposable { webview.title.label = value; } + async $setBadge(handle: string, badge: ViewBadge | undefined): Promise { + const webview = await this.getWebview(handle); + if (webview) { + webview.badge = badge?.value; + webview.badgeTooltip = badge?.tooltip; + } + } + async $setIconPath(handle: string, iconUrl: IconUrl | undefined): Promise { const webview = await this.getWebview(handle); webview.setIconUrl(iconUrl); diff --git a/packages/plugin-ext/src/plugin/tree/tree-views.ts b/packages/plugin-ext/src/plugin/tree/tree-views.ts index c5a84674b9d21..9d4e44c67787a 100644 --- a/packages/plugin-ext/src/plugin/tree/tree-views.ts +++ b/packages/plugin-ext/src/plugin/tree/tree-views.ts @@ -18,7 +18,7 @@ import { TreeDataProvider, TreeView, TreeViewExpansionEvent, TreeItem, TreeItemLabel, - TreeViewSelectionChangeEvent, TreeViewVisibilityChangeEvent, CancellationToken, DataTransferFile, TreeViewOptions + TreeViewSelectionChangeEvent, TreeViewVisibilityChangeEvent, CancellationToken, DataTransferFile, TreeViewOptions, ViewBadge } from '@theia/plugin'; // TODO: extract `@theia/util` for event, disposable, cancellation and common types // don't use @theia/core directly from plugin host @@ -120,6 +120,12 @@ export class TreeViewsExtImpl implements TreeViewsExt { set description(description: string) { treeView.description = description; }, + get badge(): ViewBadge | undefined { + return treeView.badge; + }, + set badge(badge: ViewBadge | undefined) { + treeView.badge = badge; + }, reveal: (element: T, revealOptions?: Partial): Thenable => treeView.reveal(element, revealOptions), @@ -277,6 +283,15 @@ class TreeViewExtImpl implements Disposable { this.proxy.$setDescription(this.treeViewId, this._description); } + private _badge?: ViewBadge = undefined; + get badge(): ViewBadge | undefined { + return this._badge; + } + set badge(badge: ViewBadge | undefined) { + this._badge = badge; + this.proxy.$setBadge(this.treeViewId, this._badge); + } + getElement(treeItemId: string): T | undefined { return this.nodes.get(treeItemId)?.value; } diff --git a/packages/plugin-ext/src/plugin/webview-views.ts b/packages/plugin-ext/src/plugin/webview-views.ts index 994b56a6df399..1b8a8d6ceef87 100644 --- a/packages/plugin-ext/src/plugin/webview-views.ts +++ b/packages/plugin-ext/src/plugin/webview-views.ts @@ -133,6 +133,7 @@ export class WebviewViewExtImpl implements theia.WebviewView { _isVisible: boolean; _title: string | undefined; _description: string | undefined; + _badge: theia.ViewBadge | undefined; constructor( handle: string, @@ -140,7 +141,7 @@ export class WebviewViewExtImpl implements theia.WebviewView { viewType: string, title: string | undefined, webview: WebviewImpl, - isVisible: boolean, + isVisible: boolean ) { this._viewType = viewType; this._title = title; @@ -186,6 +187,19 @@ export class WebviewViewExtImpl implements theia.WebviewView { } } + get badge(): theia.ViewBadge | undefined { + this.assertNotDisposed(); + return this._badge; + } + + set badge(badge: theia.ViewBadge | undefined) { + this.assertNotDisposed(); + if (this._badge !== badge) { + this._badge = badge; + this.proxy.$setBadge(this.handle, badge); + } + } + get visible(): boolean { return this._isVisible; } get webview(): WebviewImpl { return this._webview; } get viewType(): string { return this._viewType; } diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index a3cb8756ec8c6..8200e21202993 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -4628,6 +4628,12 @@ export module '@theia/plugin' { */ readonly visible: boolean; + /** + * The badge to display for this webview view. + * To remove the badge, set to undefined. + */ + badge?: ViewBadge | undefined; + /** * Event fired when the visibility of the view changes. * @@ -6007,6 +6013,22 @@ export module '@theia/plugin' { handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; } + /** + * A badge presenting a value for a view + */ + export interface ViewBadge { + + /** + * A label to present in tooltip for the badge. + */ + readonly tooltip: string; + + /** + * The value to present in the badge. + */ + readonly value: number; + } + /** * Represents a Tree view */ @@ -6060,6 +6082,12 @@ export module '@theia/plugin' { */ description?: string; + /** + * The badge to display for this TreeView. + * To remove the badge, set to undefined. + */ + badge: ViewBadge | undefined; + /** * Reveal an element. By default revealed element is selected. * diff --git a/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx b/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx index 15e9c9ffc9d63..ea80579d095e9 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx +++ b/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx @@ -52,6 +52,9 @@ export class VSXExtensionsWidget extends SourceTreeWidget implements BadgeWidget protected _badge?: number; protected onDidChangeBadgeEmitter = new Emitter(); + protected _badgeTooltip?: string; + protected onDidChangeBadgeTooltipEmitter = new Emitter(); + @inject(VSXExtensionsWidgetOptions) protected readonly options: VSXExtensionsWidgetOptions; @@ -90,6 +93,19 @@ export class VSXExtensionsWidget extends SourceTreeWidget implements BadgeWidget this.onDidChangeBadgeEmitter.fire(); } + get onDidChangeBadgeTooltip(): Event { + return this.onDidChangeBadgeTooltipEmitter.event; + } + + get badgeTooltip(): string | undefined { + return this._badgeTooltip; + } + + set badgeTooltip(tooltip: string | undefined) { + this._badgeTooltip = tooltip; + this.onDidChangeBadgeTooltipEmitter.fire(); + } + protected computeTitle(): string { switch (this.options.id) { case VSXExtensionsSourceOptions.INSTALLED: