From 039811643e59d31447c303fb69c05fdf4774698a Mon Sep 17 00:00:00 2001 From: Lucas Koehler Date: Thu, 9 Jun 2022 15:46:51 +0200 Subject: [PATCH] Support ThemeIcon color property (#11243) * Add optional color property to ThemeIcon * Hand over the ThemeIcon instead of just its id from ext to main * Apply the color in the `PluginTree` * Adapt `TreeViewNode.is` to also recognize sub types such as `CompositeTreeViewNode` Fixes #11128 Contributed on behalf of STMicroelectronics Signed-off-by: Lucas Koehler --- CHANGELOG.md | 1 + .../plugin-ext/src/common/plugin-api-rpc.ts | 7 +++++- .../plugin-tree-view-node-label-provider.ts | 12 +++++---- .../main/browser/view/tree-view-widget.tsx | 25 +++++++++++++------ .../plugin-ext/src/plugin/tree/tree-views.ts | 6 ++--- packages/plugin-ext/src/plugin/types-impl.ts | 2 +- packages/plugin/src/theia.d.ts | 17 ++++++++++++- 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8096b896b3c06..0b5f0caa03e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## v1.27.0 - Unreleased - [plugin] moved `WebviewViewResolveContext` from `window` to `root` namespace [#11216](https://github.com/eclipse-theia/theia/pull/11216) - Contributed on behalf of STMicroelectronics +- [plugin] Add support for property `color` of `ThemeIcon`. [#11243](https://github.com/eclipse-theia/theia/pull/11243) - Contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_1.27.0) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 40670441412c3..be6f6d66e4c01 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -709,7 +709,7 @@ export interface TreeViewItem { icon?: string; iconUrl?: IconUrl; - themeIconId?: string; + themeIcon?: ThemeIcon; resourceUri?: UriComponents; @@ -1077,6 +1077,11 @@ export interface ThemeColor { id: string; } +export interface ThemeIcon { + id: string; + color?: ThemeColor; +} + /** * Describes the behavior of decorations when typing/editing near their edges. */ diff --git a/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts b/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts index 0ecd1b9310f77..1090f48501596 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts @@ -33,7 +33,7 @@ export class PluginTreeViewNodeLabelProvider implements LabelProviderContributio // eslint-disable-next-line @typescript-eslint/no-explicit-any canHandle(element: TreeViewNode | any): number { - if (TreeNode.is(element) && ('resourceUri' in element || 'themeIconId' in element)) { + if (TreeNode.is(element) && ('resourceUri' in element || 'themeIcon' in element)) { return this.treeLabelProvider.canHandle(element) + 1; } return 0; @@ -43,12 +43,14 @@ export class PluginTreeViewNodeLabelProvider implements LabelProviderContributio if (node.icon) { return node.icon; } - if (node.themeIconId) { - if (node.themeIconId === 'file' || node.themeIconId === 'folder') { + if (node.themeIcon) { + if (node.themeIcon.id === 'file' || node.themeIcon.id === 'folder') { const uri = node.resourceUri && new URI(node.resourceUri) || undefined; - return this.labelProvider.getIcon(URIIconReference.create(node.themeIconId, uri)); + if (uri) { + return this.labelProvider.getIcon(URIIconReference.create(node.themeIcon.id, uri)); + } } - return ThemeIcon.asClassName({ id: node.themeIconId }); + return ThemeIcon.asClassName(node.themeIcon); } if (node.resourceUri) { return this.labelProvider.getIcon(new URI(node.resourceUri)); diff --git a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx index fe3b749a20bd7..d1f7fa522edce 100644 --- a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx +++ b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx @@ -16,7 +16,7 @@ import { URI } from '@theia/core/shared/vscode-uri'; import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; -import { TreeViewsExt, TreeViewItemCollapsibleState, TreeViewItem, TreeViewSelection } from '../../../common/plugin-api-rpc'; +import { TreeViewsExt, TreeViewItemCollapsibleState, TreeViewItem, TreeViewSelection, ThemeIcon } from '../../../common/plugin-api-rpc'; import { Command } from '../../../common/plugin-api-rpc-model'; import { TreeNode, @@ -46,6 +46,7 @@ import * as markdownit from '@theia/core/shared/markdown-it'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import { LabelParser } from '@theia/core/lib/browser/label-parser'; import { AccessibilityInformation } from '@theia/plugin'; +import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; export const TREE_NODE_HYPERLINK = 'theia-TreeNodeHyperlink'; export const VIEW_ITEM_CONTEXT_MENU: MenuPath = ['view-item-context-menu']; @@ -60,7 +61,7 @@ export interface TreeViewNode extends SelectableTreeNode { contextValue?: string; command?: Command; resourceUri?: string; - themeIconId?: string | 'folder' | 'file'; + themeIcon?: ThemeIcon; tooltip?: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any description?: string | boolean | any; @@ -68,7 +69,7 @@ export interface TreeViewNode extends SelectableTreeNode { } export namespace TreeViewNode { export function is(arg: TreeNode | undefined): arg is TreeViewNode { - return !!arg && SelectableTreeNode.is(arg) && !ExpandableTreeNode.is(arg) && !CompositeTreeNode.is(arg); + return !!arg && SelectableTreeNode.is(arg); } } @@ -151,12 +152,12 @@ export class PluginTree extends TreeImpl { protected createTreeNode(item: TreeViewItem, parent: CompositeTreeNode): TreeNode { const icon = this.toIconClass(item); const resourceUri = item.resourceUri && URI.revive(item.resourceUri).toString(); - const themeIconId = item.themeIconId ? item.themeIconId : item.collapsibleState !== TreeViewItemCollapsibleState.None ? 'folder' : 'file'; + const themeIcon = item.themeIcon ? item.themeIcon : item.collapsibleState !== TreeViewItemCollapsibleState.None ? { id: 'folder' } : { id: 'file' }; const update: Partial = { name: item.label, icon, description: item.description, - themeIconId, + themeIcon, resourceUri, tooltip: item.tooltip, contextValue: item.contextValue, @@ -178,7 +179,7 @@ export class PluginTree extends TreeImpl { command: item.command }, update); } - if (TreeViewNode.is(node)) { + if (TreeViewNode.is(node) && !ExpandableTreeNode.is(node)) { return Object.assign(node, update, { command: item.command }); } return Object.assign({ @@ -257,6 +258,9 @@ export class TreeViewWidget extends TreeViewWelcomeWidget { @inject(LabelParser) protected readonly labelParser: LabelParser; + @inject(ColorRegistry) + protected readonly colorRegistry: ColorRegistry; + protected readonly markdownIt = markdownit(); @postConstruct() @@ -286,7 +290,14 @@ export class TreeViewWidget extends TreeViewWelcomeWidget { protected override renderIcon(node: TreeNode, props: NodeProps): React.ReactNode { const icon = this.toNodeIcon(node); if (icon) { - return
; + let style: React.CSSProperties | undefined; + if (TreeViewNode.is(node) && node.themeIcon?.color) { + const color = this.colorRegistry.getCurrentColor(node.themeIcon.color.id); + if (color) { + style = { color }; + } + } + return
; } return undefined; } diff --git a/packages/plugin-ext/src/plugin/tree/tree-views.ts b/packages/plugin-ext/src/plugin/tree/tree-views.ts index 700b3dc3cd4ab..325492fc8019f 100644 --- a/packages/plugin-ext/src/plugin/tree/tree-views.ts +++ b/packages/plugin-ext/src/plugin/tree/tree-views.ts @@ -366,12 +366,12 @@ class TreeViewExtImpl implements Disposable { let icon; let iconUrl; - let themeIconId; + let themeIcon; const { iconPath } = treeItem; if (typeof iconPath === 'string' && iconPath.indexOf('fa-') !== -1) { icon = iconPath; } else if (ThemeIcon.is(iconPath)) { - themeIconId = iconPath.id; + themeIcon = iconPath; } else { iconUrl = PluginIconPath.toUrl(iconPath, this.plugin); } @@ -381,7 +381,7 @@ class TreeViewExtImpl implements Disposable { label, icon, iconUrl, - themeIconId, + themeIcon, description: treeItem.description, resourceUri: treeItem.resourceUri, tooltip: treeItem.tooltip, diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index b4cdab5090ad4..6108ebf44c982 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -732,7 +732,7 @@ export class ThemeIcon { static readonly Folder: ThemeIcon = new ThemeIcon('folder'); - private constructor(public id: string) { + private constructor(public id: string, public color?: ThemeColor) { } } diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 802364d6bfc79..28a1c2fcd8786 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -2652,7 +2652,22 @@ export module '@theia/plugin' { */ static readonly Folder: ThemeIcon; - private constructor(public id: string); + /** + * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + */ + readonly id: string; + + /** + * The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}. + */ + readonly color?: ThemeColor | undefined; + + /** + * Creates a reference to a theme icon. + * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + * @param color optional `ThemeColor` for the icon. The color is currently only used in {@link TreeItem}. + */ + private constructor(public id: string, public color?: ThemeColor); } /**