Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support VSCode API TreeView.badge #4185

Merged
merged 12 commits into from
Dec 5, 2024
2 changes: 1 addition & 1 deletion packages/comments/src/browser/comments.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class CommentsBrowserContribution

get panelBadge() {
const length = this.commentsService.commentsThreads.length;
return length ? length + '' : '';
return length ? { value: length, tooltip: `${length}` } : undefined;
}

registerCommands(registry: CommandRegistry) {
Expand Down
2 changes: 1 addition & 1 deletion packages/comments/src/browser/comments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ export class CommentsService extends Disposable implements ICommentsService {

get panelBadge() {
const length = this.commentsThreads.length;
return length ? length + '' : '';
return length ? { value: length, tooltip: `${length}` } : undefined;
}

registerCommentRangeProvider(id: string, provider: ICommentRangeProvider): IDisposable {
Expand Down
6 changes: 4 additions & 2 deletions packages/core-browser/src/layout/layout.interface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@

import { BasicEvent, Event, MaybeNull } from '@opensumi/ide-core-common';

import { Layout } from '../components/layout/index';

import type { IContextMenu, IMenu } from '../menu/next';
import type { SlotLocation } from '../react-providers';
import type React from 'react';
import type { ViewBadge } from 'vscode';

export type Side = 'left' | 'right' | 'bottom';

Expand Down Expand Up @@ -36,7 +38,7 @@ export interface View {
weight?: number;
priority?: number;
collapsed?: boolean;
badge?: string;
badge?: ViewBadge | undefined;
hidden?: boolean;
component?: React.ComponentType<any>;
// 使用该参数时, view 的 toolbar 默认不渲染
Expand All @@ -60,7 +62,7 @@ export interface ExtViewContainerOptions {
size?: number;
activateKeyBinding?: string;
hidden?: boolean;
badge?: string;
badge?: ViewBadge | undefined | string;
// 直接使用自定义的React组件,会失去一些对面板的控制能力
component?: React.ComponentType<any>;
// 使用自定义组件时可以传入,否则请作为View的一部分传入
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ describe('packages/extension/__tests__/hosted/api/sumi/ext.host.layout.test.ts',
it('setBadge', (done) => {
mainLayout.$setBadge = jest.fn((id, badge) => {
expect(id).toBe(`${extension.id}:${viewId}`);
expect(badge).toBe('10');
expect(badge).toBe({ value: 20, tooltip: '20' });
done();
});
const tabbar = layoutApi.getTabbarHandler(viewId);
tabbar.setBadge('10');
tabbar.setBadge({ value: 20, tooltip: '20' });
});
});
4 changes: 3 additions & 1 deletion packages/extension/src/browser/sumi/main.thread.layout.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ViewBadge } from 'vscode';

import { Autowired, Injectable } from '@opensumi/di';
import { IRPCProtocol } from '@opensumi/ide-connection';
import { Disposable, IEventBus, ILogger } from '@opensumi/ide-core-browser';
Expand Down Expand Up @@ -52,7 +54,7 @@ export class MainThreadLayout extends Disposable implements IMainThreadLayout {
this.getHandler(id)?.deactivate();
}

$setBadge(id: string, badge: string): void {
$setBadge(id: string, badge: ViewBadge | undefined): void {
this.getHandler(id)?.setBadge(badge);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
IWebviewOptions,
IWebviewPanelOptions,
IWebviewPanelViewState,
ViewBadge,
WebviewPanelShowOptions,
WebviewViewOptions,
WebviewViewResolverRegistrationEvent,
Expand Down Expand Up @@ -618,7 +619,7 @@ class WebviewPanel extends Disposable {

class WebviewView extends Disposable {
public title: string;

public badge: ViewBadge | undefined;
public viewColumn: number;

constructor(
Expand Down Expand Up @@ -779,6 +780,18 @@ export class MainThreadWebviewView extends WithEventBus implements IMainThreadWe
};
}

$setBadge(handle: string, badge: ViewBadge | undefined): void {
const webviewView = this._webviewViews.get(handle);
if (webviewView) {
webviewView.badge = badge;
const handler = this.mainLayout.getTabbarHandler(webviewView.viewType);
if (handler) {
handler.setBadge(badge);
handler.accordionService.updateViewBadge(webviewView.viewType, badge);
}
}
}

$show(handle: string, preserveFocus: boolean): void {
const webviewView = this._webviewViews.get(handle);
if (webviewView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
IconUrl,
TreeViewBaseOptions,
TreeViewItem,
ViewBadge,
} from '../../../common/vscode';
import { DataTransfer } from '../../../common/vscode/converter';
import { VSDataTransfer, createStringDataTransferItem } from '../../../common/vscode/data-transfer';
Expand Down Expand Up @@ -267,6 +268,14 @@ export class MainThreadTreeView extends WithEventBus implements IMainThreadTreeV
}
}

async $setBadge(treeViewId: string, badge: ViewBadge | undefined) {
const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
if (handler) {
handler.setBadge(badge);
handler.accordionService.updateViewBadge(treeViewId, badge);
}
}

async $setMessage(treeViewId: string, message: string) {
const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
if (handler) {
Expand Down
6 changes: 4 additions & 2 deletions packages/extension/src/common/sumi/layout.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ViewBadge } from 'vscode';

import { Event } from '@opensumi/ide-core-common';

export interface ITabbarHandler {
Expand All @@ -7,7 +9,7 @@ export interface ITabbarHandler {

setIcon(iconPath: string): void;

setBadge(badge: string): void;
setBadge(badge: ViewBadge | undefined): void;

activate(): void;

Expand All @@ -23,7 +25,7 @@ export interface IMainThreadLayout {
$setSize(id: string, size: number): void;
$setTitle(id: string, title: string): void;
$setIcon(id: string, iconPath: string): void;
$setBadge(id: string, badge: string): void;
$setBadge(id: string, badge: ViewBadge | undefined): void;
$activate(id: string): void;
$deactivate(id: string): void;
$setVisible(id: string, visible: boolean): Promise<void>;
Expand Down
21 changes: 21 additions & 0 deletions packages/extension/src/common/vscode/treeview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface IMainThreadTreeView {
$reveal(treeViewId: string, treeItemId?: string, options?: ITreeViewRevealOptions): Promise<any>;
$setTitle(treeViewId: string, message: string): Promise<void>;
$setDescription(treeViewId: string, message: string): Promise<void>;
$setBadge(treeViewId: string, badge: ViewBadge | undefined): void;
$setMessage(treeViewId: string, message: string): Promise<void>;
$resolveDropFileData(treeViewId: string, requestId: number, dataItemId: string): Promise<BinaryBuffer>;
}
Expand Down Expand Up @@ -140,6 +141,11 @@ export interface TreeView<T> extends vscode.TreeView<T> {
* 可选的节点描述信息
*/
description?: string;
/**
* TreeView 要显示的徽标
* 要删除徽标,请设置为undefined
*/
badge?: ViewBadge | undefined;
/**
* 展示节点,默认情况下展示的节点为选中状态
*
Expand All @@ -152,6 +158,21 @@ export interface TreeView<T> extends vscode.TreeView<T> {
dispose(): void;
}

/**
* 展示视图数值的徽标
*/
export interface ViewBadge {
/**
* 在徽标工具提示中显示的标签
*/
readonly tooltip: string;

/**
* 徽标中显示的值
*/
readonly value: number;
}

export interface TreeViewBaseOptions {
/**
* 是否展示折叠所有功能(panel上功能)
Expand Down
7 changes: 7 additions & 0 deletions packages/extension/src/common/vscode/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface IMainThreadWebviewView {

$setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void;
$setWebviewViewDescription(handle: WebviewHandle, value: string | undefined): void;
$setBadge(handle: WebviewHandle, badge: vscode.ViewBadge | undefined): void;

$show(handle: WebviewHandle, preserveFocus: boolean): void;
}
Expand Down Expand Up @@ -313,6 +314,12 @@ export interface WebviewView {
*/
readonly visible: boolean;

/**
* The badge to display for this webview view.
* To remove the badge, set to undefined.
*/
badge?: vscode.ViewBadge | undefined;

/**
* Event fired when the visibility of the view changes.
*
Expand Down
4 changes: 2 additions & 2 deletions packages/extension/src/hosted/api/sumi/ext.host.layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Emitter } from '@opensumi/ide-core-common';

import { MainThreadSumiAPIIdentifier } from '../../../common/sumi';
import { IExtHostLayout, IMainThreadLayout, ITabbarHandler } from '../../../common/sumi/layout';
import { IExtHostCommands, IExtensionDescription } from '../../../common/vscode';
import { IExtHostCommands, IExtensionDescription, ViewBadge } from '../../../common/vscode';

export class TabbarHandler implements ITabbarHandler {
public readonly onActivateEmitter = new Emitter<void>();
Expand Down Expand Up @@ -38,7 +38,7 @@ export class TabbarHandler implements ITabbarHandler {
this.proxy.$setVisible(this.id, visible);
}

setBadge(badge: string) {
setBadge(badge: ViewBadge | undefined) {
this.proxy.$setBadge(this.id, badge);
}

Expand Down
15 changes: 15 additions & 0 deletions packages/extension/src/hosted/api/vscode/ext.host.api.webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
IWebviewPanelOptions,
IWebviewPanelViewState,
MainThreadAPIIdentifier,
ViewBadge,
ViewColumn,
Webview,
WebviewHandle,
Expand Down Expand Up @@ -430,6 +431,7 @@ class ExtHostWebviewView extends IDEDisposable implements WebviewView {
#isVisible: boolean;
#title: string | undefined;
#description: string | undefined;
#badge: ViewBadge | undefined;

constructor(
handle: WebviewHandle,
Expand Down Expand Up @@ -499,6 +501,19 @@ class ExtHostWebviewView extends IDEDisposable implements WebviewView {
}
}

public get badge(): ViewBadge | undefined {
this.assertNotDisposed();
return this.#badge;
}

public set badge(badge: ViewBadge | undefined) {
this.assertNotDisposed();
if (this.#badge !== badge) {
this.#badge = badge;
this.#proxy.$setBadge(this.#handle, badge);
}
}

public get visible(): boolean {
return this.#isVisible;
}
Expand Down
16 changes: 16 additions & 0 deletions packages/extension/src/hosted/api/vscode/ext.host.treeview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
TreeViewItem,
TreeViewSelection,
TreeviewsService,
ViewBadge,
} from '../../../common/vscode';
import { DataTransfer } from '../../../common/vscode/converter';
import * as types from '../../../common/vscode/ext-types';
Expand Down Expand Up @@ -122,6 +123,12 @@ export class ExtHostTreeViews implements IExtHostTreeView {
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, options: ITreeViewRevealOptions): Thenable<void> => treeView.reveal(element, options),
dispose: () => {
this.treeViews.delete(treeViewId);
Expand Down Expand Up @@ -316,6 +323,7 @@ class ExtHostTreeView<T extends vscode.TreeItem> implements IDisposable {
private _title: string;
private _description: string;
private _message: string;
private _badge?: ViewBadge = undefined;

private roots: TreeViewItem[] | undefined = undefined;
private nodes: Map<T, TreeViewItem[] | undefined> = new Map();
Expand Down Expand Up @@ -492,6 +500,14 @@ class ExtHostTreeView<T extends vscode.TreeItem> implements IDisposable {
return this._visible;
}

get badge(): ViewBadge | undefined {
return this._badge;
}
set badge(badge: ViewBadge | undefined) {
this._badge = badge;
this.proxy.$setBadge(this.treeViewId, badge ? { value: badge.value, tooltip: badge.tooltip } : undefined);
}

get selectedElements(): T[] {
const items: T[] = [];
for (const id of this.selectedItemIds) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe('main layout test', () => {
title: 'test title',
expanded: false,
size: 300,
badge: '9',
badge: { value: 9, tooltip: '9' },
initialProps: { hello: 'world' },
activateKeyBinding: 'ctrlcmd+1',
hidden: false,
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('main layout test', () => {
expect(mockCb).toHaveBeenCalledTimes(4);
let newTitle = 'new title';
act(() => {
handler.setBadge('20');
handler.setBadge({ value: 20, tooltip: '20' });
handler.updateTitle(newTitle);
});
expect(tabbarService.getContainer(testContainerId2)!.options!.title).toEqual(newTitle);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import debounce from 'lodash/debounce';
import isEqual from 'lodash/isEqual';
import { ViewBadge } from 'vscode';

import { Autowired, Injectable } from '@opensumi/di';
import {
Expand Down Expand Up @@ -57,7 +58,7 @@ interface AccordionViewChangeEvent {
title?: string;
description?: string;
message?: string;
badge?: string;
badge?: ViewBadge | undefined;
}

@Injectable({ multiple: true })
Expand Down Expand Up @@ -216,7 +217,7 @@ export class AccordionService extends WithEventBus {
}
}

updateViewBadge(viewId: string, badge: string) {
updateViewBadge(viewId: string, badge: ViewBadge | undefined) {
const view = this.views.find((view) => view.id === viewId);
if (view) {
view.badge = badge;
Expand Down
7 changes: 4 additions & 3 deletions packages/main-layout/src/browser/accordion/section.view.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import cls from 'classnames';
import React from 'react';
import { ViewBadge } from 'vscode';

import {
ErrorBoundary,
Expand Down Expand Up @@ -59,7 +60,7 @@ export interface CollapsePanelProps extends React.PropsWithChildren<any> {
// Panel Accordion Service
accordionService: AccordionService;
// Panel Badge
badge?: string;
badge?: ViewBadge | undefined;
// Panel Expanded
expanded?: boolean;
// Panel Title Menu Context
Expand Down Expand Up @@ -120,7 +121,7 @@ export const AccordionSection = ({
changed = true;
}

if (viewId === id && description && description !== metadata.badge) {
if (viewId === id && description && description !== metadata.description) {
newMetadata.description = description;
changed = true;
}
Expand Down Expand Up @@ -186,7 +187,7 @@ export const AccordionSection = ({
{transformLabelWithCodicon(metadata.description, {}, iconService.fromString.bind(iconService))}
</div>
)}
{metadata.badge && <div className={styles_section_badge}>{metadata.badge}</div>}
{metadata.badge && <div className={styles_section_badge}>{metadata.badge.value}</div>}
</div>
{expanded && titleMenu && (
<div className={styles_actions_wrap}>
Expand Down
Loading