From 660264a263fcfeb5e15604ecd8eaeb71ca98cd5f Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:21:20 +0100 Subject: [PATCH] Adopt custom hover for extension and runtime view (#206154) adopt custom hover for extension and runtime view --- .../abstractRuntimeExtensionsEditor.ts | 12 ++++++--- .../extensions/browser/extensionEditor.ts | 24 ++++++++++++----- .../extensions/browser/extensionsWidgets.ts | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index 991a3df035c..cb2c01bdf75 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, clearNode } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -364,13 +366,15 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } else { title = nls.localize('extensionActivating', "Extension is activating..."); } - data.activationTime.title = title; + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), data.activationTime, title)); clearNode(data.msgContainer); if (this._getUnresponsiveProfile(element.description.identifier)) { const el = $('span', undefined, ...renderLabelWithIcons(` $(alert) Unresponsive`)); - el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + const extensionHostFreezTitle = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), el, extensionHostFreezTitle)); + data.msgContainer.appendChild(el); } @@ -416,7 +420,9 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } if (accessData?.current) { const element = $('span', undefined, nls.localize('requests count', "{0} Requests: {1} (Session)", feature.label, accessData.current.count)); - element.title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + const title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), element, title)); + data.msgContainer.appendChild(element); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 21986a4b709..097f4a3c634 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, setParentFlowTo } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { CheckboxActionViewItem } from 'vs/base/browser/ui/toggle/toggle'; import { Action, IAction } from 'vs/base/common/actions'; @@ -188,7 +190,8 @@ class VersionWidget extends ExtensionWithDifferentGalleryVersionWidget { private readonly element: HTMLElement; constructor(container: HTMLElement) { super(); - this.element = append(container, $('code.version', { title: localize('extension version', "Extension Version") })); + this.element = append(container, $('code.version')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, localize('extension version', "Extension Version"))); this.render(); } render(): void { @@ -268,25 +271,30 @@ export class ExtensionEditor extends EditorPane { const details = append(header, $('.details')); const title = append(details, $('.title')); - const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name"), role: 'heading', tabIndex: 0 })); + const name = append(title, $('span.name.clickable', { role: 'heading', tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), name, localize('name', "Extension name"))); const versionWidget = new VersionWidget(title); - const preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); + const preview = append(title, $('span.preview')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), preview, localize('preview', "Preview"))); preview.textContent = localize('preview', "Preview"); const builtin = append(title, $('span.builtin')); builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { title: localize('publisher', "Publisher"), tabIndex: 0 })); + const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), publisher, localize('publisher', "Publisher"))); publisher.setAttribute('role', 'button'); const publisherDisplayName = append(publisher, $('.publisher-name')); const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $('.verified-publisher')), false); - const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); + const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), installCount, localize('install count', "Install count"))); const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, false); - const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); + const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), rating, localize('rating', "Rating"))); rating.setAttribute('role', 'link'); // #132645 const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, rating, false); @@ -914,7 +922,9 @@ export class ExtensionEditor extends EditorPane { append(extensionResourcesContainer, $('.additional-details-title', undefined, localize('resources', "Resources"))); const resourcesElement = append(extensionResourcesContainer, $('.resources')); for (const [label, uri] of resources) { - this.transientDisposables.add(onClick(append(resourcesElement, $('a.resource', { title: uri.toString(), tabindex: '0' }, label)), () => this.openerService.open(uri))); + const resource = append(resourcesElement, $('a.resource', { tabindex: '0' }, label)); + this.transientDisposables.add(onClick(resource, () => this.openerService.open(uri))); + this.transientDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), resource, uri.toString())); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index a4a887e596a..4ad3680b7d7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -31,7 +31,7 @@ import { URI } from 'vs/base/common/uri'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import Severity from 'vs/base/common/severity'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { Color } from 'vs/base/common/color'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -41,6 +41,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -124,6 +125,8 @@ export class InstallCountWidget extends ExtensionWidget { export class RatingsWidget extends ExtensionWidget { + private readonly containerHover: ICustomHover; + constructor( private container: HTMLElement, private small: boolean @@ -135,12 +138,13 @@ export class RatingsWidget extends ExtensionWidget { container.classList.add('small'); } + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); + this.render(); } render(): void { this.container.innerText = ''; - this.container.title = ''; if (!this.extension) { return; @@ -159,7 +163,7 @@ export class RatingsWidget extends ExtensionWidget { } const rating = Math.round(this.extension.rating * 2) / 2; - this.container.title = localize('ratedLabel', "Average rating: {0} out of 5", rating); + this.containerHover.update(localize('ratedLabel', "Average rating: {0} out of 5", rating)); if (this.small) { append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); @@ -186,6 +190,7 @@ export class RatingsWidget extends ExtensionWidget { export class VerifiedPublisherWidget extends ExtensionWidget { private disposables = this._register(new DisposableStore()); + private readonly containerHover: ICustomHover; constructor( private container: HTMLElement, @@ -193,6 +198,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { @IOpenerService private readonly openerService: IOpenerService, ) { super(); + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); this.render(); } @@ -209,7 +215,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { if (!this.small) { verifiedPublisher.tabIndex = 0; - verifiedPublisher.title = `Verified Domain: ${this.extension.publisherDomain.link}`; + this.containerHover.update(`Verified Domain: ${this.extension.publisherDomain.link}`); verifiedPublisher.setAttribute('role', 'link'); append(verifiedPublisher, $('span.extension-verified-publisher-domain', undefined, publisherDomainLink.authority.startsWith('www.') ? publisherDomainLink.authority.substring(4) : publisherDomainLink.authority)); @@ -239,7 +245,8 @@ export class SponsorWidget extends ExtensionWidget { return; } - const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0, title: this.extension?.publisherSponsorLink })); + const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0 })); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), sponsor, this.extension?.publisherSponsorLink.toString() ?? '')); sponsor.setAttribute('role', 'link'); // #132645 const sponsorIconElement = renderIcon(sponsorIcon); const label = $('span', undefined, localize('sponsor', "Sponsor")); @@ -367,6 +374,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { class RemoteBadge extends Disposable { readonly element: HTMLElement; + readonly elementHover: ICustomHover; constructor( private readonly tooltip: boolean, @@ -376,6 +384,7 @@ class RemoteBadge extends Disposable { ) { super(); this.element = $('div.extension-badge.extension-remote-badge'); + this.elementHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, '')); this.render(); } @@ -397,7 +406,7 @@ class RemoteBadge extends Disposable { if (this.tooltip) { const updateTitle = () => { if (this.element && this.extensionManagementServerService.remoteExtensionManagementServer) { - this.element.title = localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label); + this.elementHover.update(localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label)); } }; this._register(this.labelService.onDidChangeFormatters(() => updateTitle())); @@ -435,6 +444,8 @@ export class ExtensionPackCountWidget extends ExtensionWidget { export class SyncIgnoredWidget extends ExtensionWidget { + private readonly disposables = this._register(new DisposableStore()); + constructor( private readonly container: HTMLElement, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -448,11 +459,12 @@ export class SyncIgnoredWidget extends ExtensionWidget { } render(): void { + this.disposables.clear(); this.container.innerText = ''; if (this.extension && this.extension.state === ExtensionState.Installed && this.userDataSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { const element = append(this.container, $('span.extension-sync-ignored' + ThemeIcon.asCSSSelector(syncIgnoredIcon))); - element.title = localize('syncingore.label', "This extension is ignored during sync."); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), element, localize('syncingore.label', "This extension is ignored during sync."))); element.classList.add(...ThemeIcon.asClassNameArray(syncIgnoredIcon)); } }