/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-timeline/src/view-base/View.js
345 строк
9 KB
Andrew Clark
[Codemod] Update copyright header to Meta (#25315)
18 окт 2022, 18:19
Не верифицирован
18 окт 2022, 18:19
9cdf8a9
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {Interaction} from './useCanvasInteraction'; import type {IntrinsicSize, Rect, Size} from './geometry'; import type {Layouter} from './layouter'; import type {ViewRefs} from './Surface'; import {Surface} from './Surface'; import { rectEqualToRect, intersectionOfRects, rectIntersectsRect, sizeIsEmpty, sizeIsValid, unionOfRects, zeroRect, } from './geometry'; import {noopLayout, viewsToLayout, collapseLayoutIntoViews} from './layouter'; /** * Base view class that can be subclassed to draw custom content or manage * subclasses. */ export class View { _backgroundColor: string | null; currentCursor: string | null = null; surface: Surface; frame: Rect; visibleArea: Rect; superview: ?View; subviews: View[] = []; /** * An injected function that lays out our subviews. * @private */ _layouter: Layouter; /** * Whether this view needs to be drawn. * * NOTE: Do not set directly! Use `setNeedsDisplay`. * * @see setNeedsDisplay * @private */ _needsDisplay: boolean = true; /** * Whether the hierarchy below this view has subviews that need display. * * NOTE: Do not set directly! Use `setSubviewsNeedDisplay`. * * @see setSubviewsNeedDisplay * @private */ _subviewsNeedDisplay: boolean = false; constructor( surface: Surface, frame: Rect, layouter: Layouter = noopLayout, visibleArea: Rect = frame, backgroundColor?: string | null = null, ) { this._backgroundColor = backgroundColor || null; this.surface = surface; this.frame = frame; this._layouter = layouter; this.visibleArea = visibleArea; } /** * Invalidates view's contents. * * Downward propagating; once called, all subviews of this view should also * be invalidated. */ setNeedsDisplay() { this._needsDisplay = true; if (this.superview) { this.superview._setSubviewsNeedDisplay(); } this.subviews.forEach(subview => subview.setNeedsDisplay()); } /** * Informs superview that it has subviews that need to be drawn. * * Upward propagating; once called, all superviews of this view should also * have `subviewsNeedDisplay` = true. * * @private */ _setSubviewsNeedDisplay() { this._subviewsNeedDisplay = true; if (this.superview) { this.superview._setSubviewsNeedDisplay(); } } setFrame(newFrame: Rect) { if (!rectEqualToRect(this.frame, newFrame)) { this.frame = newFrame; if (sizeIsValid(newFrame.size)) { this.frame = newFrame; } else { this.frame = zeroRect; } this.setNeedsDisplay(); } } setVisibleArea(newVisibleArea: Rect) { if (!rectEqualToRect(this.visibleArea, newVisibleArea)) { if (sizeIsValid(newVisibleArea.size)) { this.visibleArea = newVisibleArea; } else { this.visibleArea = zeroRect; } this.setNeedsDisplay(); } } /** * A size that can be used as a hint by layout functions. * * Implementations should typically return the intrinsic content size or a * size that fits all the view's content. * * The default implementation returns a size that fits all the view's * subviews. * * Can be overridden by subclasses. */ desiredSize(): Size | IntrinsicSize { if (this._needsDisplay) { this.layoutSubviews(); } const frames = this.subviews.map(subview => subview.frame); return unionOfRects(...frames).size; } /** * Appends `view` to the list of this view's `subviews`. */ addSubview(view: View) { if (this.subviews.includes(view)) { return; } this.subviews.push(view); view.superview = this; } /** * Breaks the subview-superview relationship between `view` and this view, if * `view` is a subview of this view. */ removeSubview(view: View) { const subviewIndex = this.subviews.indexOf(view); if (subviewIndex === -1) { return; } view.superview = undefined; this.subviews.splice(subviewIndex, 1); } /** * Removes all subviews from this view. */ removeAllSubviews() { this.subviews.forEach(subview => (subview.superview = undefined)); this.subviews = []; } /** * Executes the display flow if this view needs to be drawn. * * 1. Lays out subviews with `layoutSubviews`. * 2. Draws content with `draw`. */ displayIfNeeded(context: CanvasRenderingContext2D, viewRefs: ViewRefs) { if ( (this._needsDisplay || this._subviewsNeedDisplay) && rectIntersectsRect(this.frame, this.visibleArea) && !sizeIsEmpty(this.visibleArea.size) ) { this.layoutSubviews(); if (this._needsDisplay) { this._needsDisplay = false; } if (this._subviewsNeedDisplay) this._subviewsNeedDisplay = false; // Clip anything drawn by the view to prevent it from overflowing its visible area. const visibleArea = this.visibleArea; const region = new Path2D(); region.rect( visibleArea.origin.x, visibleArea.origin.y, visibleArea.size.width, visibleArea.size.height, ); context.save(); context.clip(region); context.beginPath(); this.draw(context, viewRefs); // Stop clipping context.restore(); } } /** * Layout self and subviews. * * Implementations should call `setNeedsDisplay` if a draw is required. * * The default implementation uses `this.layouter` to lay out subviews. * * Can be overwritten by subclasses that wish to manually manage their * subviews' layout. * * NOTE: Do not call directly! Use `displayIfNeeded`. * * @see displayIfNeeded */ layoutSubviews() { const {frame, _layouter, subviews, visibleArea} = this; const existingLayout = viewsToLayout(subviews); const newLayout = _layouter(existingLayout, frame); collapseLayoutIntoViews(newLayout); subviews.forEach((subview, subviewIndex) => { if (rectIntersectsRect(visibleArea, subview.frame)) { subview.setVisibleArea(intersectionOfRects(visibleArea, subview.frame)); } else { subview.setVisibleArea(zeroRect); } }); } /** * Draw the contents of this view in the given canvas `context`. * * Defaults to drawing this view's `subviews`. * * To be overwritten by subclasses that wish to draw custom content. * * NOTE: Do not call directly! Use `displayIfNeeded`. * * @see displayIfNeeded */ draw(context: CanvasRenderingContext2D, viewRefs: ViewRefs) { const {subviews, visibleArea} = this; subviews.forEach(subview => { if (rectIntersectsRect(visibleArea, subview.visibleArea)) { subview.displayIfNeeded(context, viewRefs); } }); const backgroundColor = this._backgroundColor; if (backgroundColor !== null) { const desiredSize = this.desiredSize(); if (visibleArea.size.height > desiredSize.height) { context.fillStyle = backgroundColor; context.fillRect( visibleArea.origin.x, visibleArea.origin.y + desiredSize.height, visibleArea.size.width, visibleArea.size.height - desiredSize.height, ); } } } /** * Handle an `interaction`. * * To be overwritten by subclasses that wish to handle interactions. * * NOTE: Do not call directly! Use `handleInteractionAndPropagateToSubviews` */ handleInteraction(interaction: Interaction, viewRefs: ViewRefs): ?boolean {} /** * Handle an `interaction` and propagates it to all of this view's * `subviews`. * * NOTE: Should not be overridden! Subclasses should override * `handleInteraction` instead. * * @see handleInteraction * @protected */ handleInteractionAndPropagateToSubviews( interaction: Interaction, viewRefs: ViewRefs, ): boolean { const {subviews, visibleArea} = this; if (visibleArea.size.height === 0) { return false; } // Pass the interaction to subviews first, // so they have the opportunity to claim it before it bubbles. // // Views are painted first to last, // so they should process interactions last to first, // so views in front (on top) can claim the interaction first. for (let i = subviews.length - 1; i >= 0; i--) { const subview = subviews[i]; if (rectIntersectsRect(visibleArea, subview.visibleArea)) { const didSubviewHandle = subview.handleInteractionAndPropagateToSubviews( interaction, viewRefs, ) === true; if (didSubviewHandle) { return true; } } } const didSelfHandle = this.handleInteraction(interaction, viewRefs) === true; if (didSelfHandle) { return true; } return false; } }