mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-07 15:26:49 +01:00
Merge pull request #198730 from GenericTSDeveloper/suggestVSCode
Fixed issue Misalignment of suggestion details widget (https://github.com/microsoft/monaco-editor/issues/3373)
This commit is contained in:
@@ -219,6 +219,13 @@ class ElementPath {
|
||||
&& path[1] === PartFingerprint.OverlayWidgets
|
||||
);
|
||||
}
|
||||
|
||||
public static isChildOfOverflowingOverlayWidgets(path: Uint8Array): boolean {
|
||||
return (
|
||||
path.length >= 1
|
||||
&& path[0] === PartFingerprint.OverflowingOverlayWidgets
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class HitTestContext {
|
||||
@@ -490,7 +497,7 @@ export class MouseTargetFactory {
|
||||
}
|
||||
|
||||
// Is it an overlay widget?
|
||||
if (ElementPath.isChildOfOverlayWidgets(path)) {
|
||||
if (ElementPath.isChildOfOverlayWidgets(path) || ElementPath.isChildOfOverflowingOverlayWidgets(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -545,7 +552,7 @@ export class MouseTargetFactory {
|
||||
|
||||
let result: IMouseTarget | null = null;
|
||||
|
||||
if (!ElementPath.isChildOfOverflowGuard(request.targetPath) && !ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) {
|
||||
if (!ElementPath.isChildOfOverflowGuard(request.targetPath) && !ElementPath.isChildOfOverflowingContentWidgets(request.targetPath) && !ElementPath.isChildOfOverflowingOverlayWidgets(request.targetPath)) {
|
||||
// We only render dom nodes inside the overflow guard or in the overflowing content widgets
|
||||
result = result || request.fulfillUnknown();
|
||||
}
|
||||
@@ -579,7 +586,7 @@ export class MouseTargetFactory {
|
||||
|
||||
private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null {
|
||||
// Is it an overlay widget?
|
||||
if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) {
|
||||
if (ElementPath.isChildOfOverlayWidgets(request.targetPath) || ElementPath.isChildOfOverflowingOverlayWidgets(request.targetPath)) {
|
||||
const widgetId = ctx.findAttribute(request.target, 'widgetId');
|
||||
if (widgetId) {
|
||||
return request.fulfillOverlayWidget(widgetId);
|
||||
|
||||
@@ -225,6 +225,22 @@ export const enum OverlayWidgetPositionPreference {
|
||||
*/
|
||||
TOP_CENTER
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents editor-relative coordinates of an overlay widget.
|
||||
*/
|
||||
export interface IOverlayWidgetPositionCoordinates {
|
||||
/**
|
||||
* The top position for the overlay widget, relative to the editor.
|
||||
*/
|
||||
top: number;
|
||||
/**
|
||||
* The left position for the overlay widget, relative to the editor.
|
||||
*/
|
||||
left: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position for rendering overlay widgets.
|
||||
*/
|
||||
@@ -232,12 +248,16 @@ export interface IOverlayWidgetPosition {
|
||||
/**
|
||||
* The position preference for the overlay widget.
|
||||
*/
|
||||
preference: OverlayWidgetPositionPreference | null;
|
||||
preference: OverlayWidgetPositionPreference | IOverlayWidgetPositionCoordinates | null;
|
||||
}
|
||||
/**
|
||||
* An overlay widgets renders on top of the text.
|
||||
*/
|
||||
export interface IOverlayWidget {
|
||||
/**
|
||||
* Render this overlay widget in a location where it could overflow the editor's view dom node.
|
||||
*/
|
||||
allowEditorOverflow?: boolean;
|
||||
/**
|
||||
* Get a unique identifier of the overlay widget.
|
||||
*/
|
||||
|
||||
@@ -194,7 +194,7 @@ export class View extends ViewEventHandler {
|
||||
this._viewParts.push(this._viewCursors);
|
||||
|
||||
// Overlay widgets
|
||||
this._overlayWidgets = new ViewOverlayWidgets(this._context);
|
||||
this._overlayWidgets = new ViewOverlayWidgets(this._context, this.domNode);
|
||||
this._viewParts.push(this._overlayWidgets);
|
||||
|
||||
const rulers = new Rulers(this._context);
|
||||
@@ -231,8 +231,10 @@ export class View extends ViewEventHandler {
|
||||
|
||||
if (overflowWidgetsDomNode) {
|
||||
overflowWidgetsDomNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode.domNode);
|
||||
overflowWidgetsDomNode.appendChild(this._overlayWidgets.overflowingOverlayWidgetsDomNode.domNode);
|
||||
} else {
|
||||
this.domNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode);
|
||||
this.domNode.appendChild(this._overlayWidgets.overflowingOverlayWidgetsDomNode);
|
||||
}
|
||||
|
||||
this._applyLayout();
|
||||
|
||||
@@ -33,6 +33,7 @@ export const enum PartFingerprint {
|
||||
OverflowingContentWidgets,
|
||||
OverflowGuard,
|
||||
OverlayWidgets,
|
||||
OverflowingOverlayWidgets,
|
||||
ScrollableElement,
|
||||
TextArea,
|
||||
ViewLines,
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
|
||||
import 'vs/css!./overlayWidgets';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { IOverlayWidget, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { IOverlayWidget, IOverlayWidgetPositionCoordinates, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/browser/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/viewModel/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/viewEvents';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
|
||||
interface IWidgetData {
|
||||
widget: IOverlayWidget;
|
||||
preference: OverlayWidgetPositionPreference | null;
|
||||
preference: OverlayWidgetPositionPreference | IOverlayWidgetPositionCoordinates | null;
|
||||
domNode: FastDomNode<HTMLElement>;
|
||||
}
|
||||
|
||||
@@ -25,17 +26,20 @@ interface IWidgetMap {
|
||||
|
||||
export class ViewOverlayWidgets extends ViewPart {
|
||||
|
||||
private readonly _viewDomNode: FastDomNode<HTMLElement>;
|
||||
private _widgets: IWidgetMap;
|
||||
private _viewDomNodeRect: dom.IDomNodePagePosition;
|
||||
private readonly _domNode: FastDomNode<HTMLElement>;
|
||||
|
||||
public readonly overflowingOverlayWidgetsDomNode: FastDomNode<HTMLElement>;
|
||||
private _verticalScrollbarWidth: number;
|
||||
private _minimapWidth: number;
|
||||
private _horizontalScrollbarHeight: number;
|
||||
private _editorHeight: number;
|
||||
private _editorWidth: number;
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
constructor(context: ViewContext, viewDomNode: FastDomNode<HTMLElement>) {
|
||||
super(context);
|
||||
this._viewDomNode = viewDomNode;
|
||||
|
||||
const options = this._context.configuration.options;
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
@@ -46,10 +50,15 @@ export class ViewOverlayWidgets extends ViewPart {
|
||||
this._horizontalScrollbarHeight = layoutInfo.horizontalScrollbarHeight;
|
||||
this._editorHeight = layoutInfo.height;
|
||||
this._editorWidth = layoutInfo.width;
|
||||
this._viewDomNodeRect = { top: 0, left: 0, width: 0, height: 0 };
|
||||
|
||||
this._domNode = createFastDomNode(document.createElement('div'));
|
||||
PartFingerprints.write(this._domNode, PartFingerprint.OverlayWidgets);
|
||||
this._domNode.setClassName('overlayWidgets');
|
||||
|
||||
this.overflowingOverlayWidgetsDomNode = createFastDomNode(document.createElement('div'));
|
||||
PartFingerprints.write(this.overflowingOverlayWidgetsDomNode, PartFingerprint.OverflowingOverlayWidgets);
|
||||
this.overflowingOverlayWidgetsDomNode.setClassName('overflowingOverlayWidgets');
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
@@ -89,13 +98,18 @@ export class ViewOverlayWidgets extends ViewPart {
|
||||
// This is sync because a widget wants to be in the dom
|
||||
domNode.setPosition('absolute');
|
||||
domNode.setAttribute('widgetId', widget.getId());
|
||||
this._domNode.appendChild(domNode);
|
||||
|
||||
if (widget.allowEditorOverflow) {
|
||||
this.overflowingOverlayWidgetsDomNode.appendChild(domNode);
|
||||
} else {
|
||||
this._domNode.appendChild(domNode);
|
||||
}
|
||||
|
||||
this.setShouldRender();
|
||||
this._updateMaxMinWidth();
|
||||
}
|
||||
|
||||
public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference | null): boolean {
|
||||
public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference | IOverlayWidgetPositionCoordinates | null): boolean {
|
||||
const widgetData = this._widgets[widget.getId()];
|
||||
if (widgetData.preference === preference) {
|
||||
this._updateMaxMinWidth();
|
||||
@@ -116,7 +130,7 @@ export class ViewOverlayWidgets extends ViewPart {
|
||||
const domNode = widgetData.domNode.domNode;
|
||||
delete this._widgets[widgetId];
|
||||
|
||||
domNode.parentNode!.removeChild(domNode);
|
||||
domNode.remove();
|
||||
this.setShouldRender();
|
||||
this._updateMaxMinWidth();
|
||||
}
|
||||
@@ -154,11 +168,26 @@ export class ViewOverlayWidgets extends ViewPart {
|
||||
} else if (widgetData.preference === OverlayWidgetPositionPreference.TOP_CENTER) {
|
||||
domNode.setTop(0);
|
||||
domNode.domNode.style.right = '50%';
|
||||
} else {
|
||||
const { top, left } = widgetData.preference;
|
||||
const fixedOverflowWidgets = this._context.configuration.options.get(EditorOption.fixedOverflowWidgets);
|
||||
if (fixedOverflowWidgets && widgetData.widget.allowEditorOverflow) {
|
||||
// top, left are computed relative to the editor and we need them relative to the page
|
||||
const editorBoundingBox = this._viewDomNodeRect!;
|
||||
domNode.setTop(top + editorBoundingBox.top);
|
||||
domNode.setLeft(left + editorBoundingBox.left);
|
||||
domNode.setPosition('fixed');
|
||||
|
||||
} else {
|
||||
domNode.setTop(top);
|
||||
domNode.setLeft(left);
|
||||
domNode.setPosition('absolute');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
// Nothing to read
|
||||
this._viewDomNodeRect = dom.getDomNodePagePosition(this._viewDomNode.domNode);
|
||||
}
|
||||
|
||||
public render(ctx: RestrictedRenderingContext): void {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer';
|
||||
import { ICodeEditor, IOverlayWidget } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ResizableHTMLElement } from 'vs/base/browser/ui/resizable/resizable';
|
||||
import * as nls from 'vs/nls';
|
||||
@@ -263,6 +263,8 @@ interface TopLeftPosition {
|
||||
|
||||
export class SuggestDetailsOverlay implements IOverlayWidget {
|
||||
|
||||
readonly allowEditorOverflow = true;
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _resizable: ResizableHTMLElement;
|
||||
|
||||
@@ -341,14 +343,13 @@ export class SuggestDetailsOverlay implements IOverlayWidget {
|
||||
return this._resizable.domNode;
|
||||
}
|
||||
|
||||
getPosition(): null {
|
||||
return null;
|
||||
getPosition(): IOverlayWidgetPosition | null {
|
||||
return this._topLeft ? { preference: this._topLeft } : null;
|
||||
}
|
||||
|
||||
show(): void {
|
||||
if (!this._added) {
|
||||
this._editor.addOverlayWidget(this);
|
||||
this.getDomNode().style.position = 'fixed';
|
||||
this._added = true;
|
||||
}
|
||||
}
|
||||
@@ -442,8 +443,18 @@ export class SuggestDetailsOverlay implements IOverlayWidget {
|
||||
}
|
||||
}
|
||||
|
||||
this._applyTopLeft({ left: placement.left, top: alignAtTop ? placement.top : bottom - height });
|
||||
this.getDomNode().style.position = 'fixed';
|
||||
let { top, left } = placement;
|
||||
if (!alignAtTop) {
|
||||
top = bottom - height;
|
||||
}
|
||||
const editorDomNode = this._editor.getDomNode();
|
||||
if (editorDomNode) {
|
||||
// get bounding rectangle of the suggest widget relative to the editor
|
||||
const editorBoundingBox = editorDomNode.getBoundingClientRect();
|
||||
top -= editorBoundingBox.top;
|
||||
left -= editorBoundingBox.left;
|
||||
}
|
||||
this._applyTopLeft({ left, top });
|
||||
|
||||
this._resizable.enableSashes(!alignAtTop, placement === eastPlacement, alignAtTop, placement !== eastPlacement);
|
||||
|
||||
@@ -455,7 +466,6 @@ export class SuggestDetailsOverlay implements IOverlayWidget {
|
||||
|
||||
private _applyTopLeft(topLeft: TopLeftPosition): void {
|
||||
this._topLeft = topLeft;
|
||||
this.getDomNode().style.left = `${this._topLeft.left}px`;
|
||||
this.getDomNode().style.top = `${this._topLeft.top}px`;
|
||||
this._editor.layoutOverlayWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+19
-1
@@ -5263,6 +5263,20 @@ declare namespace monaco.editor {
|
||||
TOP_CENTER = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents editor-relative coordinates of an overlay widget.
|
||||
*/
|
||||
export interface IOverlayWidgetPositionCoordinates {
|
||||
/**
|
||||
* The top position for the overlay widget, relative to the editor.
|
||||
*/
|
||||
top: number;
|
||||
/**
|
||||
* The left position for the overlay widget, relative to the editor.
|
||||
*/
|
||||
left: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position for rendering overlay widgets.
|
||||
*/
|
||||
@@ -5270,13 +5284,17 @@ declare namespace monaco.editor {
|
||||
/**
|
||||
* The position preference for the overlay widget.
|
||||
*/
|
||||
preference: OverlayWidgetPositionPreference | null;
|
||||
preference: OverlayWidgetPositionPreference | IOverlayWidgetPositionCoordinates | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An overlay widgets renders on top of the text.
|
||||
*/
|
||||
export interface IOverlayWidget {
|
||||
/**
|
||||
* Render this overlay widget in a location where it could overflow the editor's view dom node.
|
||||
*/
|
||||
allowEditorOverflow?: boolean;
|
||||
/**
|
||||
* Get a unique identifier of the overlay widget.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user