Path: blob/main/src/vs/editor/browser/config/elementSizeObserver.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../base/common/lifecycle.js';6import { IDimension } from '../../common/core/2d/dimension.js';7import { Emitter, Event } from '../../../base/common/event.js';8import { getWindow, scheduleAtNextAnimationFrame } from '../../../base/browser/dom.js';910export class ElementSizeObserver extends Disposable {1112private _onDidChange = this._register(new Emitter<void>());13public readonly onDidChange: Event<void> = this._onDidChange.event;1415private readonly _referenceDomElement: HTMLElement | null;16private _width: number;17private _height: number;18private _resizeObserver: ResizeObserver | null;1920constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined) {21super();22this._referenceDomElement = referenceDomElement;23this._width = -1;24this._height = -1;25this._resizeObserver = null;26this.measureReferenceDomElement(false, dimension);27}2829public override dispose(): void {30this.stopObserving();31super.dispose();32}3334public getWidth(): number {35return this._width;36}3738public getHeight(): number {39return this._height;40}4142public startObserving(): void {43if (!this._resizeObserver && this._referenceDomElement) {44// We want to react to the resize observer only once per animation frame45// The first time the resize observer fires, we will react to it immediately.46// Otherwise we will postpone to the next animation frame.47// We'll use `observeContentRect` to store the content rect we received.4849let observedDimenstion: IDimension | null = null;50const observeNow = () => {51if (observedDimenstion) {52this.observe({ width: observedDimenstion.width, height: observedDimenstion.height });53} else {54this.observe();55}56};5758let shouldObserve = false;59let alreadyObservedThisAnimationFrame = false;6061const update = () => {62if (shouldObserve && !alreadyObservedThisAnimationFrame) {63try {64shouldObserve = false;65alreadyObservedThisAnimationFrame = true;66observeNow();67} finally {68scheduleAtNextAnimationFrame(getWindow(this._referenceDomElement), () => {69alreadyObservedThisAnimationFrame = false;70update();71});72}73}74};7576this._resizeObserver = new ResizeObserver((entries) => {77if (entries && entries[0] && entries[0].contentRect) {78observedDimenstion = { width: entries[0].contentRect.width, height: entries[0].contentRect.height };79} else {80observedDimenstion = null;81}82shouldObserve = true;83update();84});85this._resizeObserver.observe(this._referenceDomElement);86}87}8889public stopObserving(): void {90if (this._resizeObserver) {91this._resizeObserver.disconnect();92this._resizeObserver = null;93}94}9596public observe(dimension?: IDimension): void {97this.measureReferenceDomElement(true, dimension);98}99100private measureReferenceDomElement(emitEvent: boolean, dimension?: IDimension): void {101let observedWidth = 0;102let observedHeight = 0;103if (dimension) {104observedWidth = dimension.width;105observedHeight = dimension.height;106} else if (this._referenceDomElement) {107observedWidth = this._referenceDomElement.clientWidth;108observedHeight = this._referenceDomElement.clientHeight;109}110observedWidth = Math.max(5, observedWidth);111observedHeight = Math.max(5, observedHeight);112if (this._width !== observedWidth || this._height !== observedHeight) {113this._width = observedWidth;114this._height = observedHeight;115if (emitEvent) {116this._onDidChange.fire();117}118}119}120}121122123