Files
NativeScript/tns-core-modules/ui/layouts/grid-layout/grid-layout.ios.ts
Hristo Hristov af034089ca iOS Frame, Page and TabView measure/layout methods removed. We now rely on the framework positioning. This will result in a change that width, height, minWidth, minHeight, margins not respected on these controls
iOS layout positioning now respects native properties like automaticallyAdjustsScrollViewInsets, edgesForExtendedLayout, extendedLayoutIncludesOpaqueBars, navigationBar.translucent, tabBar.translucent
Removed frame-tests.ios.ts - those tests are now invalid
Added new layout tests inside page-tests.ios.ts
Commented few asserts in scroll-view-tests
View now expose ios namespace with layoutView method and UILayoutViewController used by page, tab-view and application module
ViewBase now expose viewController property that should be set from all widgets that are using viewcontrollers internally (like Page, Frame, TabView)
ViewBase now sets ios property to either the view returned from createNativeView or to nativeViewProptected
fragment.transitions now use animation/transition start to add fragments to waitingQueue. Before we did it manually in navigate/goBack. This way we can reuse the fragment.transition when calling showDialog. Also when animation/transition ends we check the animation/transition to see if this fragment should be set as current.
Frame expose new loadViewFromEntry method (to load a view from URI)
Frame navigation happens once frame is loaded
Frame now supports Page as a child in XML
Fixed GridLayout row, rowSpan, column, columnSpan properties type
Fixed bug in GridLayout where add/remove of columns/rows won't update the internal state of the grid (backport from android when GridLayout is recycled)
ListView will no longer invalidate layout when cell is removed
Fixed bug in ScrollView ios where effectiveMinWidth/Height was multiplied to density (it is already on device pixels so no need to multiply)
TabView android now calls loaded only on the selected child (not all)
Core refactoring
2017-12-15 13:06:34 +02:00

968 lines
35 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);
}
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);
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);
let paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft;
let paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop;
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);
}
}
}
}