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

Implement workbench hover service #100285

Merged
merged 11 commits into from
Jun 16, 2020
43 changes: 43 additions & 0 deletions src/vs/workbench/contrib/hover/browser/hover.contribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/hover';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { HoverService } from 'vs/workbench/contrib/hover/browser/hoverService';
import { IHoverService } from 'vs/workbench/contrib/hover/browser/hover';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorHoverBackground, editorHoverBorder, textLinkForeground, editorHoverForeground, editorHoverStatusBarBackground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';

registerSingleton(IHoverService, HoverService, true);

registerThemingParticipant((theme, collector) => {
const hoverBackground = theme.getColor(editorHoverBackground);
if (hoverBackground) {
collector.addRule(`.monaco-workbench .workbench-hover { background-color: ${hoverBackground}; }`);
}
const hoverBorder = theme.getColor(editorHoverBorder);
if (hoverBorder) {
collector.addRule(`.monaco-workbench .workbench-hover { border: 1px solid ${hoverBorder}; }`);
collector.addRule(`.monaco-workbench .workbench-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`);
collector.addRule(`.monaco-workbench .workbench-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`);
collector.addRule(`.monaco-workbench .workbench-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`);
}
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-workbench .workbench-hover a { color: ${link}; }`);
}
const hoverForeground = theme.getColor(editorHoverForeground);
if (hoverForeground) {
collector.addRule(`.monaco-workbench .workbench-hover { color: ${hoverForeground}; }`);
}
const actionsBackground = theme.getColor(editorHoverStatusBarBackground);
if (actionsBackground) {
collector.addRule(`.monaco-workbench .workbench-hover .hover-row .actions { background-color: ${actionsBackground}; }`);
}
const codeBackground = theme.getColor(textCodeBlockBackground);
if (codeBackground) {
collector.addRule(`.monaco-workbench .workbench-hover code { background-color: ${codeBackground}; }`);
}
});
105 changes: 105 additions & 0 deletions src/vs/workbench/contrib/hover/browser/hover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IMarkdownString } from 'vs/base/common/htmlContent';

export const IHoverService = createDecorator<IHoverService>('hoverService');

/**
* Enables the convenient display of rich markdown-based hovers in the workbench.
*/
export interface IHoverService {
readonly _serviceBrand: undefined;

/**
* Shows a hover.
* @param options A set of options defining the characteristics of the hover.
* @param focus Whether to focus the hover (useful for keyboard accessibility).
*
* **Example:** A simple usage with a single element target.
*
* ```typescript
* showHover({
* text: new MarkdownString('Hello world'),
* target: someElement
* });
* ```
*/
showHover(options: IHoverOptions, focus?: boolean): void;

/**
* Hides the hover if it was visible.
*/
hideHover(): void;
}

export interface IHoverOptions {
/**
* The text to display in the primary section of the hover.
*/
text: IMarkdownString;

/**
* The target for the hover. This determines the position of the hover and it will only be
* hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for
* simple cases and a IHoverTarget for more complex cases where multiple elements and/or a
* dispose method is required.
*/
target: IHoverTarget | HTMLElement;

/**
* A set of actions for the hover's "status bar".
*/
actions?: IHoverAction[];

/**
* An optional array of classes to add to the hover element.
*/
additionalClasses?: string[];

/**
* An optional link handler for markdown links, if this is not provided the IOpenerService will
* be used to open the links using its default options.
*/
linkHandler?(url: string): void;
}

export interface IHoverAction {
/**
* The label to use in the hover's status bar.
*/
label: string;

/**
* The command ID of the action, this is used to resolve the keybinding to display after the
* action label.
*/
commandId: string;

/**
* An optional class of an icon that will be displayed before the label.
*/
iconClass?: string;

/**
* The callback to run the action.
* @param target The action element that was activated.
*/
run(target: HTMLElement): void;
}

/**
* A target for a hover.
*/
export interface IHoverTarget extends IDisposable {
/**
* A set of target elements used to position the hover. If multiple elements are used the hover
* will try to not overlap any target element. An example use case for this is show a hover for
* wrapped text.
*/
readonly targetElements: readonly HTMLElement[];
}
55 changes: 55 additions & 0 deletions src/vs/workbench/contrib/hover/browser/hoverService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IHoverService, IHoverOptions } from 'vs/workbench/contrib/hover/browser/hover';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { HoverWidget } from 'vs/workbench/contrib/hover/browser/hoverWidget';
import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview';

export class HoverService implements IHoverService {
declare readonly _serviceBrand: undefined;

private _currentHoverOptions: IHoverOptions | undefined;

constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IContextViewService private readonly _contextViewService: IContextViewService
) {
}

showHover(options: IHoverOptions, focus?: boolean): void {
if (this._currentHoverOptions === options) {
return;
}
this._currentHoverOptions = options;

const hover = this._instantiationService.createInstance(HoverWidget, options);
const provider = this._contextViewService as IContextViewProvider;
const contextViewDelegate: IDelegate = {
render: container => {
hover.render(container);
hover.onDispose(() => this._currentHoverOptions = undefined);
if (focus) {
hover.focus();
}
return hover;
},
anchorPosition: hover.anchor,
getAnchor: () => ({ x: hover.x, y: hover.y }),
layout: () => hover.layout()
};
provider.showContextView(contextViewDelegate);
hover.onRequestLayout(() => provider.layout());
}

hideHover(): void {
if (!this._currentHoverOptions) {
return;
}
this._currentHoverOptions = undefined;
this._contextViewService.hideContextView();
}
}
Loading