////////////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2014-present, Egret Technology. // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Egret nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////////////////////// /// namespace eui { export function getAssets(source: string, callback: (content: any) => void) { let adapter: IAssetAdapter = egret.getImplementation("eui.IAssetAdapter"); if (!adapter) { adapter = new DefaultAssetAdapter(); } adapter.getAsset(source, content => { callback(content); }, this); } export function getTheme(source: string, callback: (content: any) => void) { let adapter: IThemeAdapter = egret.getImplementation("eui.IThemeAdapter"); if (!adapter) { adapter = new DefaultThemeAdapter(); } adapter.getTheme(source, (data) => { callback(data) }, (e) => { console.log(e) }, this); } /** * The UIComponent class is the base class for all visual components, both skinnable and nonskinnable. * * @event egret.Event.RESIZE Dispatch when the component is resized. * @event eui.UIEvent.MOVE Dispatch when the object has moved. * @event eui.UIEvent.CREATION_COMPLETE Dispatch when the component has finished its construction, * property processing, measuring, layout, and drawing. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * UIComponent 类是所有可视组件(可定制皮肤和不可定制皮肤)的基类。 * * @event egret.Event.RESIZE 当UI组件的尺寸发生改变时调度 * @event eui.UIEvent.MOVE 当UI组件在父级容器中的位置发生改变时调度 * @event eui.UIEvent.CREATION_COMPLETE 当UI组件第一次被添加到舞台并完成初始化后调度 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ export interface UIComponent extends egret.DisplayObject { ///** // * 创建子项,子类覆盖此方法以完成组件子项的初始化操作, // * 请务必调用super.createChildren()以完成父类组件的初始化 // */ // protected createChildren():void{} ///** // * 提交属性,子类在调用完invalidateProperties()方法后,应覆盖此方法以应用属性 // */ // protected commitProperties():void{} ///** // * 测量组件尺寸 // */ // protected measure():void{} ///** // * 更新显示列表 // */ // protected updateDisplayList(unscaledWidth:number, unscaledHeight:number):void{} ///** // * 标记父级容器的尺寸和显示列表为失效 // */ // protected invalidateParentLayout():void{} //$getWidth():number; //$setWidth(value:number):void; //$getHeight():number; //$setHeight(value:number):void; /** * @private */ $UIComponent: Object; /** * @private */ $includeInLayout: boolean; /** * Specifies whether this component is included in the layout of the * parent container. * If false, the object size and position are not affected by its parent container's * layout. * This value is different with visible. the object size and position is still affected by its parent * container's layout when the visible is false. * @default true * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 指定此组件是否包含在父容器的布局中。若为false,则父级容器在测量和布局阶段都忽略此组件。 * 注意,visible属性与此属性不同,设置visible为false,父级容器仍会对其布局。 * * @default true * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ includeInLayout: boolean; /** * The horizontal distance in pixels from the left edge of the component to the * anchor target's left edge. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 距父级容器离左边距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ left: any; /** * The horizontal distance in pixels from the right edge of the component to the * anchor target's right edge. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 距父级容器右边距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ right: any; /** * The vertical distance in pixels from the top edge of the component to the * anchor target's top edge. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 距父级容器顶部距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ top: any; /** * The vertical distance in pixels from the bottom edge of the component to the * anchor target's bottom edge. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 距父级容器底部距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ bottom: any; /** * The horizontal distance in pixels from the center of the component to the * center of the anchor target's content area. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 在父级容器中距水平中心位置的距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ horizontalCenter: any; /** * The vertical distance in pixels from the center of the component to the * center of the anchor target's content area. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 在父级容器中距竖直中心位置的距离。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ verticalCenter: any; /** * Specifies the width of a component as a percentage * of its parent's size. Allowed values are 0-100. * Setting the width or explicitWidth properties * resets this property to NaN. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 相对父级容器宽度的百分比。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ percentWidth: number; /** * Specifies the height of a component as a percentage * of its parent's size. Allowed values are 0-100. * Setting the height or explicitHeight properties * resets this property to NaN. * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 相对父级容器高度的百分比。 * * @default NaN * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ percentHeight: number; /** * Number that specifies the explicit width of the component, * in pixels, in the component's coordinates. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 外部显式指定的宽度。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ explicitWidth: number; /** * Number that specifies the explicit height of the component, * in pixels, in the component's coordinates. * @readOnly * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 外部显式指定的高度。 * @readOnly * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ explicitHeight: number; /** * The minimum recommended width of the component to be considered * by the parent during layout. This value is in the * component's coordinates, in pixels. The default value depends on * the component's implementation. * @readOnly * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 组件的最小宽度,此属性设置为大于maxWidth的值时无效。同时影响测量和自动布局的尺寸。 * @readOnly * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ minWidth: number; /** * The maximum recommended width of the component to be considered * by the parent during layout. This value is in the * component's coordinates, in pixels. The default value of this property is * set by the component developer. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 组件的最大高度。同时影响测量和自动布局的尺寸。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ maxWidth: number; /** * The minimum recommended height of the component to be considered * by the parent during layout. This value is in the * component's coordinates, in pixels. The default value depends on * the component's implementation. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 组件的最小高度,此属性设置为大于maxHeight的值时无效。同时影响测量和自动布局的尺寸。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ minHeight: number; /** * The maximum recommended height of the component to be considered * by the parent during layout. This value is in the * component's coordinates, in pixels. The default value of this property is * set by the component developer. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 组件的最大高度,同时影响测量和自动布局的尺寸。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ maxHeight: number; /** * Set the result of measuring. * @param width measured width * @param height measured height * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 设置测量结果。 * @param width 测量宽度 * @param height 测量高度 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ setMeasuredSize(width: number, height: number): void; /** * Marks a component so that its commitProperties() * method gets called during a later screen update.

