UNPKG

@lightningjs/renderer

Version:
331 lines 11.3 kB
/* * If not stated otherwise in this file or this component's LICENSE file the * following copyright and licenses apply: * * Copyright 2023 Comcast Cable Communications Management, LLC. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { CoreNode, UpdateType } from './CoreNode.js'; import { assertTruthy } from '../utils.js'; import { Matrix3d } from './lib/Matrix3d.js'; /** * An CoreNode in the Renderer scene graph that renders text. * * @remarks * A Text Node is the second graphical building block of the Renderer scene * graph. It renders text using a specific text renderer that is automatically * chosen based on the font requested and what type of fonts are installed * into an app. * * The text renderer can be overridden by setting the `textRendererOverride` * * The `texture` and `shader` properties are managed by loaded text renderer and * should not be set directly. * * For non-text rendering, see {@link CoreNode}. */ export class CoreTextNode extends CoreNode { textRenderer; trState; _textRendererOverride = null; constructor(stage, props, textRenderer) { super(stage, props); this._textRendererOverride = props.textRendererOverride; this.textRenderer = textRenderer; const textRendererState = this.createState({ x: 0, y: 0, width: props.width, height: props.height, textAlign: props.textAlign, color: props.color, zIndex: props.zIndex, contain: props.contain, scrollable: props.scrollable, scrollY: props.scrollY, offsetY: props.offsetY, letterSpacing: props.letterSpacing, debug: props.debug, fontFamily: props.fontFamily, fontSize: props.fontSize, fontStretch: props.fontStretch, fontStyle: props.fontStyle, fontWeight: props.fontWeight, text: props.text, lineHeight: props.lineHeight, maxLines: props.maxLines, textBaseline: props.textBaseline, verticalAlign: props.verticalAlign, overflowSuffix: props.overflowSuffix, }); this.trState = textRendererState; } onTextLoaded = () => { const { contain } = this; const setWidth = this.trState.props.width; const setHeight = this.trState.props.height; const calcWidth = this.trState.textW || 0; const calcHeight = this.trState.textH || 0; if (contain === 'both') { this.props.width = setWidth; this.props.height = setHeight; } else if (contain === 'width') { this.props.width = setWidth; this.props.height = calcHeight; } else if (contain === 'none') { this.props.width = calcWidth; this.props.height = calcHeight; } this.updateLocalTransform(); // Incase the RAF loop has been stopped already before text was loaded, // we request a render so it can be drawn. this.stage.requestRender(); this.emit('loaded', { type: 'text', dimensions: { width: this.trState.textW || 0, height: this.trState.textH || 0, }, }); }; onTextFailed = (target, error) => { this.emit('failed', { type: 'text', error, }); }; get width() { return this.props.width; } set width(value) { this.props.width = value; this.textRenderer.set.width(this.trState, value); // If not containing, we must update the local transform to account for the // new width if (this.contain === 'none') { this.setUpdateType(UpdateType.Local); } } get height() { return this.props.height; } set height(value) { this.props.height = value; this.textRenderer.set.height(this.trState, value); // If not containing in the horizontal direction, we must update the local // transform to account for the new height if (this.contain !== 'both') { this.setUpdateType(UpdateType.Local); } } get color() { return this.trState.props.color; } set color(value) { this.textRenderer.set.color(this.trState, value); } get text() { return this.trState.props.text; } set text(value) { this.textRenderer.set.text(this.trState, value); } get textRendererOverride() { return this._textRendererOverride; } set textRendererOverride(value) { this._textRendererOverride = value; this.textRenderer.destroyState(this.trState); const textRenderer = this.stage.resolveTextRenderer(this.trState.props, this._textRendererOverride); if (!textRenderer) { console.warn('Text Renderer not found for font', this.trState.props.fontFamily); return; } this.textRenderer = textRenderer; this.trState = this.createState(this.trState.props); } get fontSize() { return this.trState.props.fontSize; } set fontSize(value) { this.textRenderer.set.fontSize(this.trState, value); } get fontFamily() { return this.trState.props.fontFamily; } set fontFamily(value) { this.textRenderer.set.fontFamily(this.trState, value); } get fontStretch() { return this.trState.props.fontStretch; } set fontStretch(value) { this.textRenderer.set.fontStretch(this.trState, value); } get fontStyle() { return this.trState.props.fontStyle; } set fontStyle(value) { this.textRenderer.set.fontStyle(this.trState, value); } get fontWeight() { return this.trState.props.fontWeight; } set fontWeight(value) { this.textRenderer.set.fontWeight(this.trState, value); } get textAlign() { return this.trState.props.textAlign; } set textAlign(value) { this.textRenderer.set.textAlign(this.trState, value); } get contain() { return this.trState.props.contain; } set contain(value) { this.textRenderer.set.contain(this.trState, value); } get scrollable() { return this.trState.props.scrollable; } set scrollable(value) { this.textRenderer.set.scrollable(this.trState, value); } get scrollY() { return this.trState.props.scrollY; } set scrollY(value) { this.textRenderer.set.scrollY(this.trState, value); } get offsetY() { return this.trState.props.offsetY; } set offsetY(value) { this.textRenderer.set.offsetY(this.trState, value); } get letterSpacing() { return this.trState.props.letterSpacing; } set letterSpacing(value) { this.textRenderer.set.letterSpacing(this.trState, value); } get lineHeight() { return this.trState.props.lineHeight; } set lineHeight(value) { this.textRenderer.set.lineHeight(this.trState, value); } get maxLines() { return this.trState.props.maxLines; } set maxLines(value) { this.textRenderer.set.maxLines(this.trState, value); } get textBaseline() { return this.trState.props.textBaseline; } set textBaseline(value) { this.textRenderer.set.textBaseline(this.trState, value); } get verticalAlign() { return this.trState.props.verticalAlign; } set verticalAlign(value) { this.textRenderer.set.verticalAlign(this.trState, value); } get overflowSuffix() { return this.trState.props.overflowSuffix; } set overflowSuffix(value) { this.textRenderer.set.overflowSuffix(this.trState, value); } get debug() { return this.trState.props.debug; } set debug(value) { this.textRenderer.set.debug(this.trState, value); } update(delta, parentClippingRect) { super.update(delta, parentClippingRect); assertTruthy(this.globalTransform); // globalTransform is updated in super.update(delta) this.textRenderer.set.x(this.trState, this.globalTransform.tx); this.textRenderer.set.y(this.trState, this.globalTransform.ty); } checkBasicRenderability() { if (this.worldAlpha === 0 || this.isOutOfBounds() === true) { return false; } if (this.trState && this.trState.props.text !== '') { return true; } return false; } setRenderable(isRenderable) { super.setRenderable(isRenderable); this.textRenderer.setIsRenderable(this.trState, isRenderable); } renderQuads(renderer) { assertTruthy(this.globalTransform); // If the text renderer does not support rendering quads, fallback to the // default renderQuads method if (!this.textRenderer.renderQuads) { super.renderQuads(renderer); return; } // If the text renderer does support rendering quads, use it... // Prevent quad rendering if parent has a render texture // and this node is not the render texture if (this.parentHasRenderTexture) { if (!renderer.renderToTextureActive) { return; } // Prevent quad rendering if parent render texture is not the active render texture if (this.parentRenderTexture !== renderer.activeRttNode) { return; } } if (this.parentHasRenderTexture && this.props.parent?.rtt) { this.globalTransform = Matrix3d.identity(); if (this.localTransform) { this.globalTransform.multiply(this.localTransform); } } assertTruthy(this.globalTransform); this.textRenderer.renderQuads(this.trState, this.globalTransform, this.clippingRect, this.worldAlpha, this.parentHasRenderTexture, this.framebufferDimensions); } /** * Destroy the node and cleanup all resources */ destroy() { super.destroy(); this.textRenderer.destroyState(this.trState); } /** * Resolve a text renderer and a new state based on the current text renderer props provided * @param props * @returns */ createState(props) { const textRendererState = this.textRenderer.createState(props, this); textRendererState.emitter.on('loaded', this.onTextLoaded); textRendererState.emitter.on('failed', this.onTextFailed); this.textRenderer.scheduleUpdateState(textRendererState); return textRendererState; } } //# sourceMappingURL=CoreTextNode.js.map