From 751fdcb9045ffa71755279602c493a8715793a2f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 May 2023 16:51:12 -0700 Subject: [PATCH] Try to lay debug hover out correctly with scrollbar (#182817) Fix #173980 --- .../contrib/debug/browser/debugHover.ts | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 0b707139699..e507fe990c3 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -14,10 +14,12 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as lifecycle from 'vs/base/common/lifecycle'; +import { clamp } from 'vs/base/common/numbers'; import { isMacintosh } from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IDimension } from 'vs/editor/common/core/dimension'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -91,6 +93,9 @@ export class DebugHoverWidget implements IContentWidget { private scrollbar!: DomScrollableElement; private debugHoverComputer: DebugHoverComputer; + private expressionToRender: IExpression | undefined; + private isUpdatingTree = false; + constructor( private editor: ICodeEditor, @IDebugService private readonly debugService: IDebugService, @@ -107,6 +112,7 @@ export class DebugHoverWidget implements IContentWidget { private create(): void { this.domNode = $('.debug-hover-widget'); this.complexValueContainer = dom.append(this.domNode, $('.complex-value')); + this.complexValueContainer.style.visibility = 'hidden'; this.complexValueTitle = dom.append(this.complexValueContainer, $('.title')); this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree')); this.treeContainer.setAttribute('role', 'tree'); @@ -138,7 +144,12 @@ export class DebugHoverWidget implements IContentWidget { this.domNode.style.border = `1px solid ${asCssVariable(editorHoverBorder)}`; this.domNode.style.color = asCssVariable(editorHoverForeground); - this.toDispose.push(this.tree.onDidChangeContentHeight(() => this.layoutTreeAndContainer(false))); + this.toDispose.push(this.tree.onDidChangeContentHeight(() => { + if (!this.isUpdatingTree) { + // Don't do a layout in the middle of the async setInput + this.layoutTreeAndContainer(); + } + })); this.registerListeners(); this.editor.addContentWidget(this); @@ -261,10 +272,10 @@ export class DebugHoverWidget implements IContentWidget { this.valueContainer.hidden = true; - await this.tree.setInput(expression); + this.expressionToRender = expression; this.complexValueTitle.textContent = expression.value; this.complexValueTitle.title = expression.value; - this.layoutTreeAndContainer(true); + this.editor.layoutContentWidget(this); this.tree.scrollTop = 0; this.tree.scrollLeft = 0; this.complexValueContainer.hidden = false; @@ -275,15 +286,49 @@ export class DebugHoverWidget implements IContentWidget { } } - private layoutTreeAndContainer(initialLayout: boolean): void { + private layoutTreeAndContainer(): void { + this.layoutTree(); + this.editor.layoutContentWidget(this); + } + + private layoutTree(): void { const scrollBarHeight = 10; const treeHeight = Math.min(Math.max(266, this.editor.getLayoutInfo().height * 0.55), this.tree.contentHeight + scrollBarHeight); + + // Reset to a smaller width, if it was previously rendered wide + this.tree.layout(treeHeight, 400); + + // const titleWidth = this.complexValueTitle.clientWidth; + const realTreeWidth = this.tree.getHTMLElement().offsetWidth; + // const contentWidth = Math.max(titleWidth, realTreeWidth); + const treeWidth = clamp(realTreeWidth, 400, 550); + this.tree.layout(treeHeight, treeWidth); this.treeContainer.style.height = `${treeHeight}px`; - this.tree.layout(treeHeight, initialLayout ? 400 : undefined); - this.editor.layoutContentWidget(this); this.scrollbar.scanDomNode(); } + beforeRender(): IDimension | null { + // beforeRender will be called each time the hover size changes, and the content widget is layed out again. + if (this.expressionToRender) { + const expression = this.expressionToRender; + this.expressionToRender = undefined; + + // Do this in beforeRender once the content widget is no longer display=none so that its elements' sizes will be measured correctly. + this.isUpdatingTree = true; + this.tree.setInput(expression).then(() => { + this.isUpdatingTree = false; + dom.scheduleAtNextAnimationFrame(() => { + // Wait for scrollWidth to update after a frame + this.layoutTree(); + this.editor.layoutContentWidget(this); + this.complexValueContainer.style.visibility = ''; + }); + }); + } + + return null; + } + afterRender(positionPreference: ContentWidgetPositionPreference | null) { if (positionPreference) { // Remember where the editor placed you to keep position stable #109226 @@ -293,6 +338,7 @@ export class DebugHoverWidget implements IContentWidget { hide(): void { + this.complexValueContainer.style.visibility = 'hidden'; if (this.showCancellationSource) { this.showCancellationSource.cancel(); this.showCancellationSource = undefined;