dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
550 lines (517 loc) • 23.1 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// 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 {
let UIComponentClass = "eui.UIComponent";
/**
* The VerticalLayout class arranges the layout elements in a vertical sequence,
* top to bottom, with optional gaps between the elements and optional padding
* around the sequence of elements.
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
* @includeExample extension/eui/layout/VerticalLayoutExample.ts
* @language en_US
*/
/**
* VerticalLayout 类按垂直顺序从上向下排列布局元素,在元素和围绕元素顺序的可选填充之间带有可选间隙。
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
* @includeExample extension/eui/layout/VerticalLayoutExample.ts
* @language zh_CN
*/
export class VerticalLayout extends LinearLayoutBase {
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected measureReal():void {
let target = this.$target;
let count = target.numElements;
let numElements = count;
let measuredWidth = 0;
let measuredHeight = 0;
let bounds = egret.$TempRectangle;
for (let i = 0; i < count; i++) {
let layoutElement = <UIComponent> (target.getElementAt(i));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
numElements--;
continue;
}
layoutElement.getPreferredBounds(bounds);
measuredHeight += bounds.height;
measuredWidth = Math.max(measuredWidth, bounds.width);
}
measuredHeight += (numElements - 1) * this.$gap;
let hPadding = this.$paddingLeft + this.$paddingRight;
let vPadding = this.$paddingTop + this.$paddingBottom;
target.setMeasuredSize(measuredWidth + hPadding, measuredHeight + vPadding);
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected measureVirtual():void {
let target = this.$target;
let typicalHeight = this.$typicalHeight;
let measuredHeight = this.getElementTotalSize();
let measuredWidth = Math.max(this.maxElementSize, this.$typicalWidth);
let bounds = egret.$TempRectangle;
let endIndex = this.endIndex;
let elementSizeTable = this.elementSizeTable;
for (let index = this.startIndex; index < endIndex; index++) {
let layoutElement = <UIComponent> (target.getElementAt(index));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
continue;
}
layoutElement.getPreferredBounds(bounds);
measuredHeight += bounds.height;
measuredHeight -= isNaN(elementSizeTable[index]) ? typicalHeight : elementSizeTable[index];
measuredWidth = Math.max(measuredWidth, bounds.width);
}
let hPadding = this.$paddingLeft + this.$paddingRight;
let vPadding = this.$paddingTop + this.$paddingBottom;
target.setMeasuredSize(measuredWidth + hPadding, measuredHeight + vPadding);
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected updateDisplayListReal(width:number, height:number):void {
let target = this.$target;
let paddingL = this.$paddingLeft;
let paddingR = this.$paddingRight;
let paddingT = this.$paddingTop;
let paddingB = this.$paddingBottom;
let gap = this.$gap;
let targetWidth = Math.max(0, width - paddingL - paddingR);
let targetHeight = Math.max(0, height - paddingT - paddingB);
let vJustify = this.$verticalAlign == JustifyAlign.JUSTIFY;
let hJustify = this.$horizontalAlign == JustifyAlign.JUSTIFY || this.$horizontalAlign == JustifyAlign.CONTENT_JUSTIFY;
let hAlign = 0;
if (!hJustify) {
if (this.$horizontalAlign == egret.HorizontalAlign.CENTER) {
hAlign = 0.5;
}
else if (this.$horizontalAlign == egret.HorizontalAlign.RIGHT) {
hAlign = 1;
}
}
let count = target.numElements;
let numElements = count;
let x = paddingL;
let y = paddingT;
let i:number;
let layoutElement:UIComponent;
let totalPreferredHeight = 0;
let totalPercentHeight = 0;
let childInfoArray:any[] = [];
let childInfo:sys.ChildInfo;
let heightToDistribute = targetHeight;
let maxElementWidth = this.maxElementSize;
let bounds = egret.$TempRectangle;
for (i = 0; i < count; i++) {
let layoutElement = <UIComponent> (target.getElementAt(i));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
numElements--;
continue;
}
layoutElement.getPreferredBounds(bounds);
maxElementWidth = Math.max(maxElementWidth, bounds.width);
if (vJustify) {
totalPreferredHeight += bounds.height;
}
else {
let values = layoutElement.$UIComponent;
if (!isNaN(values[sys.UIKeys.percentHeight])) {
totalPercentHeight += values[sys.UIKeys.percentHeight];
childInfo = new sys.ChildInfo();
childInfo.layoutElement = layoutElement;
childInfo.percent = values[sys.UIKeys.percentHeight];
childInfo.min = values[sys.UIKeys.minHeight];
childInfo.max = values[sys.UIKeys.maxHeight];
childInfoArray.push(childInfo);
}
else {
heightToDistribute -= bounds.height;
}
}
}
heightToDistribute -= gap * (numElements - 1);
heightToDistribute = heightToDistribute > 0 ? heightToDistribute : 0;
let excessSpace = targetHeight - totalPreferredHeight - gap * (numElements - 1);
let averageHeight:number;
let largeChildrenCount = numElements;
let heightDic:any = {};
if (vJustify) {
if (excessSpace < 0) {
averageHeight = heightToDistribute / numElements;
for (i = 0; i < count; i++) {
layoutElement = <UIComponent> (target.getElementAt(i));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
continue;
}
layoutElement.getPreferredBounds(bounds);
if (bounds.height <= averageHeight) {
heightToDistribute -= bounds.height;
largeChildrenCount--;
continue;
}
}
heightToDistribute = heightToDistribute > 0 ? heightToDistribute : 0;
}
}
else {
if (totalPercentHeight > 0) {
this.flexChildrenProportionally(targetHeight, heightToDistribute,
totalPercentHeight, childInfoArray);
let roundOff = 0;
let length = childInfoArray.length;
for (i = 0; i < length; i++) {
childInfo = childInfoArray[i];
let childSize = Math.round(childInfo.size + roundOff);
roundOff += childInfo.size - childSize;
heightDic[childInfo.layoutElement.$hashCode] = childSize;
heightToDistribute -= childSize;
}
heightToDistribute = heightToDistribute > 0 ? heightToDistribute : 0;
}
}
if (this.$verticalAlign == egret.VerticalAlign.MIDDLE) {
y = paddingT + heightToDistribute * 0.5;
}
else if (this.$verticalAlign == egret.VerticalAlign.BOTTOM) {
y = paddingT + heightToDistribute;
}
let maxX = paddingL;
let maxY = paddingT;
let dx = 0;
let dy = 0;
let justifyWidth:number = Math.ceil(targetWidth);
if (this.$horizontalAlign == JustifyAlign.CONTENT_JUSTIFY)
justifyWidth = Math.ceil(Math.max(targetWidth, maxElementWidth));
let roundOff = 0;
let layoutElementHeight:number;
let childHeight:number;
for (i = 0; i < count; i++) {
let exceesWidth = 0;
layoutElement = <UIComponent> (target.getElementAt(i));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
continue;
}
layoutElement.getPreferredBounds(bounds);
layoutElementHeight = NaN;
if (vJustify) {
childHeight = NaN;
if (excessSpace > 0) {
childHeight = heightToDistribute * bounds.height / totalPreferredHeight;
}
else if (excessSpace < 0 && bounds.height > averageHeight) {
childHeight = heightToDistribute / largeChildrenCount
}
if (!isNaN(childHeight)) {
layoutElementHeight = Math.round(childHeight + roundOff);
roundOff += childHeight - layoutElementHeight;
}
}
else {
layoutElementHeight = heightDic[layoutElement.$hashCode];
}
if (hJustify) {
x = paddingL;
layoutElement.setLayoutBoundsSize(justifyWidth, layoutElementHeight);
layoutElement.getLayoutBounds(bounds);
}
else {
let layoutElementWidth = NaN;
let values = layoutElement.$UIComponent;
if (!isNaN(values[sys.UIKeys.percentWidth])) {
let percent = Math.min(100, values[sys.UIKeys.percentWidth]);
layoutElementWidth = Math.round(targetWidth * percent * 0.01);
}
layoutElement.setLayoutBoundsSize(layoutElementWidth, layoutElementHeight);
layoutElement.getLayoutBounds(bounds);
exceesWidth = (targetWidth - bounds.width) * hAlign;
exceesWidth = exceesWidth > 0 ? exceesWidth : 0;
x = paddingL + exceesWidth;
}
layoutElement.setLayoutBoundsPosition(Math.round(x), Math.round(y));
dx = Math.ceil(bounds.width);
dy = Math.ceil(bounds.height);
maxX = Math.max(maxX, x + dx);
maxY = Math.max(maxY, y + dy);
y += dy + gap;
}
this.maxElementSize = maxElementWidth;
target.setContentSize(maxX + paddingR, maxY + paddingB);
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected updateDisplayListVirtual(width:number, height:number):void {
let target = this.$target;
if (this.indexInViewCalculated)
this.indexInViewCalculated = false;
else
this.getIndexInView();
let paddingB = this.$paddingBottom;
let paddingL = this.$paddingLeft;
let paddingR = this.$paddingRight;
let gap = this.$gap;
let contentHeight:number;
let numElements = target.numElements;
if (this.startIndex == -1 || this.endIndex == -1) {
contentHeight = this.getStartPosition(numElements) - gap + paddingB;
target.setContentSize(target.contentWidth, contentHeight);
return;
}
let endIndex = this.endIndex;
target.setVirtualElementIndicesInView(this.startIndex, endIndex);
//获取垂直布局参数
let justify = this.$horizontalAlign == JustifyAlign.JUSTIFY || this.$horizontalAlign == JustifyAlign.CONTENT_JUSTIFY;
let contentJustify = this.$horizontalAlign == JustifyAlign.CONTENT_JUSTIFY;
let hAlign = 0;
if (!justify) {
if (this.$horizontalAlign == egret.HorizontalAlign.CENTER) {
hAlign = 0.5;
}
else if (this.$horizontalAlign == egret.HorizontalAlign.RIGHT) {
hAlign = 1;
}
}
let bounds = egret.$TempRectangle;
let targetWidth = Math.max(0, width - paddingL - paddingR);
let justifyWidth = Math.ceil(targetWidth);
let layoutElement:UIComponent;
let typicalHeight = this.$typicalHeight;
let typicalWidth = this.$typicalWidth;
let maxElementWidth = this.maxElementSize;
let oldMaxW = Math.max(typicalWidth, this.maxElementSize);
if (contentJustify) {
for (let index = this.startIndex; index <= endIndex; index++) {
layoutElement = <UIComponent> (target.getVirtualElementAt(index));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
continue;
}
layoutElement.getPreferredBounds(bounds);
maxElementWidth = Math.max(maxElementWidth, bounds.width);
}
justifyWidth = Math.ceil(Math.max(targetWidth, maxElementWidth));
}
let x = 0;
let y = 0;
let contentWidth = 0;
let oldElementSize:number;
let needInvalidateSize = false;
let elementSizeTable = this.elementSizeTable;
//对可见区域进行布局
for (let i = this.startIndex; i <= endIndex; i++) {
let exceesWidth = 0;
layoutElement = <UIComponent> (target.getVirtualElementAt(i));
if (!egret.is(layoutElement, UIComponentClass) || !layoutElement.$includeInLayout) {
continue;
}
layoutElement.getPreferredBounds(bounds);
if (!contentJustify) {
maxElementWidth = Math.max(maxElementWidth, bounds.width);
}
if (justify) {
x = paddingL;
layoutElement.setLayoutBoundsSize(justifyWidth, NaN);
layoutElement.getLayoutBounds(bounds);
}
else {
layoutElement.getLayoutBounds(bounds);
exceesWidth = (targetWidth - bounds.width) * hAlign;
exceesWidth = exceesWidth > 0 ? exceesWidth : 0;
x = paddingL + exceesWidth;
}
contentWidth = Math.max(contentWidth, bounds.width);
if (!needInvalidateSize) {
oldElementSize = isNaN(elementSizeTable[i]) ? typicalHeight : elementSizeTable[i];
if (oldElementSize != bounds.height)
needInvalidateSize = true;
}
elementSizeTable[i] = bounds.height;
y = this.getStartPosition(i);
layoutElement.setLayoutBoundsPosition(Math.round(x), Math.round(y));
}
contentWidth += paddingL + paddingR;
contentHeight = this.getStartPosition(numElements) - gap + paddingB;
this.maxElementSize = maxElementWidth;
target.setContentSize(contentWidth, contentHeight);
if (needInvalidateSize || oldMaxW < this.maxElementSize) {
target.invalidateSize();
}
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected getStartPosition(index:number):number {
if (!this.$useVirtualLayout) {
if (this.$target) {
let element = <UIComponent>this.$target.getElementAt(index);
if (element) {
return element.y;
}
}
}
let typicalHeight = this.$typicalHeight;
let startPos = this.$paddingTop;
let gap = this.$gap;
let elementSizeTable = this.elementSizeTable;
for (let i = 0; i < index; i++) {
let h = elementSizeTable[i];
if(isNaN(h)) {
h = typicalHeight;
}
startPos += h + gap;
}
return startPos;
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected getElementSize(index:number):number {
if (this.$useVirtualLayout) {
let size = this.elementSizeTable[index];
if (isNaN(size)) {
size = this.$typicalHeight;
}
return size;
}
if (this.$target) {
return this.$target.getElementAt(index).height;
}
return 0;
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected getElementTotalSize():number {
let typicalHeight = this.$typicalHeight;
let gap = this.$gap;
let totalSize = 0;
let length = this.$target.numElements;
let elementSizeTable = this.elementSizeTable;
for (let i = 0; i < length; i++) {
let h = elementSizeTable[i];
if(isNaN(h)) {
h = typicalHeight;
}
totalSize += h + gap;
}
totalSize -= gap;
return totalSize;
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
public elementAdded(index:number):void {
if (!this.$useVirtualLayout)
return;
super.elementAdded(index);
this.elementSizeTable.splice(index, 0, this.$typicalHeight);
}
/**
* @inheritDoc
*
* @version Egret 2.4
* @version eui 1.0
* @platform Web,Native
*/
protected getIndexInView():boolean {
let target = this.$target;
if (!target || target.numElements == 0) {
this.startIndex = this.endIndex = -1;
return false;
}
let values = target.$UIComponent;
if (values[sys.UIKeys.width] == 0 || values[sys.UIKeys.height] == 0) {
this.startIndex = this.endIndex = -1;
return false;
}
let numElements = target.numElements;
let contentHeight = this.getStartPosition(numElements - 1) +
this.elementSizeTable[numElements - 1] + this.$paddingBottom;
let minVisibleY = target.scrollV;
if (minVisibleY > contentHeight - this.$paddingBottom) {
this.startIndex = -1;
this.endIndex = -1;
return false;
}
let maxVisibleY = target.scrollV + values[sys.UIKeys.height];
if (maxVisibleY < this.$paddingTop) {
this.startIndex = -1;
this.endIndex = -1;
return false;
}
let oldStartIndex = this.startIndex;
let oldEndIndex = this.endIndex;
this.startIndex = this.findIndexAt(minVisibleY, 0, numElements - 1);
if (this.startIndex == -1)
this.startIndex = 0;
this.endIndex = this.findIndexAt(maxVisibleY, 0, numElements - 1);
if (this.endIndex == -1)
this.endIndex = numElements - 1;
return oldStartIndex != this.startIndex || oldEndIndex != this.endIndex;
}
}
}