feat(all): 迁移扩展相关功能
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from './../../core/elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
/**
|
||||
* Job:
|
||||
* - recording components required updates
|
||||
* - trigger those updates when 'update' is called
|
||||
*
|
||||
* This module is a bit special. It is, with FontLibrary, one of the only modules in the 'component'
|
||||
* directory not to be used in component composition (Object.assign).
|
||||
*
|
||||
* When MeshUIComponent is instanciated, it calls UpdateManager.register().
|
||||
*
|
||||
* Then when MeshUIComponent receives new attributes, it doesn't update the component right away.
|
||||
* Instead, it calls UpdateManager.requestUpdate(), so that the component is updated when the user
|
||||
* decides it (usually in the render loop).
|
||||
*
|
||||
* This is best for performance, because when a UI is created, thousands of componants can
|
||||
* potentially be instantiated. If they called updates function on their ancestors right away,
|
||||
* a given component could be updated thousands of times in one frame, which is very ineficient.
|
||||
*
|
||||
* Instead, redundant update request are moot, the component will update once when the use calls
|
||||
* update() in their render loop.
|
||||
*/
|
||||
export default class UpdateManager {
|
||||
|
||||
|
||||
static register( component ) {
|
||||
|
||||
if ( !this.elements.includes( component ) ) {
|
||||
|
||||
this.elements.push( component );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static remove( component ) {
|
||||
|
||||
const index = this.elements.indexOf( component );
|
||||
if ( index !== -1 ) {
|
||||
|
||||
this.elements.splice( index, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static update() {
|
||||
|
||||
for ( const UIElement of this.elements ) {
|
||||
UIElement.update();
|
||||
|
||||
UIElement.process(); // Natural process
|
||||
UIElement.process(); // Actual process (optional) - For auto size and stretch
|
||||
|
||||
UIElement.render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
*/
|
||||
UpdateManager.elements = [];
|
||||
@@ -0,0 +1,68 @@
|
||||
/** List the default values of the lib components */
|
||||
const _values = {
|
||||
fog: false,
|
||||
fontFamily: null,
|
||||
fontSize: 0.05,
|
||||
fontKerning: 'auto',
|
||||
fontStyle: 'normal',
|
||||
fontWeight : 'normal',
|
||||
offset: 0.005,
|
||||
lineHeight: 1.2,
|
||||
lineBreak: '- ,.:?!\n',// added '\n' to also acts as friendly breaks when white-space:normal
|
||||
whiteSpace: 'pre-line',
|
||||
flexDirection : 'column',
|
||||
justifyContent : 'start',
|
||||
alignItems : 'start',
|
||||
backgroundImage: null,
|
||||
textAlign : 'left',
|
||||
boxSizing: 'content-box',
|
||||
position: 'static',
|
||||
color: 0xffffff,
|
||||
fontColor: 0xffffff,
|
||||
fontOpacity: 1,
|
||||
opacity: 1,
|
||||
fontPXRange: 4,
|
||||
fontSupersampling: true,
|
||||
fontSmooth: 'antialiased',
|
||||
borderRadius: 0,
|
||||
borderWidth: 0,
|
||||
borderColor: 'black',
|
||||
borderOpacity: 1,
|
||||
backgroundSize: "cover",
|
||||
backgroundColor: 0x000000,
|
||||
backgroundOpacity: 0,
|
||||
overflow: 'visible',
|
||||
letterSpacing: 0,
|
||||
invertAlpha : false,
|
||||
segments: 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./../core/elements/MeshUIBaseElement').Options} overrideProperties
|
||||
*/
|
||||
export const set = function ( overrideProperties ) {
|
||||
|
||||
for ( const property in overrideProperties ) {
|
||||
|
||||
_values[property] = overrideProperties[property];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} property
|
||||
* @return {any}
|
||||
*/
|
||||
export const get = function ( property ) {
|
||||
|
||||
if( !Object.prototype.hasOwnProperty.call( _values, property) ) {
|
||||
|
||||
console.warn( `ThreeMeshUI::DefaultValues is trying to retrieve non-existing property '${property}'`);
|
||||
|
||||
}
|
||||
|
||||
return _values[property];
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* This is the abstract/base class / interface of any inline
|
||||
* Inline can be positioned according to text rules
|
||||
*/
|
||||
export default class Inline {
|
||||
|
||||
constructor() {
|
||||
|
||||
/** @protected */ this._offsetX = 0;
|
||||
/** @protected */ this._offsetY = 0;
|
||||
|
||||
/** @protected */ this._lineBreak = null;
|
||||
|
||||
/** @protected */ this._kerning = 0;
|
||||
|
||||
/** @protected */ this._fontFactor = 1;
|
||||
/** @protected */ this._fontSize = 0;
|
||||
|
||||
/** @protected */ this._cumulativeWidth = 0;
|
||||
|
||||
/** @protected */ this._paddingLeft = 0;
|
||||
/** @protected */ this._paddingRight = 0;
|
||||
|
||||
/** @protected */ this._marginLeft = 0;
|
||||
/** @protected */ this._marginRight = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
resetOffsets() {
|
||||
|
||||
this._offsetX = this._offsetY = 0;
|
||||
this._cumulativeWidth = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The horizontal distance this inline fills
|
||||
* @returns {number}
|
||||
*/
|
||||
get xadvance() { return 0 }
|
||||
|
||||
/**
|
||||
* The offset x of this inline in a line
|
||||
* @returns {number}
|
||||
*/
|
||||
get xoffset() { return 0 }
|
||||
|
||||
/**
|
||||
* The offset y of this inline in a line
|
||||
* @returns {number}
|
||||
*/
|
||||
get yoffset() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get width() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get height() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|null} value
|
||||
*/
|
||||
set lineBreak( value ) {
|
||||
|
||||
this._lineBreak = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string|null}
|
||||
*/
|
||||
get lineBreak() { return this._lineBreak; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get anchor() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get kerning() { return this._kerning * this._fontFactor; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set kerning( value ) {
|
||||
|
||||
this._kerning = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get fontSize() { return this._fontSize }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set fontSize( value ) {
|
||||
|
||||
this._fontSize = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get lineHeight() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get offsetX() { return this._offsetX; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
set offsetX( value ){
|
||||
|
||||
this._offsetX = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get offsetY() { return this._offsetY; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set offsetY( value ){
|
||||
|
||||
this._offsetY = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get cumulativeWidth() { return this._cumulativeWidth; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set cumulativeWidth( value ) {
|
||||
|
||||
this._cumulativeWidth = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get marginLeft() { return this._marginLeft; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set marginLeft( value ) {
|
||||
|
||||
this._marginLeft = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get marginRight() { return this._marginRight; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set marginRight( value ) {
|
||||
|
||||
this._marginRight = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get paddingLeft() { return this._paddingLeft; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set paddingLeft( value ) {
|
||||
|
||||
this._paddingLeft = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get paddingRight() { return this._paddingRight; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set paddingRight( value ) {
|
||||
|
||||
this._paddingRight = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get lineBase() { return 0 }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set fontFactor( value ){
|
||||
|
||||
this._fontFactor = value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get fontFactor() { return this._fontFactor }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import Inline from './Inline';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
|
||||
/**
|
||||
* Line represents an horizontal combination of positioned inlines with additional properties
|
||||
*/
|
||||
export default class Line extends Array {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Inline[]} items
|
||||
*/
|
||||
constructor(...items) {
|
||||
super(...items);
|
||||
|
||||
/**
|
||||
* The width of this line
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = 0;
|
||||
|
||||
/**
|
||||
* The maximum lineBase of this line of inlines
|
||||
* @type {number}
|
||||
*/
|
||||
this.lineBase = 0;
|
||||
|
||||
/**
|
||||
* The maximum lineHeight of this line of inlines
|
||||
* @type {number}
|
||||
*/
|
||||
this.lineHeight = 0;
|
||||
|
||||
/**
|
||||
* The vertical position of this line
|
||||
* @type {number}
|
||||
*/
|
||||
this.y = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import Line from './Line';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
/**
|
||||
* Lines represents a vertical succession of Line
|
||||
*/
|
||||
export default class Lines extends Array {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Line} items
|
||||
*/
|
||||
constructor(...items) {
|
||||
super(...items);
|
||||
|
||||
/**
|
||||
* The maximum width of Line items
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = 0;
|
||||
|
||||
/**
|
||||
* The addition of height of any Line
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
/**
|
||||
* Autosize are only trigger when natural size changed
|
||||
*/
|
||||
export default class AutoSizePropertyBox extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'autosize' );
|
||||
|
||||
this._needsProcess = true;
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
// if( parent ) return;
|
||||
|
||||
|
||||
// has auto size get the height from children
|
||||
if ( element._width._auto ) _processAutoWidth( element );
|
||||
if ( element._height._auto ) _processAutoHeight( element );
|
||||
|
||||
const stretch = element._alignItems._value === 'stretch';
|
||||
const stretchChildrenWidth = stretch && element._flexDirection._value.indexOf( 'column' ) !== -1;
|
||||
const stretchChildrenHeight = stretch && !stretchChildrenWidth;
|
||||
|
||||
for ( const box of element._children._boxes ) {
|
||||
|
||||
if ( ( box._width._auto && stretchChildrenWidth ) || box._width._relative ) {
|
||||
|
||||
box._bounds.setReferenceWidth( box, element._bounds._innerWidth );
|
||||
|
||||
}
|
||||
|
||||
if ( ( box._height._auto && stretchChildrenHeight ) || box._height._relative ) {
|
||||
|
||||
box._bounds.setReferenceHeight( box, element._bounds._innerHeight );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// // justify stretch - Not that easy
|
||||
// const stretchD = element._justifyContent._value === 'stretch';
|
||||
// const stretchChildrenWidthD = stretchD && element._flexDirection._value.indexOf( 'row' ) !== -1;
|
||||
// const stretchChildrenHeightD = stretchD && !stretchChildrenWidthD;
|
||||
//
|
||||
//
|
||||
// if ( stretchChildrenWidthD ) {
|
||||
//
|
||||
// const used = _computeChildrenSideWidth( element );
|
||||
// const available = element._bounds._innerWidth - used;
|
||||
// if ( available > 0 ) {
|
||||
//
|
||||
// const autoElement = element._children._uis.filter( c => c._width._auto );
|
||||
// const distributed = available / autoElement.length;
|
||||
//
|
||||
// for ( const child of autoElement ) {
|
||||
//
|
||||
// const width = child._bounds._offsetWidth + distributed;
|
||||
// child._bounds.setReferenceWidth( child, width );
|
||||
//
|
||||
// }
|
||||
//
|
||||
// element._layouter._needsProcess = true;
|
||||
// element._flexDirection._needsProcess = true;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// } else if ( stretchChildrenHeightD ) {
|
||||
//
|
||||
// const used = _computeChildrenSideHeight( element );
|
||||
// const available = element._bounds._innerHeight - used;
|
||||
// if ( available > 0 ) {
|
||||
//
|
||||
// const autoElement = element._children._uis.filter( c => c._height._auto );
|
||||
// const distributed = available / autoElement.length;
|
||||
//
|
||||
// for ( const child of autoElement ) {
|
||||
//
|
||||
// const height = child._bounds._offsetHeight + distributed;
|
||||
// child._bounds.setReferenceHeight( child, height );
|
||||
//
|
||||
// }
|
||||
//
|
||||
// element._layouter._needsProcess = true;
|
||||
// element._flexDirection._needsProcess = true;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _processAutoWidth( element ) {
|
||||
|
||||
// column : retrieve the biggest child width
|
||||
// row : retrieve the sum of children width
|
||||
element._bounds.setChildrenWidth( element, _computeAutoWidth( element ) );
|
||||
|
||||
}
|
||||
|
||||
function _processAutoHeight( element ) {
|
||||
|
||||
// column : retrieve the sum of children height
|
||||
// row : retrieve the biggest child height
|
||||
element._bounds.setChildrenHeight( element, _computeAutoHeight( element ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the automatic height from children boxes
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeAutoHeight( element ) {
|
||||
|
||||
switch ( element._flexDirection._value ) {
|
||||
|
||||
case 'row' :
|
||||
case 'row-reverse' :
|
||||
return _computeHighestChildHeight( element );
|
||||
|
||||
|
||||
case 'column' :
|
||||
case 'column-reverse' :
|
||||
return _computeChildrenSideHeight( element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*
|
||||
*/
|
||||
function _computeAutoWidth( element ) {
|
||||
|
||||
switch ( element._flexDirection._value ) {
|
||||
|
||||
case 'row' :
|
||||
case 'row-reverse' :
|
||||
return _computeChildrenSideWidth( element );
|
||||
|
||||
|
||||
case 'column' :
|
||||
case 'column-reverse' :
|
||||
return _computeHighestChildWidth( element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of all this component's children width
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeChildrenSideWidth( element ) {
|
||||
|
||||
let sumWidth = 0;
|
||||
for ( const box of element._children._boxes ) {
|
||||
|
||||
if ( box._position._value !== 'static' ) continue;
|
||||
|
||||
const margin = box._margin._value;
|
||||
const width = box._bounds._offsetWidth + margin.y + margin.w;
|
||||
|
||||
sumWidth += width;
|
||||
|
||||
}
|
||||
|
||||
return sumWidth;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of all this component's children width
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeChildrenSideHeight( element ) {
|
||||
|
||||
let sumHeight = 0;
|
||||
for ( const box of element._children._boxes ) {
|
||||
|
||||
if ( box._position._value !== 'static' ) continue;
|
||||
|
||||
const margin = box._margin._value;
|
||||
const height = box._bounds._offsetHeight + margin.x + margin.z;
|
||||
|
||||
sumHeight += height;
|
||||
|
||||
}
|
||||
|
||||
return sumHeight;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest linear dimension among all the children of the passed component
|
||||
* MARGIN INCLUDED
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeHighestChildWidth( element ) {
|
||||
|
||||
let maxWidth = 0;
|
||||
for ( const box of element._children._boxes ) {
|
||||
|
||||
if ( box._position._value !== 'static' ) continue;
|
||||
|
||||
const margin = box._margin._value;
|
||||
const width = box._bounds._offsetWidth + margin.y + margin.w;
|
||||
|
||||
if ( width > maxWidth ) maxWidth = width;
|
||||
|
||||
}
|
||||
|
||||
return maxWidth;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest linear dimension among all the children of the passed component
|
||||
* MARGIN INCLUDED
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeHighestChildHeight( element ) {
|
||||
|
||||
let maxHeight = 0;
|
||||
for ( const box of element._children._boxes ) {
|
||||
|
||||
if ( box._position._value !== 'static' ) continue;
|
||||
|
||||
const margin = box._margin._value;
|
||||
const height = box._bounds._offsetHeight + margin.x + margin.z;
|
||||
|
||||
if ( height > maxHeight ) maxHeight = height;
|
||||
|
||||
}
|
||||
|
||||
return maxHeight;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
/**
|
||||
* Autosize are only trigger when natural size changed
|
||||
*/
|
||||
export default class AutoSizePropertyText extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'autosize' );
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
if( element._layouter._value && element._layouter._value.length ) {
|
||||
|
||||
const lines = element._layouter._value;
|
||||
|
||||
// as this is from children offsetWidth, it means parent innerWidth
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
// has auto size get the height from children
|
||||
if ( element._width._auto ) {
|
||||
|
||||
element._bounds.setOffsetWidth( element, lines.width + padding.w + padding.y + border.w + border.y );
|
||||
|
||||
}
|
||||
|
||||
if ( element._height._auto ) {
|
||||
|
||||
element._bounds.setOffsetHeight( element, lines.height + padding.x + padding.z + border.x + border.z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import * as DefaultValues from '../DefaultValues';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from './../../core/elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class BaseProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} [value=null]
|
||||
* @param primitive
|
||||
*/
|
||||
constructor( propertyId, value = null, primitive = true ) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @internal
|
||||
*/
|
||||
this._id = propertyId;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @internal
|
||||
*/
|
||||
this._value = value;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @internal
|
||||
*/
|
||||
this._needsUpdate = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @internal
|
||||
*/
|
||||
this._needsProcess = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @internal
|
||||
*/
|
||||
this._needsRender = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @protected
|
||||
*/
|
||||
this._isPrimitive = primitive;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
get id() { return this._id; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {any}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
if ( !this.isValid( value ) ) return;
|
||||
|
||||
if ( this._value !== value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// the value has been updated from setter
|
||||
// if there is no additional logic
|
||||
// then just output it
|
||||
// => out[this._id] = this._value;
|
||||
this.output( out );
|
||||
|
||||
|
||||
// ??
|
||||
//this.computeOutputValue( element );
|
||||
// if( this._isPrimitive ) this.output( out );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Output this property in a dictionnary
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
output( out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// ie:
|
||||
// out['borderRadius'] = this;
|
||||
// out[this._id] = this._value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Out} out
|
||||
*/
|
||||
_outputValue( out ) {
|
||||
|
||||
out[ this._id ] = this._value;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Execute additional process after all properties have been updated
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
process( element ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Execute additional process after all properties have been updated
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
render( element ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
getInheritedInput( element ) {
|
||||
|
||||
if ( this._value !== 'inherit' ) return this._value;
|
||||
|
||||
const parent = element._parent._value;
|
||||
if ( parent && parent[ `_${this._id}` ] ) {
|
||||
|
||||
return parent[ `_${this._id}` ].getInheritedInput( parent )
|
||||
|
||||
}
|
||||
|
||||
return this.getDefaultValue();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {any}
|
||||
*/
|
||||
getDefaultValue() {
|
||||
|
||||
return DefaultValues.get( this._id );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
*/
|
||||
isValid( value ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
emptyStrategyLogic() {
|
||||
|
||||
throw new Error( `ThreeMeshUI::${this.constructor.name} has empty strategy. Update has not been processed.` );
|
||||
|
||||
}
|
||||
|
||||
requestUpdate() {
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
requestProcess() {
|
||||
this._needsProcess = false;
|
||||
}
|
||||
|
||||
requestRender() {
|
||||
this._needsRender = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @typedef Out
|
||||
* @type {Object & Object.<string,any>}
|
||||
*/
|
||||
@@ -0,0 +1,43 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class BooleanProperty extends BaseProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} [value=null]
|
||||
*/
|
||||
constructor( propertyId, value = true ) {
|
||||
|
||||
super( propertyId, value, true );
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._value = value;
|
||||
|
||||
this.output = this._outputValue;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} value
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
import { Vector3 } from 'three';
|
||||
import { numberEquals } from '../../utils/NumberUtils';
|
||||
|
||||
export default class BoundsBox extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'bounds', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector3}
|
||||
* @internal
|
||||
*/
|
||||
this._size = new Vector3( 1, 1, 1 );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._offsetWidth = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._offsetHeight = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._innerWidth = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._innerHeight = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._centerX = 0.5;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._centerY = 0.5;
|
||||
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the width 100%
|
||||
* @param element
|
||||
* @param value
|
||||
*/
|
||||
setReferenceWidth( element, value ) {
|
||||
|
||||
const width = element._width;
|
||||
const padding = element._padding._value;
|
||||
const borderWidth = element._borderWidth._value;
|
||||
const margin = element._margin._value;
|
||||
|
||||
const factor = width._auto ? 1 : width._value;
|
||||
// const newOffsetWidth = (value * factor) - (margin.y + margin.w);
|
||||
const newOffsetWidth = (value * factor) - (margin.y + margin.w);
|
||||
if ( numberEquals( newOffsetWidth, this._offsetWidth ) ) return;
|
||||
|
||||
this._offsetWidth = newOffsetWidth;
|
||||
this._innerWidth = this._offsetWidth - ( padding.y + padding.w + borderWidth.y + borderWidth.w );
|
||||
|
||||
this._centerX = _computeCenterX( element );
|
||||
|
||||
this._propagateWidth( element );
|
||||
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the height 100%
|
||||
* @param element
|
||||
* @param value
|
||||
*/
|
||||
setReferenceHeight( element, value ) {
|
||||
|
||||
const height = element._height;
|
||||
const padding = element._padding._value;
|
||||
const borderWidth = element._borderWidth._value;
|
||||
const margin = element._margin._value;
|
||||
|
||||
const factor = height._auto ? 1 : height._value;
|
||||
|
||||
const newOffsetHeight = (value * factor) - ( margin.x + margin.z );
|
||||
if ( numberEquals( newOffsetHeight, this._offsetHeight ) ) return;
|
||||
|
||||
this._offsetHeight = newOffsetHeight;
|
||||
this._innerHeight = this._offsetHeight - ( padding.x + padding.z + borderWidth.x + borderWidth.z );
|
||||
this._centerY = _computeCenterY( element );
|
||||
|
||||
this._propagateHeight( element );
|
||||
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
}
|
||||
|
||||
setChildrenWidth( element, value ) {
|
||||
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
this._innerWidth = value;
|
||||
this._offsetWidth = this._innerWidth + ( padding.y + padding.w + border.y + border.w )
|
||||
|
||||
this._centerX = _computeCenterX( element );
|
||||
|
||||
this._propagateWidth( element );
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
|
||||
}
|
||||
|
||||
setChildrenHeight( element, value ) {
|
||||
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
this._innerHeight = value;
|
||||
this._offsetHeight = this._innerHeight + ( padding.x + padding.z + border.x + border.z )
|
||||
|
||||
this._centerY = _computeCenterY( element );
|
||||
|
||||
this._propagateHeight( element );
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
// only compute new width if explicitely defined
|
||||
const width = element._width;
|
||||
if( !width._auto && !width._relative ) {
|
||||
|
||||
if ( element._boxSizing._value === 'content-box' ) {
|
||||
|
||||
this._innerWidth = width._value;
|
||||
this._offsetWidth = this._innerWidth + padding.y + padding.w + border.y + border.w;
|
||||
|
||||
} else {
|
||||
|
||||
this._offsetWidth = width._value;
|
||||
this._innerWidth = this._offsetWidth - ( padding.y + padding.w + border.y + border.w );
|
||||
|
||||
}
|
||||
|
||||
this._centerX = _computeCenterX( element );
|
||||
this._needsProcess = true;
|
||||
|
||||
// tells children width has changed
|
||||
this._propagateWidth( element );
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
}
|
||||
|
||||
const height = element._height;
|
||||
if( !height._auto && !height._relative ) {
|
||||
|
||||
if ( element._boxSizing._value === 'content-box' ) {
|
||||
|
||||
this._innerHeight = height._value;
|
||||
this._offsetHeight = this._innerHeight + padding.x + padding.z + border.x + border.z;
|
||||
|
||||
} else {
|
||||
|
||||
this._offsetHeight = height._value;
|
||||
this._innerHeight = this._offsetHeight - ( padding.x + padding.z + border.x + border.z );
|
||||
|
||||
}
|
||||
|
||||
this._centerY = _computeCenterY( element );
|
||||
this._needsProcess = true;
|
||||
|
||||
// tells children height has changed
|
||||
this._propagateHeight( element );
|
||||
this._triggerCascadingDependencies( element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ render( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._size.x = this._offsetWidth;
|
||||
this._size.y = this._offsetHeight;
|
||||
|
||||
if( element._backgroundMesh ){
|
||||
element._backgroundMesh.updateScale();
|
||||
}
|
||||
|
||||
element._renderer._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
output( out ) {
|
||||
|
||||
out[ 'size' ] = this._size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// this._triggerCascadingDependencies( element )
|
||||
|
||||
//console.log( 'process bounds box', element.name );
|
||||
|
||||
// update primitives or unbinded values
|
||||
|
||||
// require cascading processes
|
||||
|
||||
element._overflow._needsRender = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @internal
|
||||
*/
|
||||
_computeChildrenSideWidth( element ) {
|
||||
|
||||
return _computeChildrenSideWidth( element );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @internal
|
||||
*/
|
||||
_computeChildrenSideHeight( element ) {
|
||||
|
||||
return _computeChildrenSideHeight( element );
|
||||
|
||||
}
|
||||
|
||||
_propagateWidth( element ) {
|
||||
|
||||
for ( let i = 0; i < element._children._boxes.length; i++ ) {
|
||||
|
||||
const box = element._children._boxes[ i ];
|
||||
const width = box._width;
|
||||
|
||||
if( width._relative ) box._bounds.setReferenceWidth( box, this._innerWidth );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_propagateHeight( element ) {
|
||||
|
||||
for ( let i = 0; i < element._children._boxes.length; i++ ) {
|
||||
|
||||
const box = element._children._boxes[ i ];
|
||||
const height = box._height;
|
||||
|
||||
if( height._relative ) box._bounds.setReferenceHeight( box, this._innerHeight );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_triggerCascadingDependencies( element ) {
|
||||
|
||||
// also change parent when require
|
||||
if ( element._parent._value ) {
|
||||
element._parent._value._autoSize._needsProcess = true;
|
||||
}
|
||||
|
||||
element._flexDirection._needsProcess = true;
|
||||
element._fontSize._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
this._needsRender = true;
|
||||
|
||||
element._borderWidth._needsRender = true;
|
||||
element._borderRadius._needsRender = true;
|
||||
|
||||
element._overflow._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* INTERNAL FUNCTIONS
|
||||
**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Retrieve the center X according to box sized dimensions
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeCenterX( element ) {
|
||||
|
||||
const padding = element._padding._value;
|
||||
const borderWidth = element._borderWidth._value;
|
||||
|
||||
const leftSide = padding.w + borderWidth.w;
|
||||
const rightSide = padding.y + borderWidth.y;
|
||||
|
||||
return ( leftSide - rightSide ) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the center Y according to box sized dimensions
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeCenterY( element ) {
|
||||
|
||||
|
||||
const padding = element._padding._value;
|
||||
const borderWidth = element._borderWidth._value;
|
||||
|
||||
const topSide = padding.x + borderWidth.x;
|
||||
const bottomSide = padding.z + borderWidth.z;
|
||||
|
||||
return ( bottomSide - topSide ) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of all this component's children width
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeChildrenSideWidth( element ) {
|
||||
|
||||
return element._children._boxes.reduce( ( accu, child ) => {
|
||||
// if ( child._bounds._needsProcess ) child._bounds.process( child );
|
||||
|
||||
const margin = child._margin._value;
|
||||
const CHILD_SIZE = child._bounds._offsetWidth + margin.y + margin.w;
|
||||
|
||||
return accu + CHILD_SIZE;
|
||||
|
||||
}, 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sum of all this component's children width
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {number}
|
||||
*/
|
||||
function _computeChildrenSideHeight( element ) {
|
||||
|
||||
return element._children._boxes.reduce( ( accu, child ) => {
|
||||
// if ( child._bounds._needsProcess ) child._bounds.process( child );
|
||||
|
||||
const margin = child._margin._value;
|
||||
|
||||
const CHILD_SIZE = child._bounds._offsetHeight + margin.x + margin.z;
|
||||
|
||||
return accu + CHILD_SIZE;
|
||||
|
||||
}, 0 );
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
import { Vector3 } from 'three';
|
||||
|
||||
export default class BoundsInlineBlock extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'bounds', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector3}
|
||||
* @internal
|
||||
*/
|
||||
this._size = new Vector3( 1, 1, 1 );
|
||||
|
||||
this._offsetWidth = 0;
|
||||
this._offsetHeight = 0;
|
||||
|
||||
this._innerWidth = 0;
|
||||
this._innerHeight = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this.output( out );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
this._offsetWidth = this._innerWidth = element._inlines._value[0].width;
|
||||
this._offsetHeight = this._innerHeight = element._inlines._value[0].height;
|
||||
|
||||
this._needsRender = true;
|
||||
|
||||
element._borderWidth._needsRender = true;
|
||||
element._borderRadius._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ render( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._size.x = this._offsetWidth;
|
||||
this._size.y = this._offsetHeight;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
output( out ) {
|
||||
|
||||
out[ 'size' ] = this._size;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import BoundsBox from './BoundsBox';
|
||||
|
||||
export default class BoundsText extends BoundsBox {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this._innerWidth = Infinity;
|
||||
this._innerHeight = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from './../../core/elements/MeshUIBaseElement';
|
||||
import Line from './../../core/elements/glyphs/Line';
|
||||
import { Vector3 } from 'three';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class BoxLayouter extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'layouter', null, false);
|
||||
|
||||
// configure
|
||||
this._needsUpdate = true;
|
||||
|
||||
/**
|
||||
* @typedef ChildrenPos
|
||||
* @type {Object & Object.<string,Vector3>}
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ChildrenPos}
|
||||
* @internal
|
||||
*/
|
||||
this._childrenPos = {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Updated when :
|
||||
* - New child added
|
||||
* - Child removed
|
||||
* - Child position changed
|
||||
* - Child visibility changed
|
||||
* - ...?
|
||||
* @override
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
//console.log( "BoxLayouter update", element.name );
|
||||
// reset
|
||||
this._childrenPos = {};
|
||||
|
||||
for ( const uiBoxElement of element._children._boxes ) {
|
||||
|
||||
//console.log( uiBoxElement._position._value )
|
||||
if( uiBoxElement._position._value === 'static' ) {
|
||||
|
||||
// bind position
|
||||
this._childrenPos[ uiBoxElement.id ] = uiBoxElement.position;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
/* eslint-disable no-unused-vars */ process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// As _childrenPos are bounds with child.position, this is not required anymore
|
||||
//
|
||||
// element._position._needsProcess = true;
|
||||
//
|
||||
// for ( const box of element._children._boxes ) {
|
||||
//
|
||||
// if( this._childrenPos[box.id] ) {
|
||||
//
|
||||
// box.position.x = this._childrenPos[box.id].x;
|
||||
// box.position.y = this._childrenPos[box.id].y;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class EmptyProperty extends BaseProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
*/
|
||||
constructor( propertyId = 'untitled' ) {
|
||||
|
||||
super( propertyId, undefined, false );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
update( element , out ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Output this property in a dictionnary
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
output( out ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
import FontVariant from '../../font/FontVariant';
|
||||
|
||||
export default class FontProperty extends BaseProperty{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {FontVariant} [value=null]
|
||||
*/
|
||||
constructor( value = null ) {
|
||||
|
||||
super( 'font', value, false);
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {FontVariant|null}
|
||||
* @internal
|
||||
*/
|
||||
this._fontVariant = null;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef ReadyClosure
|
||||
* @type { ()=> void|null }
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {ReadyClosure}
|
||||
* @private
|
||||
*/
|
||||
this._handleFontReadyClosure = null;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
this.isValid = _isValid;
|
||||
|
||||
}
|
||||
|
||||
output( out ) {
|
||||
|
||||
out[this._id] = this._fontVariant;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// if a previous font isset, be sure no event remains
|
||||
if ( this._fontVariant && !this._fontVariant.isReady ) {
|
||||
|
||||
this._fontVariant.removeEventListener( 'ready', this._handleFontReadyClosure );
|
||||
|
||||
}
|
||||
|
||||
// obtain font from value or from style combinaison
|
||||
if( this._value && this._value instanceof FontVariant ) {
|
||||
|
||||
this._fontVariant = this._value;
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
const fontFamily = element._fontFamily._value;
|
||||
if( fontFamily ) {
|
||||
|
||||
this._fontVariant = fontFamily.getVariant(
|
||||
element._fontWeight._value,
|
||||
element._fontStyle._value,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( !this._fontVariant ) return;
|
||||
|
||||
this._fontVariant._alterElementProperties( element );
|
||||
|
||||
this._handleFontReadyClosure = _readyClosure( element, this );
|
||||
|
||||
// new font, means rebuild inlines, now or soon
|
||||
if ( !this._fontVariant.isReady ) {
|
||||
|
||||
// @TODO : clear inlines components
|
||||
// this.inlines = null;
|
||||
|
||||
this._fontVariant.addEventListener( 'ready', this._handleFontReadyClosure );
|
||||
|
||||
} else {
|
||||
|
||||
this._handleFontReadyClosure();
|
||||
|
||||
}
|
||||
|
||||
// Set the default material
|
||||
if( !element._fontMaterial._defaultMaterial || !(element._fontMaterial._defaultMaterial instanceof this._fontVariant.fontMaterial) ) {
|
||||
|
||||
element._fontMaterial._defaultMaterial = new this._fontVariant.fontMaterial();
|
||||
element._fontMaterial._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {FontVariant} value
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
if( ! this.isValid( value) ) return;
|
||||
|
||||
if( this._value !== value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {FontVariant}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {FontVariant|null}
|
||||
*/
|
||||
get fontVariant() { return this._fontVariant; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose () {
|
||||
|
||||
if( this._handleFontReadyClosure ) {
|
||||
|
||||
this._fontVariant.removeEventListener( 'ready', this._handleFontReadyClosure );
|
||||
this._handleFontReadyClosure = null;
|
||||
|
||||
}
|
||||
|
||||
this._value = null;
|
||||
this._fontVariant = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( ! ( value instanceof FontVariant ) ) {
|
||||
|
||||
console.warn(`.font value '${value}' is not valid. It requires a FontVariant instance. Aborted`);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @param {FontProperty} fontProperty
|
||||
* @return {() => void}
|
||||
* @private
|
||||
*/
|
||||
function _readyClosure( element, fontProperty ) {
|
||||
return function () {
|
||||
|
||||
fontProperty._needsUpdate = true;// ? update itself?
|
||||
element._glyphs._needsProcess = true;
|
||||
|
||||
// this._transferToMaterial();
|
||||
|
||||
// request parse update and parent layout
|
||||
// this.update( true, true, false );
|
||||
// this.getHighestParent().update( false, true, false );
|
||||
|
||||
// remove the listener
|
||||
fontProperty._fontVariant.removeEventListener( 'ready', fontProperty._handleFontReadyClosure );
|
||||
fontProperty._handleFontReadyClosure = null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
|
||||
export default class FontSmoothProperty extends InheritableProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'fontSmooth', 'inherit', true);
|
||||
|
||||
// configure
|
||||
this._needsUpdate = false;
|
||||
this.isValid = _isValid;
|
||||
this.output = this._outputValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['inherit','none','antialiased'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn(`.fontSmoothing value '${value}' is not valid. Aborted`);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import FontSmoothProperty from './FontSmoothProperty';
|
||||
|
||||
export default class FontSmoothPropertyInline extends FontSmoothProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this.output = this._outputValue;
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import TypographicGlyph from '../../font/TypographicGlyph';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class GlyphsProperty extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "glyphs", null, false);
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<TypographicGlyph>}
|
||||
* @private
|
||||
*/
|
||||
this._value = null;
|
||||
|
||||
// value
|
||||
|
||||
// 1. collapsed whiteSpace;
|
||||
// this._textContent = Whitespace.collapseWhitespaceOnString( this.content, this.getWhiteSpace() );
|
||||
|
||||
// 2. glyphs
|
||||
// this._textContentGlyphs = this._textContent.split( '' ).map( ( char ) => this._font.getTypographicGlyph( char ) );
|
||||
|
||||
// 3. Inlines
|
||||
// this._textContentInlines = this._textContentGlyphs.map( ( glyphBox ) => glyphBox.asInlineGlyph() );
|
||||
|
||||
// 4. kerning
|
||||
// this._buildContentKernings();
|
||||
|
||||
|
||||
// 5.? Apply margin and padding on first and last inlines
|
||||
// if( this._textContentInlines.length ) {
|
||||
//
|
||||
// // First gets left side
|
||||
// this._textContentInlines[0].paddingLeft = this._padding.w;
|
||||
// this._textContentInlines[0].marginLeft = this._margin.w;
|
||||
//
|
||||
// // Last gets right side
|
||||
// const lastIndex = this._textContentInlines.length - 1;
|
||||
// this._textContentInlines[lastIndex].paddingRight = this._padding.y;
|
||||
// this._textContentInlines[lastIndex].marginRight = this._margin.y;
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
if( !element._font._fontVariant ) return;
|
||||
if( !element._font._fontVariant.isReady ) return;
|
||||
|
||||
this._value = element._whiteSpace._whiteSpacedContent.split( '' ).map( ( char ) => element._font._fontVariant.getTypographicGlyph( char ) );
|
||||
|
||||
// @TODO : Even if the value is removed it should trigger a rebuild.
|
||||
if( this._value ) element._inlines._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {Array.<TypographicGlyph>}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
|
||||
/**
|
||||
* @property {boolean|"inherit"} value
|
||||
*/
|
||||
export default class InheritableBooleanProperty extends InheritableProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
*/
|
||||
constructor( propertyId) {
|
||||
|
||||
super( propertyId, 'inherit', true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
import { alphaTestTransformer } from '../../utils/mediator/transformers/MaterialTransformers';
|
||||
|
||||
/**
|
||||
* @property {Material|null|"inherit"} value
|
||||
*/
|
||||
export default class InheritableMaterialProperty extends InheritableProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
*/
|
||||
constructor( propertyId ) {
|
||||
|
||||
super( propertyId, 'inherit', false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Object.<{m:string, t?:(target:any, targetProperty:string, value:any) => void}>}
|
||||
* @internal
|
||||
*/
|
||||
this._mediation = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {null}
|
||||
* @internal
|
||||
*/
|
||||
this._defaultMaterial = null;
|
||||
|
||||
}
|
||||
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._notInheritedValue = this._value;
|
||||
|
||||
if ( this._notInheritedValue === 'inherit' ) {
|
||||
this._notInheritedValue = this.getInheritedInput( element );
|
||||
} else {
|
||||
this.propagate( element );
|
||||
}
|
||||
|
||||
// no material
|
||||
if ( !this._notInheritedValue ) {
|
||||
|
||||
// reset mediation
|
||||
this._mediation = {};
|
||||
|
||||
} else if ( this._notInheritedValue.constructor.mediation ) {
|
||||
|
||||
this._mediation = { ...this._notInheritedValue.constructor.mediation };
|
||||
|
||||
} else {
|
||||
|
||||
this._mediation = {
|
||||
clippingPlanes: { m: 'clippingPlanes' },
|
||||
fontAlphaTest: { m: 'alphaTest', t: alphaTestTransformer },
|
||||
fontSide: { m: 'side' },
|
||||
color: { m: 'color' },
|
||||
fontOpacity: { m: 'opacity' }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
element._transferToFontMaterial();
|
||||
|
||||
// dispatch to children
|
||||
|
||||
|
||||
this._outputValue( out );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getInheritedInput( element ) {
|
||||
|
||||
if ( this._value !== 'inherit' ) return this._value;
|
||||
|
||||
let recursiveParent = element;
|
||||
let inheritedValue = null;
|
||||
while ( recursiveParent._parent._value ) {
|
||||
|
||||
recursiveParent = recursiveParent._parent._value;
|
||||
if ( recursiveParent[ `_${this._id}` ]._value !== 'inherit' ) {
|
||||
|
||||
inheritedValue = recursiveParent[ `_${this._id}` ]._value;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( inheritedValue !== null ) {
|
||||
return inheritedValue;
|
||||
}
|
||||
|
||||
return this.getDefaultValue();
|
||||
|
||||
}
|
||||
|
||||
getDefaultValue() {
|
||||
return this._defaultMaterial;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class InheritableProperty extends BaseProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} [value=null]
|
||||
* @param primitive
|
||||
*/
|
||||
constructor( propertyId, value = null, primitive = true ) {
|
||||
|
||||
super( propertyId, value, primitive );
|
||||
|
||||
// @TODO : I would like to remove this rules ( here )
|
||||
this.output = this._outputValue;
|
||||
|
||||
this._notInheritedValue = null;
|
||||
}
|
||||
|
||||
update( element , out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._notInheritedValue = this._value;
|
||||
|
||||
if( this._notInheritedValue === 'inherit' )
|
||||
{
|
||||
this._notInheritedValue = this.getInheritedInput( element );
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// this.propagate( element );
|
||||
// }
|
||||
|
||||
// @TODO: Evaluate. This might be too much
|
||||
this.propagate( element );
|
||||
|
||||
this._outputValue( out );
|
||||
|
||||
}
|
||||
|
||||
propagate( element ) {
|
||||
|
||||
// rebuild same properties on children 'inheritance'
|
||||
for ( const childUIElement of element._children._uis ) {
|
||||
|
||||
const property = childUIElement[`_${this._id}`];
|
||||
if( property !== undefined && property._value === 'inherit' ) {
|
||||
childUIElement[`_${this._id}`]._needsUpdate = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output this property in a dictionnary
|
||||
* @override
|
||||
*/
|
||||
_outputValue( out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
out[this._id] = this._notInheritedValue;
|
||||
|
||||
}
|
||||
|
||||
set value ( value ) {
|
||||
|
||||
if( ! this.isValid( value) ) return;
|
||||
|
||||
if( this._value !== value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
* @return {any|"inherit"}
|
||||
*/
|
||||
get value() {
|
||||
|
||||
if( this._value === 'inherit' ) return this._notInheritedValue;
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class InlineJustificator extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
super( 'inlineJustificator', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Lines}
|
||||
* @private
|
||||
*/
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
const INNER_HEIGHT = element._bounds._innerHeight;
|
||||
const lines = element._layouter._value;
|
||||
|
||||
const textHeight = Math.abs( lines.height );
|
||||
|
||||
|
||||
// Line vertical positioning
|
||||
let justificationOffset = ( () => {
|
||||
|
||||
switch ( element._alignItems._value ) {
|
||||
|
||||
case 'inherit':
|
||||
case 'start':
|
||||
return INNER_HEIGHT / 2 ;
|
||||
|
||||
case 'end':
|
||||
return textHeight - ( INNER_HEIGHT / 2 );
|
||||
|
||||
|
||||
case 'stretch': // @TODO : Stretch should trigger an error in own property
|
||||
case 'center':
|
||||
return textHeight/2;
|
||||
|
||||
}
|
||||
} )();
|
||||
|
||||
//console.log( justificationOffset );
|
||||
|
||||
// Apply padding
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
justificationOffset += ( - padding.x + padding.z ) / 2 + ( - border.x + border.z ) / 2;
|
||||
|
||||
//
|
||||
|
||||
lines.forEach( ( line ) => {
|
||||
|
||||
line.y += justificationOffset;
|
||||
|
||||
line.forEach( ( inline ) => {
|
||||
|
||||
inline.offsetY += justificationOffset;
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class InlineLayouter extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'layouter', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {MeshUIBaseElement}
|
||||
* @private
|
||||
*/
|
||||
this._value = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// find the first text parent;
|
||||
this._value = element._parent.find( (p) => { return p.isUI && p.isText } );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
|
||||
// layout has been changed
|
||||
if( this._value ) {
|
||||
|
||||
this._value._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import Inline from '../elements/glyphs/Inline';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class InlinesProperty extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "inlines", null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<Inline>}
|
||||
* @private
|
||||
*/
|
||||
this._value = null;
|
||||
|
||||
// value
|
||||
|
||||
// 3. Inlines
|
||||
// this._textContentInlines = this._textContentGlyphs.map( ( glyphBox ) => glyphBox.asInlineGlyph() );
|
||||
|
||||
// 4. kerning
|
||||
// this._buildContentKernings();
|
||||
|
||||
|
||||
// 5.? Apply margin and padding on first and last inlines
|
||||
// if( this._textContentInlines.length ) {
|
||||
//
|
||||
// // First gets left side
|
||||
// this._textContentInlines[0].paddingLeft = this._padding.w;
|
||||
// this._textContentInlines[0].marginLeft = this._margin.w;
|
||||
//
|
||||
// // Last gets right side
|
||||
// const lastIndex = this._textContentInlines.length - 1;
|
||||
// this._textContentInlines[lastIndex].paddingRight = this._padding.y;
|
||||
// this._textContentInlines[lastIndex].marginRight = this._margin.y;
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
this._value = element._glyphs._value.map( ( glyphBox ) => glyphBox.asInlineGlyph() );
|
||||
|
||||
if( this._value.length ) {
|
||||
|
||||
// First gets left side
|
||||
this._value[0].paddingLeft = element._padding._value.w;
|
||||
this._value[0].marginLeft = element._margin._value.w;
|
||||
|
||||
// Last gets right side
|
||||
const lastIndex = this._value.length - 1;
|
||||
this._value[lastIndex].paddingRight = element._padding._value.y;
|
||||
this._value[lastIndex].marginRight = element._margin._value.y;
|
||||
|
||||
}
|
||||
|
||||
|
||||
element._fontSize._needsProcess = true;
|
||||
element._lineBreak._needsProcess = true;
|
||||
element._fontKerning._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {Array.<Inline>}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import Inline from '../elements/glyphs/Inline';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
/**
|
||||
* @property {Array.<InlineGlyph>} value
|
||||
*/
|
||||
export default class InlinesPropertyInlineBlock extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "inlines", null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<Inline>}
|
||||
* @internal
|
||||
*/
|
||||
this._value = [];
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
// First gets left side
|
||||
this._value[0].paddingLeft = element._padding._value.w;
|
||||
this._value[0].marginLeft = element._margin._value.w;
|
||||
|
||||
// Last gets right side
|
||||
const lastIndex = this._value.length - 1;
|
||||
this._value[lastIndex].paddingRight = element._padding._value.y;
|
||||
this._value[lastIndex].marginRight = element._margin._value.y;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
* @property {boolean|"inherit"} value - propriety description
|
||||
*
|
||||
*/
|
||||
export default class InvertAlphaProperty extends InheritableProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'invertAlpha', 'inherit' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class LineBreakProperty extends BaseProperty{
|
||||
|
||||
constructor( defaultValue = "- ,.:?!\n" ) {
|
||||
|
||||
super( "lineBreak", defaultValue, true );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {"mandatory"|"possible"|null}
|
||||
* @private
|
||||
*/
|
||||
this._newLineBreakability = null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
const newLineBreakability = element._whiteSpace._newLineBreakability;
|
||||
|
||||
if( !element._inlines._value ) return;
|
||||
|
||||
// update inlines properties before inline placements in lines
|
||||
for ( let i = 0; i < element._inlines._value.length; i++ ) {
|
||||
|
||||
|
||||
const inline = element._inlines._value[ i ];
|
||||
const char = inline.char;
|
||||
|
||||
// Whitespace Breakability ---------------------------------------------------------------------------------------
|
||||
let lineBreak = null;
|
||||
|
||||
// could be inlineBlock without char
|
||||
if( char !== undefined ) {
|
||||
|
||||
// @question : Does it worth to be strategy? I don't really think so
|
||||
if ( newLineBreakability !== 'nowrap' ) {
|
||||
|
||||
if ( this._value.includes( char ) || char.match( /\s/g ) ) lineBreak = 'possible';
|
||||
|
||||
}
|
||||
|
||||
if ( char.match( /\n/g ) ) {
|
||||
|
||||
lineBreak = newLineBreakability;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline.lineBreak = lineBreak;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {string}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
import { numberEquals } from '../../utils/NumberUtils';
|
||||
|
||||
export default class NumberProperty extends BaseProperty{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {number} [value]
|
||||
*/
|
||||
constructor( propertyId, value ) {
|
||||
|
||||
super( propertyId, value, true);
|
||||
|
||||
this.output = this._outputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
if( ! this.isValid( value) ) return;
|
||||
|
||||
if( numberEquals(this._value, value) ) return;
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
|
||||
export default class OffsetProperty extends InheritableProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'offset', 'inherit', false );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
super.update( element, out);
|
||||
|
||||
// only process if element has ui parent
|
||||
if( element._parent._value !== null ) element.position.z = this._notInheritedValue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class RenderOrderProperty extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'renderOrder', 'auto', true);
|
||||
|
||||
this.output = this._outputValue;
|
||||
|
||||
this._actualValue = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} value
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
if( ! this.isValid( value) ) return;
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
update( element, out ) {
|
||||
|
||||
if( this._value !== 'auto' ) {
|
||||
|
||||
this._actualValue = this._value;
|
||||
|
||||
} else {
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent !== null ) {
|
||||
|
||||
const parentIndex = parent._renderOrder._actualValue;
|
||||
const positionInParent = 1 + parent._children._uis.indexOf( element );
|
||||
|
||||
this._actualValue = parentIndex + positionInParent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update any children
|
||||
for ( const childUIElement of element._children._uis ) {
|
||||
|
||||
const property = childUIElement[`_renderOrder`];
|
||||
if( property._value === 'auto' ) childUIElement[`_renderOrder`]._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
this._outputValue( out );
|
||||
|
||||
}
|
||||
|
||||
_outputValue( out ) {
|
||||
|
||||
out[this._id] = this._actualValue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { BackSide, DoubleSide, FrontSide } from 'three';
|
||||
import InheritableProperty from './InheritableProperty';
|
||||
|
||||
/**
|
||||
* @property {number|"inherit"} value
|
||||
*/
|
||||
export default class SideProperty extends InheritableProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
*/
|
||||
constructor( propertyId ) {
|
||||
|
||||
super( propertyId, 'inherit', true);
|
||||
|
||||
this.isValid = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = [ FrontSide, BackSide, DoubleSide ];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value) === -1 ){
|
||||
|
||||
console.warn(`SideProperty value '${value}' is not valid. Abort`);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import EmptyProperty from './EmptyProperty';
|
||||
|
||||
export default class TextContentDefault extends EmptyProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "textContent" );
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ set value( v ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
let content = "";
|
||||
for ( let i = 0; i < element.children.length; i++ ) {
|
||||
const child = element.children[i];
|
||||
|
||||
if( child.isUI ) {
|
||||
|
||||
content += child.textContent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._value = content;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class TextContentInline extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "textContent", null, false );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
element._glyphs._needsUpdate = true;
|
||||
element._whiteSpace._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import InlineElement from '../../elements/basic/InlineElement';
|
||||
import TextContentDefault from './TextContentDefault';
|
||||
|
||||
export default class TextContentProperty extends TextContentDefault{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( "textContent", null, false );
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
set value( value ) {
|
||||
|
||||
// If content hasn't change, dont update it
|
||||
if( this._value !== value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// prevent multiple update
|
||||
this._needsUpdate = false;
|
||||
|
||||
// Remove all its children (Inlines)
|
||||
for ( let i = element.children.length - 1 ; i >= 0; i-- ) {
|
||||
const child = element.children[ i ];
|
||||
if( child.isUI ) {
|
||||
|
||||
element.remove( child );
|
||||
child.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Rebuild its child list
|
||||
element._children._uis = [];
|
||||
|
||||
// If a value, add a child
|
||||
if( this._value ) {
|
||||
|
||||
const childrenInlines = this.buildInlines( this._value );
|
||||
if( childrenInlines.length ){
|
||||
element.add( ...childrenInlines );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( element.isInline ) {
|
||||
|
||||
element._glyphs._needsUpdate = true;
|
||||
element._whiteSpace._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} textContent
|
||||
* @return {InlineElement[]}
|
||||
*/
|
||||
buildInlines( textContent ) {
|
||||
|
||||
return [ new InlineElement({name:'anonymousInline',textContent}) ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
import Lines from '../elements/glyphs/Lines';
|
||||
import Line from '../elements/glyphs/Line';
|
||||
|
||||
export default class TextLayouter extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'layouter', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Lines}
|
||||
* @private
|
||||
*/
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
|
||||
let INNER_WIDTH = element._width._value;
|
||||
|
||||
if ( element._width._auto ) {
|
||||
|
||||
INNER_WIDTH = Infinity;
|
||||
|
||||
} else {
|
||||
|
||||
INNER_WIDTH = element._bounds._innerWidth;
|
||||
|
||||
}
|
||||
|
||||
// Compute lines
|
||||
|
||||
const INTERLINE = element._lineHeight._value;
|
||||
|
||||
// Will stock the characters of each line, so that we can
|
||||
// correct lines position before to merge
|
||||
const lines = new Lines( new Line() );
|
||||
|
||||
let lastInlineOffset = 0;
|
||||
element._children._inlines.forEach( ( inlineElement ) => {
|
||||
|
||||
// Abort condition
|
||||
|
||||
if ( !inlineElement._inlines.value ) return;
|
||||
|
||||
this._resetInlines( inlineElement );
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Compute offset of each children according to its dimensions
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// @TODO: Fontsize best fit
|
||||
const FONTSIZE = inlineElement._fontSize._value;
|
||||
|
||||
const LETTERSPACING = inlineElement._letterSpacing._value * FONTSIZE;
|
||||
|
||||
const WHITESPACE = inlineElement._whiteSpace._value;
|
||||
|
||||
const BREAKON = inlineElement._lineBreak._value;
|
||||
|
||||
const whiteSpaceOptions = {
|
||||
WHITESPACE,
|
||||
LETTERSPACING,
|
||||
BREAKON,
|
||||
INNER_WIDTH
|
||||
}
|
||||
|
||||
const inlineWrapper = inlineElement._whiteSpace._inlineWrapper;
|
||||
|
||||
lastInlineOffset += inlineElement._margin._value.w + inlineElement._padding._value.w;
|
||||
|
||||
inlineElement._inlines.value.forEach( ( inline, i, inlines ) => {
|
||||
|
||||
const line = lines[lines.length - 1];
|
||||
|
||||
// Line break
|
||||
const shouldBreak = inlineWrapper(inlines,i,lastInlineOffset, whiteSpaceOptions );
|
||||
|
||||
if ( shouldBreak ) {
|
||||
|
||||
lines.push( new Line( inline ) );
|
||||
|
||||
inline.offsetX = inline.xoffset;
|
||||
|
||||
// restart the lastInlineOffset as zero.
|
||||
if ( inline.width === 0 ) {
|
||||
lastInlineOffset = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// compute lastInlineOffset normally
|
||||
// except for kerning which won't apply
|
||||
// as there is visually no lefthanded glyph to kern with
|
||||
inline.cumulativeWidth = inline.xadvance + LETTERSPACING;
|
||||
lastInlineOffset = inline.cumulativeWidth;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
lines[ lines.length - 1 ].push( inline );
|
||||
inline.offsetX = lastInlineOffset + inline.xoffset + inline.kerning;
|
||||
|
||||
inline.cumulativeWidth = inline.xadvance + inline.kerning + LETTERSPACING;
|
||||
lastInlineOffset += inline.cumulativeWidth;
|
||||
|
||||
// in case of lineBreak mandatory
|
||||
if( line.length-1 === 1) {
|
||||
|
||||
if ( line[ line.length - 2 ].width === 0 ) {
|
||||
|
||||
// remove the offset of the character following the newline
|
||||
inline.offsetX -= inline.xoffset;
|
||||
lastInlineOffset -= inline.xoffset;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
lastInlineOffset += inlineElement._margin._value.y + inlineElement._padding._value.y;
|
||||
|
||||
} );
|
||||
|
||||
// Compute single line and combined lines dimensions
|
||||
const inlineCollapser = element._whiteSpace._inlineCollapser;
|
||||
|
||||
|
||||
let width = 0, height =0, lineOffsetY = 0;
|
||||
|
||||
// calculates lines
|
||||
lines.forEach( ( line, i ) => {
|
||||
|
||||
// starts by processing whitespace, it will return a collapsed left offset
|
||||
const whiteSpaceOffset = inlineCollapser( line );
|
||||
|
||||
//
|
||||
let lineHeight = 0;
|
||||
let lineBase = 0;
|
||||
|
||||
line.forEach( ( inline ) => {
|
||||
|
||||
lineHeight = Math.max( lineHeight, inline.lineHeight );
|
||||
lineBase = Math.max( lineBase, inline.lineBase );
|
||||
|
||||
inline.offsetX -= whiteSpaceOffset;
|
||||
|
||||
});
|
||||
|
||||
line.lineHeight = lineHeight;
|
||||
line.lineBase = lineBase;
|
||||
|
||||
// const baseLineDelta = lineHeight - lineBase;
|
||||
|
||||
if( i === 0 ){
|
||||
lineOffsetY = -(lineHeight*INTERLINE - lineHeight) * 0.5;
|
||||
} else {
|
||||
lineOffsetY -= lines[i-1].lineHeight*INTERLINE;
|
||||
}
|
||||
|
||||
line.y = lineOffsetY;
|
||||
line.x = 0;
|
||||
|
||||
// process yoffset
|
||||
line.forEach( ( inline ) => {
|
||||
|
||||
inline.offsetY = lineOffsetY - inline.anchor;
|
||||
|
||||
if( inline.lineHeight < line.lineHeight ){
|
||||
inline.offsetY -= line.lineBase- inline.lineBase;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
height += ( line.lineHeight * INTERLINE );
|
||||
// height += ( line.lineHeight);
|
||||
|
||||
//
|
||||
line.width = 0;
|
||||
// if this line have inlines
|
||||
if ( line[ 0 ] ) {
|
||||
|
||||
// compute its width: length from firstInline:LEFT to lastInline:RIGHT
|
||||
// only by the length of its extremities
|
||||
const lastInline = line[ line.length - 1 ];
|
||||
|
||||
// Right + Left ( left is negative )
|
||||
line.width = ( lastInline.offsetX + lastInline.cumulativeWidth + lastInline.paddingRight + lastInline.marginRight ) + line[ 0 ].offsetX;
|
||||
|
||||
width = Math.max( width, line.width);
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
lines.height = height;
|
||||
lines.width = width;
|
||||
|
||||
this._value = lines;
|
||||
|
||||
if( INNER_WIDTH === Infinity ) {
|
||||
|
||||
element._bounds.setChildrenWidth( element, lines.width );
|
||||
|
||||
}
|
||||
|
||||
if( element._height._auto ) {
|
||||
|
||||
element._bounds.setChildrenHeight( element, lines.height );
|
||||
|
||||
}
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent ) {
|
||||
|
||||
parent._autoSize._needsProcess = true;
|
||||
parent._flexDirection._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
element._inlineJustificator._needsProcess = true;
|
||||
element._textAlign._needsProcess = true;
|
||||
|
||||
element._overflow._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param inlineElement
|
||||
* @protected
|
||||
*/
|
||||
_resetInlines ( inlineElement ) {
|
||||
|
||||
// ensure no collapsed remains
|
||||
inlineElement._fontSize.process( inlineElement );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import BaseProperty from './BaseProperty';
|
||||
|
||||
export default class VisibleProperty extends BaseProperty{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} [value=null]
|
||||
*/
|
||||
constructor( propertyId, value = true ) {
|
||||
|
||||
super( 'visible', value, true );
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
element.visible = this._value;
|
||||
|
||||
if( element._parent._value ) {
|
||||
|
||||
element._parent._value._children._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set value( value ) {
|
||||
|
||||
if( ! this.isValid( value) ) return;
|
||||
|
||||
if( this._value !== value ) {
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
export default class SegmentsProperty extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'segments', 1, false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import SegmentsPropertyText from './SegmentsPropertyText';
|
||||
|
||||
export default class SegmentsPropertyInline extends SegmentsPropertyText {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this._value = 'inherit';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import SegmentsProperty from './SegmentsProperty';
|
||||
|
||||
export default class SegmentsPropertyText extends SegmentsProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'segments', 1, false );
|
||||
|
||||
this._notInheritedValue = undefined;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._notInheritedValue = this._value;
|
||||
if ( this._notInheritedValue === 'inherit' ) {
|
||||
|
||||
this._notInheritedValue = this.getInheritedInput( element );
|
||||
|
||||
}
|
||||
|
||||
element._layouter._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number|"inherit"} v
|
||||
*/
|
||||
set value( v ) {
|
||||
|
||||
if ( this._value === v ) return;
|
||||
|
||||
this._value = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
* @return {number}
|
||||
*/
|
||||
get value() {
|
||||
|
||||
if ( this._value === 'inherit' ) return this._notInheritedValue;
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ChildrenBox extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'children', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @private
|
||||
*/
|
||||
this._uis = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._boxes = [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Update requested when :
|
||||
* - New child has been added
|
||||
* - Existing child has been removed
|
||||
*
|
||||
* @param element
|
||||
* @param out
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
|
||||
|
||||
this._compute( element );
|
||||
|
||||
element._layouter._needsUpdate = true;
|
||||
element._renderOrder._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process when :
|
||||
* - Existing child visibility changed
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
this._compute( element );
|
||||
|
||||
element._flexDirection._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
element._overflow._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
_compute( element ) {
|
||||
|
||||
// Stores all children that are box
|
||||
this._uis = element.children.filter( child => child.visible && child.isUI );
|
||||
this._boxes = this._uis.filter( child => child.isBox ).sort( this._sortOrder );
|
||||
|
||||
// @TODO: check if it has changes boxes values? with array join to 'fingerprint'?
|
||||
// computation to remove computation? Does it worth it? When would it worth it?
|
||||
// // Changed order property of children but doesn't impact the output of boxes => Order have change, okay to have more computation
|
||||
// // Removed the Added the same element, at the same position => Rare case
|
||||
// Conclusion : Not worth it at the time of writing
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._uis = null;
|
||||
this._boxes = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sort children according to their .style.order property or fallback on children index
|
||||
*
|
||||
* @param {HTMLElementVR} a
|
||||
* @param {HTMLElementVR} b
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
_sortOrder = ( a, b ) => {
|
||||
|
||||
if( a._order._value < b._order._value ) return -1;
|
||||
if( a._order._value > b._order._value ) return 1;
|
||||
|
||||
// if both children have the same order value, use their children index to order them
|
||||
if( this._uis.indexOf(a) < this._uis.indexOf(b) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ChildrenBox extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'children', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @private
|
||||
*/
|
||||
this._uis = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._boxes = [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Update requested when :
|
||||
* - New child has been added
|
||||
* - Existing child has been removed
|
||||
*
|
||||
* @param element
|
||||
* @param out
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._compute( element );
|
||||
|
||||
element._layouter._needsUpdate = true;
|
||||
element._renderOrder._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process when :
|
||||
* - Existing child visibility changed
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
this._compute( element );
|
||||
|
||||
element._flexDirection._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
element._overflow._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
_compute( element ) {
|
||||
|
||||
// Stores all children that are box
|
||||
this._uis = element.children.filter( child => child.visible && child.isUI );
|
||||
this._boxes = this._uis.filter( child => child.isBox ).sort( this._sortOrder );
|
||||
|
||||
// @TODO: check if it has changes boxes values? with array join to 'fingerprint'?
|
||||
// computation to remove computation? Does it worth it? When would it worth it?
|
||||
// // Changed order property of children but doesn't impact the output of boxes => Order have change, okay to have more computation
|
||||
// // Removed the Added the same element, at the same position => Rare case
|
||||
// Conclusion : Not worth it at the time of writing
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._uis = null;
|
||||
this._boxes = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sort children according to their .style.order property or fallback on children index
|
||||
*
|
||||
* @param {HTMLElementVR} a
|
||||
* @param {HTMLElementVR} b
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
_sortOrder = ( a, b ) => {
|
||||
|
||||
if( a._order._value < b._order._value ) return -1;
|
||||
if( a._order._value > b._order._value ) return 1;
|
||||
|
||||
// if both children have the same order value, use their children index to order them
|
||||
if( this._uis.indexOf(a) < this._uis.indexOf(b) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ChildrenInline extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'children', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._uis = [];
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Update requested when :
|
||||
* - New child has been added
|
||||
* - Existing child has been removed
|
||||
*
|
||||
* @param element
|
||||
* @param out
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// this._compute( element );
|
||||
//
|
||||
// this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Process when :
|
||||
* - Existing child visibility changed
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// this._compute( element );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ _compute( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// this._uis = element.children.filter( child => child.visible && child.isUI );
|
||||
//
|
||||
// this._inlines = this._uis.filter( child => child.isInline );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
// this._inlines = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ChildrenText extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'children', null, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._uis = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._inlines = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<MeshUIBaseElement>}
|
||||
* @internal
|
||||
*/
|
||||
this._boxes = [];
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Update requested when :
|
||||
* - New child has been added
|
||||
* - Existing child has been removed
|
||||
*
|
||||
* @param element
|
||||
* @param out
|
||||
*/
|
||||
update( element, out ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._compute( element );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process when :
|
||||
* - Existing child visibility changed
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
// this._compute( element );
|
||||
|
||||
element._overflow._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
_compute( element ) {
|
||||
|
||||
this._uis = element.children.filter( child => child.visible && child.isUI );
|
||||
|
||||
this._inlines = this._uis.filter( child => child.isInline ).sort( this._sortOrder );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._inlines = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sort children according to their .style.order property or fallback on children index
|
||||
*
|
||||
* @param {HTMLElementVR} a
|
||||
* @param {HTMLElementVR} b
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
_sortOrder = ( a, b ) => {
|
||||
|
||||
if( a._order._value < b._order._value ) return -1;
|
||||
if( a._order._value > b._order._value ) return 1;
|
||||
|
||||
// if both children have the same order value, use their children index to order them
|
||||
if( this._uis.indexOf(a) < this._uis.indexOf(b) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { Object3D } from 'three';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ParentProperty extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super('parent', null, false);
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* Update when :
|
||||
* - element has been added
|
||||
* - element has been removed
|
||||
*
|
||||
* @param element
|
||||
* @param out
|
||||
*/
|
||||
update( element, out ) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
if ( element.parent && element.parent.isUI ) {
|
||||
|
||||
this._value = element.parent;
|
||||
// this.position.z = this.getOffset();
|
||||
|
||||
} else {
|
||||
|
||||
this._value = null;
|
||||
|
||||
}
|
||||
|
||||
// @TODO : parentElement
|
||||
// // set elements as root
|
||||
// if ( element.isBlock && !this._value ) {
|
||||
//
|
||||
// ThreeMeshUI.addRoot( element );
|
||||
// element.pseudoClassList.add('root');
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// ThreeMeshUI.removeRoot( element );
|
||||
// element.pseudoClassList.remove('root');
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
set value( value ) {
|
||||
|
||||
console.warn('ParentProperty is readonly');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {MeshUIBaseElement}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {(p:Object3D)=>boolean } conditionCallback
|
||||
*/
|
||||
find( conditionCallback ) {
|
||||
|
||||
if( this._value ) {
|
||||
|
||||
if( conditionCallback( this._value) ) {
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
return this._value._parent.find( conditionCallback );
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._value = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
import Frame from '../../../frame/Frame';
|
||||
|
||||
export default class RendererPropertyBox extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'renderer' );
|
||||
|
||||
}
|
||||
|
||||
|
||||
render( element ) {
|
||||
|
||||
if( !element._backgroundMesh ) {
|
||||
|
||||
element.setBackgroundMesh( new Frame(element) );
|
||||
|
||||
}
|
||||
|
||||
element.performAfterUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';
|
||||
import { Mesh } from 'three';
|
||||
|
||||
export default class RendererPropertyInline extends BaseProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super('renderer');
|
||||
|
||||
}
|
||||
|
||||
|
||||
render(element) {
|
||||
|
||||
if (!element._inlines._value || !element._inlines._value.length) return;
|
||||
|
||||
const charactersAsGeometries = element._inlines._value.map(
|
||||
inline =>
|
||||
element._font._fontVariant.getGeometricGlyph(inline, element)
|
||||
.translate(inline.offsetX, inline.offsetY, 0)
|
||||
|
||||
);
|
||||
|
||||
const mergedGeom = mergeGeometries(charactersAsGeometries);
|
||||
|
||||
element.setFontMesh(new Mesh(mergedGeom, element.fontMaterial));
|
||||
|
||||
element._fontMesh.renderOrder = Infinity;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
import Frame from '../../../frame/Frame';
|
||||
|
||||
export default class RendererPropertyInlineBox extends BaseProperty{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'renderer' );
|
||||
|
||||
}
|
||||
|
||||
|
||||
render( element ) {
|
||||
|
||||
if( !element._backgroundMesh ) {
|
||||
|
||||
element.setBackgroundMesh( new Frame(element) );
|
||||
|
||||
}
|
||||
|
||||
element._backgroundMesh.position.x = element._inlines._value[0].offsetX + element._inlines._value[0].width/2;
|
||||
// element._backgroundMesh.position.y = element._inlines._value[0].offsetY + element._inlines._value[0].lineBase/4;
|
||||
element._backgroundMesh.position.y = element._inlines._value[0].offsetY + element._inlines._value[0].lineBase/2;
|
||||
|
||||
element._bounds.render( element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import RendererPropertyBox from './RendererPropertyBox';
|
||||
|
||||
export default class RendererPropertyText extends RendererPropertyBox{
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'renderer' );
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
render( element ) {
|
||||
|
||||
super.render( element );
|
||||
|
||||
for ( const inlineElement of element._children._inlines ) {
|
||||
|
||||
inlineElement._renderer.render( inlineElement );
|
||||
|
||||
}
|
||||
|
||||
element.performAfterUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
|
||||
|
||||
export default class PositionProperty extends SubStyleProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'position', 'static', true );
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
|
||||
this._value = 'static';
|
||||
this._needsUpdate = false;
|
||||
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
_computeFromInherited( element ) {
|
||||
super._computeFromInherited( element );
|
||||
|
||||
//console.log( "Position update")
|
||||
// require parent to compute children -> bounds -> etc...
|
||||
if( element._parent._value ) element._parent._value._children._needsProcess = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['static', 'absolute'];
|
||||
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) position value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import PositionProperty from './PositionProperty';
|
||||
|
||||
export default class PositionPropertyBox extends PositionProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'position');
|
||||
|
||||
}
|
||||
|
||||
update( element, out ) {
|
||||
|
||||
super.update( element, out );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
import { Color } from 'three';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class StyleColorProperty extends SubStyleProperty {
|
||||
|
||||
constructor( propertyId, defaultValue ) {
|
||||
|
||||
super( propertyId, defaultValue, false );
|
||||
|
||||
/**
|
||||
* @type {Color}
|
||||
* @protected
|
||||
*/
|
||||
this._value = new Color();
|
||||
|
||||
this.output = this._outputValue;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
if( this._input !== 'inherit' ) {
|
||||
|
||||
this._value.set(this._input);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set inline( value ) {
|
||||
|
||||
// Colors are too wide to perform validation checks each time
|
||||
// if( ! this.isValidValue( value ) ) return;
|
||||
|
||||
this._input = this._inline = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
|
||||
export default class StyleEmptyProperty extends SubStyleProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
*/
|
||||
constructor( propertyId = 'untitled' ) {
|
||||
|
||||
super( propertyId , undefined, false);
|
||||
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ output( out ) {} /* eslint-enable no-unused-vars */
|
||||
}
|
||||
|
||||
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
|
||||
export default class StyleFactorProperty extends SubStyleProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} defaultValue
|
||||
*/
|
||||
constructor( propertyId, defaultValue ) {
|
||||
|
||||
super( propertyId, defaultValue, true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
this._allowsInherit = false;
|
||||
|
||||
this._input = defaultValue;
|
||||
|
||||
this._value = defaultValue;
|
||||
|
||||
this.output = this._outputValue;
|
||||
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
}
|
||||
|
||||
|
||||
_outputValue( out ) {
|
||||
|
||||
out[this._id] = this._inheritedInput;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if ( value < 0 && value > 1.0 ) {
|
||||
|
||||
console.warn( `(.style) styleFactorProperty('${this.id}') value '${value}' is not valid)` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
|
||||
export default class StyleNumberProperty extends SubStyleProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {any} defaultValue
|
||||
*/
|
||||
constructor( propertyId, defaultValue ) {
|
||||
|
||||
super( propertyId, defaultValue );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {Number}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
import { Vector4 } from 'three';
|
||||
import SubStyleProperty from './SubStyleProperty';
|
||||
|
||||
export default class StyleVector4Property extends SubStyleProperty {
|
||||
|
||||
constructor( propertyId, defaultValue ) {
|
||||
|
||||
super( propertyId, defaultValue, false );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector4}
|
||||
* @private
|
||||
*/
|
||||
this._input = new Vector4(0,0,0,0);
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @internal
|
||||
*/
|
||||
this._inline = null;
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @type {Vector4}
|
||||
* @protected
|
||||
*/
|
||||
this._value = new Vector4(0,0,0,0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {Vector4}
|
||||
*/
|
||||
get value(){
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._vector4ValueSetter( this._value, this._input );
|
||||
|
||||
}
|
||||
|
||||
set inline( value ) {
|
||||
|
||||
this._vector4ValueSetter( this._input, value );
|
||||
|
||||
if( this._input.equals( this._value) ) return;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set top( v ) {
|
||||
|
||||
if( this._input.x === v ) return;
|
||||
|
||||
this._input.x = v;
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get top() { return this._input.x; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set right( v ) {
|
||||
|
||||
if( this._input.y === v ) return;
|
||||
|
||||
this._input.y = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get right() { return this._input.y; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set bottom( v ) {
|
||||
if( this._input.z === v ) return;
|
||||
|
||||
this._input.z = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get bottom() { return this._input.z; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set left( v ) {
|
||||
|
||||
if( this._input.w === v ) return;
|
||||
|
||||
this._input.w = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get left() { return this._input.w; }
|
||||
|
||||
dispose(){
|
||||
|
||||
this._computed = null;
|
||||
this._inline = null;
|
||||
this._input = null;
|
||||
this._output = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Vector4} vector4
|
||||
* @param {Vector4|Array.<Number>|Number|String} value
|
||||
* @protected
|
||||
*/
|
||||
_vector4ValueSetter( vector4, value ) {
|
||||
|
||||
if ( value instanceof Vector4 ) {
|
||||
|
||||
vector4.copy( value );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ( typeof value === 'string' || value instanceof String ) {
|
||||
|
||||
value = value.split( ' ' );
|
||||
|
||||
}
|
||||
|
||||
if ( Array.isArray( value ) ) {
|
||||
|
||||
value = value.map( v => parseFloat( v ) );
|
||||
|
||||
switch ( value.length ) {
|
||||
|
||||
case 1:
|
||||
vector4.setScalar( value[ 0 ] );
|
||||
return;
|
||||
|
||||
case 2:
|
||||
vector4.x = vector4.z = value[ 0 ];
|
||||
vector4.y = vector4.w = value[ 1 ];
|
||||
return;
|
||||
|
||||
case 3:
|
||||
vector4.x = value[ 0 ];
|
||||
vector4.y = value[ 1 ];
|
||||
vector4.z = value[ 2 ];
|
||||
return;
|
||||
|
||||
case 4:
|
||||
vector4.x = value[ 0 ];
|
||||
vector4.y = value[ 1 ];
|
||||
vector4.z = value[ 2 ];
|
||||
vector4.w = value[ 3 ];
|
||||
return;
|
||||
|
||||
default:
|
||||
console.error( 'StyleVector4Property::set() Four Dimension property had more than four values' );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( !isNaN( value ) ) {
|
||||
|
||||
vector4.setScalar( value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
import BaseProperty from '../BaseProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
export default class SubStyleProperty extends BaseProperty{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {boolean} [primitive=true]
|
||||
* @param {any} defaultValue
|
||||
*/
|
||||
constructor( propertyId, defaultValue, primitive = true) {
|
||||
|
||||
super( propertyId, 'unset', primitive );
|
||||
|
||||
/**
|
||||
* @type {any}
|
||||
* @internal
|
||||
*/
|
||||
this._input = 'inherit';
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @protected
|
||||
*/
|
||||
this._allowsInherit = true;
|
||||
|
||||
/**
|
||||
* The input value that won't be 'inherit'
|
||||
* @type {any}
|
||||
* @protected
|
||||
*/
|
||||
this._inheritedInput = undefined;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @internal
|
||||
*/
|
||||
this._inline = undefined;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @param {Object.<string,any> } out
|
||||
*/
|
||||
update( element, out ) {
|
||||
|
||||
if( !this._allowsInherit ) {
|
||||
|
||||
this._inheritedInput = this.getInheritedInput( element );
|
||||
|
||||
}
|
||||
|
||||
this.computeOutputValue( element );
|
||||
|
||||
// rebuild same properties on children 'inheritance'
|
||||
for ( const childUIElement of element._children._uis ) {
|
||||
|
||||
const property = childUIElement[`_${this._id}`];
|
||||
const target = property._input ? property._input : property._value;
|
||||
|
||||
if( target === 'inherit' ) childUIElement[`_${this._id}`]._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
this.output( out );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = this._input;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
_computeFromInherited( element ) {
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @deprecated
|
||||
* @param {any} v
|
||||
*/
|
||||
set value( v ) {
|
||||
|
||||
console.warn(".(style) sub-property cannot be directly set. It must comes from inline or computed setter.")
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
*/
|
||||
set inline( value ) {
|
||||
|
||||
if( ! this.isValidValue( value ) ) return;
|
||||
|
||||
if( value === this._inline ) {
|
||||
|
||||
// do nothing no update, the value hasn't changed
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this._input = this._inline = value;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {any}
|
||||
*/
|
||||
get inline() { return this._inline; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
*/
|
||||
isValidValue( value ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
getInheritedInput ( element ) {
|
||||
|
||||
if( this._input !== 'inherit' ) return this._input;
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent ) {
|
||||
|
||||
return parent[`_${this._id}`].getInheritedInput( parent )
|
||||
|
||||
}
|
||||
|
||||
return this.getDefaultValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import StyleColorProperty from '../StyleColorProperty';
|
||||
|
||||
|
||||
export default class BackgroundColorProperty extends StyleColorProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'backgroundColor', defaultValue, false );
|
||||
|
||||
this._input = 'transparent';
|
||||
|
||||
this._allowsInherit = false;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
|
||||
element._backgroundMesh.visible = !(this._input === 'none' || this._input === 'transparent');
|
||||
|
||||
if( this._input === 'inherit' ) {
|
||||
|
||||
// @TODO: Background should not be inheritable
|
||||
this._value.set(this.getInheritedInput( element ));
|
||||
|
||||
} else if( !(this._input === 'transparent' || this._input === 'none') ) {
|
||||
this._value.set( this._input );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import StyleColorProperty from '../StyleColorProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class BackgroundColorPropertyInline extends StyleColorProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'backgroundColor', defaultValue, false );
|
||||
|
||||
this._allowsInherit = false;
|
||||
|
||||
this._input = 0x000000;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// @TODO : Changes multiple mesh visibility
|
||||
// element._backgroundMesh.visible = !(this._input === 'none' || this._input === 'transparent');
|
||||
|
||||
if( this._input === 'inherit' ) {
|
||||
|
||||
this._value.set(this.getInheritedInput( element ));
|
||||
|
||||
} else {
|
||||
|
||||
this._value.set( this._input );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { Texture, Vector2 } from 'three';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
|
||||
export default class BackgroundImage extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'backgroundImage', defaultValue, true );
|
||||
|
||||
|
||||
this._input = null;
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @internal
|
||||
*/
|
||||
this._textureSize = new Vector2( 1,1 );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {any|Texture|null}
|
||||
*/
|
||||
get value() {
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
output( out ) {
|
||||
|
||||
out[this._id] = this._value;
|
||||
|
||||
out['tSize'] = this._textureSize;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// @TODO : URL
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
// ?
|
||||
// out[this.id] = this._value;
|
||||
|
||||
if( this._value instanceof Texture && !this._value.image ) {
|
||||
console.warn( `ThreeMeshUI - .backgroundImage :: Please provide preloaded texture in order to have accurate sizing.`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
if( this._value ) {
|
||||
|
||||
this._textureSize.set( this._value.image.width, this._value.image.height );
|
||||
|
||||
} else {
|
||||
|
||||
this._textureSize.set( 1 , 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// @TODO : Texture or URL() or String or ID ?
|
||||
//console.log( "todo, validate image value", value);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
/**
|
||||
* @property {"cover"|"contain"|"stretch"} value
|
||||
*/
|
||||
export default class BackgroundSize extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'backgroundSize', defaultValue, true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
this.output = this._outputValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['cover','contain','stretch'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) backgroundSize value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+513
@@ -0,0 +1,513 @@
|
||||
import { Vector2 } from 'three';
|
||||
import * as Units from '../../../../utils/Units';
|
||||
import StyleVector4Property from '../StyleVector4Property';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { Vector4 } from 'three';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class BorderRadius extends StyleVector4Property {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Vector4} defaultValue
|
||||
*/
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'borderRadius', defaultValue );
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector4}
|
||||
* @private
|
||||
*/
|
||||
this._valueUV = this._value.clone();
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector4}
|
||||
* @private
|
||||
*/
|
||||
this._input = new Vector4(0,0,0,0);
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._mediation = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @private
|
||||
*/
|
||||
this._cornerTL = new Vector2(0, 1);
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @private
|
||||
*/
|
||||
this._cornerTR = new Vector2(1, 1);
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @private
|
||||
*/
|
||||
this._cornerBR = new Vector2(1, 0);
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @private
|
||||
*/
|
||||
this._cornerBL = new Vector2(0, 0);
|
||||
|
||||
const mediationTop = new BorderRadiusMediator( this._valueUV, [ 'x', 'y' ] ); // bottom
|
||||
const mediationBottom = new BorderRadiusMediator( this._valueUV, [ 'z', 'w'] ); // top
|
||||
const mediationLeft = new BorderRadiusMediator( this._valueUV, [ 'x', 'w'] ); // right
|
||||
const mediationRight = new BorderRadiusMediator( this._valueUV, [ 'y', 'z'] ); // left
|
||||
|
||||
mediationTop.complementaryMediation = mediationBottom;
|
||||
mediationBottom.complementaryMediation = mediationTop;
|
||||
mediationLeft.complementaryMediation = mediationRight;
|
||||
mediationRight.complementaryMediation = mediationLeft;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<BorderRadiusMediator>}
|
||||
* @private
|
||||
*/
|
||||
this._sideMediators = [ mediationTop, mediationBottom, mediationLeft, mediationRight ];
|
||||
|
||||
this._units = Units.WORLD_UNITS;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} units
|
||||
*/
|
||||
set units( units ) {
|
||||
|
||||
this._units = Units.validateUnits( units );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get units() { return this._units; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} v
|
||||
*/
|
||||
set mediation ( v) {
|
||||
|
||||
if( v !== this._mediation ){
|
||||
|
||||
this._mediation = v;
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get mediation () { return this._mediation; }
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object.<string,any>} out
|
||||
*/
|
||||
|
||||
output( out ) {
|
||||
|
||||
out["cornerTL"] = this._cornerTL;
|
||||
out["cornerTR"] = this._cornerTR;
|
||||
out["cornerBR"] = this._cornerBR;
|
||||
out["cornerBL"] = this._cornerBL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
/* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._vector4ValueSetter( this._value, this._input );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process( element ){ /* eslint-enable no-unused-vars */
|
||||
|
||||
this._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
render( element ){
|
||||
|
||||
this._valueUV.copy( this._value );
|
||||
|
||||
const elementWidth = element._bounds._offsetWidth;
|
||||
const elementHeight = element._bounds._offsetHeight;
|
||||
|
||||
// @TODO: Units process could be strategies
|
||||
if( this._units === Units.PERCENT ) {
|
||||
this._valueUV.divideScalar(100);
|
||||
}
|
||||
|
||||
// @TODO: Units process could be strategies
|
||||
if( this._units === Units.WORLD_UNITS ) {
|
||||
this._valueUV.divideScalar( Math.min(elementWidth , elementHeight) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mediate
|
||||
* Be sure no side is greater than 1 (uv units)
|
||||
*/
|
||||
if( this._mediation ) {
|
||||
|
||||
do {
|
||||
|
||||
this._sideMediators.forEach( srm => srm.computeValue() );
|
||||
this._sideMediators.sort( ( a, b ) => {
|
||||
return a.value < b.value ? 1 : -1
|
||||
} );
|
||||
|
||||
if ( this._sideMediators[ 0 ].value > 1.0 ) {
|
||||
this._sideMediators[ 0 ].mediate();
|
||||
}
|
||||
|
||||
} while ( this._sideMediators[ 0 ].value > 1.0 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
let sX = elementWidth > elementHeight ? elementHeight/elementWidth : 1;
|
||||
let sY = elementWidth < elementHeight ? elementWidth/elementHeight : 1;
|
||||
|
||||
// Do not scale pourcentages, allowing ovals
|
||||
if( this._units === Units.PERCENT ){
|
||||
sX = sY = 1.0;
|
||||
}
|
||||
|
||||
this._cornerTL.x = this._valueUV.x * sX;
|
||||
this._cornerTL.y = 1 - (this._valueUV.x * sY );
|
||||
|
||||
this._cornerTR.x = 1 - (this._valueUV.y * sX );
|
||||
this._cornerTR.y = 1 - (this._valueUV.y * sY );
|
||||
|
||||
this._cornerBR.x = 1 - (this._valueUV.z * sX );
|
||||
this._cornerBR.y = this._valueUV.z * sY;
|
||||
|
||||
this._cornerBL.x = this._valueUV.w * sX;
|
||||
this._cornerBL.y = this._valueUV.w * sY;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
for ( const sideMediator of this._sideMediators ) {
|
||||
|
||||
sideMediator.dispose();
|
||||
|
||||
}
|
||||
|
||||
this._sideMediators = null;
|
||||
|
||||
this._cornerTL = null;
|
||||
this._cornerTR = null;
|
||||
this._cornerBR = null;
|
||||
this._cornerBL = null;
|
||||
|
||||
super.dispose();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set topLeft( v ) {
|
||||
|
||||
if( this._input.x === v ) return;
|
||||
|
||||
this._input.x = v;
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get topLeft() { return this._input.x; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set topRight( v ) {
|
||||
|
||||
if( this._input.y === v ) return;
|
||||
|
||||
this._input.y = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get topRight() { return this._input.y; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set bottomRight( v ) {
|
||||
if( this._input.z === v ) return;
|
||||
|
||||
this._input.z = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get bottomRight() { return this._input.z; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} v
|
||||
*/
|
||||
set bottomLeft( v ) {
|
||||
|
||||
if( this._input.w === v ) return;
|
||||
|
||||
this._input.w = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get bottomLeft() { return this._input.w; }
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Number} v
|
||||
*/
|
||||
set top( v ) {
|
||||
|
||||
if( this._input.x === v && this._input.y === v ) return;
|
||||
|
||||
this._input.x = this._input.y = v;
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {number}
|
||||
*/
|
||||
get top() { return (this._input.x + this._input.y) / 2; }
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Number} v
|
||||
*/
|
||||
set right( v ) {
|
||||
|
||||
if( this._input.y === v && this._input.z === v ) return;
|
||||
|
||||
this._input.y = this._input.z = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {number}
|
||||
*/
|
||||
get right() { return (this._input.y + this._input.z) / 2; }
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Number} v
|
||||
*/
|
||||
set bottom( v ) {
|
||||
if( this._input.z === v && this._input.w === v ) return;
|
||||
|
||||
this._input.z = this._input.w = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {number}
|
||||
*/
|
||||
get bottom() { return (this._input.z + this._input.w) / 2; }
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Number} v
|
||||
*/
|
||||
set left( v ) {
|
||||
|
||||
if( this._input.w === v && this._input.x === v ) return;
|
||||
|
||||
this._input.w = this._input.x = v;
|
||||
this._needsUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {number}
|
||||
*/
|
||||
get left() { return (this._input.w + this._input.x) / 2; }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Job: Contains two border radiuses values of the same side
|
||||
* If their sums is greater than 1 (uv units) mediation could occurs
|
||||
*/
|
||||
class BorderRadiusMediator {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Vector4} borderRadiuses
|
||||
* @param {Array.<string>} sideProperties
|
||||
*/
|
||||
constructor( borderRadiuses, sideProperties ) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Vector4}
|
||||
* @private
|
||||
*/
|
||||
this._borderRadiuses = borderRadiuses;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @private
|
||||
*/
|
||||
this._sideProperties = sideProperties;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {BorderRadiusMediator|null}
|
||||
* @private
|
||||
*/
|
||||
this._complementaryMediation = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this._value = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sum of the border radius of that side
|
||||
* @returns {number}
|
||||
*/
|
||||
get value(){ return this._value; }
|
||||
|
||||
/**
|
||||
* A complementary side mediation ie: For top, complementary is bottom
|
||||
* @param {BorderRadiusMediator} brm
|
||||
*/
|
||||
set complementaryMediation( brm ){
|
||||
|
||||
this._complementaryMediation = brm;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all side property to compute the value of that side
|
||||
*/
|
||||
computeValue(){
|
||||
|
||||
let totalRadius = 0;
|
||||
|
||||
for ( const radius of this._sideProperties ) {
|
||||
|
||||
totalRadius += this._borderRadiuses[radius];
|
||||
|
||||
}
|
||||
|
||||
this._value = totalRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {boolean} [mediateOpposite=true]
|
||||
*/
|
||||
mediate( mediateOpposite = true ){
|
||||
|
||||
// Mediation only occurs when sum of radius are greater than 1 (uv units)
|
||||
if( this._value < 1.0 ) return;
|
||||
|
||||
// Simply divide each component by the sum
|
||||
for ( const radius of this._sideProperties ) {
|
||||
|
||||
this._borderRadiuses[radius] /= this._value;
|
||||
|
||||
}
|
||||
|
||||
if( mediateOpposite ) {
|
||||
|
||||
// and also mediate the complementary
|
||||
this._complementaryMediation.mediate( false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._complementaryMediation = null;
|
||||
this._borderRadiuses = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
import * as Units from '../../../../utils/Units';
|
||||
import StyleVector4Property from '../StyleVector4Property';
|
||||
|
||||
export default class BorderWidth extends StyleVector4Property{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param defaultValue
|
||||
*/
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super ( 'borderWidth', defaultValue, false );
|
||||
|
||||
|
||||
this._valueUV = this._value.clone();
|
||||
|
||||
// configure
|
||||
this.output = this._outputValue;
|
||||
|
||||
this._units = Units.WORLD_UNITS;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} units
|
||||
*/
|
||||
set units( units ) {
|
||||
|
||||
this._units = Units.validateUnits( units );
|
||||
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get units() { return this._units; }
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
computeOutputValue( element) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._vector4ValueSetter( this._value, this._input );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
element._bounds._needsUpdate = true;
|
||||
element._layouter._needsUpdate = true;
|
||||
|
||||
// for ( let i = 0; i < element._children._uis.length; i++ ) {
|
||||
// const child = element._children._uis[ i ];
|
||||
// element._bounds._needsUpdate = true;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
_outputValue( out ) {
|
||||
out[this._id] = this._valueUV;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
process( element ) {
|
||||
this._needsRender = true;
|
||||
element._borderRadius._needsRender = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
render( element ) {
|
||||
|
||||
this._valueUV.copy( this._value );
|
||||
|
||||
const offsetWidth = element._bounds._offsetWidth;
|
||||
const offsetHeight = element._bounds._offsetHeight;
|
||||
|
||||
// @TODO: Units process could be strategies
|
||||
if( this._units === Units.PERCENT ){
|
||||
|
||||
console.log( "Percent" );
|
||||
// this._valueUV.divideScalar( 100 );
|
||||
console.log( this._valueUV );
|
||||
|
||||
}
|
||||
|
||||
// @TODO: Units process could be strategies
|
||||
if( this._units === Units.WORLD_UNITS ) {
|
||||
|
||||
if( offsetWidth !== 0) {
|
||||
this._valueUV.w /= offsetWidth;
|
||||
this._valueUV.y /= offsetWidth;
|
||||
}
|
||||
|
||||
|
||||
if( offsetHeight !== 0 ) {
|
||||
this._valueUV.x /= offsetHeight;
|
||||
this._valueUV.z /= offsetHeight;
|
||||
}
|
||||
|
||||
|
||||
} else if( this._units === Units.UV ) {
|
||||
|
||||
// @TODO: Units process could be strategies
|
||||
|
||||
if( offsetWidth !== 0 ) {
|
||||
const sX = offsetWidth > offsetHeight ? offsetHeight/offsetWidth : 1;
|
||||
this._valueUV.y *= sX;
|
||||
this._valueUV.w *= sX;
|
||||
}
|
||||
|
||||
if( offsetHeight !== 0 ) {
|
||||
const sY = offsetWidth < offsetHeight ? offsetWidth/offsetHeight : 1;
|
||||
|
||||
this._valueUV.x *= sY;
|
||||
this._valueUV.z *= sY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class BoxSizing extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'boxSizing', defaultValue );
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
element._bounds._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
const AVAILABLE_VALUES = ['border-box', 'content-box'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) boxSizing value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import StyleSideProperty from './StyleSideProperty';
|
||||
|
||||
export default class HeightProperty extends StyleSideProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'height' );
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
super.computeOutputValue( element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import StyleVector4Property from '../StyleVector4Property';
|
||||
import { Vector4 } from 'three';
|
||||
|
||||
export default class MarginProperty extends StyleVector4Property {
|
||||
|
||||
constructor() {
|
||||
|
||||
super('margin', new Vector4(0,0,0,0) )
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
super.computeOutputValue( element );
|
||||
|
||||
element._renderer._needsRender = true;
|
||||
|
||||
if( element._parent._value ){
|
||||
element._parent._value._flexDirection._needsProcess = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import StyleVector4Property from '../StyleVector4Property';
|
||||
import { Vector4 } from 'three';
|
||||
|
||||
export default class PaddingProperty extends StyleVector4Property {
|
||||
|
||||
constructor() {
|
||||
|
||||
super('padding', new Vector4(0,0,0,0) )
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
super.computeOutputValue( element );
|
||||
|
||||
element._bounds._needsUpdate = true;
|
||||
element._bounds._needsRender = true;
|
||||
element._layouter._needsProcess = true;
|
||||
element._renderer._needsRender = true;
|
||||
|
||||
if( element._parent._value ){
|
||||
element._parent._value._layouter._needsProcess = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class StyleSideProperty extends SubStyleProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} propertyId
|
||||
* @param {number} defaultValue
|
||||
*/
|
||||
constructor( propertyId, defaultValue = null ) {
|
||||
|
||||
super( propertyId, defaultValue, true );
|
||||
|
||||
/**
|
||||
* @type {any}
|
||||
* @internal
|
||||
*/
|
||||
this._input = 'auto';
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @internal
|
||||
*/
|
||||
this._auto = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @internal
|
||||
*/
|
||||
this._relative = false;
|
||||
|
||||
this._updateRequired = true;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
*/
|
||||
set inline( value ) {
|
||||
|
||||
if( ! this.isValidValue( value ) ) return;
|
||||
|
||||
if( value === this._inline ) {
|
||||
|
||||
// do nothing no update, the value hasn't changed
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this._inline = value;
|
||||
|
||||
if( this._input === this._inline ) return;
|
||||
|
||||
this._parseInput();
|
||||
|
||||
}
|
||||
|
||||
get inline() { return this._inline; }
|
||||
|
||||
_parseInput() {
|
||||
|
||||
let updateRequired = true;
|
||||
|
||||
// Inline has priority if set
|
||||
if( this._inline !== undefined && this._inline !== 'unset' ) {
|
||||
|
||||
this._input = this._inline;
|
||||
|
||||
}
|
||||
// or fallback on computed
|
||||
else if( this._computed !== undefined ) {
|
||||
|
||||
// do not require an update if the value remains
|
||||
if( this._computed === this._input ) updateRequired = false;
|
||||
this._input = this._computed;
|
||||
|
||||
}
|
||||
// or fallback on default value
|
||||
else {
|
||||
|
||||
updateRequired = this._input === 'inherit';
|
||||
|
||||
}
|
||||
|
||||
if( updateRequired ) {
|
||||
|
||||
this._auto = !this._input || this._input === 'auto';
|
||||
|
||||
|
||||
if ( !this._auto ) {
|
||||
|
||||
// string can be percentages
|
||||
// console.log( this._input, typeof this._input, this._input.endsWith('%'))
|
||||
if ( ( typeof this._input === 'string' || this._input instanceof String ) && this._input.endsWith( '%' ) ) {
|
||||
|
||||
|
||||
this._relative = true;
|
||||
this._value = 0;
|
||||
|
||||
|
||||
const floatValue = parseFloat( this._input.replace( '%', '' ).trim() );
|
||||
if ( !isNaN( floatValue ) ) {
|
||||
|
||||
this._value = floatValue / 100;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this._relative = false;
|
||||
this._value = this._input;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
this._relative = false;
|
||||
|
||||
}
|
||||
|
||||
this._needsUpdate = this._updateRequired = updateRequired;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update( element, out ) {
|
||||
|
||||
if( this._updateRequired ) {
|
||||
|
||||
this._updateRequired = false;
|
||||
|
||||
if( !this._allowsInherit ) {
|
||||
|
||||
this._inheritedInput = this.getInheritedInput( element );
|
||||
|
||||
}
|
||||
|
||||
this.computeOutputValue( element );
|
||||
|
||||
// rebuild same properties on children 'inheritance'
|
||||
for ( const childUIElement of element._children._uis ) {
|
||||
|
||||
childUIElement[`_${this._id}`]._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
this.output( out );
|
||||
|
||||
if( element._parent._value ) element._parent._value._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
element._bounds._needsUpdate = true;
|
||||
element._renderer._needsRender = true;
|
||||
// element._autoSize._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
getInheritedInput ( element ) {
|
||||
|
||||
if( this._input !== 'inherit' && !this._auto ) return this._input;
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent ) {
|
||||
|
||||
return parent[`_${this._id}`].getInheritedInput( parent )
|
||||
|
||||
}
|
||||
|
||||
return this.getDefaultValue();
|
||||
|
||||
}
|
||||
|
||||
getDefaultValue() {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import StyleSideProperty from './StyleSideProperty';
|
||||
|
||||
export default class WidthProperty extends StyleSideProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'width' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
const AVAILABLE_VALUES = ['start', 'center', 'end', 'stretch'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
const _isValid = function ( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) alignItems value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
export default class AlignItemsProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'alignItems', 'inherit', true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
import AlignItemsProperty from './AlignItemsProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from './../../../../core/elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class AlignItemsPropertyBox extends AlignItemsProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super();
|
||||
|
||||
// configure this property
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = true;
|
||||
|
||||
// strategies
|
||||
/**
|
||||
*
|
||||
* @type {(element:MeshUIBaseElement, (child:MeshUIBaseElement, parentOffset:number )=> number ) => void }
|
||||
* @private
|
||||
*/
|
||||
this._process = this.emptyStrategyLogic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {(child:MeshUIBaseElement, parentOffset:number )=> number}
|
||||
* @private
|
||||
*/
|
||||
this._childAlign = this.emptyStrategyLogic;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
computeOutputValue( element ) {
|
||||
|
||||
// Stretch : Current or previous requires a bounds update of children
|
||||
// if( this._value === 'stretch' || this._input === 'stretch' ) {
|
||||
//
|
||||
// for ( let i = 0; i < element._children._boxes.length; i++ ) {
|
||||
// element._children._boxes[ i ]._bounds._needsProcess = true;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
switch( element._flexDirection._value ) {
|
||||
|
||||
case 'row':
|
||||
case 'row-reverse':
|
||||
this._process = _processRow;
|
||||
switch ( this._value ) {
|
||||
case 'start':
|
||||
this._childAlign = _alignChildRowStart;
|
||||
break;
|
||||
case 'end':
|
||||
this._childAlign = _alignChildRowEnd;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._childAlign = _alignChild;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'column':
|
||||
case 'column-reverse':
|
||||
this._process = _processColumn;
|
||||
|
||||
switch ( this._value ) {
|
||||
case 'start':
|
||||
this._childAlign = _alignChildColumnStart;
|
||||
break;
|
||||
case 'end':
|
||||
this._childAlign = _alignChildColumnEnd;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._childAlign = _alignChild;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
this._needsProcess = true;
|
||||
// @TODO: Store children here
|
||||
element._autoSize._needsProcess = true;
|
||||
|
||||
element._flexDirection._needsProcess = true; //not mandatory
|
||||
element._justifyContent._needsProcess = true;
|
||||
|
||||
this._needsProcess = true;
|
||||
element._fontSize._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
process( element ) {
|
||||
|
||||
// return;
|
||||
// if( !element._children._boxes.length ) return;
|
||||
|
||||
this._process( element, this._childAlign );
|
||||
|
||||
// @TODO : Could be strategized
|
||||
let snap = 'center';
|
||||
let snapXon = 'center';
|
||||
let snapYon = 'center';
|
||||
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
if( element._flexDirection._value.indexOf('column') !== -1 ) {
|
||||
|
||||
if( this._value === 'start' ) {
|
||||
snap = snapXon = 'left';
|
||||
}else if( this._value === 'end' ){
|
||||
snap = snapXon ='right';
|
||||
}else {
|
||||
snap = 'centerX';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* eslint-disable no-lonely-if */
|
||||
if( this._value === 'start' ) {
|
||||
snap = snapYon = 'top';
|
||||
}else if( this._value === 'end' ){
|
||||
snap = snapYon ='bottom';
|
||||
}else{
|
||||
snap = 'centerY';
|
||||
}
|
||||
/* eslint-enable no-lonely-if */
|
||||
|
||||
}
|
||||
|
||||
// apply 4 directional padding and borders
|
||||
let y = -(padding.x - padding.z) / 2 - (border.x - border.z) / 2;
|
||||
let x = -(padding.y - padding.w) / 2 - ( border.y - border.w ) / 2;
|
||||
|
||||
|
||||
if( snapXon === 'left' ) {
|
||||
|
||||
x = (padding.w - padding.y) / 2 + (border.w - border.y) / 2;
|
||||
|
||||
} else if( snapXon === 'right' ) {
|
||||
|
||||
x = - ( padding.y - padding.w ) / 2 - ( border.y - border.w ) / 2;
|
||||
|
||||
}
|
||||
|
||||
if( snapYon === 'top' ) {
|
||||
|
||||
y = - (padding.x - padding.z) / 2 - (border.x - border.z) / 2;
|
||||
|
||||
} else if( snapYon === 'bottom' ) {
|
||||
|
||||
y = (padding.z - padding.x) / 2 + (border.z - border.x) / 2;
|
||||
|
||||
}
|
||||
|
||||
|
||||
element._children._boxes.forEach( ( child ) => {
|
||||
let marginX = 0;
|
||||
let marginY = 0;
|
||||
// let marginY = ( -child._margin._value.x + child._margin._value.z ) /2;
|
||||
// let marginY = ( -child._margin._value.x + child._margin._value.z ) /2;
|
||||
|
||||
if( snap === 'top' ) {
|
||||
|
||||
marginY = - child._margin._value.x;
|
||||
|
||||
} else if( snap === 'bottom' ) {
|
||||
|
||||
marginY = child._margin._value.z;
|
||||
|
||||
} else if( snap === 'left' ) {
|
||||
|
||||
marginX = child._margin._value.w;
|
||||
|
||||
} else if( snap === 'right' ) {
|
||||
|
||||
marginX = - child._margin._value.y;
|
||||
|
||||
} else if( snap === 'centerX' ) {
|
||||
|
||||
marginX = ( child._margin._value.w - child._margin._value.y ) /2;
|
||||
|
||||
} else if( snap === 'centerY' ) {
|
||||
|
||||
marginY = ( - child._margin._value.x + child._margin._value.z ) /2;
|
||||
|
||||
}
|
||||
|
||||
element._layouter._childrenPos[ child.id ].x += x + marginX;
|
||||
element._layouter._childrenPos[ child.id ].y += y + marginY;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* STRATEGIES
|
||||
**********************************************************************************************************************/
|
||||
|
||||
function _alignChild() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param child
|
||||
* @param parentOffset
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
function _alignChildRowEnd( child, parentOffset ) {
|
||||
return - parentOffset + ( child._bounds._offsetHeight / 2 );
|
||||
}
|
||||
|
||||
function _alignChildRowStart( child, parentOffset ) {
|
||||
return parentOffset - ( child._bounds._offsetHeight / 2 );
|
||||
}
|
||||
|
||||
function _alignChildColumnEnd( child, parentOffset ) {
|
||||
return parentOffset - ( child._bounds._offsetWidth / 2 );
|
||||
}
|
||||
|
||||
function _alignChildColumnStart( child, parentOffset ) {
|
||||
return - parentOffset + ( child._bounds._offsetWidth / 2 );
|
||||
}
|
||||
|
||||
function _processColumn( element, childAligner ) {
|
||||
|
||||
const AXIS_TARGET = element._bounds._innerWidth / 2;
|
||||
|
||||
element._children._boxes.forEach( ( child ) => {
|
||||
element._layouter._childrenPos[ child.id ].x = childAligner( child, AXIS_TARGET );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
function _processRow( element, childAligner ) {
|
||||
|
||||
const AXIS_TARGET = element._bounds._innerHeight / 2;
|
||||
|
||||
element._children._boxes.forEach( ( child ) => {
|
||||
element._layouter._childrenPos[ child.id ].y = childAligner( child, AXIS_TARGET );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class FlexDirectionProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'flexDirection', 'inherit', true );
|
||||
|
||||
// configure
|
||||
|
||||
this.isValid = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['row','row-reverse', 'column', 'column-reverse'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) flexDirection value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
import FlexDirectionProperty from './FlexDirectionProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class FlexDirectionPropertyBox extends FlexDirectionProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super();
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._offset = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @internal
|
||||
*/
|
||||
this._reverse = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param { (element:MeshUIBaseElement) => void} element
|
||||
* @private
|
||||
*/
|
||||
this._process = this.emptyStrategyLogic;
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
switch ( this._value ) {
|
||||
case "row":
|
||||
this._process = _processRow;
|
||||
// this._offset = - element._bounds._innerWidth / 2;
|
||||
break;
|
||||
|
||||
case "row-reverse":
|
||||
this._process = _processRowReverse;
|
||||
// this._offset = element._bounds._innerWidth / 2;
|
||||
break;
|
||||
|
||||
case "column":
|
||||
this._process = _processColumn;
|
||||
// this._offset = element._bounds._innerHeight / 2;
|
||||
break;
|
||||
|
||||
case "column-reverse":
|
||||
this._process = _processColumnReverse;
|
||||
// this._offset = - element._bounds._innerHeight / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// also update dependencies
|
||||
if( !element._justifyContent._needsUpdate ) element._justifyContent.computeOutputValue( element );
|
||||
if( !element._alignItems._needsUpdate ) element._alignItems.computeOutputValue( element );
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
// this will be defined from strategy
|
||||
|
||||
//console.log( element.name, 'flexDirection process');
|
||||
|
||||
|
||||
switch ( this._value ) {
|
||||
case "row":
|
||||
this._offset = - element._bounds._innerWidth / 2;
|
||||
break;
|
||||
|
||||
case "row-reverse":
|
||||
this._offset = element._bounds._innerWidth / 2;
|
||||
break;
|
||||
|
||||
case "column":
|
||||
this._offset = element._bounds._innerHeight / 2;
|
||||
break;
|
||||
|
||||
case "column-reverse":
|
||||
this._offset = - element._bounds._innerHeight / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
this._reverse = -Math.sign( this._offset );
|
||||
if( this._reverse === 0 ) {
|
||||
this._reverse = 1;
|
||||
}
|
||||
|
||||
|
||||
this._process( element );
|
||||
|
||||
element._justifyContent._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* STRATEGIES
|
||||
**********************************************************************************************************************/
|
||||
|
||||
function _processRow( element ) {
|
||||
|
||||
// end to end children
|
||||
let accu = element._flexDirection._offset;
|
||||
|
||||
const REVERSE = element._flexDirection._reverse;
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
|
||||
// Refactor reduce into fori in order to get rid of this keyword
|
||||
for ( let i = 0; i < boxes.length; i++ ) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {MeshUIBaseElement}
|
||||
*/
|
||||
const child = boxes[ i ];
|
||||
|
||||
const CHILD_ID = child.id;
|
||||
|
||||
// @TODO : use getter instead of compute function if possible
|
||||
const CHILD_SIZE = child._bounds._offsetWidth;
|
||||
|
||||
// increase with the left margin before placing the child
|
||||
accu += child._margin._value.w * REVERSE;
|
||||
|
||||
const position = element._layouter._childrenPos[ CHILD_ID ];
|
||||
|
||||
position.x = accu + ( CHILD_SIZE / 2 ) * REVERSE;
|
||||
position.y = 0;
|
||||
|
||||
|
||||
// increase the next child with this child right margin
|
||||
accu += REVERSE * ( CHILD_SIZE + child._margin._value.y ) ;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _processRowReverse( element ) {
|
||||
|
||||
// end to end children
|
||||
let accu = element._flexDirection._offset;
|
||||
const REVERSE = element._flexDirection._reverse;
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
|
||||
// Refactor reduce into fori in order to get rid of this keyword
|
||||
for ( let i = 0; i < boxes.length; i++ ) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {MeshUIBaseElement}
|
||||
*/
|
||||
const child = boxes[ i ];
|
||||
|
||||
const CHILD_ID = child.id;
|
||||
|
||||
// @TODO : use getter instead of compute function if possible
|
||||
const CHILD_SIZE = child._bounds._offsetWidth;
|
||||
|
||||
// decrease with the right margin before placing the child
|
||||
accu += child._margin._value.y * REVERSE;
|
||||
|
||||
|
||||
const position = element._layouter._childrenPos[ CHILD_ID ];
|
||||
|
||||
position.x = accu + ( CHILD_SIZE / 2 ) * REVERSE;
|
||||
position.y = 0;
|
||||
|
||||
// decrease the next child with this child left margin
|
||||
accu += (CHILD_SIZE + child._margin._value.w) * REVERSE ;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _processColumn( element ) {
|
||||
|
||||
// end to end children
|
||||
let accu = element._flexDirection._offset;
|
||||
const REVERSE = element._flexDirection._reverse;
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
|
||||
// Refactor reduce into fori in order to get rid of this keyword
|
||||
for ( let i = 0; i < boxes.length; i++ ) {
|
||||
|
||||
const child = boxes[ i ];
|
||||
|
||||
const CHILD_ID = child.id;
|
||||
|
||||
// @TODO : use getter instead of compute function if possible
|
||||
const CHILD_SIZE = child._bounds._offsetHeight;
|
||||
|
||||
// increase with the top margin before placing the child
|
||||
accu += child._margin._value.x * REVERSE;
|
||||
|
||||
const position = element._layouter._childrenPos[ CHILD_ID ];
|
||||
|
||||
position.x = 0;
|
||||
position.y = accu + ( CHILD_SIZE / 2 ) * REVERSE;
|
||||
|
||||
// increase the next child with this child bottom margin
|
||||
accu += (CHILD_SIZE + child._margin._value.z) * REVERSE ;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _processColumnReverse( element ) {
|
||||
|
||||
// end to end children
|
||||
let accu = element._flexDirection._offset;
|
||||
const REVERSE = element._flexDirection._reverse;
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
|
||||
// Refactor reduce into fori in order to get rid of this keyword
|
||||
for ( let i = 0; i < boxes.length; i++ ) {
|
||||
|
||||
const child = boxes[ i ];
|
||||
|
||||
const CHILD_ID = child.id;
|
||||
|
||||
// @TODO : use getter instead of compute function if possible
|
||||
const CHILD_SIZE = child._bounds._offsetHeight;
|
||||
|
||||
// decrease with the bottom margin before placing the child
|
||||
accu += child._margin._value.z * REVERSE;
|
||||
|
||||
const position = element._layouter._childrenPos[ CHILD_ID ];
|
||||
|
||||
position.x = 0;
|
||||
position.y = accu + ( CHILD_SIZE / 2 ) * REVERSE;
|
||||
|
||||
// decrease the next child with this child top margin
|
||||
accu += ( CHILD_SIZE + child._margin._value.x ) * REVERSE ;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import FlexDirectionProperty from './FlexDirectionProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import MeshUIBaseElement from '../../../elements/MeshUIBaseElement';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class FlexDirectionPropertyText extends FlexDirectionProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super();
|
||||
|
||||
this._value = this._input = 'column';
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
// @TODO : Evaluate the needs of this property. Could be empty
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class JustifyContentProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'justifyContent', 'inherit', true );
|
||||
|
||||
/**
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
const AVAILABLE_VALUES = [ 'start', 'center', 'end', 'space-between', 'space-around', 'space-evenly' ];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if ( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) justifyContent value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
import JustifyContentProperty from './JustifyContentProperty';
|
||||
|
||||
export default class JustifyContentPropertyBox extends JustifyContentProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'justifyContent', defaultValue, true );
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = true;
|
||||
|
||||
// strategies
|
||||
/**
|
||||
*
|
||||
* @type {(axisOffset:number) => number}
|
||||
* @private
|
||||
*/
|
||||
this._computeOffset = this.emptyStrategyLogic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {(element:MeshUIBaseElement, availableSpace:number, reverse:number) => Array.<number> }
|
||||
* @private
|
||||
*/
|
||||
this._computeMargin = this.emptyStrategyLogic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {(element:MeshUIBaseElement) => void}
|
||||
* @private
|
||||
*/
|
||||
this._process = this.emptyStrategyLogic;
|
||||
|
||||
}
|
||||
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
//console.log( element._flexDirection._value );
|
||||
switch ( element._flexDirection._value ) {
|
||||
|
||||
case 'column-reverse':
|
||||
case 'column':
|
||||
this._process = _column.bind( this );
|
||||
break;
|
||||
|
||||
|
||||
case 'row-reverse':
|
||||
case 'row':
|
||||
this._process = _row.bind( this );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
switch ( this._value ) {
|
||||
case 'end':
|
||||
this._computeOffset = _justificationOffsetEnd;
|
||||
this._computeMargin = _justificationMargin;
|
||||
break;
|
||||
|
||||
case 'center':
|
||||
this._computeOffset = _justificationOffsetCenter;
|
||||
this._computeMargin = _justificationMargin;
|
||||
break;
|
||||
|
||||
case 'start':
|
||||
this._computeOffset = _justificationOffset;
|
||||
this._computeMargin = _justificationMargin;
|
||||
break;
|
||||
|
||||
case 'space-between':
|
||||
this._computeOffset = _justificationOffset;
|
||||
this._computeMargin = _justificationMarginSpaceBetween;
|
||||
break;
|
||||
|
||||
case 'space-around':
|
||||
this._computeOffset = _justificationOffset;
|
||||
this._computeMargin = _justificationMarginSpaceAround;
|
||||
break;
|
||||
|
||||
case 'space-evenly':
|
||||
this._computeOffset = _justificationOffset;
|
||||
this._computeMargin = _justificationMarginSpaceEvenly;
|
||||
break;
|
||||
}
|
||||
|
||||
// @TODO : If flexDirection was keeping its children position,
|
||||
// it won't be necessary to compute the same result again
|
||||
// but it will increase the memory footprint
|
||||
element._flexDirection._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
this._process( element );
|
||||
|
||||
element._alignItems._needsProcess = true; // not mandatory : Layout could sum each
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _row( element ) {
|
||||
|
||||
const startPos = element._flexDirection._offset;
|
||||
|
||||
const { usedDirectionSpace, remainingSpace } = _rowRemainingSpace( element );
|
||||
|
||||
// Items Offset
|
||||
const axisOffset = ( startPos * 2 ) - ( usedDirectionSpace * Math.sign( startPos ) );
|
||||
const justificationOffset = this._computeOffset( axisOffset );
|
||||
|
||||
// Items margin
|
||||
// const justificationMargins = _getJustificationMargin( boxComponent.childrenBoxes, remainingSpace, JUSTIFICATION, REVERSE );
|
||||
const justificationMargins = this._computeMargin( element, remainingSpace, element._flexDirection._reverse );
|
||||
|
||||
|
||||
// Apply
|
||||
element._children._boxes.forEach( ( child, childIndex ) => {
|
||||
|
||||
element._layouter._childrenPos[ child.id ].x -= justificationOffset - justificationMargins[ childIndex ];
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
function _column( element ) {
|
||||
|
||||
|
||||
const startPos = element._flexDirection._offset;
|
||||
|
||||
const { usedDirectionSpace, remainingSpace } = _columnRemainingSpace( element );
|
||||
|
||||
// Items Offset
|
||||
const axisOffset = ( startPos * 2 ) - ( usedDirectionSpace * Math.sign( startPos ) );
|
||||
const justificationOffset = this._computeOffset( axisOffset );
|
||||
|
||||
// Items margin
|
||||
const justificationMargins = this._computeMargin( element, remainingSpace, element._flexDirection._reverse );
|
||||
|
||||
// Apply
|
||||
element._children._boxes.forEach( ( child, childIndex ) => {
|
||||
|
||||
element._layouter._childrenPos[ child.id ].y -= justificationOffset - justificationMargins[ childIndex ];
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* STRATEGIES
|
||||
**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
* @return {{usedDirectionSpace: *, remainingSpace: number}}
|
||||
* @private
|
||||
*/
|
||||
function _rowRemainingSpace( element ) {
|
||||
|
||||
const usedDirectionSpace = element._bounds._computeChildrenSideWidth( element );
|
||||
return { usedDirectionSpace, remainingSpace: element._bounds._innerWidth - usedDirectionSpace };
|
||||
|
||||
}
|
||||
|
||||
function _columnRemainingSpace( element ) {
|
||||
|
||||
const usedDirectionSpace = element._bounds._computeChildrenSideHeight( element );
|
||||
return { usedDirectionSpace, remainingSpace: element._bounds._innerHeight - usedDirectionSpace };
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ function _justificationOffset( axisOffset ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
function _justificationOffsetEnd( axisOffset ) {
|
||||
|
||||
return axisOffset;
|
||||
|
||||
}
|
||||
|
||||
function _justificationOffsetCenter( axisOffset ) {
|
||||
|
||||
return axisOffset / 2;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
function _justificationMargin( element, availableSpace = 0, reverse = 1 ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
return Array( element._children._boxes.length ).fill( 0 );
|
||||
|
||||
}
|
||||
|
||||
function _justificationMarginSpaceBetween( element, availableSpace = 0, reverse = 1 ) {
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
const length = boxes.length;
|
||||
const justificationMargins = Array( length ).fill( 0 );
|
||||
|
||||
if ( availableSpace > 0 ) {
|
||||
|
||||
// only one children would act as start
|
||||
if ( length > 1 ) {
|
||||
|
||||
const margin = availableSpace / ( length - 1 ) * reverse;
|
||||
// set this margin for any children
|
||||
|
||||
// except for first child
|
||||
justificationMargins[ 0 ] = 0;
|
||||
|
||||
for ( let i = 1; i < length; i++ ) {
|
||||
|
||||
justificationMargins[ i ] = margin * i;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return justificationMargins;
|
||||
|
||||
}
|
||||
|
||||
function _justificationMarginSpaceEvenly( element, availableSpace = 0, reverse = 1 ) {
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
const length = boxes.length;
|
||||
const justificationMargins = Array( length ).fill( 0 );
|
||||
|
||||
if ( availableSpace > 0 ) {
|
||||
|
||||
const margin = availableSpace / ( length + 1 ) * reverse;
|
||||
|
||||
// set this margin for any children
|
||||
for ( let i = 0; i < length; i++ ) {
|
||||
|
||||
justificationMargins[ i ] = margin * ( i + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return justificationMargins;
|
||||
|
||||
}
|
||||
|
||||
function _justificationMarginSpaceAround( element, availableSpace = 0, reverse = 1 ) {
|
||||
|
||||
const boxes = element._children._boxes;
|
||||
const length = boxes.length;
|
||||
const justificationMargins = Array( length ).fill( 0 );
|
||||
|
||||
if ( availableSpace > 0 ) {
|
||||
|
||||
|
||||
const margin = availableSpace / ( length ) * reverse;
|
||||
|
||||
const start = margin / 2;
|
||||
justificationMargins[ 0 ] = start;
|
||||
|
||||
// set this margin for any children
|
||||
for ( let i = 1; i < length; i++ ) {
|
||||
|
||||
justificationMargins[ i ] = start + margin * i;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return justificationMargins;
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class OrderProperty extends SubStyleProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'order', 0, true );
|
||||
|
||||
this._value = 0;
|
||||
|
||||
this._input = 0;
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
// require parent children (order) update, which will require layout update
|
||||
if( element._parent._value ) {
|
||||
|
||||
element._parent._value._children._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import StyleColorProperty from '../StyleColorProperty';
|
||||
|
||||
//JSDoc related imports
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { Color } from 'three';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class ColorProperty extends StyleColorProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'color', 'inherit', false );
|
||||
|
||||
this.output = this._outputValue;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param {MeshUIBaseElement} element
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
if( this._input === 'inherit' ) {
|
||||
|
||||
this._value.set( this.getInheritedInput( element ) );
|
||||
|
||||
} else {
|
||||
|
||||
this._value.set( this._input);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
import FontLibrary from '../../../../font/FontLibrary';
|
||||
import FontFamily from '../../../../font/FontFamily';
|
||||
|
||||
|
||||
export default class FontFamilyProperty extends SubStyleProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'fontFamily', 'inherit' , true );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
if( this._input instanceof FontFamily ) {
|
||||
|
||||
this._value = this._input;
|
||||
|
||||
} else if ( this._input === 'inherit' ) {
|
||||
|
||||
// do nothing
|
||||
|
||||
} else if ( typeof this._input === 'string' ) {
|
||||
|
||||
// string - family
|
||||
const fontFamily = FontLibrary.getFontFamily( this._input );
|
||||
|
||||
if( fontFamily ) {
|
||||
|
||||
this._value = fontFamily;
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( `(.style) fontFamily, the font '${this._input}' is not registered. Aborted.`)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( `(.style) fontFamily requires a registered fontFamily instance, or the id of a registered fontFamily.`);
|
||||
console.warn( `If you want to set a specific font, please use .font property instead.`);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {any|FontFamily|null}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
getInheritedInput ( element ) {
|
||||
|
||||
if( this._input !== 'inherit' ) return this._input;
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent ) {
|
||||
|
||||
return parent[`_${this._id}`].getInheritedInput( parent )
|
||||
|
||||
}
|
||||
|
||||
return this.getDefaultValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import FontLibrary from '../../../../font/FontLibrary';
|
||||
import { default as RegisteredFontFamily } from '../../../../font/FontFamily';
|
||||
import FontFamilyProperty from './FontFamilyProperty';
|
||||
|
||||
|
||||
export default class FontFamilyPropertyInline extends FontFamilyProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'fontFamily', 'inherit' , true );
|
||||
|
||||
this._input = 'inherit';
|
||||
this._needsUpdate = true;
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
let abstractedInput = this._inheritedInput;
|
||||
|
||||
if( abstractedInput === 'inherit' ) {
|
||||
abstractedInput = this.getInheritedInput( element );
|
||||
}
|
||||
|
||||
if( abstractedInput instanceof RegisteredFontFamily ) {
|
||||
|
||||
this._value = abstractedInput;
|
||||
element._font._needsUpdate = true;
|
||||
|
||||
} else if ( typeof abstractedInput === 'string' ) {
|
||||
|
||||
// string - family
|
||||
const fontFamily = FontLibrary.getFontFamily(abstractedInput);
|
||||
|
||||
if( fontFamily ) {
|
||||
|
||||
this._value = fontFamily;
|
||||
element._font._needsUpdate = true;
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( `(.style) fontFamily, the font '${abstractedInput}' is not registered. Aborted.`)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( `(.style) fontFamily requires a registered fontFamily instance, or the id of a registered fontFamily.`);
|
||||
console.warn( `If you want to set a specific font, please use .font property instead.`);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {any|FontFamilyPropertyInline|null}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class FontKerningProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'fontKerning', 'inherit' );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const AVAILABLE_VALUES = ['normal', 'none', 'inherit'];
|
||||
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) fontKerning value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import FontKerningProperty from './FontKerningProperty';
|
||||
|
||||
|
||||
export default class FontKerningPropertyInline extends FontKerningProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
}
|
||||
|
||||
|
||||
_computeFromInherited( element ) {
|
||||
super._computeFromInherited(element);
|
||||
|
||||
// this._needsProcess = true;
|
||||
element._parent._value._layouter._needsProcess = false;
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
// apply kerning on inlines
|
||||
if ( this._value !== 'none' ) {
|
||||
|
||||
// First character won't be kerned with its void lefthanded peer
|
||||
const whiteSpacedContent = element._whiteSpace._whiteSpacedContent;
|
||||
const inlines = element._inlines._value;
|
||||
for ( let i = 1; i < inlines.length; i++ ) {
|
||||
|
||||
const glyphPair = whiteSpacedContent[ i - 1 ] + whiteSpacedContent[ i ];
|
||||
|
||||
// retrieve the kerning from the font
|
||||
// as set it on the characterInline
|
||||
inlines[ i ].kerning = element._font._fontVariant.getKerningAmount( glyphPair );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import FontKerningProperty from './FontKerningProperty';
|
||||
|
||||
|
||||
export default class FontKerningPropertyText extends FontKerningProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this._value = this._input = this.getDefaultValue();
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
}
|
||||
|
||||
|
||||
_computeFromInherited( element ) {
|
||||
super._computeFromInherited(element);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class FontSizeProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'fontSize', 'inherit', true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class FontSizePropertyInline extends SubStyleProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'fontSize', 'inherit', true );
|
||||
|
||||
// Configure
|
||||
this._allowsInherit = false;
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
if( element._font._fontVariant ) {
|
||||
element._bounds._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
if( !element._font._fontVariant || !element._font._fontVariant.isReady ) return;
|
||||
|
||||
const SCALE_MULT = this._value / element._font._fontVariant.typographic.size;
|
||||
|
||||
// First character won't be kerned with its void lefthanded peer
|
||||
const inlines = element._inlines._value;
|
||||
|
||||
// update inlines properties before inline placements in lines
|
||||
for ( let i = 0; i < inlines.length; i++ ) {
|
||||
|
||||
const inline = inlines[ i ];
|
||||
|
||||
inline.resetOffsets();
|
||||
|
||||
inline.fontSize = this._value;
|
||||
inline.fontFactor = SCALE_MULT;
|
||||
|
||||
}
|
||||
|
||||
// element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
get value() { return this._value; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class FontStyleProperty extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'fontStyle', defaultValue, true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const AVAILABLE_VALUES = ['normal', 'italic'];
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) fontStyle value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import FontStyleProperty from './FontStyleProperty';
|
||||
|
||||
|
||||
export default class FontStylePropertyInline extends FontStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class FontWeightProperty extends SubStyleProperty {
|
||||
|
||||
constructor( ) {
|
||||
|
||||
super( 'fontWeight', 'inherit', true );
|
||||
|
||||
this.isValid = _isValid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['100','200','300','400','500','600','700','800','900','light','normal','bold','bolder'];
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value.toString() ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) fontWeight value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import FontWeightProperty from './FontWeightProperty';
|
||||
import { uniformizeFontWeight } from '../../../../font/utils/FontUtils';
|
||||
|
||||
export default class FontWeightPropertyInline extends FontWeightProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
this._value = uniformizeFontWeight( this.getInheritedInput( element ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class LetterSpacingProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'letterSpacing', 'inherit', true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import LetterSpacingProperty from './LetterSpacingProperty';
|
||||
|
||||
export default class LetterSpacingPropertyInline extends LetterSpacingProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._input = 'inherit';
|
||||
this._allowsInherit = false;
|
||||
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
}
|
||||
|
||||
_computeFromInherited( element ) {
|
||||
super._computeFromInherited( element );
|
||||
|
||||
|
||||
element._fontSize._needsProcess = true;
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
export default class LineHeightProperty extends SubStyleProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super( 'lineHeight', 'inherit', true );
|
||||
|
||||
}
|
||||
|
||||
update( element, out ) {
|
||||
super.update( element, out );
|
||||
|
||||
element._layouter._needsProcess = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
import LineHeightProperty from './LineHeightProperty';
|
||||
|
||||
export default class LineHeightPropertyInline extends LineHeightProperty {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class TextAlignProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'textAlign', 'inherit', true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
const AVAILABLE_VALUES = ['left', 'right', 'center', 'justify', 'justify-left', 'justify-right','justify-center'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
const _isValid = function ( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) textAlign value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import TextAlignProperty from './TextAlignProperty';
|
||||
|
||||
|
||||
export default class TextAlignPropertyInline extends TextAlignProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
element._layouter._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
import TextAlignProperty from './TextAlignProperty';
|
||||
|
||||
|
||||
export default class TextAlignPropertyText extends TextAlignProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = true;
|
||||
|
||||
//
|
||||
// @TODO : strategies
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
this._value = this._inheritedInput;
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
process( element ) {
|
||||
|
||||
_process( element );
|
||||
|
||||
element._renderer._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _process( element ) {
|
||||
|
||||
const lines = element._layouter._value;
|
||||
const ALIGNMENT = element._textAlign._value;
|
||||
const INNER_WIDTH = element._bounds._innerWidth;
|
||||
|
||||
// Start the alignment by sticking to directions : left, right, center
|
||||
for ( let i = 0; i < lines.length; i++ ) {
|
||||
|
||||
const line = lines[ i ];
|
||||
|
||||
// compute the alignment offset of the line
|
||||
const offsetX = _computeLineOffset( element, line, i === lines.length - 1 );
|
||||
|
||||
const padding = element._padding._value;
|
||||
const border = element._borderWidth._value;
|
||||
|
||||
// const paddingAmount = - ( padding.w + padding.y ) / 2 - ( border.w + border.y ) / 2;
|
||||
// const paddingAmount = - ( padding.w + padding.y ) / 2;
|
||||
const paddingAmount = ( - padding.w + padding.y ) / 2 + ( - border.w + border.y ) / 2;
|
||||
|
||||
line.x += offsetX;
|
||||
|
||||
// apply the offset to each characters of the line
|
||||
for ( let j = 0; j < line.length; j++ ) {
|
||||
|
||||
line[ j ].offsetX += offsetX - paddingAmount;
|
||||
// line[ j ].offsetX += offsetX;
|
||||
|
||||
}
|
||||
|
||||
// line.x = line[ 0 ].offsetX;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// last operations for justifications alignments
|
||||
if ( ALIGNMENT.indexOf( 'justify' ) === 0 ) {
|
||||
|
||||
for ( let i = 0; i < lines.length; i++ ) {
|
||||
|
||||
const line = lines[ i ];
|
||||
|
||||
|
||||
// do not process last line for justify-left or justify-right
|
||||
if ( ALIGNMENT.indexOf( '-' ) !== -1 && i === lines.length - 1 ) return;
|
||||
|
||||
// can only justify is space is remaining
|
||||
const REMAINING_SPACE = INNER_WIDTH - line.width;
|
||||
if ( REMAINING_SPACE <= 0 ) return;
|
||||
|
||||
// count the valid spaces to extend
|
||||
// Do not take the first nor the last space into account
|
||||
let validSpaces = 0;
|
||||
for ( let j = 1; j < line.length - 1; j++ ) {
|
||||
|
||||
validSpaces += line[ j ].char === ' ' ? 1 : 0;
|
||||
|
||||
}
|
||||
const additionalSpace = REMAINING_SPACE / validSpaces;
|
||||
|
||||
|
||||
// for right justification, process the loop in reverse
|
||||
let inverter = 1;
|
||||
if ( ALIGNMENT === 'justify-right' ) {
|
||||
|
||||
line.reverse();
|
||||
inverter = -1;
|
||||
|
||||
}
|
||||
|
||||
let incrementalOffsetX = 0;
|
||||
|
||||
// start at ONE to avoid first space
|
||||
for ( let j = 1; j <= line.length - 1; j++ ) {
|
||||
|
||||
// apply offset on each char
|
||||
const inlineCharacter = line[ j ];
|
||||
inlineCharacter.offsetX += incrementalOffsetX * inverter;
|
||||
|
||||
// and increase it when space
|
||||
incrementalOffsetX += inlineCharacter.char === ' ' ? additionalSpace : 0;
|
||||
|
||||
}
|
||||
|
||||
// for right justification, the loop was processed in reverse
|
||||
if ( ALIGNMENT === 'justify-right' ) {
|
||||
line.reverse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _computeLineOffset ( element, line, lastLine ) {
|
||||
|
||||
switch ( element._textAlign._value ) {
|
||||
|
||||
case 'justify-left':
|
||||
case 'justify':
|
||||
case 'left':
|
||||
return - element._bounds._innerWidth / 2;
|
||||
|
||||
case 'justify-right':
|
||||
case 'right':
|
||||
return -line.width + ( element._bounds._innerWidth / 2 );
|
||||
|
||||
|
||||
case 'center':
|
||||
return -line.width / 2;
|
||||
|
||||
case 'justify-center':
|
||||
if ( lastLine ) {
|
||||
|
||||
// center alignement
|
||||
return -line.width / 2;
|
||||
|
||||
}
|
||||
|
||||
// left alignment
|
||||
return - element._bounds._innerWidth / 2;
|
||||
|
||||
default:
|
||||
console.warn( `textAlign: '${element._textAlign._value}' is not valid` );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class WhiteSpaceProperty extends SubStyleProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( 'whiteSpace', 'inherit' );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
const AVAILABLE_VALUES = ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) whiteSpace value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
import WhiteSpaceProperty from './WhiteSpaceProperty';
|
||||
|
||||
/**
|
||||
* @typedef StringCollapserStrategy
|
||||
* @type {(textContent:{string}) => string}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @typedef InlineCollapserStrategy
|
||||
* @type {(line:{Line}) => number }
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @typedef InlineWrapperStrategy
|
||||
* @type {(inlines:{Array}, i:{number}, lastInlineOffset:{number}, options:Object<string,any>) => boolean}
|
||||
*/
|
||||
|
||||
export default class WhiteSpacePropertyInline extends WhiteSpaceProperty {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
// configure
|
||||
this._allowsInherit = false;
|
||||
this.computeOutputValue = this._computeFromInherited;
|
||||
|
||||
this._whiteSpacedContent = '';
|
||||
|
||||
// strategies
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {StringCollapserStrategy}
|
||||
* @internal
|
||||
*/
|
||||
this._stringCollapser = this.emptyStrategyLogic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {InlineCollapserStrategy}
|
||||
* @internal
|
||||
*/
|
||||
this._inlineCollapser = this.emptyStrategyLogic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {InlineWrapperStrategy}
|
||||
* @internal
|
||||
*/
|
||||
this._inlineWrapper = this.emptyStrategyLogic;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @private
|
||||
*/
|
||||
_computeFromInherited( element ) { /* eslint-enable no-unused-vars */
|
||||
super._computeFromInherited( element );
|
||||
|
||||
// set strategies
|
||||
this._newLineBreakability = _newlineBreakability( this._value );
|
||||
|
||||
// REDO Whitespace Matrix
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
|
||||
|
||||
switch ( this._value ) {
|
||||
|
||||
case 'nowrap':
|
||||
case 'normal':
|
||||
this._stringCollapser = _stringCollapseNewLine;
|
||||
break;
|
||||
|
||||
case 'pre-line':
|
||||
this._stringCollapser = _stringCollapseMultipleSpace;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._stringCollapser = _stringCollapseNothing;
|
||||
|
||||
}
|
||||
|
||||
switch ( this._value ) {
|
||||
|
||||
case 'pre-line':
|
||||
case 'nowrap':
|
||||
case 'normal':
|
||||
this._inlineCollapser = _inlineCollapseMultiple;
|
||||
break;
|
||||
|
||||
case 'pre-wrap':
|
||||
this._inlineCollapser = _inlineCollapseSingle;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._inlineCollapser = _inlineCollapseNothing;
|
||||
|
||||
}
|
||||
|
||||
switch ( this._value ) {
|
||||
|
||||
case 'pre-line':
|
||||
case 'pre-wrap':
|
||||
case 'normal':
|
||||
this._inlineWrapper = _lineBreakerWrapText;
|
||||
break;
|
||||
|
||||
case 'pre':
|
||||
this._inlineWrapper = _lineBreakerLineBreakOnly;
|
||||
break;
|
||||
|
||||
default:
|
||||
this._inlineWrapper = _lineBreakerNoWrap;
|
||||
|
||||
}
|
||||
|
||||
|
||||
this._needsProcess = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
process( element ) {
|
||||
|
||||
// @TODO: Make a property for Text -> inlineCollapser
|
||||
if( element.isInline && !element.isInlineBlock ) {
|
||||
|
||||
this._whiteSpacedContent = this._stringCollapser( element._textContent._value );
|
||||
|
||||
element._glyphs._needsProcess = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* STRATEGIES
|
||||
**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#whitespace_helper_functions
|
||||
*
|
||||
* Throughout, whitespace is defined as one of the characters
|
||||
* "\t" TAB \u0009
|
||||
* "\n" LF \u000A
|
||||
* "\r" CR \u000D
|
||||
* " " SPC \u0020
|
||||
*
|
||||
* This does not use Javascript's "\s" because that includes non-breaking
|
||||
* spaces (and also some other characters).
|
||||
**/
|
||||
const WHITE_CHARS = { '\t': '\u0009', '\n': '\u000A', '\r': '\u000D', ' ': '\u0020' };
|
||||
|
||||
/**
|
||||
* Get the breakability of a newline character according to white-space property
|
||||
*
|
||||
* @param whiteSpace
|
||||
* @returns {string|null}
|
||||
*/
|
||||
const _newlineBreakability = function ( whiteSpace ) {
|
||||
|
||||
switch ( whiteSpace ) {
|
||||
|
||||
case 'pre':
|
||||
case 'pre-wrap':
|
||||
case 'pre-line':
|
||||
return 'mandatory';
|
||||
}
|
||||
|
||||
// case NOWRAP:
|
||||
// case NORMAL:
|
||||
// default:
|
||||
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// STRING COLLAPSER -----------------------------------------------------
|
||||
|
||||
/**
|
||||
* Treat newlines as spaces
|
||||
* @param textContentValue
|
||||
* @return {*}
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
function _stringCollapseNewLine( textContentValue ) {
|
||||
|
||||
return _stringCollapseMultipleSpace( textContentValue.replace( /\n/g, ' ' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat sequences of spaces as only one space
|
||||
* @param textContentValue
|
||||
* @return {*}
|
||||
* @private
|
||||
*/
|
||||
function _stringCollapseMultipleSpace( textContentValue ) {
|
||||
|
||||
return textContentValue.replace( /[ ]{2,}/g, ' ' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param textContentValue
|
||||
* @return {*}
|
||||
* @private
|
||||
*/
|
||||
function _stringCollapseNothing ( textContentValue ) {
|
||||
return textContentValue;
|
||||
}
|
||||
|
||||
// LineBreakers -----------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @param inlines
|
||||
* @param i
|
||||
* @param lastInlineOffset
|
||||
* @param options
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _lineBreakerWrapText( inlines, i, lastInlineOffset, options ) {
|
||||
const inline = inlines[ i ];
|
||||
|
||||
// prevent additional computation if line break is mandatory
|
||||
if ( inline.lineBreak === 'mandatory' ) return true;
|
||||
|
||||
// ?? Missing letterSpacing ?
|
||||
// prevent additional computation if this character already exceed the available size
|
||||
if ( lastInlineOffset + inline.xadvance + inline.xoffset + inline.kerning > options.INNER_WIDTH ) return true;
|
||||
|
||||
|
||||
const nextBreak = _distanceToNextBreak( inlines, i, options );
|
||||
return _shouldFriendlyBreak( inlines[ i - 1 ], lastInlineOffset, nextBreak, options );
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
*
|
||||
* @param inlines
|
||||
* @param i
|
||||
* @param lastInlineOffset
|
||||
* @param options
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _lineBreakerLineBreakOnly( inlines, i, lastInlineOffset, options ) { /* eslint-enable no-unused-vars */
|
||||
|
||||
return inlines[ i ].lineBreak === 'mandatory';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _lineBreakerNoWrap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inlines collapser -----------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @param line
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
function _inlineCollapseSingle( line ) {
|
||||
if ( !line[ 0 ] ) return 0;
|
||||
|
||||
const firstInline = line[ 0 ];
|
||||
const lastInline = line[ line.length - 1 ];
|
||||
|
||||
// only process whiteChars glyphs inlines
|
||||
// if( firstInline.glyph && whiteChars[firstInline.glyph] && line.length > 1 ){
|
||||
if ( firstInline.char && firstInline.char === '\n' && line.length > 1 ) {
|
||||
// if ( firstInline.char && WHITE_CHARS[ firstInline.char ] && line.length > 1 ) {
|
||||
|
||||
_collapseLeftInlines( [ firstInline ], line[ 1 ] );
|
||||
|
||||
}
|
||||
|
||||
// if( lastInline.glyph && whiteChars[lastInline.glyph] && line.length > 1 ){
|
||||
if ( lastInline.char && lastInline.char === '\n' && line.length > 1 ) {
|
||||
// if ( lastInline.char && WHITE_CHARS[ firstInline.char ] && line.length > 1 ) {
|
||||
|
||||
_collapseRightInlines( [ lastInline ], line[ line.length - 2 ] );
|
||||
|
||||
}
|
||||
|
||||
return firstInline.offsetX;
|
||||
|
||||
}
|
||||
|
||||
function _inlineCollapseMultiple( line ) {
|
||||
|
||||
if ( !line[ 0 ] ) return 0;
|
||||
|
||||
let inlinesToCollapse = [];
|
||||
let collapsingTarget;
|
||||
// collect starting whitespaces to collapse
|
||||
for ( let i = 0; i < line.length; i++ ) {
|
||||
|
||||
const inline = line[ i ];
|
||||
|
||||
if ( inline.char && WHITE_CHARS[ inline.char ] && line.length > i ) {
|
||||
|
||||
inlinesToCollapse.push( inline );
|
||||
collapsingTarget = line[ i + 1 ];
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
_collapseLeftInlines( inlinesToCollapse, collapsingTarget );
|
||||
|
||||
|
||||
inlinesToCollapse = [];
|
||||
collapsingTarget = null;
|
||||
// collect ending whitespace to collapse
|
||||
for ( let i = line.length - 1; i > 0; i-- ) {
|
||||
|
||||
const inline = line[ i ];
|
||||
if ( inline.char && WHITE_CHARS[ inline.char ] && i > 0 ) {
|
||||
|
||||
inlinesToCollapse.push( inline );
|
||||
collapsingTarget = line[ i - 1 ];
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
_collapseRightInlines( inlinesToCollapse, collapsingTarget );
|
||||
|
||||
return line[ 0 ].offsetX;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param line
|
||||
* @return {number|*}
|
||||
* @private
|
||||
*/
|
||||
function _inlineCollapseNothing( line ) {
|
||||
|
||||
if ( !line[ 0 ] ) return 0;
|
||||
return line[ 0 ].offsetX;
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* Internal logics
|
||||
**********************************************************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Visually collapse inlines from right to left ( endtrim )
|
||||
* @param {Array} inlines
|
||||
* @param targetInline
|
||||
* @private
|
||||
*/
|
||||
function _collapseRightInlines( inlines, targetInline ) {
|
||||
|
||||
if ( !targetInline ) return;
|
||||
|
||||
for ( let i = 0; i < inlines.length; i++ ) {
|
||||
|
||||
const inline = inlines[ i ];
|
||||
|
||||
inline.fontFactor = 0;
|
||||
inline.offsetX = targetInline.offsetX + targetInline.cumulativeWidth;
|
||||
inline.cumulativeWidth = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Visually collapse inlines from left to right (starttrim)
|
||||
* @param {Array} inlines
|
||||
* @param targetInline
|
||||
* @private
|
||||
*/
|
||||
function _collapseLeftInlines( inlines, targetInline ) {
|
||||
|
||||
if ( !targetInline ) return;
|
||||
|
||||
for ( let i = 0; i < inlines.length; i++ ) {
|
||||
|
||||
const inline = inlines[ i ];
|
||||
|
||||
inline.fontFactor = 0;
|
||||
// inline.offsetX += inline.cumulativeWidth;
|
||||
inline.offsetX = targetInline.offsetX;
|
||||
inline.cumulativeWidth = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the distance in world coord to the next glyph defined
|
||||
* as break-line-safe ( like whitespace for instance )
|
||||
* @private
|
||||
*/
|
||||
function _distanceToNextBreak( inlines, currentIdx, options, accu ) {
|
||||
|
||||
accu = accu || 0;
|
||||
|
||||
// end of the text
|
||||
if ( !inlines[ currentIdx ] ) return accu;
|
||||
|
||||
const inline = inlines[ currentIdx ];
|
||||
|
||||
// const kerning = inline.kerning ? inline.kerning : 0;
|
||||
// const xoffset = inline.xoffset ? inline.xoffset : 0;
|
||||
// const xadvance = inline.xadvance ? inline.xadvance : inline.width;
|
||||
|
||||
// if inline.lineBreak is set, it is 'mandatory' or 'possible'
|
||||
if ( inline.lineBreak ) return accu + inline.xadvance;
|
||||
|
||||
// no line break is possible on this character
|
||||
return _distanceToNextBreak(
|
||||
inlines,
|
||||
currentIdx + 1,
|
||||
options,
|
||||
accu + inline.xadvance + inline.xoffset + inline.kerning + options.LETTERSPACING
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if we should line break here even if the current glyph is not out of boundary.
|
||||
* It might be necessary if the last glyph was break-line-friendly (whitespace, hyphen..)
|
||||
* and the distance to the next friendly glyph is out of boundary.
|
||||
*/
|
||||
function _shouldFriendlyBreak( prevChar, lastInlineOffset, nextBreak, options ) {
|
||||
|
||||
// We can't check if last glyph is break-line-friendly it does not exist
|
||||
if ( !prevChar || !prevChar.char ) return false;
|
||||
|
||||
// Next break-line-friendly glyph is inside boundary
|
||||
if ( lastInlineOffset + nextBreak < options.INNER_WIDTH ) return false;
|
||||
|
||||
// Previous glyph was break-line-friendly
|
||||
return options.BREAKON.indexOf( prevChar.char ) > -1;
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
|
||||
|
||||
export default class Display extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'display', defaultValue );
|
||||
|
||||
// configure
|
||||
this._value = 'flex';
|
||||
this._allowsInherit = false;
|
||||
this._needsUpdate = false;
|
||||
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
element._visible._value = this._output !== 'none';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['none','flex'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} value
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) display value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
import SubStyleProperty from '../SubStyleProperty';
|
||||
import { Plane, Vector3 } from 'three';
|
||||
|
||||
|
||||
export default class Overflow extends SubStyleProperty {
|
||||
|
||||
constructor( defaultValue ) {
|
||||
|
||||
super( 'overflow', defaultValue, true );
|
||||
|
||||
this.isValidValue = _isValid;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Array.<Plane>|null}
|
||||
* @internal
|
||||
*/
|
||||
this._clippingPlanes = null;
|
||||
|
||||
this._renderStrategy = this._emptyRender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update of overflow is a bit different, as parent may trigger changes on it
|
||||
* @override
|
||||
*/
|
||||
update( element, out ) {
|
||||
|
||||
// in any case, it will compute value. It doesn't have updateRequire evaluation
|
||||
// let updateRequired = true;
|
||||
|
||||
// Inline has priority if set
|
||||
if ( this._inline !== undefined && this._inline !== 'unset' ) {
|
||||
|
||||
this._input = this._inline;
|
||||
|
||||
}
|
||||
// or fallback on computed
|
||||
else if ( this._computed !== undefined ) {
|
||||
|
||||
this._input = this._computed;
|
||||
|
||||
}
|
||||
|
||||
if ( !this._allowsInherit ) {
|
||||
|
||||
this._inheritedInput = this.getInheritedInput( element );
|
||||
|
||||
}
|
||||
|
||||
this.computeOutputValue( element );
|
||||
|
||||
// rebuild same properties on children 'inheritance'
|
||||
for ( const childUIElement of element._children._uis ) {
|
||||
childUIElement[ `_overflow` ]._needsUpdate = true;
|
||||
}
|
||||
|
||||
this.output( out );
|
||||
|
||||
}
|
||||
|
||||
output( out ) {
|
||||
|
||||
out['clippingPlanes'] = this._clippingPlanes;
|
||||
|
||||
}
|
||||
|
||||
computeOutputValue( element ) {
|
||||
|
||||
// update self --------------------
|
||||
super.computeOutputValue( element );
|
||||
|
||||
if( this._value === 'hidden' ) {
|
||||
|
||||
this._renderStrategy = this._propagateRender;
|
||||
|
||||
}else{
|
||||
|
||||
this._renderStrategy = this._emptyRender;
|
||||
this._clippingPlanes = null;
|
||||
}
|
||||
|
||||
|
||||
const parent = element._parent._value;
|
||||
if( parent !== null ) {
|
||||
|
||||
// Check that parent is hiddenOverflow or has clippingPlanes
|
||||
const overflowParent = parent._overflow;
|
||||
if ( ( overflowParent._value === 'hidden' || overflowParent._clippingPlanes !== null ) && !this._clippingPlanes ) {
|
||||
|
||||
// add planes and render
|
||||
this._clippingPlanes = [
|
||||
// top
|
||||
new Plane( new Vector3( 0, -1, 0 ), 1 ),
|
||||
// right
|
||||
new Plane( new Vector3( -1, 0, 0 ), 1 ),
|
||||
// bottom
|
||||
new Plane( new Vector3( 0, 1, 0 ), 1 ),
|
||||
// left
|
||||
new Plane( new Vector3( 1, 0, 0 ), 1 ),
|
||||
];
|
||||
|
||||
// bind the parent to the clipping plane in a custom property
|
||||
for ( let i = 0; i < this._clippingPlanes.length; i++ ) {
|
||||
this._clippingPlanes[ i ].parent = parent;
|
||||
}
|
||||
|
||||
// Also add parent clipping planes if isset
|
||||
if( overflowParent._clippingPlanes !== null ) {
|
||||
this._clippingPlanes.push( ...overflowParent._clippingPlanes );
|
||||
}
|
||||
|
||||
this._renderStrategy = this._hiddenRender;
|
||||
this._needsRender = true;
|
||||
|
||||
} else if ( ( overflowParent._value === 'visible' || overflowParent._clippingPlanes === null ) && this._clippingPlanes !== null ){
|
||||
|
||||
// remove planes and render
|
||||
this._clippingPlanes = null;
|
||||
this._renderStrategy = this._emptyRender;
|
||||
this._needsRender = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render( element) {
|
||||
|
||||
this._renderStrategy( element );
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */ _emptyRender( element ) { /* eslint-enable no-unused-vars */ }
|
||||
|
||||
_hiddenRender( element ) {
|
||||
|
||||
|
||||
const parentUI = element._parent._value;
|
||||
|
||||
const yLimit = parentUI._bounds._offsetHeight;
|
||||
const xLimit = parentUI._bounds._offsetWidth;
|
||||
const padding = parentUI._padding._value;
|
||||
const border = parentUI._borderWidth._value;
|
||||
|
||||
for ( let i = 0; i < 4 && i < this._clippingPlanes.length ; i++ ) {
|
||||
const clippingPlane = this._clippingPlanes[ i ];
|
||||
|
||||
switch ( i % 4 ) {
|
||||
// top
|
||||
case 0:
|
||||
clippingPlane.constant = yLimit / 2 - ( padding.x + border.x );
|
||||
break;
|
||||
|
||||
// right
|
||||
case 1:
|
||||
clippingPlane.constant = xLimit / 2 - ( padding.y + border.y );
|
||||
break;
|
||||
|
||||
// bottom
|
||||
case 2:
|
||||
clippingPlane.constant = yLimit / 2 - ( padding.z + border.z );
|
||||
break;
|
||||
|
||||
// left
|
||||
case 3:
|
||||
clippingPlane.constant = xLimit / 2 - ( padding.w + border.w );
|
||||
break;
|
||||
}
|
||||
|
||||
clippingPlane.applyMatrix4( parentUI.matrixWorld )
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < element._children._uis.length; i++ ) {
|
||||
const ui = element._children._uis[ i ];
|
||||
ui._overflow._needsRender = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_propagateRender( element ) {
|
||||
|
||||
for ( let i = 0; i < element._children._uis.length; i++ ) {
|
||||
const ui = element._children._uis[ i ];
|
||||
ui._overflow._needsRender = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const AVAILABLE_VALUES = ['visible', 'hidden'];
|
||||
function _isValid( value ) {
|
||||
|
||||
if( AVAILABLE_VALUES.indexOf( value ) === -1 ) {
|
||||
|
||||
console.warn( `(.style) overflow value '${value}' is not valid. Aborted` );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user