* * Invalidation is a useful mechanism for eliminating duplicate * work by delaying processing of changes to a component until a * later screen update.

* * For example, if you want to change the text color and size, * it would be wasteful to update the color immediately after you * change it and then update the size when it gets set. * It is more efficient to change both properties and then render * the text with its new size and color once.

* * Invalidation methods rarely get called. * In general, setting a property on a component automatically * calls the appropriate invalidation method. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 标记提交过需要延迟应用的属性,以便在稍后屏幕更新期间调用该组件的 commitProperties() 方法。

* * 这是一个很有用的机制,可将组件更改延迟到稍后屏幕更新时进行处理,从而消除了重复的工作。

* * 例如,要更改文本颜色和大小,如果在更改颜色后立即进行更新,然后在设置大小后再更新大小,就有些浪费。 * 同时更改两个属性后再使用新的大小和颜色一次性呈示文本,效率会更高。

* * 很少调用 Invalidation 方法。通常,在组件上设置属性会自动调用合适的 invalidation 方法。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ invalidateProperties(): void; /** * Used by layout logic to validate the properties of a component * by calling the commitProperties() method. * In general, subclassers should * override the commitProperties() method and not this method. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 由布局逻辑用于通过调用 commitProperties() 方法来验证组件的属性。 * 通常,子类应覆盖 commitProperties() 方法,而不是覆盖此方法。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ validateProperties(): void; /** * Marks a component so that its measure() * method gets called during a later screen update.

* * Invalidation is a useful mechanism for eliminating duplicate * work by delaying processing of changes to a component until a * later screen update.

* * For example, if you want to change the text and font size, * it would be wasteful to update the text immediately after you * change it and then update the size when it gets set. * It is more efficient to change both properties and then render * the text with its new size once.

* * Invalidation methods rarely get called. * In general, setting a property on a component automatically * calls the appropriate invalidation method. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 标记提交过需要验证组件尺寸,以便在稍后屏幕更新期间调用该组件的 measure() 方法。

* * Invalidation 是一个很有用的机制,可将组件更改延迟到稍后屏幕更新时进行处理,从而消除了重复的工作。

* * 例如,要更改文本和字体大小,如果在更改文本后立即进行更新,然后在设置大小后再更新大小,就有些浪费。 * 更改两个属性后再使用新的大小一次性呈示文本,效率会更高。

* * 很少调用 Invalidation 方法。通常,在组件上设置属性会自动调用合适的 invalidation 方法。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ invalidateSize(): void; /** * Validates the measured size of the component. * @param recursive If true, call this method * on the objects children. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 验证组件的尺寸。 * @param recursive 如果为 true,则调用对象子项的此方法。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ validateSize(recursive?: boolean): void; /** * Marks a component so that its updateDisplayList() * method gets called during a later screen update.

* * Invalidation is a useful mechanism for eliminating duplicate * work by delaying processing of changes to a component until a * later screen update.

* * For example, if you want to change the width and height, * it would be wasteful to update the component immediately after you * change the width and then update again with the new height. * It is more efficient to change both properties and then render * the component with its new size once.

* * Invalidation methods rarely get called. * In general, setting a property on a component automatically * calls the appropriate invalidation method. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 标记需要验证显示列表,以便在稍后屏幕更新期间调用该组件的 updateDisplayList() 方法。

* * Invalidation 是一个很有用的机制,可将组件更改延迟到稍后屏幕更新时进行处理,从而消除了重复的工作。

* * 例如,要更改宽度和高度,如果在更改宽度后立即更新组件,然后在设置新高度后再次更新组件,就有些浪费。 * 更改两个属性后再使用新的大小一次性呈示组件,效率会更高。

* * 很少调用 Invalidation 方法。通常,在组件上设置属性会自动调用合适的 invalidation 方法。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ invalidateDisplayList(): void; /** * Validates the position and size of children and draws other * visuals. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 验证子项的位置和大小,并绘制其他可视内容。 * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ validateDisplayList(): void; /** * Validate and update the properties and layout of this object * and redraw it, if necessary.

* * Processing properties that require substantial computation are normally * not processed until the script finishes executing.

* * For example setting the width property is delayed, because it can * require recalculating the widths of the objects children or its parent. * Delaying the processing prevents it from being repeated * multiple times if the script sets the width property more than once. * This method lets you manually override this behavior. * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 验证并更新此对象的属性和布局,如果需要的话重绘对象。

* * 通常只有当脚本执行完毕后,才会处理要求进行大量计算的处理属性。

* * 例如,对 width 属性的设置可能会延迟,因为此设置需要重新计算这些对象的子项或父项的宽度。 * 如果脚本多次设置了 width 属性,则延迟处理可防止进行多次处理。此方法允许您手动覆盖此行为。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ validateNow(): void; /** * Sets the layout size of the element. * This is the size that the element uses to draw on screen.

* * If the width and/or height parameters are left unspecified (NaN), * EUI sets the element's layout size to its preferred width and/or preferred height.

* * Note that calls to the setLayoutBoundSize() method can affect the layout position, so * call setLayoutBoundPosition() after calling setLayoutBoundSize().

* * @param layoutWidth The element's layout width. * @param layoutHeight The element's layout height. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 设置元素的布局大小。这是元素在屏幕上进行绘制时所用的大小。

* * 如果 width 和/或 height 参数尚未指定 (NaN)),则 EUI 会将该元素的布局大小设置为首选宽度和/或首选高度。

* * 请注意,调用 setLayoutBoundSize() 方法会影响布局位置,因此请在调用 setLayoutBoundSize() * 之后再调用 setLayoutBoundPosition()。 * * @param layoutWidth 元素的布局宽度。 * @param layoutHeight 元素的布局高度。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ setLayoutBoundsSize(layoutWidth: number, layoutHeight: number): void; /** * Sets the coordinates that the element uses to draw on screen.

* * Note that calls to the setLayoutBoundSize() method can affect the layout position, so * call setLayoutBoundPosition() after calling setLayoutBoundSize().

* * @param x The x-coordinate of the top-left corner of the bounding box. * @param y The y-coordinate of the top-left corner of the bounding box. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 设置元素在屏幕上进行绘制时所用的布局坐标。

* * 请注意,调用 setLayoutBoundSize() 方法会影响布局位置,因此请在调用 setLayoutBoundSize() * 之后再调用 setLayoutBoundPosition()。 * * @param x 边框左上角的 X 坐标。 * @param y 边框左上角的 Y 坐标。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ setLayoutBoundsPosition(x: number, y: number): void; /** * Get the layout bounds that the element uses to draw on screen. * Commonly used in the updateDisplayList() method in parent container.

* Priority: layout > explicit > measure.

* The result of this method is contains scale and rotation. * * @param bounds the instance of egret.Rectangle can set result. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 组件的布局尺寸,常用于父级的updateDisplayList()方法中。

* 按照:布局尺寸>外部显式设置尺寸>测量尺寸 的优先级顺序返回尺寸。

* 注意此方法返回值已经包含scale和rotation。 * * @param bounds 可以放置结果的egret.Rectangle实例。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ getLayoutBounds(bounds: egret.Rectangle): void; /** * Get the element's preferred bounds。 * Commonly used in the measure() method in parent container.

* Priority: explicit > measure.

* The result of this method is contains scale and rotation. * * @param bounds the instance of egret.Rectangle can set result. * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language en_US */ /** * 获取组件的首选尺寸,常用于父级的measure()方法中。

* 按照:外部显式设置尺寸>测量尺寸 的优先级顺序返回尺寸。

* 注意此方法返回值已经包含scale和rotation。 * * @param bounds 可以放置结果的egret.Rectangle实例。 * * @version Egret 2.4 * @version eui 1.0 * @platform Web,Native * @language zh_CN */ getPreferredBounds(bounds: egret.Rectangle): void; } } namespace eui.sys { /** * @private */ export const enum UIKeys { left, right, top, bottom, horizontalCenter, verticalCenter, percentWidth, percentHeight, explicitWidth, explicitHeight, width, height, minWidth, maxWidth, minHeight, maxHeight, measuredWidth, measuredHeight, oldPreferWidth, oldPreferHeight, oldX, oldY, oldWidth, oldHeight, invalidatePropertiesFlag, invalidateSizeFlag, invalidateDisplayListFlag, layoutWidthExplicitlySet, layoutHeightExplicitlySet, initialized } let UIComponentClass = "eui.UIComponent"; function isDeltaIdentity(m: egret.Matrix): boolean { return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1); } let validator = new sys.Validator(); /** * @private * EUI 显示对象基类模板。仅作为 UIComponent 的默认实现,为egret.sys.implemenetUIComponenet()方法提供代码模板。 * 注意:在此类里不允许直接使用super关键字访问父类方法。一律使用this.$super属性访问。 */ export class UIComponentImpl extends egret.DisplayObject implements eui.UIComponent { /** * @private * 构造函数 */ public constructor() { super(); this.initializeUIValues(); } /** * @private * UIComponentImpl 定义的所有变量请不要添加任何初始值,必须统一在此处初始化。 */ private initializeUIValues(): void { this.$UIComponent = { 0: NaN, //left 1: NaN, //right 2: NaN, //top 3: NaN, //bottom 4: NaN, //horizontalCenter 5: NaN, //verticalCenter 6: NaN, //percentWidth 7: NaN, //percentHeight 8: NaN, //explicitWidth 9: NaN, //explicitHeight 10: 0, //width 11: 0, //height 12: 0, //minWidth 13: 100000, //maxWidth 14: 0, //minHeight 15: 100000, //maxHeight 16: 0, //measuredWidth 17: 0, //measuredHeight 18: NaN, //oldPreferWidth 19: NaN, //oldPreferHeight 20: 0, //oldX 21: 0, //oldY 22: 0, //oldWidth 23: 0, //oldHeight 24: true, //invalidatePropertiesFlag 25: true, //invalidateSizeFlag 26: true, //invalidateDisplayListFlag 27: false, //layoutWidthExplicitlySet 28: false, //layoutHeightExplicitlySet 29: false, //initialized }; this.$includeInLayout = true; //if egret this.$touchEnabled = true; //endif*/ } /** * @private * 子类覆盖此方法可以执行一些初始化子项操作。此方法仅在组件第一次添加到舞台时回调一次。 * 请务必调用super.createChildren()以完成父类组件的初始化 */ protected createChildren(): void { } /** * @private * 子项创建完成,此方法在createChildren()之后执行。 */ protected childrenCreated(): void { } /** * @private * 提交属性,子类在调用完invalidateProperties()方法后,应覆盖此方法以应用属性 */ protected commitProperties(): void { let values = this.$UIComponent; if (values[UIKeys.oldWidth] != values[UIKeys.width] || values[UIKeys.oldHeight] != values[UIKeys.height]) { this.dispatchEventWith(egret.Event.RESIZE); values[UIKeys.oldWidth] = values[UIKeys.width]; values[UIKeys.oldHeight] = values[UIKeys.height]; } if (values[UIKeys.oldX] != this.$getX() || values[UIKeys.oldY] != this.$getY()) { UIEvent.dispatchUIEvent(this, UIEvent.MOVE); values[UIKeys.oldX] = this.$getX(); values[UIKeys.oldY] = this.$getY(); } } /** * @private * 测量组件尺寸 */ protected measure(): void { } /** * @private * 更新显示列表 */ protected updateDisplayList(unscaledWidth: number, unscaledHeight: number): void { } $super: any; $UIComponent: Object; $includeInLayout: boolean; /** * @private * 指定此组件是否包含在父容器的布局中。若为false,则父级容器在测量和布局阶段都忽略此组件。默认值为true。 * 注意,visible属性与此属性不同,设置visible为false,父级容器仍会对其布局。 */ public get includeInLayout(): boolean { return this.$includeInLayout; } public set includeInLayout(value: boolean) { value = !!value; if (this.$includeInLayout === value) return; this.$includeInLayout = true; this.invalidateParentLayout(); this.$includeInLayout = value; } /** * @private * * @param stage * @param nestLevel */ $onAddToStage(stage: egret.Stage, nestLevel: number): void { this.$super.$onAddToStage.call(this, stage, nestLevel); this.checkInvalidateFlag(); let values = this.$UIComponent; if (!values[sys.UIKeys.initialized]) { values[sys.UIKeys.initialized] = true; this.createChildren(); this.childrenCreated(); UIEvent.dispatchUIEvent(this, UIEvent.CREATION_COMPLETE); } } /** * @private * 检查属性失效标记并应用 */ private checkInvalidateFlag(event?: Event): void { let values = this.$UIComponent; if (values[sys.UIKeys.invalidatePropertiesFlag]) { validator.invalidateProperties(this); } if (values[sys.UIKeys.invalidateSizeFlag]) { validator.invalidateSize(this); } if (values[sys.UIKeys.invalidateDisplayListFlag]) { validator.invalidateDisplayList(this); } } /** * @private * 距父级容器离左边距离 */ public get left(): any { return this.$UIComponent[UIKeys.left]; } public set left(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.left] === value) return; values[UIKeys.left] = value; this.invalidateParentLayout(); } /** * @private * 距父级容器右边距离 */ public get right(): any { return this.$UIComponent[UIKeys.right]; } public set right(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.right] === value) return; values[UIKeys.right] = value; this.invalidateParentLayout(); } /** * @private * 距父级容器顶部距离 */ public get top(): any { return this.$UIComponent[UIKeys.top]; } public set top(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.top] === value) return; values[UIKeys.top] = value; this.invalidateParentLayout(); } /** * @private * 距父级容器底部距离 */ public get bottom(): any { return this.$UIComponent[UIKeys.bottom]; } public set bottom(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.bottom] == value) return; values[UIKeys.bottom] = value; this.invalidateParentLayout(); } /** * @private * 在父级容器中距水平中心位置的距离 */ public get horizontalCenter(): any { return this.$UIComponent[UIKeys.horizontalCenter]; } public set horizontalCenter(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.horizontalCenter] === value) return; values[UIKeys.horizontalCenter] = value; this.invalidateParentLayout(); } /** * @private * 在父级容器中距竖直中心位置的距离 */ public get verticalCenter(): any { return this.$UIComponent[UIKeys.verticalCenter]; } public set verticalCenter(value: any) { if (!value || typeof value == "number") { value = +value; } else { value = value.toString().trim(); } let values = this.$UIComponent; if (values[UIKeys.verticalCenter] === value) return; values[UIKeys.verticalCenter] = value; this.invalidateParentLayout(); } /** * @private * 相对父级容器宽度的百分比 */ public get percentWidth(): number { return this.$UIComponent[UIKeys.percentWidth]; } public set percentWidth(value: number) { value = +value; let values = this.$UIComponent; if (values[UIKeys.percentWidth] === value) return; values[UIKeys.percentWidth] = value; this.invalidateParentLayout(); } /** * @private * 相对父级容器高度的百分比 */ public get percentHeight(): number { return this.$UIComponent[UIKeys.percentHeight]; } public set percentHeight(value: number) { value = +value; let values = this.$UIComponent; if (values[UIKeys.percentHeight] === value) return; values[UIKeys.percentHeight] = value; this.invalidateParentLayout(); } /** * @private * 外部显式指定的宽度 */ public get explicitWidth(): number { return this.$UIComponent[UIKeys.explicitWidth]; } /** * @private * 外部显式指定的高度 */ public get explicitHeight(): number { return this.$UIComponent[UIKeys.explicitHeight]; } /** * @private * 组件宽度,默认值为egret.NaN,设置为NaN将使用组件的measure()方法自动计算尺寸 */ $getWidth(): number { this.validateSizeNow(); return this.$UIComponent[UIKeys.width]; } /** * @private * * @param value */ $setWidth(value: number): boolean { value = +value; let values = this.$UIComponent; if (value < 0 || values[UIKeys.width] === value && values[UIKeys.explicitWidth] === value) return false; values[UIKeys.explicitWidth] = value; if (isNaN(value)) this.invalidateSize(); this.invalidateProperties(); this.invalidateDisplayList(); this.invalidateParentLayout(); return true; } /** * @private * 立即验证自身的尺寸。 */ private validateSizeNow(): void { this.validateSize(true); this.updateFinalSize(); } /** * @private * 组件高度,默认值为NaN,设置为NaN将使用组件的measure()方法自动计算尺寸 */ $getHeight(): number { this.validateSizeNow(); return this.$UIComponent[UIKeys.height]; } /** * @private * * @param value */ $setHeight(value: number): boolean { value = +value; let values = this.$UIComponent; if (value < 0 || values[UIKeys.height] === value && values[UIKeys.explicitHeight] === value) return false; values[UIKeys.explicitHeight] = value; if (isNaN(value)) this.invalidateSize(); this.invalidateProperties(); this.invalidateDisplayList(); this.invalidateParentLayout(); return true; } /** * @private * 组件的最小宽度,此属性设置为大于maxWidth的值时无效。同时影响测量和自动布局的尺寸。 */ public get minWidth(): number { return this.$UIComponent[UIKeys.minWidth]; } public set minWidth(value: number) { value = +value || 0; let values = this.$UIComponent; if (value < 0 || values[UIKeys.minWidth] === value) { return; } values[UIKeys.minWidth] = value; this.invalidateSize(); this.invalidateParentLayout(); } /** * @private * 组件的最大高度。同时影响测量和自动布局的尺寸。 */ public get maxWidth(): number { return this.$UIComponent[UIKeys.maxWidth]; } public set maxWidth(value: number) { value = +value || 0; let values = this.$UIComponent; if (value < 0 || values[UIKeys.maxWidth] === value) { return; } values[UIKeys.maxWidth] = value; this.invalidateSize(); this.invalidateParentLayout(); } /** * @private * 组件的最小高度,此属性设置为大于maxHeight的值时无效。同时影响测量和自动布局的尺寸。 */ public get minHeight(): number { return this.$UIComponent[UIKeys.minHeight]; } public set minHeight(value: number) { value = +value || 0; let values = this.$UIComponent; if (value < 0 || values[UIKeys.minHeight] === value) { return; } values[UIKeys.minHeight] = value; this.invalidateSize(); this.invalidateParentLayout(); } /** * @private * 组件的最大高度,同时影响测量和自动布局的尺寸。 */ public get maxHeight(): number { return this.$UIComponent[UIKeys.maxHeight]; } public set maxHeight(value: number) { value = +value || 0; let values = this.$UIComponent; if (value < 0 || values[UIKeys.maxHeight] === value) { return; } values[UIKeys.maxHeight] = value; this.invalidateSize(); this.invalidateParentLayout(); } /** * @private * 设置测量结果。 * @param width 测量宽度 * @param height 测量高度 */ public setMeasuredSize(width: number, height: number): void { let values = this.$UIComponent; values[UIKeys.measuredWidth] = Math.ceil(+width || 0); values[UIKeys.measuredHeight] = Math.ceil(+height || 0); } /** * @private * 设置组件的宽高。此方法不同于直接设置width,height属性, * 不会影响显式标记尺寸属性 */ private setActualSize(w: number, h: number): void { let change = false; let values = this.$UIComponent; if (values[UIKeys.width] !== w) { values[UIKeys.width] = w; change = true; } if (values[UIKeys.height] !== h) { values[UIKeys.height] = h; change = true; } if (change) { this.invalidateDisplayList(); this.dispatchEventWith(egret.Event.RESIZE); } } /** * @private */ $invalidateMatrix(): void { this.$super.$invalidateMatrix.call(this); this.invalidateParentLayout(); } /** * @private */ $setMatrix(matrix: egret.Matrix, needUpdateProperties: boolean = true): boolean { this.$super.$setMatrix.call(this, matrix, needUpdateProperties); this.invalidateParentLayout(); return true; } /** * @private */ $setAnchorOffsetX(value: number): boolean { this.$super.$setAnchorOffsetX.call(this, value); this.invalidateParentLayout(); return true; } /** * @private */ $setAnchorOffsetY(value: number): boolean { this.$super.$setAnchorOffsetY.call(this, value); this.invalidateParentLayout(); return true; } /** * @private * * @param value * @returns */ $setX(value: number): boolean { let change = this.$super.$setX.call(this, value); if (change) { this.invalidateParentLayout(); this.invalidateProperties(); } return change; } /** * @private * * @param value * @returns */ $setY(value: number): boolean { let change = this.$super.$setY.call(this, value); if (change) { this.invalidateParentLayout(); this.invalidateProperties(); } return change; } /** * @private * 标记属性失效 */ public invalidateProperties(): void { let values = this.$UIComponent; if (!values[sys.UIKeys.invalidatePropertiesFlag]) { values[sys.UIKeys.invalidatePropertiesFlag] = true; if (this.$stage) validator.invalidateProperties(this); } } /** * @private * 验证组件的属性 */ public validateProperties(): void { let values = this.$UIComponent; if (values[sys.UIKeys.invalidatePropertiesFlag]) { this.commitProperties(); values[sys.UIKeys.invalidatePropertiesFlag] = false; } } /** * @private * 标记提交过需要验证组件尺寸 */ public invalidateSize(): void { let values = this.$UIComponent; if (!values[sys.UIKeys.invalidateSizeFlag]) { values[sys.UIKeys.invalidateSizeFlag] = true; if (this.$stage) validator.invalidateSize(this); } } /** * @private * 验证组件的尺寸 */ public validateSize(recursive?: boolean): void { if (recursive) { let children = this.$children; if (children) { let length = children.length; for (let i = 0; i < length; i++) { let child = children[i]; if (egret.is(child, UIComponentClass)) { (child).validateSize(true); } } } } let values = this.$UIComponent; if (values[sys.UIKeys.invalidateSizeFlag]) { let changed = this.measureSizes(); if (changed) { this.invalidateDisplayList(); this.invalidateParentLayout(); } values[sys.UIKeys.invalidateSizeFlag] = false; } } /** * @private * 测量组件尺寸,返回尺寸是否发生变化 */ private measureSizes(): boolean { let changed = false; let values = this.$UIComponent; if (!values[sys.UIKeys.invalidateSizeFlag]) return changed; if (isNaN(values[UIKeys.explicitWidth]) || isNaN(values[UIKeys.explicitHeight])) { this.measure(); if (values[UIKeys.measuredWidth] < values[UIKeys.minWidth]) { values[UIKeys.measuredWidth] = values[UIKeys.minWidth]; } if (values[UIKeys.measuredWidth] > values[UIKeys.maxWidth]) { values[UIKeys.measuredWidth] = values[UIKeys.maxWidth]; } if (values[UIKeys.measuredHeight] < values[UIKeys.minHeight]) { values[UIKeys.measuredHeight] = values[UIKeys.minHeight]; } if (values[UIKeys.measuredHeight] > values[UIKeys.maxHeight]) { values[UIKeys.measuredHeight] = values[UIKeys.maxHeight] } } let preferredW = this.getPreferredUWidth(); let preferredH = this.getPreferredUHeight(); if (preferredW !== values[UIKeys.oldPreferWidth] || preferredH !== values[UIKeys.oldPreferHeight]) { values[UIKeys.oldPreferWidth] = preferredW; values[UIKeys.oldPreferHeight] = preferredH; changed = true; } return changed; } /** * @private * 标记需要验证显示列表 */ public invalidateDisplayList(): void { let values = this.$UIComponent; if (!values[sys.UIKeys.invalidateDisplayListFlag]) { values[sys.UIKeys.invalidateDisplayListFlag] = true; if (this.$stage) validator.invalidateDisplayList(this); } } /** * @private * 验证子项的位置和大小,并绘制其他可视内容 */ public validateDisplayList(): void { let values = this.$UIComponent; if (values[sys.UIKeys.invalidateDisplayListFlag]) { this.updateFinalSize(); this.updateDisplayList(values[UIKeys.width], values[UIKeys.height]); values[sys.UIKeys.invalidateDisplayListFlag] = false; } } /** * @private * 更新最终的组件宽高 */ private updateFinalSize(): void { let unscaledWidth = 0; let unscaledHeight = 0; let values = this.$UIComponent; if (values[sys.UIKeys.layoutWidthExplicitlySet]) { unscaledWidth = values[UIKeys.width]; } else if (!isNaN(values[UIKeys.explicitWidth])) { unscaledWidth = values[UIKeys.explicitWidth]; } else { unscaledWidth = values[UIKeys.measuredWidth]; } if (values[sys.UIKeys.layoutHeightExplicitlySet]) { unscaledHeight = values[UIKeys.height]; } else if (!isNaN(values[UIKeys.explicitHeight])) { unscaledHeight = values[UIKeys.explicitHeight]; } else { unscaledHeight = values[UIKeys.measuredHeight]; } this.setActualSize(unscaledWidth, unscaledHeight); } /** * @private * 立即应用组件及其子项的所有属性 */ public validateNow(): void { if (this.$stage) validator.validateClient(this); } /** * @private * 标记父级容器的尺寸和显示列表为失效 */ protected invalidateParentLayout(): void { let parent = this.$parent; if (!parent || !this.$includeInLayout || !egret.is(parent, UIComponentClass)) return; (parent).invalidateSize(); (parent).invalidateDisplayList(); } /** * @private * 设置组件的布局宽高 */ public setLayoutBoundsSize(layoutWidth: number, layoutHeight: number): void { layoutHeight = +layoutHeight; layoutWidth = +layoutWidth; if (layoutHeight < 0 || layoutWidth < 0) { return; } let values = this.$UIComponent; let maxWidth = values[UIKeys.maxWidth]; let maxHeight = values[UIKeys.maxHeight]; let minWidth = Math.min(values[UIKeys.minWidth], maxWidth); let minHeight = Math.min(values[UIKeys.minHeight], maxHeight); let width: number; let height: number; if (isNaN(layoutWidth)) { values[sys.UIKeys.layoutWidthExplicitlySet] = false; width = this.getPreferredUWidth(); } else { values[sys.UIKeys.layoutWidthExplicitlySet] = true; width = Math.max(minWidth, Math.min(maxWidth, layoutWidth)); } if (isNaN(layoutHeight)) { values[sys.UIKeys.layoutHeightExplicitlySet] = false; height = this.getPreferredUHeight(); } else { values[sys.UIKeys.layoutHeightExplicitlySet] = true; height = Math.max(minHeight, Math.min(maxHeight, layoutHeight)); } let matrix = this.getAnchorMatrix(); if (isDeltaIdentity(matrix)) { this.setActualSize(width, height); return; } let fitSize = sys.MatrixUtil.fitBounds(layoutWidth, layoutHeight, matrix, values[UIKeys.explicitWidth], values[UIKeys.explicitHeight], this.getPreferredUWidth(), this.getPreferredUHeight(), minWidth, minHeight, maxWidth, maxHeight); if (!fitSize) { fitSize = egret.Point.create(minWidth, minHeight); } this.setActualSize(fitSize.x, fitSize.y); egret.Point.release(fitSize); } /** * @private * 设置组件的布局位置 */ public setLayoutBoundsPosition(x: number, y: number): void { let matrix = this.$getMatrix(); if (!isDeltaIdentity(matrix) || this.anchorOffsetX != 0 || this.anchorOffsetY != 0) { let bounds = egret.$TempRectangle; this.getLayoutBounds(bounds); x += this.$getX() - bounds.x; y += this.$getY() - bounds.y; } let changed: boolean = this.$super.$setX.call(this, x); if (this.$super.$setY.call(this, y) || changed) { UIEvent.dispatchUIEvent(this, UIEvent.MOVE); } } /** * @private * 组件的布局尺寸,常用于父级的updateDisplayList()方法中 * 按照:布局尺寸>外部显式设置尺寸>测量尺寸 的优先级顺序返回尺寸, * 注意此方法返回值已经包含scale和rotation。 */ public getLayoutBounds(bounds: egret.Rectangle): void { let values = this.$UIComponent; let w: number; if (values[sys.UIKeys.layoutWidthExplicitlySet]) { w = values[UIKeys.width]; } else if (!isNaN(values[UIKeys.explicitWidth])) { w = values[UIKeys.explicitWidth]; } else { w = values[UIKeys.measuredWidth]; } let h: number; if (values[sys.UIKeys.layoutHeightExplicitlySet]) { h = values[UIKeys.height]; } else if (!isNaN(values[UIKeys.explicitHeight])) { h = values[UIKeys.explicitHeight]; } else { h = values[UIKeys.measuredHeight]; } this.applyMatrix(bounds, w, h); } /** * @private * * @returns */ private getPreferredUWidth(): number { let values = this.$UIComponent; return isNaN(values[UIKeys.explicitWidth]) ? values[UIKeys.measuredWidth] : values[UIKeys.explicitWidth]; } /** * @private * * @returns */ private getPreferredUHeight(): number { let values = this.$UIComponent; return isNaN(values[UIKeys.explicitHeight]) ? values[UIKeys.measuredHeight] : values[UIKeys.explicitHeight]; } /** * @private * 获取组件的首选尺寸,常用于父级的measure()方法中 * 按照:外部显式设置尺寸>测量尺寸 的优先级顺序返回尺寸, * 注意此方法返回值已经包含scale和rotation。 */ public getPreferredBounds(bounds: egret.Rectangle): void { let w = this.getPreferredUWidth(); let h = this.getPreferredUHeight(); this.applyMatrix(bounds, w, h); } /** * @private */ private applyMatrix(bounds: egret.Rectangle, w: number, h: number): void { bounds.setTo(0, 0, w, h); let matrix = this.getAnchorMatrix(); if (isDeltaIdentity(matrix)) { bounds.x += matrix.tx; bounds.y += matrix.ty; } else { matrix.$transformBounds(bounds); } } /** * @private */ private getAnchorMatrix(): egret.Matrix { let matrix = this.$getMatrix(); let offsetX = this.anchorOffsetX; let offsetY = this.anchorOffsetY; if (offsetX != 0 || offsetY != 0) { let tempM = egret.$TempMatrix; matrix.$preMultiplyInto(tempM.setTo(1, 0, 0, 1, -offsetX, -offsetY), tempM); return tempM; } return matrix; } } /** * 检查一个函数的方法体是否为空。 */ function isEmptyFunction(prototype: any, key: string): boolean { if (typeof prototype[key] != "function") { return false; } let body = prototype[key].toString(); let index = body.indexOf("{"); let lastIndex = body.lastIndexOf("}"); body = body.substring(index + 1, lastIndex); return body.trim() == ""; } /** * @private * 拷贝模板类的方法体和属性到目标类上。 * @param target 目标类 * @param template 模板类 */ export function mixin(target: any, template: any): void { for (let property in template) { if (property != "prototype" && template.hasOwnProperty(property)) { target[property] = template[property]; } } let prototype = target.prototype; let protoBase = template.prototype; let keys = Object.keys(protoBase); let length = keys.length; for (let i = 0; i < length; i++) { let key = keys[i]; if (key == "__meta__") { continue; } if (!prototype.hasOwnProperty(key) || isEmptyFunction(prototype, key)) { let value = Object.getOwnPropertyDescriptor(protoBase, key); Object.defineProperty(prototype, key, value); } } } /** * @private * 自定义类实现UIComponent的步骤: * 1.在自定义类的构造函数里调用:this.initializeUIValues(); * 2.拷贝UIComponent接口定义的所有内容(包括注释掉的protected函数)到自定义类,将所有子类需要覆盖的方法都声明为空方法体。 * 3.在定义类结尾的外部调用sys.implementUIComponent(),并传入自定义类。 * 4.若覆盖了某个UIComponent的方法,需要手动调用UIComponentImpl.prototype["方法名"].call(this); * @param descendant 自定义的UIComponent子类 * @param base 自定义子类继承的父类 */ export function implementUIComponent(descendant: any, base: any, isContainer?: boolean): void { mixin(descendant, UIComponentImpl); let prototype = descendant.prototype; prototype.$super = base.prototype; registerProperty(descendant, "left", "Percentage"); registerProperty(descendant, "right", "Percentage"); registerProperty(descendant, "top", "Percentage"); registerProperty(descendant, "bottom", "Percentage"); registerProperty(descendant, "horizontalCenter", "Percentage"); registerProperty(descendant, "verticalCenter", "Percentage"); if (isContainer) { prototype.$childAdded = function (child: egret.DisplayObject, index: number): void { this.invalidateSize(); this.invalidateDisplayList(); }; prototype.$childRemoved = function (child: egret.DisplayObject, index: number): void { this.invalidateSize(); this.invalidateDisplayList(); }; } if (DEBUG) {//用于调试时查看布局尺寸的便利属性,发行版时移除。 Object.defineProperty(prototype, "preferredWidth", { get: function () { let bounds = egret.$TempRectangle; this.getPreferredBounds(bounds); return bounds.width; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "preferredHeight", { get: function () { let bounds = egret.$TempRectangle; this.getPreferredBounds(bounds); return bounds.height; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "preferredX", { get: function () { let bounds = egret.$TempRectangle; this.getPreferredBounds(bounds); return bounds.x; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "preferredY", { get: function () { let bounds = egret.$TempRectangle; this.getPreferredBounds(bounds); return bounds.y; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutBoundsX", { get: function () { let bounds = egret.$TempRectangle; this.getLayoutBounds(bounds); return bounds.x; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutBoundsY", { get: function () { let bounds = egret.$TempRectangle; this.getLayoutBounds(bounds); return bounds.y; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutBoundsWidth", { get: function () { let bounds = egret.$TempRectangle; this.getLayoutBounds(bounds); return bounds.width; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutBoundsHeight", { get: function () { let bounds = egret.$TempRectangle; this.getLayoutBounds(bounds); return bounds.height; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "measuredWidth", { get: function () { return this.$UIComponent[UIKeys.measuredWidth]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "measuredHeight", { get: function () { return this.$UIComponent[UIKeys.measuredHeight]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutWidthExplicitlySet", { get: function () { return this.$UIComponent[UIKeys.layoutWidthExplicitlySet]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "layoutHeightExplicitlySet", { get: function () { return this.$UIComponent[UIKeys.layoutHeightExplicitlySet]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "invalidatePropertiesFlag", { get: function () { return this.$UIComponent[UIKeys.invalidatePropertiesFlag]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "invalidateSizeFlag", { get: function () { return this.$UIComponent[UIKeys.invalidateSizeFlag]; }, enumerable: true, configurable: true }); Object.defineProperty(prototype, "invalidateDisplayListFlag", { get: function () { return this.$UIComponent[UIKeys.invalidateDisplayListFlag]; }, enumerable: true, configurable: true }); } } }