@lightningjs/renderer
Version:
Lightning 3 Renderer
331 lines • 11.3 kB
JavaScript
/*
* 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