mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00

* chore: move tns-core-modules to nativescript-core * chore: preparing compat generate script * chore: add missing definitions * chore: no need for http-request to be private * chore: packages chore * test: generate tests for tns-core-modules * chore: add anroid module for consistency * chore: add .npmignore * chore: added privateModulesWhitelist * chore(webpack): added bundle-entry-points * chore: scripts * chore: tests changed to use @ns/core * test: add scoped-packages test project * test: fix types * test: update test project * chore: build scripts * chore: update build script * chore: npm scripts cleanup * chore: make the compat pgk work with old wp config * test: generate diff friendly tests * chore: create barrel exports * chore: move files after rebase * chore: typedoc config * chore: compat mode * chore: review of barrels * chore: remove tns-core-modules import after rebase * chore: dev workflow setup * chore: update developer-workflow * docs: experiment with API extractor * chore: api-extractor and barrel exports * chore: api-extractor configs * chore: generate d.ts rollup with api-extractor * refactor: move methods inside Frame * chore: fic tests to use Frame static methods * refactor: create Builder class * refactor: use Builder class in tests * refactor: include Style in ui barrel * chore: separate compat build script * chore: fix tslint errors * chore: update NATIVESCRIPT_CORE_ARGS * chore: fix compat pack * chore: fix ui-test-app build with linked modules * chore: Application, ApplicationSettings, Connectivity and Http * chore: export Trace, Profiling and Utils * refactor: Static create methods for ImageSource * chore: fix deprecated usages of ImageSource * chore: move Span and FormattedString to ui * chore: add events-args and ImageSource to index files * chore: check for CLI >= 6.2 when building for IOS * chore: update travis build * chore: copy Pod file to compat package * chore: update error msg ui-tests-app * refactor: Apply suggestions from code review Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com> * chore: typings and refs * chore: add missing d.ts files for public API * chore: adress code review FB * chore: update api-report * chore: dev-workflow for other apps * chore: api update * chore: update api-report
980 lines
36 KiB
TypeScript
980 lines
36 KiB
TypeScript
import {
|
|
GridLayoutBase, ItemSpec, View, layout
|
|
} from "./grid-layout-common";
|
|
|
|
export * from "./grid-layout-common";
|
|
|
|
export class GridLayout extends GridLayoutBase {
|
|
private helper: MeasureHelper;
|
|
private columnOffsets = new Array<number>();
|
|
private rowOffsets = new Array<number>();
|
|
private map = new Map<View, MeasureSpecs>();
|
|
|
|
constructor() {
|
|
super();
|
|
this.helper = new MeasureHelper(this);
|
|
}
|
|
|
|
public _onRowAdded(itemSpec: ItemSpec) {
|
|
this.helper.rows.push(new ItemGroup(itemSpec));
|
|
}
|
|
|
|
public _onColumnAdded(itemSpec: ItemSpec) {
|
|
this.helper.columns.push(new ItemGroup(itemSpec));
|
|
}
|
|
|
|
public _onRowRemoved(itemSpec: ItemSpec, index: number) {
|
|
this.helper.rows[index].children.length = 0;
|
|
this.helper.rows.splice(index, 1);
|
|
}
|
|
|
|
public _onColumnRemoved(itemSpec: ItemSpec, index: number) {
|
|
this.helper.columns[index].children.length = 0;
|
|
this.helper.columns.splice(index, 1);
|
|
}
|
|
|
|
public _registerLayoutChild(child: View) {
|
|
this.addToMap(child);
|
|
}
|
|
|
|
public _unregisterLayoutChild(child: View) {
|
|
this.removeFromMap(child);
|
|
}
|
|
|
|
protected invalidate(): void {
|
|
super.invalidate();
|
|
this.requestLayout();
|
|
}
|
|
|
|
private getColumnIndex(view: View): number {
|
|
return Math.max(0, Math.min(GridLayout.getColumn(view), this.columnsInternal.length - 1));
|
|
}
|
|
|
|
private getRowIndex(view: View): number {
|
|
return Math.max(0, Math.min(GridLayout.getRow(view), this.rowsInternal.length - 1));
|
|
}
|
|
|
|
private getColumnSpan(view: View, columnIndex: number): number {
|
|
return Math.max(1, Math.min(GridLayout.getColumnSpan(view), this.columnsInternal.length - columnIndex));
|
|
}
|
|
|
|
private getRowSpan(view: View, rowIndex: number): number {
|
|
return Math.max(1, Math.min(GridLayout.getRowSpan(view), this.rowsInternal.length - rowIndex));
|
|
}
|
|
|
|
private getColumnSpec(view: View): ItemSpec {
|
|
return this.columnsInternal[this.getColumnIndex(view)] || this.helper.singleColumn;
|
|
}
|
|
|
|
private getRowSpec(view: View): ItemSpec {
|
|
return this.rowsInternal[this.getRowIndex(view)] || this.helper.singleRow;
|
|
}
|
|
|
|
private updateMeasureSpecs(child: View, measureSpec: MeasureSpecs): void {
|
|
let column = this.getColumnSpec(child);
|
|
let columnIndex = this.getColumnIndex(child);
|
|
let columnSpan = this.getColumnSpan(child, columnIndex);
|
|
|
|
let row = this.getRowSpec(child);
|
|
let rowIndex = this.getRowIndex(child);
|
|
let rowSpan = this.getRowSpan(child, rowIndex);
|
|
|
|
measureSpec.setColumn(column);
|
|
measureSpec.setColumnIndex(columnIndex);
|
|
measureSpec.setColumnSpan(columnSpan);
|
|
|
|
measureSpec.setRow(row);
|
|
measureSpec.setRowIndex(rowIndex);
|
|
measureSpec.setRowSpan(rowSpan);
|
|
|
|
measureSpec.autoColumnsCount = 0;
|
|
measureSpec.autoRowsCount = 0;
|
|
measureSpec.measured = false;
|
|
measureSpec.pixelHeight = 0;
|
|
measureSpec.pixelWidth = 0;
|
|
measureSpec.starColumnsCount = 0;
|
|
measureSpec.starRowsCount = 0;
|
|
}
|
|
|
|
private addToMap(child: View): void {
|
|
this.map.set(child, new MeasureSpecs(child));
|
|
}
|
|
|
|
private removeFromMap(child: View): void {
|
|
this.map.delete(child);
|
|
}
|
|
|
|
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
let measureWidth = 0;
|
|
let measureHeight = 0;
|
|
|
|
const width = layout.getMeasureSpecSize(widthMeasureSpec);
|
|
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
|
|
|
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
|
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
|
|
|
const horizontalPaddingsAndMargins = this.effectivePaddingLeft + this.effectivePaddingRight + this.effectiveBorderLeftWidth + this.effectiveBorderRightWidth;
|
|
const verticalPaddingsAndMargins = this.effectivePaddingTop + this.effectivePaddingBottom + this.effectiveBorderTopWidth + this.effectiveBorderBottomWidth;
|
|
|
|
let infinityWidth = widthMode === layout.UNSPECIFIED;
|
|
let infinityHeight = heightMode === layout.UNSPECIFIED;
|
|
|
|
this.helper.width = Math.max(0, width - horizontalPaddingsAndMargins);
|
|
this.helper.height = Math.max(0, height - verticalPaddingsAndMargins);
|
|
|
|
this.helper.stretchedHorizontally = widthMode === layout.EXACTLY || (this.horizontalAlignment === "stretch" && !infinityWidth);
|
|
this.helper.stretchedVertically = heightMode === layout.EXACTLY || (this.verticalAlignment === "stretch" && !infinityHeight);
|
|
|
|
this.helper.setInfinityWidth(infinityWidth);
|
|
this.helper.setInfinityHeight(infinityHeight);
|
|
|
|
this.helper.clearMeasureSpecs();
|
|
this.helper.init();
|
|
|
|
this.eachLayoutChild((child, last) => {
|
|
let measureSpecs = this.map.get(child);
|
|
if (!measureSpecs) {
|
|
return;
|
|
}
|
|
|
|
this.updateMeasureSpecs(child, measureSpecs);
|
|
this.helper.addMeasureSpec(measureSpecs);
|
|
});
|
|
|
|
this.helper.measure();
|
|
|
|
// Add in our padding
|
|
measureWidth = this.helper.measuredWidth + horizontalPaddingsAndMargins;
|
|
measureHeight = this.helper.measuredHeight + verticalPaddingsAndMargins;
|
|
|
|
// Check against our minimum sizes
|
|
measureWidth = Math.max(measureWidth, this.effectiveMinWidth);
|
|
measureHeight = Math.max(measureHeight, this.effectiveMinHeight);
|
|
|
|
const widthSizeAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
|
|
const heightSizeAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
|
|
|
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
|
}
|
|
|
|
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
|
super.onLayout(left, top, right, bottom);
|
|
|
|
const insets = this.getSafeAreaInsets();
|
|
|
|
let paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left;
|
|
let paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top;
|
|
|
|
this.columnOffsets.length = 0;
|
|
this.rowOffsets.length = 0;
|
|
|
|
this.columnOffsets.push(paddingLeft);
|
|
this.rowOffsets.push(paddingTop);
|
|
|
|
let offset = this.columnOffsets[0];
|
|
let roundedOffset = paddingLeft;
|
|
let roundedLength = 0;
|
|
let actualLength = 0;
|
|
|
|
for (let i = 0, size = this.helper.columns.length; i < size; i++) {
|
|
let columnGroup = this.helper.columns[i];
|
|
offset += columnGroup.length;
|
|
|
|
actualLength = offset - roundedOffset;
|
|
roundedLength = Math.round(actualLength);
|
|
columnGroup.rowOrColumn._actualLength = layout.round(layout.toDeviceIndependentPixels(roundedLength));
|
|
roundedOffset += roundedLength;
|
|
|
|
this.columnOffsets.push(roundedOffset);
|
|
}
|
|
|
|
offset = this.rowOffsets[0];
|
|
roundedOffset = paddingTop;
|
|
roundedLength = 0;
|
|
actualLength = 0;
|
|
|
|
for (let i = 0, size = this.helper.rows.length; i < size; i++) {
|
|
let rowGroup = this.helper.rows[i];
|
|
offset += rowGroup.length;
|
|
|
|
actualLength = offset - roundedOffset;
|
|
roundedLength = Math.round(actualLength);
|
|
rowGroup.rowOrColumn._actualLength = layout.round(layout.toDeviceIndependentPixels(roundedLength));
|
|
roundedOffset += roundedLength;
|
|
|
|
this.rowOffsets.push(roundedOffset);
|
|
}
|
|
|
|
for (let i = 0, columns = this.helper.columns.length; i < columns; i++) {
|
|
let columnGroup = this.helper.columns[i];
|
|
for (let j = 0, children = columnGroup.children.length; j < children; j++) {
|
|
|
|
let measureSpec = columnGroup.children[j];
|
|
let childLeft = this.columnOffsets[measureSpec.getColumnIndex()];
|
|
let childRight = this.columnOffsets[measureSpec.getColumnIndex() + measureSpec.getColumnSpan()];
|
|
let childTop = this.rowOffsets[measureSpec.getRowIndex()];
|
|
let childBottom = this.rowOffsets[measureSpec.getRowIndex() + measureSpec.getRowSpan()];
|
|
|
|
// No need to include margins in the width, height
|
|
View.layoutChild(this, measureSpec.child, childLeft, childTop, childRight, childBottom);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class MeasureSpecs {
|
|
private _columnSpan = 1;
|
|
private _rowSpan = 1;
|
|
|
|
public pixelWidth = 0;
|
|
public pixelHeight = 0;
|
|
|
|
public starColumnsCount = 0;
|
|
public starRowsCount = 0;
|
|
|
|
public autoColumnsCount = 0;
|
|
public autoRowsCount = 0;
|
|
|
|
public measured = false;
|
|
|
|
public child: View;
|
|
private column: ItemSpec;
|
|
private row: ItemSpec;
|
|
private columnIndex: number = 0;
|
|
private rowIndex: number = 0;
|
|
|
|
constructor(child: View) {
|
|
this.child = child;
|
|
}
|
|
|
|
public getSpanned(): boolean {
|
|
return this._columnSpan > 1 || this._rowSpan > 1;
|
|
}
|
|
|
|
public getIsStar(): boolean {
|
|
return this.starRowsCount > 0 || this.starColumnsCount > 0;
|
|
}
|
|
|
|
public getColumnSpan(): number {
|
|
return this._columnSpan;
|
|
}
|
|
|
|
public getRowSpan(): number {
|
|
return this._rowSpan;
|
|
}
|
|
|
|
public setRowSpan(value: number): void {
|
|
// cannot have zero rowSpan.
|
|
this._rowSpan = Math.max(1, value);
|
|
}
|
|
|
|
public setColumnSpan(value: number): void {
|
|
// cannot have zero colSpan.
|
|
this._columnSpan = Math.max(1, value);
|
|
}
|
|
|
|
public getRowIndex(): number {
|
|
return this.rowIndex;
|
|
}
|
|
|
|
public getColumnIndex(): number {
|
|
return this.columnIndex;
|
|
}
|
|
|
|
public setRowIndex(value: number): void {
|
|
this.rowIndex = value;
|
|
}
|
|
|
|
public setColumnIndex(value: number): void {
|
|
this.columnIndex = value;
|
|
}
|
|
|
|
public getRow(): ItemSpec {
|
|
return this.row;
|
|
}
|
|
|
|
public getColumn(): ItemSpec {
|
|
return this.column;
|
|
}
|
|
|
|
public setRow(value: ItemSpec): void {
|
|
this.row = value;
|
|
}
|
|
|
|
public setColumn(value: ItemSpec): void {
|
|
this.column = value;
|
|
}
|
|
}
|
|
|
|
class ItemGroup {
|
|
public length = 0;
|
|
public measuredCount = 0;
|
|
public rowOrColumn: ItemSpec;
|
|
public children: Array<MeasureSpecs> = new Array<MeasureSpecs>();
|
|
|
|
public measureToFix = 0;
|
|
public currentMeasureToFixCount = 0;
|
|
private infinityLength = false;
|
|
|
|
constructor(spec: ItemSpec) {
|
|
this.rowOrColumn = spec;
|
|
}
|
|
|
|
public setIsLengthInfinity(infinityLength: boolean): void {
|
|
this.infinityLength = infinityLength;
|
|
}
|
|
|
|
public init(density): void {
|
|
this.measuredCount = 0;
|
|
this.currentMeasureToFixCount = 0;
|
|
this.length = this.rowOrColumn.isAbsolute ? this.rowOrColumn.value * density : 0;
|
|
}
|
|
|
|
public getAllMeasured(): boolean {
|
|
return this.measuredCount === this.children.length;
|
|
}
|
|
|
|
public getCanBeFixed(): boolean {
|
|
return this.currentMeasureToFixCount === this.measureToFix;
|
|
}
|
|
|
|
public getIsAuto(): boolean {
|
|
return this.rowOrColumn.isAuto || (this.rowOrColumn.isStar && this.infinityLength);
|
|
}
|
|
|
|
public getIsStar(): boolean {
|
|
return this.rowOrColumn.isStar && !this.infinityLength;
|
|
}
|
|
|
|
public getIsAbsolute(): boolean {
|
|
return this.rowOrColumn.isAbsolute;
|
|
}
|
|
}
|
|
|
|
class MeasureHelper {
|
|
singleRow: ItemSpec;
|
|
singleColumn: ItemSpec;
|
|
grid: GridLayout;
|
|
|
|
infinity: number = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
|
|
rows: Array<ItemGroup> = new Array<ItemGroup>();
|
|
columns: Array<ItemGroup> = new Array<ItemGroup>();
|
|
|
|
width: number = 0;
|
|
height: number = 0;
|
|
stretchedHorizontally: boolean = false;
|
|
stretchedVertically: boolean = false;
|
|
|
|
infinityWidth: boolean = false;
|
|
infinityHeight: boolean = false;
|
|
|
|
private minColumnStarValue: number = 0;
|
|
private maxColumnStarValue: number = 0;
|
|
|
|
private minRowStarValue: number = 0;
|
|
private maxRowStarValue: number = 0;
|
|
|
|
measuredWidth: number = 0;
|
|
measuredHeight: number = 0;
|
|
|
|
private fakeRowAdded: boolean = false;
|
|
private fakeColumnAdded: boolean = false;
|
|
|
|
private singleRowGroup: ItemGroup;
|
|
private singleColumnGroup: ItemGroup;
|
|
|
|
constructor(grid: GridLayout) {
|
|
this.grid = grid;
|
|
this.singleRow = new ItemSpec();
|
|
this.singleColumn = new ItemSpec();
|
|
this.singleRowGroup = new ItemGroup(this.singleRow);
|
|
this.singleColumnGroup = new ItemGroup(this.singleColumn);
|
|
}
|
|
|
|
public setInfinityWidth(value: boolean): void {
|
|
this.infinityWidth = value;
|
|
for (let i = 0, size = this.columns.length; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
columnGroup.setIsLengthInfinity(value);
|
|
}
|
|
}
|
|
|
|
public setInfinityHeight(value: boolean): void {
|
|
this.infinityHeight = value;
|
|
for (let i = 0, size = this.rows.length; i < size; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
rowGroup.setIsLengthInfinity(value);
|
|
}
|
|
}
|
|
|
|
public addMeasureSpec(measureSpec: MeasureSpecs): void {
|
|
// Get column stats
|
|
let size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan();
|
|
for (let i = measureSpec.getColumnIndex(); i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
if (columnGroup.getIsAuto()) {
|
|
measureSpec.autoColumnsCount++;
|
|
}
|
|
else if (columnGroup.getIsStar()) {
|
|
measureSpec.starColumnsCount += columnGroup.rowOrColumn.value;
|
|
}
|
|
else if (columnGroup.getIsAbsolute()) {
|
|
measureSpec.pixelWidth += layout.toDevicePixels(columnGroup.rowOrColumn.value);
|
|
}
|
|
}
|
|
|
|
if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount === 0) {
|
|
// Determine which auto columns are affected by this element
|
|
for (let i = measureSpec.getColumnIndex(); i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
if (columnGroup.getIsAuto()) {
|
|
columnGroup.measureToFix++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get row stats
|
|
size = measureSpec.getRowIndex() + measureSpec.getRowSpan();
|
|
for (let i = measureSpec.getRowIndex(); i < size; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
if (rowGroup.getIsAuto()) {
|
|
measureSpec.autoRowsCount++;
|
|
}
|
|
else if (rowGroup.getIsStar()) {
|
|
measureSpec.starRowsCount += rowGroup.rowOrColumn.value;
|
|
}
|
|
else if (rowGroup.getIsAbsolute()) {
|
|
measureSpec.pixelHeight += layout.toDevicePixels(rowGroup.rowOrColumn.value);
|
|
}
|
|
}
|
|
|
|
if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount === 0) {
|
|
// Determine which auto rows are affected by this element
|
|
for (let i = measureSpec.getRowIndex(); i < size; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
if (rowGroup.getIsAuto()) {
|
|
rowGroup.measureToFix++;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.columns[measureSpec.getColumnIndex()].children.push(measureSpec);
|
|
this.rows[measureSpec.getRowIndex()].children.push(measureSpec);
|
|
}
|
|
|
|
public clearMeasureSpecs(): void {
|
|
for (let i = 0, size = this.columns.length; i < size; i++) {
|
|
this.columns[i].children.length = 0;
|
|
}
|
|
|
|
for (let i = 0, size = this.rows.length; i < size; i++) {
|
|
this.rows[i].children.length = 0;
|
|
}
|
|
}
|
|
|
|
private static initList(list: Array<ItemGroup>): void {
|
|
let density = layout.getDisplayDensity();
|
|
for (let i = 0, size = list.length; i < size; i++) {
|
|
let item: ItemGroup = list[i];
|
|
item.init(density);
|
|
}
|
|
}
|
|
|
|
init(): void {
|
|
|
|
let rows = this.rows.length;
|
|
if (rows === 0) {
|
|
this.singleRowGroup.setIsLengthInfinity(this.infinityHeight);
|
|
this.rows.push(this.singleRowGroup);
|
|
this.fakeRowAdded = true;
|
|
} else if (rows > 1 && this.fakeRowAdded) {
|
|
this.rows.splice(0, 1);
|
|
this.fakeRowAdded = false;
|
|
}
|
|
|
|
let cols = this.columns.length;
|
|
if (cols === 0) {
|
|
this.fakeColumnAdded = true;
|
|
this.singleColumnGroup.setIsLengthInfinity(this.infinityWidth);
|
|
this.columns.push(this.singleColumnGroup);
|
|
}
|
|
else if (cols > 1 && this.fakeColumnAdded) {
|
|
this.columns.splice(0, 1);
|
|
this.fakeColumnAdded = false;
|
|
}
|
|
|
|
MeasureHelper.initList(this.rows);
|
|
MeasureHelper.initList(this.columns);
|
|
|
|
this.minColumnStarValue = -1;
|
|
this.minRowStarValue = -1;
|
|
this.maxColumnStarValue = -1;
|
|
this.maxRowStarValue = -1;
|
|
}
|
|
|
|
private itemMeasured(measureSpec: MeasureSpecs, isFakeMeasure: boolean): void {
|
|
if (!isFakeMeasure) {
|
|
this.columns[measureSpec.getColumnIndex()].measuredCount++;
|
|
this.rows[measureSpec.getRowIndex()].measuredCount++;
|
|
measureSpec.measured = true;
|
|
}
|
|
|
|
if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount === 0) {
|
|
let size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan();
|
|
for (let i = measureSpec.getColumnIndex(); i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
if (columnGroup.getIsAuto()) {
|
|
columnGroup.currentMeasureToFixCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount === 0) {
|
|
let size = measureSpec.getRowIndex() + measureSpec.getRowSpan();
|
|
for (let i = measureSpec.getRowIndex(); i < size; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
if (rowGroup.getIsAuto()) {
|
|
rowGroup.currentMeasureToFixCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fixColumns(): void {
|
|
let currentColumnWidth = 0;
|
|
let columnStarCount = 0;
|
|
|
|
let columnCount = this.columns.length;
|
|
for (let i = 0; i < columnCount; i++) {
|
|
let item: ItemGroup = this.columns[i];
|
|
if (item.rowOrColumn.isStar) {
|
|
columnStarCount += item.rowOrColumn.value;
|
|
}
|
|
else {
|
|
// Star columns are still zeros (not calculated).
|
|
currentColumnWidth += item.length;
|
|
}
|
|
}
|
|
|
|
let widthForStarColumns = Math.max(0, this.width - currentColumnWidth);
|
|
this.maxColumnStarValue = columnStarCount > 0 ? widthForStarColumns / columnStarCount : 0;
|
|
|
|
MeasureHelper.updateStarLength(this.columns, this.maxColumnStarValue);
|
|
}
|
|
|
|
private fixRows(): void {
|
|
let currentRowHeight = 0;
|
|
let rowStarCount = 0;
|
|
|
|
let rowCount = this.rows.length;
|
|
for (let i = 0; i < rowCount; i++) {
|
|
let item: ItemGroup = this.rows[i];
|
|
if (item.rowOrColumn.isStar) {
|
|
rowStarCount += item.rowOrColumn.value;
|
|
}
|
|
else {
|
|
// Star rows are still zeros (not calculated).
|
|
currentRowHeight += item.length;
|
|
}
|
|
}
|
|
|
|
let heightForStarRows = Math.max(0, this.height - currentRowHeight);
|
|
this.maxRowStarValue = rowStarCount > 0 ? heightForStarRows / rowStarCount : 0;
|
|
|
|
MeasureHelper.updateStarLength(this.rows, this.maxRowStarValue);
|
|
}
|
|
|
|
private static updateStarLength(list: Array<ItemGroup>, starValue: number): void {
|
|
let offset = 0;
|
|
let roundedOffset = 0;
|
|
for (let i = 0, rowCount = list.length; i < rowCount; i++) {
|
|
let item = list[i];
|
|
if (item.getIsStar()) {
|
|
offset += item.rowOrColumn.value * starValue;
|
|
|
|
let actualLength = offset - roundedOffset;
|
|
let roundedLength = Math.round(actualLength);
|
|
item.length = roundedLength;
|
|
roundedOffset += roundedLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
private fakeMeasure(): void {
|
|
// Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height
|
|
for (let i = 0, size = this.columns.length; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
if (columnGroup.getAllMeasured()) {
|
|
continue;
|
|
}
|
|
|
|
for (let j = 0, childrenCount = columnGroup.children.length; j < childrenCount; j++) {
|
|
let measureSpec: MeasureSpecs = columnGroup.children[j];
|
|
if (measureSpec.starRowsCount > 0 && measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount === 0) {
|
|
this.measureChild(measureSpec, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private measureFixedColumnsNoStarRows(): void {
|
|
for (let i = 0, size = this.columns.length; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
for (let j = 0, childrenCount = columnGroup.children.length; j < childrenCount; j++) {
|
|
let measureSpec: MeasureSpecs = columnGroup.children[j];
|
|
if (measureSpec.starColumnsCount > 0 && measureSpec.starRowsCount === 0) {
|
|
this.measureChildFixedColumns(measureSpec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private measureNoStarColumnsFixedRows(): void {
|
|
for (let i = 0, size = this.columns.length; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
for (let j = 0, childrenCount = columnGroup.children.length; j < childrenCount; j++) {
|
|
let measureSpec: MeasureSpecs = columnGroup.children[j];
|
|
if (measureSpec.starRowsCount > 0 && measureSpec.starColumnsCount === 0) {
|
|
this.measureChildFixedRows(measureSpec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static canFix(list: Array<ItemGroup>): boolean {
|
|
for (let i = 0, size = list.length; i < size; i++) {
|
|
let item: ItemGroup = list[i];
|
|
if (!item.getCanBeFixed()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static getMeasureLength(list: Array<ItemGroup>): number {
|
|
let result = 0;
|
|
for (let i = 0, size = list.length; i < size; i++) {
|
|
let item: ItemGroup = list[i];
|
|
result += item.length;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public measure(): void {
|
|
// Measure auto & pixel columns and rows (no spans).
|
|
let size = this.columns.length;
|
|
for (let i = 0; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
for (let j = 0, childrenCount = columnGroup.children.length; j < childrenCount; j++) {
|
|
let measureSpec: MeasureSpecs = columnGroup.children[j];
|
|
if (measureSpec.getIsStar() || measureSpec.getSpanned()) {
|
|
continue;
|
|
}
|
|
|
|
this.measureChild(measureSpec, false);
|
|
}
|
|
}
|
|
|
|
// Measure auto & pixel columns and rows (with spans).
|
|
for (let i = 0; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
for (let j = 0, childrenCount = columnGroup.children.length; j < childrenCount; j++) {
|
|
let measureSpec = columnGroup.children[j];
|
|
if (measureSpec.getIsStar() || !measureSpec.getSpanned()) {
|
|
continue;
|
|
}
|
|
|
|
this.measureChild(measureSpec, false);
|
|
}
|
|
}
|
|
|
|
// try fix stars!
|
|
let fixColumns: boolean = MeasureHelper.canFix(this.columns);
|
|
let fixRows: boolean = MeasureHelper.canFix(this.rows);
|
|
|
|
if (fixColumns) {
|
|
this.fixColumns();
|
|
}
|
|
|
|
if (fixRows) {
|
|
this.fixRows();
|
|
}
|
|
|
|
if (!fixColumns && !fixRows) {
|
|
// Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height
|
|
// should be able to fix rows after that
|
|
this.fakeMeasure();
|
|
|
|
this.fixColumns();
|
|
|
|
// Measure all elements that have star columns(already fixed), but no stars at the rows
|
|
this.measureFixedColumnsNoStarRows();
|
|
|
|
this.fixRows();
|
|
}
|
|
else if (fixColumns && !fixRows) {
|
|
// Measure all elements that have star columns(already fixed) but no stars at the rows
|
|
this.measureFixedColumnsNoStarRows();
|
|
|
|
// Then
|
|
this.fixRows();
|
|
}
|
|
else if (!fixColumns && fixRows) {
|
|
// Measure all elements that have star rows(already fixed) but no star at the columns
|
|
this.measureNoStarColumnsFixedRows();
|
|
|
|
// Then
|
|
this.fixColumns();
|
|
}
|
|
|
|
// Rows and columns are fixed here - measure remaining elements
|
|
size = this.columns.length;
|
|
for (let i = 0; i < size; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
for (let j = 0, childCount = columnGroup.children.length; j < childCount; j++) {
|
|
let measureSpec: MeasureSpecs = columnGroup.children[j];
|
|
if (!measureSpec.measured) {
|
|
this.measureChildFixedColumnsAndRows(measureSpec);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we are not stretched and minColumnStarValue is less than maxColumnStarValue
|
|
// we need to reduce the width of star columns.
|
|
if (!this.stretchedHorizontally && this.minColumnStarValue !== -1 && this.minColumnStarValue < this.maxColumnStarValue) {
|
|
MeasureHelper.updateStarLength(this.columns, this.minColumnStarValue);
|
|
}
|
|
|
|
// If we are not stretched and minRowStarValue is less than maxRowStarValue
|
|
// we need to reduce the height of star maxRowStarValue.
|
|
if (!this.stretchedVertically && this.minRowStarValue !== -1 && this.minRowStarValue < this.maxRowStarValue) {
|
|
MeasureHelper.updateStarLength(this.rows, this.minRowStarValue);
|
|
}
|
|
|
|
this.measuredWidth = MeasureHelper.getMeasureLength(this.columns);
|
|
this.measuredHeight = MeasureHelper.getMeasureLength(this.rows);
|
|
}
|
|
|
|
private measureChild(measureSpec: MeasureSpecs, isFakeMeasure: boolean): void {
|
|
let widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? this.infinity : layout.makeMeasureSpec(measureSpec.pixelWidth, layout.EXACTLY);
|
|
let heightMeasureSpec = (isFakeMeasure || measureSpec.autoRowsCount > 0) ? this.infinity : layout.makeMeasureSpec(measureSpec.pixelHeight, layout.EXACTLY);
|
|
|
|
let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
|
|
let childMeasuredWidth: number = childSize.measuredWidth;
|
|
let childMeasuredHeight: number = childSize.measuredHeight;
|
|
|
|
let columnSpanEnd: number = measureSpec.getColumnIndex() + measureSpec.getColumnSpan();
|
|
let rowSpanEnd: number = measureSpec.getRowIndex() + measureSpec.getRowSpan();
|
|
|
|
if (measureSpec.autoColumnsCount > 0) {
|
|
let remainingSpace = childMeasuredWidth;
|
|
|
|
for (let i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
remainingSpace -= columnGroup.length;
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
let growSize = remainingSpace / measureSpec.autoColumnsCount;
|
|
for (let i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
if (columnGroup.getIsAuto()) {
|
|
columnGroup.length += growSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isFakeMeasure && measureSpec.autoRowsCount > 0) {
|
|
let remainingSpace: number = childMeasuredHeight;
|
|
|
|
for (let i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
remainingSpace -= rowGroup.length;
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
let growSize = remainingSpace / measureSpec.autoRowsCount;
|
|
for (let i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
if (rowGroup.getIsAuto()) {
|
|
rowGroup.length += growSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.itemMeasured(measureSpec, isFakeMeasure);
|
|
}
|
|
|
|
private measureChildFixedColumns(measureSpec: MeasureSpecs): void {
|
|
let columnIndex = measureSpec.getColumnIndex();
|
|
let columnSpanEnd = columnIndex + measureSpec.getColumnSpan();
|
|
let rowIndex = measureSpec.getRowIndex();
|
|
let rowSpanEnd = rowIndex + measureSpec.getRowSpan();
|
|
|
|
let measureWidth = 0;
|
|
let growSize = 0;
|
|
|
|
for (let i = columnIndex; i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
measureWidth += columnGroup.length;
|
|
}
|
|
|
|
let widthMeasureSpec = layout.makeMeasureSpec(measureWidth, this.stretchedHorizontally ? layout.EXACTLY : layout.AT_MOST);
|
|
let heightMeasureSpec = (measureSpec.autoRowsCount > 0) ? this.infinity : layout.makeMeasureSpec(measureSpec.pixelHeight, layout.EXACTLY);
|
|
|
|
let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
|
|
let childMeasuredWidth = childSize.measuredWidth;
|
|
let childMeasuredHeight = childSize.measuredHeight;
|
|
|
|
this.updateMinColumnStarValueIfNeeded(measureSpec, childMeasuredWidth);
|
|
|
|
// Distribute height over auto rows
|
|
if (measureSpec.autoRowsCount > 0) {
|
|
let remainingSpace = childMeasuredHeight;
|
|
|
|
for (let i = rowIndex; i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
remainingSpace -= rowGroup.length;
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
growSize = remainingSpace / measureSpec.autoRowsCount;
|
|
for (let i = rowIndex; i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
if (rowGroup.getIsAuto()) {
|
|
rowGroup.length += growSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.itemMeasured(measureSpec, false);
|
|
}
|
|
|
|
private measureChildFixedRows(measureSpec: MeasureSpecs): void {
|
|
let columnIndex = measureSpec.getColumnIndex();
|
|
let columnSpanEnd = columnIndex + measureSpec.getColumnSpan();
|
|
let rowIndex = measureSpec.getRowIndex();
|
|
let rowSpanEnd = rowIndex + measureSpec.getRowSpan();
|
|
let measureHeight = 0;
|
|
|
|
for (let i = rowIndex; i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
measureHeight += rowGroup.length;
|
|
}
|
|
|
|
let widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? this.infinity : layout.makeMeasureSpec(measureSpec.pixelWidth, layout.EXACTLY);
|
|
let heightMeasureSpec = layout.makeMeasureSpec(measureHeight, this.stretchedVertically ? layout.EXACTLY : layout.AT_MOST);
|
|
|
|
let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
|
|
let childMeasuredWidth = childSize.measuredWidth;
|
|
let childMeasuredHeight = childSize.measuredHeight;
|
|
|
|
let remainingSpace = 0;
|
|
let growSize = 0;
|
|
|
|
// Distribute width over auto columns
|
|
if (measureSpec.autoColumnsCount > 0) {
|
|
remainingSpace = childMeasuredWidth;
|
|
|
|
for (let i = columnIndex; i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
remainingSpace -= columnGroup.length;
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
growSize = remainingSpace / measureSpec.autoColumnsCount;
|
|
for (let i = columnIndex; i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
|
|
if (columnGroup.getIsAuto()) {
|
|
columnGroup.length += growSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.updateMinRowStarValueIfNeeded(measureSpec, childMeasuredHeight);
|
|
this.itemMeasured(measureSpec, false);
|
|
}
|
|
|
|
private measureChildFixedColumnsAndRows(measureSpec: MeasureSpecs): void {
|
|
let columnIndex = measureSpec.getColumnIndex();
|
|
let columnSpanEnd = columnIndex + measureSpec.getColumnSpan();
|
|
let rowIndex = measureSpec.getRowIndex();
|
|
let rowSpanEnd = rowIndex + measureSpec.getRowSpan();
|
|
|
|
let measureWidth = 0;
|
|
for (let i = columnIndex; i < columnSpanEnd; i++) {
|
|
let columnGroup: ItemGroup = this.columns[i];
|
|
measureWidth += columnGroup.length;
|
|
}
|
|
|
|
let measureHeight = 0;
|
|
for (let i = rowIndex; i < rowSpanEnd; i++) {
|
|
let rowGroup: ItemGroup = this.rows[i];
|
|
measureHeight += rowGroup.length;
|
|
}
|
|
|
|
// if (have stars) & (not stretch) - at most
|
|
let widthMeasureSpec = layout.makeMeasureSpec(measureWidth,
|
|
(measureSpec.starColumnsCount > 0 && !this.stretchedHorizontally) ? layout.AT_MOST : layout.EXACTLY);
|
|
|
|
let heightMeasureSpec = layout.makeMeasureSpec(measureHeight,
|
|
(measureSpec.starRowsCount > 0 && !this.stretchedVertically) ? layout.AT_MOST : layout.EXACTLY);
|
|
|
|
let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
|
|
let childMeasuredWidth = childSize.measuredWidth;
|
|
let childMeasuredHeight = childSize.measuredHeight;
|
|
|
|
this.updateMinColumnStarValueIfNeeded(measureSpec, childMeasuredWidth);
|
|
this.updateMinRowStarValueIfNeeded(measureSpec, childMeasuredHeight);
|
|
this.itemMeasured(measureSpec, false);
|
|
}
|
|
|
|
private updateMinRowStarValueIfNeeded(measureSpec: MeasureSpecs, childMeasuredHeight: number): void {
|
|
if (!this.stretchedVertically && measureSpec.starRowsCount > 0) {
|
|
let remainingSpace = childMeasuredHeight;
|
|
let rowIndex = measureSpec.getRowIndex();
|
|
let rowSpanEnd = rowIndex + measureSpec.getRowSpan();
|
|
for (let i = rowIndex; i < rowSpanEnd; i++) {
|
|
let rowGroup = this.rows[i];
|
|
if (!rowGroup.getIsStar()) {
|
|
remainingSpace -= rowGroup.length;
|
|
}
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
this.minRowStarValue = Math.max(remainingSpace / measureSpec.starRowsCount, this.minRowStarValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
private updateMinColumnStarValueIfNeeded(measureSpec: MeasureSpecs, childMeasuredWidth: number): void {
|
|
// When not stretched star columns are not fixed so we may grow them here
|
|
// if there is an element that spans on multiple columns
|
|
if (!this.stretchedHorizontally && measureSpec.starColumnsCount > 0) {
|
|
let remainingSpace = childMeasuredWidth;
|
|
let columnIndex = measureSpec.getColumnIndex();
|
|
let columnSpanEnd = columnIndex + measureSpec.getColumnSpan();
|
|
for (let i = columnIndex; i < columnSpanEnd; i++) {
|
|
let columnGroup = this.columns[i];
|
|
if (!columnGroup.getIsStar()) {
|
|
remainingSpace -= columnGroup.length;
|
|
}
|
|
}
|
|
|
|
if (remainingSpace > 0) {
|
|
this.minColumnStarValue = Math.max(remainingSpace / measureSpec.starColumnsCount, this.minColumnStarValue);
|
|
}
|
|
}
|
|
}
|
|
}
|