From 42637a1d9c04a2bbb72d3b770722eca3db2a4c1e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 29 Apr 2020 07:34:31 -0700 Subject: [PATCH] Make widget scrollable Fixes #96499 --- .../terminal/browser/widgets/hoverWidget.ts | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts index ab1e60cb45b..54799091ba2 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts @@ -14,18 +14,21 @@ import * as dom from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IHoverTarget, HorizontalAnchorSide, VerticalAnchorSide } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; const $ = dom.$; export class HoverWidget extends Widget { + private readonly _containerDomNode: HTMLElement; private readonly _domNode: HTMLElement; private readonly _messageListeners = new DisposableStore(); private readonly _mouseTracker: CompositeMouseTracker; + private readonly _scrollbar: DomScrollableElement; private _isDisposed: boolean = false; get isDisposed(): boolean { return this._isDisposed; } - get domNode(): HTMLElement { return this._domNode; } + get domNode(): HTMLElement { return this._containerDomNode; } private readonly _onDispose = new Emitter(); get onDispose(): Event { return this._onDispose.event; } @@ -39,17 +42,24 @@ export class HoverWidget extends Widget { @IKeybindingService private readonly _keybindingService: IKeybindingService ) { super(); + this._containerDomNode = document.createElement('div'); + this._containerDomNode.classList.add('terminal-hover-widget', 'fadeIn', 'monaco-editor-hover', 'xterm-hover'); + this._containerDomNode.tabIndex = 0; + this._containerDomNode.setAttribute('role', 'tooltip'); + this._domNode = document.createElement('div'); - this._domNode.classList.add('terminal-hover-widget', 'fadeIn', 'monaco-editor-hover', 'xterm-hover'); - this._domNode.tabIndex = 0; - this._domNode.setAttribute('role', 'tooltip'); + this._domNode.className = 'monaco-editor-hover-content'; + + this._scrollbar = new DomScrollableElement(this._domNode, {}); + this._register(this._scrollbar); + this._containerDomNode.appendChild(this._scrollbar.getDomNode()); // Don't allow mousedown out of the widget, otherwise preventDefault will call and text will // not be selected. - this.onmousedown(this._domNode, e => e.stopPropagation()); + this.onmousedown(this._containerDomNode, e => e.stopPropagation()); // Hide hover on escape - this.onkeydown(this._domNode, e => { + this.onkeydown(this._containerDomNode, e => { if (e.equals(KeyCode.Escape)) { this.dispose(); } @@ -75,11 +85,11 @@ export class HoverWidget extends Widget { this._domNode.appendChild(statusBarElement); } - this._mouseTracker = new CompositeMouseTracker([this._domNode, ..._target.targetElements]); + this._mouseTracker = new CompositeMouseTracker([this._containerDomNode, ..._target.targetElements]); this._register(this._mouseTracker.onMouseOut(() => this.dispose())); this._register(this._mouseTracker); - this._container.appendChild(this._domNode); + this._container.appendChild(this._containerDomNode); this.layout(); } @@ -106,51 +116,55 @@ export class HoverWidget extends Widget { public layout(): void { const anchor = this._target.anchor; - this._domNode.classList.remove('right-aligned'); + this._containerDomNode.classList.remove('right-aligned'); + this._domNode.style.maxHeight = ''; if (anchor.horizontalAnchorSide === HorizontalAnchorSide.Left) { - if (anchor.x + this._domNode.clientWidth > document.documentElement.clientWidth) { + if (anchor.x + this._containerDomNode.clientWidth > document.documentElement.clientWidth) { // Shift the hover to the left when part of it would get cut off - const width = Math.round(this._domNode.clientWidth); - this._domNode.style.width = `${width - 1}px`; - this._domNode.style.maxWidth = ''; + const width = Math.round(this._containerDomNode.clientWidth); + this._containerDomNode.style.width = `${width - 1}px`; + this._containerDomNode.style.maxWidth = ''; const left = document.documentElement.clientWidth - width - 1; - this._domNode.style.left = `${left}px`; + this._containerDomNode.style.left = `${left}px`; // Right align if the right edge is closer to the anchor than the left edge if (left + width / 2 < anchor.x) { - this._domNode.classList.add('right-aligned'); + this._containerDomNode.classList.add('right-aligned'); } } else { - this._domNode.style.width = ''; - this._domNode.style.maxWidth = `${document.documentElement.clientWidth - anchor.x - 1}px`; - this._domNode.style.left = `${anchor.x}px`; + this._containerDomNode.style.width = ''; + this._containerDomNode.style.maxWidth = `${document.documentElement.clientWidth - anchor.x - 1}px`; + this._containerDomNode.style.left = `${anchor.x}px`; } } else { - this._domNode.style.right = `${anchor.x}px`; + this._containerDomNode.style.right = `${anchor.x}px`; } // Use fallback y value if there is not enough vertical space if (anchor.verticalAnchorSide === VerticalAnchorSide.Bottom) { - if (anchor.y + this._domNode.clientHeight > document.documentElement.clientHeight) { - this._domNode.style.top = `${anchor.fallbackY}px`; + if (anchor.y + this._containerDomNode.clientHeight > document.documentElement.clientHeight) { + this._containerDomNode.style.top = `${anchor.fallbackY}px`; + this._domNode.style.maxHeight = `${document.documentElement.clientHeight - anchor.fallbackY}px`; } else { - this._domNode.style.bottom = `${anchor.y}px`; + this._containerDomNode.style.bottom = `${anchor.y}px`; + this._containerDomNode.style.maxHeight = ''; } } else { - if (anchor.y + this._domNode.clientHeight > document.documentElement.clientHeight) { - this._domNode.style.bottom = `${anchor.fallbackY}px`; + if (anchor.y + this._containerDomNode.clientHeight > document.documentElement.clientHeight) { + this._containerDomNode.style.bottom = `${anchor.fallbackY}px`; } else { - this._domNode.style.top = `${anchor.y}px`; + this._containerDomNode.style.top = `${anchor.y}px`; } } + this._scrollbar.scanDomNode(); } public focus() { - this._domNode.focus(); + this._containerDomNode.focus(); } public dispose(): void { if (!this._isDisposed) { this._onDispose.fire(); - this._domNode.parentElement?.removeChild(this.domNode); + this._containerDomNode.parentElement?.removeChild(this.domNode); this._messageListeners.dispose(); this._target.dispose(); super.dispose(); -- GitLab