Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"perfect-scrollbar": "^1.3.0",
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-tooltip": "^4.2.21",
"react-virtualized": "^9.20.0",
"reconnecting-websocket": "^4.2.0",
"reflect-metadata": "^0.1.10",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/browser/core-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const corePreferenceSchema: PreferenceSchema = {
default: 'onHover',
description: 'Controls whether the tree should render indent guides.'
},
'workbench.hover.delay': {
type: 'number',
default: isOSX ? 1500 : 500,
description: 'Controls the delay in milliseconds after which the hover is shown for workbench items (ex. some extension provided tree view items).'
},
}
};

Expand All @@ -142,6 +147,7 @@ export interface CoreConfiguration {
'workbench.silentNotifications': boolean;
'workbench.statusBar.visible': boolean;
'workbench.tree.renderIndentGuides': 'onHover' | 'none' | 'always';
'workbench.hover.delay': number;
}

export const CorePreferenceContribution = Symbol('CorePreferenceContribution');
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import {
DefaultBreadcrumbRenderer,
} from './breadcrumbs';
import { RendererHost } from './widgets';
import { TooltipService, TooltipServiceImpl } from './tooltip-service';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

Expand Down Expand Up @@ -186,6 +187,9 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
bind(CommandOpenHandler).toSelf().inSingletonScope();
bind(OpenHandler).toService(CommandOpenHandler);

bind(TooltipServiceImpl).toSelf().inSingletonScope();
bind(TooltipService).toService(TooltipServiceImpl);

