import { Nullable } from "babylonjs/types" ; import { Logger } from "babylonjs/Misc/logger" ; import { Control } from "./control" ; import { Measure } from "../measure" ; import { AdvancedDynamicTexture } from "../advancedDynamicTexture" ; import { RegisterClass } from 'babylonjs/Misc/typeStore' ; import { PointerInfoBase } from 'babylonjs/Events/pointerEvents' ; import { serialize } from 'babylonjs/Misc/decorators' ; import { ICanvasRenderingContext } from 'babylonjs/Engines/ICanvas' ; import { DynamicTexture } from "babylonjs/Materials/Textures/dynamicTexture" ; import { Texture } from "babylonjs/Materials/Textures/texture" ; import { Constants } from 'babylonjs/Engines/constants' ; see https export class Container extends Control { hidden public _children = new Array < Control > ( ) ; hidden protected _measureForChildren = Measure . Empty ( ) ; hidden protected _background = "" ; hidden protected _adaptWidthToChildren = false ; hidden protected _adaptHeightToChildren = false ; hidden protected _renderToIntermediateTexture : boolean = false ; hidden protected _intermediateTexture : Nullable < DynamicTexture > = null ; /** Gets or sets boolean indicating if children should be rendered to an intermediate texture rather than directly to host, useful for alpha blending */ @ serialize ( ) public get renderToIntermediateTexture ( ) : boolean { return this . _renderToIntermediateTexture ; } public set renderToIntermediateTexture ( value ) { if ( this . _renderToIntermediateTexture === value ) { return ; } this . _renderToIntermediateTexture = value ; this . _markAsDirty ( ) ; } /**
     * Gets or sets a boolean indicating that layout cycle errors should be displayed on the console
     */ public logLayoutCycleErrors = false ; /**
     * Gets or sets the number of layout cycles (a change involved by a control while evaluating the layout) allowed
     */ @ serialize ( ) public maxLayoutCycle = 3 ; /** Gets or sets a boolean indicating if the container should try to adapt to its children height */ @ serialize ( ) public get adaptHeightToChildren ( ) : boolean { return this . _adaptHeightToChildren ; } public set adaptHeightToChildren ( value ) { if ( this . _adaptHeightToChildren === value ) { return ; } this . _adaptHeightToChildren = value ; if ( value ) { this . height = "100%" ; } this . _markAsDirty ( ) ; } /** Gets or sets a boolean indicating if the container should try to adapt to its children width */ @ serialize ( ) public get adaptWidthToChildren ( ) : boolean { return this . _adaptWidthToChildren ; } public set adaptWidthToChildren ( value ) { if ( this . _adaptWidthToChildren === value ) { return ; } this . _adaptWidthToChildren = value ; if ( value ) { this . width = "100%" ; } this . _markAsDirty ( ) ; } /** Gets or sets background color */ @ serialize ( ) public get background ( ) : string { return this . _background ; } public set background ( value ) { if ( this . _background === value ) { return ; } this . _background = value ; this . _markAsDirty ( ) ; } /** Gets the list of children */ public get children ( ) : Control [ ] { return this . _children ; } public get isReadOnly ( ) { return this . _isReadOnly ; } public set isReadOnly ( value ) { this . _isReadOnly = value ; for ( const child of this . _children ) { child . isReadOnly = value ; } } param name constructor ( public name ? ) { super ( name ) ; } protected _getTypeName ( ) { return "Container" ; } public _flagDescendantsAsMatrixDirty ( ) { for ( var child of this . children ) { child . _markMatrixAsDirty ( ) ; } } param name returns public getChildByName ( name ) { for ( var child of this . children ) { if ( child . name === name ) { return child ; } } return null ; } param name param type returns public getChildByType ( name , type ) { for ( var child of this . children ) { if ( child . typeName === type ) { return child ; } } return null ; } param control returns public containsControl ( control ) { return this . children . indexOf ( control ) !== - 1 ; } param control returns public addControl ( control ) { if ( ! control ) { return this ; } var index = this . _children . indexOf ( control ) ; if ( index !== - 1 ) { return this ; } control . _link ( this . _host ) ; control . _markAllAsDirty ( ) ; this . _reOrderControl ( control ) ; this . _markAsDirty ( ) ; return this ; } returns public clearControls ( ) { let children = this . children . slice ( ) ; for ( var child of children ) { this . removeControl ( child ) ; } return this ; } param control returns public removeControl ( control ) { var index = this . _children . indexOf ( control ) ; if ( index !== - 1 ) { this . _children . splice ( index , 1 ) ; control . parent = null ; } control . linkWithMesh ( null ) ; if ( this . _host ) { this . _host . _cleanControlAfterRemoval ( control ) ; } this . _markAsDirty ( ) ; return this ; } hidden public _reOrderControl ( control ) { this . removeControl ( control ) ; let wasAdded = false ; for ( var index = 0 ; index < this . _children . length ; index ++ ) { if ( this . _children [ index ] . zIndex > control . zIndex ) { this . _children . splice ( index , 0 , control ) ; wasAdded = true ; break ; } } if ( ! wasAdded ) { this . _children . push ( control ) ; } control . parent = this ; this . _markAsDirty ( ) ; } hidden public _offsetLeft ( offset ) { super . _offsetLeft ( offset ) ; for ( var child of this . _children ) { child . _offsetLeft ( offset ) ; } } hidden public _offsetTop ( offset ) { super . _offsetTop ( offset ) ; for ( var child of this . _children ) { child . _offsetTop ( offset ) ; } } hidden public _markAllAsDirty ( ) { super . _markAllAsDirty ( ) ; for ( var index = 0 ; index < this . _children . length ; index ++ ) { this . _children [ index ] . _markAllAsDirty ( ) ; } } hidden protected _localDraw ( context ) { if ( this . _background ) { context . save ( ) ; if ( this . shadowBlur || this . shadowOffsetX || this . shadowOffsetY ) { context . shadowColor = this . shadowColor ; context . shadowBlur = this . shadowBlur ; context . shadowOffsetX = this . shadowOffsetX ; context . shadowOffsetY = this . shadowOffsetY ; } context . fillStyle = this . _background ; context . fillRect ( this . _currentMeasure . left , this . _currentMeasure . top , this . _currentMeasure . width , this . _currentMeasure . height ) ; context . restore ( ) ; } } hidden public _link ( host ) { super . _link ( host ) ; for ( var child of this . _children ) { child . _link ( host ) ; } } hidden protected _beforeLayout ( ) { } hidden protected _processMeasures ( parentMeasure , context ) { if ( this . _isDirty || ! this . _cachedParentMeasure . isEqualsTo ( parentMeasure ) ) { super . _processMeasures ( parentMeasure , context ) ; this . _evaluateClippingState ( parentMeasure ) ; if ( this . _renderToIntermediateTexture ) { if ( this . _intermediateTexture && this . _host . getScene ( ) != this . _intermediateTexture . getScene ( ) ) { this . _intermediateTexture . dispose ( ) ; this . _intermediateTexture = null ; } if ( ! this . _intermediateTexture ) { this . _intermediateTexture = new DynamicTexture ( '' , { width : this . _currentMeasure . width , height : this . _currentMeasure . height } , this . _host . getScene ( ) , false , Texture . NEAREST_SAMPLINGMODE , Constants . TEXTUREFORMAT_RGBA , false ) ; this . _intermediateTexture . hasAlpha = true ; } else { this . _intermediateTexture . scaleTo ( this . _currentMeasure . width , this . _currentMeasure . height ) ; } } } } hidden public _layout ( parentMeasure , context ) { if ( ! this . isDirty && ( ! this . isVisible || this . notRenderable ) ) { return false ; } this . host . _numLayoutCalls ++ ; if ( this . _isDirty ) { this . _currentMeasure . transformToRef ( this . _transformMatrix , this . _prevCurrentMeasureTransformedIntoGlobalSpace ) ; } let rebuildCount = 0 ; context . save ( ) ; this . _applyStates ( context ) ; this . _beforeLayout ( ) ; do { let computedWidth = - 1 ; let computedHeight = - 1 ; this . _rebuildLayout = false ; this . _processMeasures ( parentMeasure , context ) ; if ( ! this . _isClipped ) { for ( var child of this . _children ) { child . _tempParentMeasure . copyFrom ( this . _measureForChildren ) ; if ( child . _layout ( this . _measureForChildren , context ) ) { if ( this . adaptWidthToChildren && child . _width . isPixel ) { computedWidth = Math . max ( computedWidth , child . _currentMeasure . width + child . _paddingLeftInPixels + child . _paddingRightInPixels ) ; } if ( this . adaptHeightToChildren && child . _height . isPixel ) { computedHeight = Math . max ( computedHeight , child . _currentMeasure . height + child . _paddingTopInPixels + child . _paddingBottomInPixels ) ; } } } if ( this . adaptWidthToChildren && computedWidth >= 0 ) { computedWidth += this . paddingLeftInPixels + this . paddingRightInPixels ; if ( this . width !== computedWidth + "px" ) { this . parent ?. _markAsDirty ( ) ; this . width = computedWidth + "px" ; this . _rebuildLayout = true ; } } if ( this . adaptHeightToChildren && computedHeight >= 0 ) { computedHeight += this . paddingTopInPixels + this . paddingBottomInPixels ; if ( this . height !== computedHeight + "px" ) { this . parent ?. _markAsDirty ( ) ; this . height = computedHeight + "px" ; this . _rebuildLayout = true ; } } this . _postMeasure ( ) ; } rebuildCount ++ ; } while ( this . _rebuildLayout && rebuildCount < this . maxLayoutCycle ) ; if ( rebuildCount >= 3 && this . logLayoutCycleErrors ) { Logger . Error ( `Layout cycle detected in GUI (Container name=${ this . name }, uniqueId=${ this . uniqueId })` ) ; } context . restore ( ) ; if ( this . _isDirty ) { this . invalidateRect ( ) ; this . _isDirty = false ; } return true ; } protected _postMeasure ( ) { } hidden public _draw ( context , invalidatedRectangle ? ) { const renderToIntermediateTextureThisDraw = this . _renderToIntermediateTexture && this . _intermediateTexture ; const contextToDrawTo = renderToIntermediateTextureThisDraw ? ( < DynamicTexture > this . _intermediateTexture ) . getContext ( ) : context ; if ( renderToIntermediateTextureThisDraw ) { contextToDrawTo . save ( ) ; contextToDrawTo . translate ( - this . _currentMeasure . left , - this . _currentMeasure . top ) ; if ( invalidatedRectangle ) { contextToDrawTo . clearRect ( invalidatedRectangle . left , invalidatedRectangle . top , invalidatedRectangle . width , invalidatedRectangle . height ) ; } else { contextToDrawTo . clearRect ( this . _currentMeasure . left , this . _currentMeasure . top , this . _currentMeasure . width , this . _currentMeasure . height ) ; } } this . _localDraw ( contextToDrawTo ) ; context . save ( ) ; if ( this . clipChildren ) { this . _clipForChildren ( contextToDrawTo ) ; } for ( var child of this . _children ) { if ( invalidatedRectangle ) { if ( ! child . _intersectsRect ( invalidatedRectangle ) ) { continue ; } } child . _render ( contextToDrawTo , invalidatedRectangle ) ; } if ( renderToIntermediateTextureThisDraw ) { contextToDrawTo . restore ( ) ; context . save ( ) ; context . globalAlpha = this . alpha ; context . drawImage ( contextToDrawTo . canvas , this . _currentMeasure . left , this . _currentMeasure . top ) ; context . restore ( ) ; } context . restore ( ) ; } public getDescendantsToRef ( results , directDescendantsOnly = false , predicate ? ) { if ( ! this . children ) { return ; } for ( var index = 0 ; index < this . children . length ; index ++ ) { var item = this . children [ index ] ; if ( ! predicate || predicate ( item ) ) { results . push ( item ) ; } if ( ! directDescendantsOnly ) { item . getDescendantsToRef ( results , false , predicate ) ; } } } hidden public _processPicking ( x , y , pi , type , pointerId , buttonIndex , deltaX ? , deltaY ? ) { if ( ! this . _isEnabled || ! this . isVisible || this . notRenderable ) { return false ; } if ( ! super . contains ( x , y ) ) { return false ; } for ( var index = this . _children . length - 1 ; index >= 0 ; index -- ) { var child = this . _children [ index ] ; if ( child . _processPicking ( x , y , pi , type , pointerId , buttonIndex , deltaX , deltaY ) ) { if ( child . hoverCursor ) { this . _host . _changeCursor ( child . hoverCursor ) ; } return true ; } } if ( ! this . isHitTestVisible ) { return false ; } return this . _processObservables ( type , x , y , pi , pointerId , buttonIndex , deltaX , deltaY ) ; } hidden protected _additionalProcessing ( parentMeasure , context ) { super . _additionalProcessing ( parentMeasure , context ) ; this . _measureForChildren . copyFrom ( this . _currentMeasure ) ; } param serializationObject public serialize ( serializationObject ) { super . serialize ( serializationObject ) ; if ( ! this . children . length ) { return ; } serializationObject . children = [ ] ; for ( var child of this . children ) { let childSerializationObject = { } ; child . serialize ( childSerializationObject ) ; serializationObject . children . push ( childSerializationObject ) ; } } /** Releases associated resources */ public dispose ( ) { super . dispose ( ) ; for ( var index = this . children . length - 1 ; index >= 0 ; index -- ) { this . children [ index ] . dispose ( ) ; } this . _intermediateTexture ?. dispose ( ) ; } hidden public _parseFromContent ( serializedObject , host ) { super . _parseFromContent ( serializedObject , host ) ; this . _link ( host ) ; if ( ! serializedObject . children ) { return ; } for ( var childData of serializedObject . children ) { this . addControl ( Control . Parse ( childData , host ) ) ; } } } RegisterClass ( "BABYLON.GUI.Container" , Container ) ;