Skip to content

Commit 7acbd9d

Browse files
authored
Try passing offset to editors on layout (microsoft#164287)
* Try passing offset to editors on layout When a notebook editor is created, it currently has to force a relayout to determine where the overlay webview should be positioned. We do this with a call to `getBoundingClientRect` However the grid view should already have information about where the editor is positioned. If we can use this, then we can skip the expensive call to `getBoundingClientRect` entirely This change attempts to pass the `top` and `left` offsets to the `EditorGroupView` and eventually down into `NotebookEditorWidget` The PR does not work properly however as the offset that the `EditorGroupView` gets is wrong. I've added some todo comments about where this seems to be happening * Remove todo and update layout over
1 parent 05ec316 commit 7acbd9d

File tree

6 files changed

+64
-41
lines changed

6 files changed

+64
-41
lines changed

src/vs/base/browser/dom.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,12 @@ export class Dimension implements IDimension {
417417
}
418418
}
419419

420-
export function getTopLeftOffset(element: HTMLElement): { left: number; top: number } {
420+
export interface IDomPosition {
421+
readonly left: number;
422+
readonly top: number;
423+
}
424+
425+
export function getTopLeftOffset(element: HTMLElement): IDomPosition {
421426
// Adapted from WinJS.Utilities.getPosition
422427
// and added borders to the mix
423428

src/vs/workbench/browser/composite.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IComposite, ICompositeControl } from 'vs/workbench/common/composite';
1010
import { Event, Emitter } from 'vs/base/common/event';
1111
import { IThemeService } from 'vs/platform/theme/common/themeService';
1212
import { IConstructorSignature, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
13-
import { trackFocus, Dimension } from 'vs/base/browser/dom';
13+
import { trackFocus, Dimension, IDomPosition } from 'vs/base/browser/dom';
1414
import { IStorageService } from 'vs/platform/storage/common/storage';
1515
import { Disposable } from 'vs/base/common/lifecycle';
1616
import { assertIsDefined } from 'vs/base/common/types';
@@ -153,7 +153,7 @@ export abstract class Composite extends Component implements IComposite {
153153
/**
154154
* Layout the contents of this composite using the provided dimensions.
155155
*/
156-
abstract layout(dimension: Dimension): void;
156+
abstract layout(dimension: Dimension, position?: IDomPosition): void;
157157

158158
/**
159159
* Update the styles of the contents of this composite.

src/vs/workbench/browser/parts/editor/editorGroupView.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput';
1111
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
1212
import { Emitter, Relay } from 'vs/base/common/event';
1313
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
14-
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, asCSSUrl } from 'vs/base/browser/dom';
14+
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, asCSSUrl, IDomNodePagePosition } from 'vs/base/browser/dom';
1515
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
1616
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1717
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
@@ -111,7 +111,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
111111
private readonly model: EditorGroupModel;
112112

113113
private active: boolean | undefined;
114-
private dimension: Dimension | undefined;
114+
private lastLayout: IDomNodePagePosition | undefined;
115115

116116
private readonly scopedInstantiationService: IInstantiationService;
117117

@@ -1891,25 +1891,25 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
18911891
private _onDidChange = this._register(new Relay<{ width: number; height: number } | undefined>());
18921892
readonly onDidChange = this._onDidChange.event;
18931893

1894-
layout(width: number, height: number): void {
1895-
this.dimension = new Dimension(width, height);
1894+
layout(width: number, height: number, top: number, left: number): void {
1895+
this.lastLayout = { width, height, top, left };
18961896

18971897
// Layout the title area first to receive the size it occupies
18981898
const titleAreaSize = this.titleAreaControl.layout({
1899-
container: this.dimension,
1899+
container: new Dimension(width, height),
19001900
available: new Dimension(width, height - this.editorPane.minimumHeight)
19011901
});
19021902

19031903
// Pass the container width and remaining height to the editor layout
19041904
const editorHeight = Math.max(0, height - titleAreaSize.height);
19051905
this.editorContainer.style.height = `${editorHeight}px`;
1906-
this.editorPane.layout(new Dimension(width, editorHeight));
1906+
this.editorPane.layout({ width, height: editorHeight, top: top + titleAreaSize.height, left });
19071907
}
19081908

19091909
relayout(): void {
1910-
if (this.dimension) {
1911-
const { width, height } = this.dimension;
1912-
this.layout(width, height);
1910+
if (this.lastLayout) {
1911+
const { width, height, top, left } = this.lastLayout;
1912+
this.layout(width, height, top, left);
19131913
}
19141914
}
19151915

src/vs/workbench/browser/parts/editor/editorPanes.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Severity from 'vs/base/common/severity';
1010
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
1111
import { EditorExtensions, EditorInputCapabilities, IEditorOpenContext, IVisibleEditorPane } from 'vs/workbench/common/editor';
1212
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
13-
import { Dimension, show, hide } from 'vs/base/browser/dom';
13+
import { Dimension, show, hide, IDomNodePagePosition } from 'vs/base/browser/dom';
1414
import { Registry } from 'vs/platform/registry/common/platform';
1515
import { IEditorPaneRegistry, IEditorPaneDescriptor } from 'vs/workbench/browser/editor';
1616
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
@@ -84,7 +84,7 @@ export class EditorPanes extends Disposable {
8484
private readonly editorPanes: EditorPane[] = [];
8585

8686
private readonly activeEditorPaneDisposables = this._register(new DisposableStore());
87-
private dimension: Dimension | undefined;
87+
private pagePosition: IDomNodePagePosition | undefined;
8888
private readonly editorOperation = this._register(new LongRunningOperation(this.editorProgressService));
8989
private readonly editorPanesRegistry = Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane);
9090

@@ -269,8 +269,8 @@ export class EditorPanes extends Disposable {
269269
editorPane.setVisible(true, this.groupView);
270270

271271
// Layout
272-
if (this.dimension) {
273-
editorPane.layout(this.dimension);
272+
if (this.pagePosition) {
273+
editorPane.layout(new Dimension(this.pagePosition.width, this.pagePosition.height), { top: this.pagePosition.top, left: this.pagePosition.left });
274274
}
275275

276276
return editorPane;
@@ -397,9 +397,9 @@ export class EditorPanes extends Disposable {
397397
this._activeEditorPane?.setVisible(visible, this.groupView);
398398
}
399399

400-
layout(dimension: Dimension): void {
401-
this.dimension = dimension;
400+
layout(pagePosition: IDomNodePagePosition): void {
401+
this.pagePosition = pagePosition;
402402

403-
this._activeEditorPane?.layout(dimension);
403+
this._activeEditorPane?.layout(new Dimension(pagePosition.width, pagePosition.height), pagePosition);
404404
}
405405
}

src/vs/workbench/contrib/notebook/browser/notebookEditor.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
4848
private readonly _widgetDisposableStore: DisposableStore = this._register(new DisposableStore());
4949
private _widget: IBorrowValue<NotebookEditorWidget> = { value: undefined };
5050
private _rootElement!: HTMLElement;
51-
private _dimension?: DOM.Dimension;
51+
private _pagePosition?: { readonly dimension: DOM.Dimension; readonly position: DOM.IDomPosition };
5252

5353
private readonly _inputListener = this._register(new MutableDisposable());
5454

@@ -180,7 +180,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
180180
// we need to hide it before getting a new widget
181181
this._widget.value?.onWillHide();
182182

183-
this._widget = <IBorrowValue<NotebookEditorWidget>>this._instantiationService.invokeFunction(this._notebookWidgetService.retrieveWidget, group, input, undefined, this._dimension);
183+
this._widget = <IBorrowValue<NotebookEditorWidget>>this._instantiationService.invokeFunction(this._notebookWidgetService.retrieveWidget, group, input, undefined, this._pagePosition?.dimension);
184184

185185
if (this._rootElement && this._widget.value!.getDomNode()) {
186186
this._rootElement.setAttribute('aria-flowto', this._widget.value!.getDomNode().id || '');
@@ -190,8 +190,8 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
190190
this._widgetDisposableStore.add(this._widget.value!.onDidChangeModel(() => this._onDidChangeModel.fire()));
191191
this._widgetDisposableStore.add(this._widget.value!.onDidChangeActiveCell(() => this._onDidChangeSelection.fire({ reason: EditorPaneSelectionChangeReason.USER })));
192192

193-
if (this._dimension) {
194-
this._widget.value!.layout(this._dimension, this._rootElement);
193+
if (this._pagePosition) {
194+
this._widget.value!.layout(this._pagePosition.dimension, this._rootElement, this._pagePosition.position);
195195
}
196196

197197
// only now `setInput` and yield/await. this is AFTER the actual widget is ready. This is very important
@@ -395,10 +395,10 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
395395
return;
396396
}
397397

398-
layout(dimension: DOM.Dimension): void {
398+
layout(dimension: DOM.Dimension, position: DOM.IDomPosition): void {
399399
this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600);
400400
this._rootElement.classList.toggle('narrow-width', dimension.width < 600);
401-
this._dimension = dimension;
401+
this._pagePosition = { dimension, position };
402402

403403
if (!this._widget.value || !(this._input instanceof NotebookEditorInput)) {
404404
return;
@@ -414,7 +414,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti
414414
return;
415415
}
416416

417-
this._widget.value.layout(this._dimension, this._rootElement);
417+
this._widget.value.layout(dimension, this._rootElement, position);
418418
}
419419

420420
//#endregion

src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import { NotebookPerfMarks } from 'vs/workbench/contrib/notebook/common/notebook
8383
import { BaseCellEditorOptions } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEditorOptions';
8484
import { ILogService } from 'vs/platform/log/common/log';
8585
import { FloatingClickMenu } from 'vs/workbench/browser/codeeditor';
86+
import { IDimension } from 'vs/editor/common/core/dimension';
8687

8788
const $ = DOM.$;
8889

@@ -323,12 +324,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
323324
}
324325
}));
325326

326-
this._register(editorGroupsService.onDidScroll(() => {
327+
this._register(editorGroupsService.onDidScroll(e => {
327328
if (!this._shadowElement || !this._isVisible) {
328329
return;
329330
}
330331

331-
this.updateShadowElement(this._shadowElement);
332+
this.updateShadowElement(this._shadowElement, this._dimension);
332333
this.layoutContainerOverShadowElement(this._dimension);
333334
}));
334335

@@ -1691,7 +1692,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
16911692
return this._scrollBeyondLastLine && !this.isEmbedded;
16921693
}
16931694

1694-
layout(dimension: DOM.Dimension, shadowElement?: HTMLElement): void {
1695+
layout(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition): void {
16951696
if (!shadowElement && this._shadowElementViewInfo === null) {
16961697
this._dimension = dimension;
16971698
return;
@@ -1703,7 +1704,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
17031704
}
17041705

17051706
if (shadowElement) {
1706-
this.updateShadowElement(shadowElement);
1707+
this.updateShadowElement(shadowElement, dimension, position);
17071708
}
17081709

17091710
if (this._shadowElementViewInfo && this._shadowElementViewInfo.width <= 0 && this._shadowElementViewInfo.height <= 0) {
@@ -1732,7 +1733,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
17321733
this._overlayContainer.style.position = 'absolute';
17331734
this._overlayContainer.style.overflow = 'hidden';
17341735

1735-
this.layoutContainerOverShadowElement(dimension);
1736+
this.layoutContainerOverShadowElement(dimension, position);
17361737

17371738
if (this._webviewTransparentCover) {
17381739
this._webviewTransparentCover.style.height = `${dimension.height}px`;
@@ -1745,19 +1746,36 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
17451746
this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
17461747
}
17471748

1748-
private updateShadowElement(shadowElement: HTMLElement) {
1749-
const containerRect = shadowElement.getBoundingClientRect();
1750-
1749+
private updateShadowElement(shadowElement: HTMLElement, dimension?: IDimension, position?: DOM.IDomPosition) {
17511750
this._shadowElement = shadowElement;
1752-
this._shadowElementViewInfo = {
1753-
height: containerRect.height,
1754-
width: containerRect.width,
1755-
top: containerRect.top,
1756-
left: containerRect.left
1757-
};
1751+
if (dimension && position) {
1752+
this._shadowElementViewInfo = {
1753+
height: dimension.height,
1754+
width: dimension.width,
1755+
top: position.top,
1756+
left: position.left,
1757+
};
1758+
} else {
1759+
// We have to recompute position and size ourselves (which is slow)
1760+
const containerRect = shadowElement.getBoundingClientRect();
1761+
this._shadowElementViewInfo = {
1762+
height: containerRect.height,
1763+
width: containerRect.width,
1764+
top: containerRect.top,
1765+
left: containerRect.left
1766+
};
1767+
}
17581768
}
17591769

1760-
private layoutContainerOverShadowElement(dimension?: DOM.Dimension | null): void {
1770+
private layoutContainerOverShadowElement(dimension?: DOM.Dimension, position?: DOM.IDomPosition): void {
1771+
if (dimension && position) {
1772+
this._overlayContainer.style.top = `${position.top}px`;
1773+
this._overlayContainer.style.left = `${position.left}px`;
1774+
this._overlayContainer.style.width = `${dimension.width}px`;
1775+
this._overlayContainer.style.height = `${dimension.height}px`;
1776+
return;
1777+
}
1778+
17611779
if (!this._shadowElementViewInfo) {
17621780
return;
17631781
}

0 commit comments

Comments
 (0)