bindContributionProvider(bind, ApplicationShellLayoutMigration);
bind<ApplicationShellLayoutMigration>(ApplicationShellLayoutMigration).toConstantValue({
layoutVersion: 2.0,
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/browser/frontend-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { FrontendApplicationStateService } from './frontend-application-state';
import { preventNavigation, parseCssTime, animationFrame } from './browser';
import { CorePreferences } from './core-preferences';
import { WindowService } from './window/window-service';
import { TooltipService } from './tooltip-service';

/**
* Clients can implement to get a callback for contributing widgets to a shell on start.
Expand Down Expand Up @@ -100,6 +101,9 @@ export class FrontendApplication {
@inject(WindowService)
protected readonly windowsService: WindowService;

@inject(TooltipService)
protected readonly tooltipService: TooltipService;

constructor(
@inject(CommandRegistry) protected readonly commands: CommandRegistry,
@inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry,
Expand Down Expand Up @@ -130,6 +134,7 @@ export class FrontendApplication {

const host = await this.getHost();
this.attachShell(host);
this.attachTooltip(host);
await animationFrame();
this.stateService.state = 'attached_shell';

Expand Down Expand Up @@ -221,6 +226,13 @@ export class FrontendApplication {
Widget.attach(this.shell, host, ref);
}

/**
* Attach the tooltip container to the host element.
*/
protected attachTooltip(host: HTMLElement): void {
this.tooltipService.attachTo(host);
}

/**
* If a startup indicator is present, it is first hidden with the `theia-hidden` CSS class and then
* removed after a while. The delay until removal is taken from the CSS transition duration.
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ export * from './diff-uris';
export * from './core-preferences';
export * from './view-container';
export * from './breadcrumbs';
export * from './tooltip-service';
1 change: 1 addition & 0 deletions packages/core/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,4 @@ button.secondary[disabled], .theia-button.secondary[disabled] {
@import './quick-title-bar.css';
@import './progress-bar.css';
@import './breadcrumbs.css';
@import './tooltip.css';
28 changes: 28 additions & 0 deletions packages/core/src/browser/style/tooltip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/********************************************************************************
* Copyright (C) 2021 Arm and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

.theia-tooltip {
color: var(--theia-list-hoverForeground) !important;
background: var(--theia-list-hoverBackground) !important;
border: 1px solid !important;
border-color: var(--theia-list-hoverForeground) !important;
}

/* Hide tooltip arrow */
.theia-tooltip::before,
.theia-tooltip::after {
border: none !important;
}
98 changes: 98 additions & 0 deletions packages/core/src/browser/tooltip-service.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/********************************************************************************
* Copyright (C) 2021 Arm and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject, optional, postConstruct } from 'inversify';
import * as React from 'react';
import ReactTooltip from 'react-tooltip';
import { ReactRenderer, RendererHost } from './widgets/react-renderer';
import { CorePreferences } from './core-preferences';
import { DisposableCollection } from '../common/disposable';
import { v4 } from 'uuid';

export const TooltipService = Symbol('TooltipService');

export interface TooltipService {
tooltipId: string;
attachTo(host: HTMLElement): void;
update(fullRender?: boolean): void;
}

/**
* Attributes to be added to an HTML element to enable
* rich HTML tooltip rendering
*/
export interface TooltipAttributes {
/**
* HTML to render in the tooltip.
*/
'data-tip': string;
/**
* The ID of the tooltip renderer. Should be TOOLTIP_ID.
*/
'data-for': string;
}

const DELAY_PREFERENCE = 'workbench.hover.delay';

@injectable()
export class TooltipServiceImpl extends ReactRenderer implements TooltipService {

@inject(CorePreferences)
protected readonly corePreferences: CorePreferences;

public readonly tooltipId: string;
protected rendered = false;
protected toDispose: DisposableCollection = new DisposableCollection();

constructor(
@inject(RendererHost) @optional() host?: RendererHost
) {
super(host);
this.tooltipId = v4();
}

@postConstruct()
protected init(): void {
this.toDispose.push(this.corePreferences.onPreferenceChanged(preference => {
if (preference.preferenceName === DELAY_PREFERENCE) {
this.update(true);
}
}));
}

public attachTo(host: HTMLElement): void {
host.appendChild(this.host);
}

public update(fullRender = false): void {
if (fullRender || !this.rendered) {
this.render();
this.rendered = true;
}

ReactTooltip.rebuild();
}

protected doRender(): React.ReactNode {
const hoverDelay = this.corePreferences.get(DELAY_PREFERENCE);
return <ReactTooltip id={this.tooltipId} className='theia-tooltip' html={true} delayShow={hoverDelay} />;
}

public dispose(): void {
this.toDispose.dispose();
super.dispose();
}
}
2 changes: 2 additions & 0 deletions packages/plugin-ext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@theia/terminal": "1.18.0",
"@theia/timeline": "1.18.0",
"@theia/workspace": "1.18.0",
"@types/markdown-it": "*",
"@types/mime": "^2.0.1",
"decompress": "^4.2.1",
"escape-html": "^1.0.3",
Expand All @@ -34,6 +35,7 @@
"jsonc-parser": "^2.2.0",
"lodash.clonedeep": "^4.5.0",
"macaddress": "^0.2.9",
"markdown-it": "^8.4.0",
"mime": "^2.4.4",
"ps-tree": "^1.2.0",
"request": "^2.82.0",
Expand Down
46 changes: 37 additions & 9 deletions packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import {
TREE_NODE_SEGMENT_GROW_CLASS,
TREE_NODE_TAIL_CLASS,
TreeModelImpl,
TreeViewWelcomeWidget
TreeViewWelcomeWidget,
TooltipService,
TooltipAttributes
} from '@theia/core/lib/browser';
import { TreeViewItem, TreeViewItemCollapsibleState } from '../../../common/plugin-api-rpc';
import { MenuPath, MenuModelRegistry, ActionMenuNode } from '@theia/core/lib/common/menu';
Expand All @@ -42,6 +44,8 @@ import { MessageService } from '@theia/core/lib/common/message-service';
import { View } from '../../../common/plugin-protocol';
import CoreURI from '@theia/core/lib/common/uri';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import * as markdownit from 'markdown-it';
import { isMarkdownString } from '../../../plugin/markdown-string';

export const TREE_NODE_HYPERLINK = 'theia-TreeNodeHyperlink';
export const VIEW_ITEM_CONTEXT_MENU: MenuPath = ['view-item-context-menu'];
Expand Down Expand Up @@ -245,6 +249,9 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

@inject(TooltipService)
protected readonly tooltipService: TooltipService;

protected readonly onDidChangeVisibilityEmitter = new Emitter<boolean>();
readonly onDidChangeVisibility = this.onDidChangeVisibilityEmitter.event;

Expand Down Expand Up @@ -274,13 +281,32 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
classes.push(TREE_NODE_SEGMENT_GROW_CLASS);
}
const className = classes.join(' ');
const title = node.tooltip ||
(node.resourceUri && this.labelProvider.getLongName(new CoreURI(node.resourceUri)))
|| this.toNodeName(node);
const attrs = this.decorateCaption(node, {
className, id: node.id,
title
});

let attrs: React.HTMLAttributes<HTMLElement> & Partial<TooltipAttributes> = {
...this.decorateCaption(node, {}),
className,
id: node.id
};

if (node.tooltip && isMarkdownString(node.tooltip)) {
// Render markdown in custom tooltip
const tooltip = markdownit().render(node.tooltip.value);

attrs = {
...attrs,
'data-tip': tooltip,
'data-for': this.tooltipService.tooltipId
};
} else {
const title = node.tooltip ||
(node.resourceUri && this.labelProvider.getLongName(new CoreURI(node.resourceUri)))
|| this.toNodeName(node);

attrs = {
...attrs,
title
};
}

const children: React.ReactNode[] = [];
const caption = this.toNodeName(node);
Expand Down Expand Up @@ -444,7 +470,9 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
}

protected render(): React.ReactNode {
return React.createElement('div', this.createContainerAttributes(), this.renderSearchInfo(), this.renderTree(this.model));
const node = React.createElement('div', this.createContainerAttributes(), this.renderSearchInfo(), this.renderTree(this.model));
this.tooltipService.update();
return node;
}

protected renderSearchInfo(): React.ReactNode {
Expand Down
2 changes: 2 additions & 0 deletions packages/vsx-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
"@theia/plugin-ext-vscode": "1.18.0",
"@theia/preferences": "1.18.0",
"@theia/workspace": "1.18.0",
"@types/markdown-it": "*",
"@types/bent": "^7.0.1",
"@types/sanitize-html": "^2.3.1",
"@types/showdown": "^1.7.1",
"bent": "^7.1.0",
"markdown-it": "^8.4.0",
"p-debounce": "^2.1.0",
"requestretry": "^3.1.0",
"sanitize-html": "^2.3.2",
Expand Down
Loading