only flex-box left

This commit is contained in:
Hristo Hristov
2016-12-12 18:39:30 +02:00
parent 12ffd9c1e8
commit 440f924131
24 changed files with 793 additions and 973 deletions

View File

@@ -14,13 +14,14 @@ import { fontSizeConverter } from "../styling/converters";
// TODO: Remove this and start using string as source (for android).
import { fromFileOrResource, fromBase64, fromUrl } from "image-source";
import { isDataURI, isFileOrResourcePath } from "utils/utils";
import { isDataURI, isFileOrResourcePath, layout } from "utils/utils";
export { layout };
export * from "./view-base";
export {
GestureTypes, GesturesObserver, GestureEventData,
Animation, AnimationPromise,
Animation, AnimationPromise,
Background, Font, Color
}
@@ -52,67 +53,6 @@ declare module "ui/styling/style" {
}
}
export namespace layout {
const MODE_SHIFT = 30;
const MODE_MASK = 0x3 << MODE_SHIFT;
export const UNSPECIFIED = 0 << MODE_SHIFT;
export const EXACTLY = 1 << MODE_SHIFT;
export const AT_MOST = 2 << MODE_SHIFT;
export const MEASURED_HEIGHT_STATE_SHIFT = 0x00000010; /* 16 */
export const MEASURED_STATE_TOO_SMALL = 0x01000000;
export const MEASURED_STATE_MASK = 0xff000000;
export const MEASURED_SIZE_MASK = 0x00ffffff;
export function getMeasureSpecMode(spec: number): number {
return (spec & MODE_MASK);
}
export function getMeasureSpecSize(spec: number): number {
return (spec & ~MODE_MASK);
}
export function getDisplayDensity(): number {
return 1;
}
export function makeMeasureSpec(size: number, mode: number): number {
return (Math.round(size) & ~MODE_MASK) | (mode & MODE_MASK);
}
export function toDevicePixels(value: number): number {
return value * getDisplayDensity();
}
export function toDeviceIndependentPixels(value: number): number {
return value / getDisplayDensity();
}
export function measureSpecToString(measureSpec: number): string {
let mode = getMeasureSpecMode(measureSpec);
let size = getMeasureSpecSize(measureSpec);
let text = "MeasureSpec: ";
if (mode === UNSPECIFIED) {
text += "UNSPECIFIED ";
}
else if (mode === EXACTLY) {
text += "EXACTLY ";
}
else if (mode === AT_MOST) {
text += "AT_MOST ";
}
else {
text += mode + " ";
}
text += size;
return text;
}
}
export function getViewById(view: ViewDefinition, id: string): ViewDefinition {
if (!view) {
return undefined;
@@ -174,6 +114,17 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator
let viewIdCounter = 0;
export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// Dynamic properties.
left: Length;
top: Length;
effectiveLeft: number;
effectiveTop: number;
dock: "left" | "top" | "right" | "bottom";
row: number;
col: number;
rowSpan: number;
colSpan: number;
public static loadedEvent = "loaded";
public static unloadedEvent = "unloaded";
@@ -187,9 +138,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
private _oldRight: number;
private _oldBottom: number;
private _parent: ViewCommon;
private _isLayoutValid: boolean;
private _cssType: string;
@@ -200,7 +148,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public _isAddedToNativeVisualTree: boolean;
public _gestureObservers = {};
public parent: ViewCommon;
// public parent: ViewCommon;
constructor() {
super();
@@ -276,10 +224,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
}
public eachChild(callback: (child: ViewCommon) => boolean): void {
this._eachChildView(callback);
}
private _isEvent(name: string): boolean {
return this.constructor && `${name}Event` in this.constructor;
}
@@ -558,7 +502,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public isEnabled: boolean;
public isUserInteractionEnabled: boolean;
get isLayoutValid(): boolean {
return this._isLayoutValid;
}
@@ -814,8 +757,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
private static getMeasureSpec(parentLength: number, parentSpecMode: number, margins: number, childLength: number, stretched: boolean): number {
let resultSize = 0;
let resultMode = 0;
let resultSize: number;
let resultMode: number;
// We want a specific size... let be it.
if (childLength >= 0) {
@@ -901,8 +844,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
//@endios
public eachChild(callback: (child: ViewBase) => boolean): void {
this._eachChildView(<any>callback);
}
public _eachChildView(callback: (view: ViewCommon) => boolean) {
public _eachChildView(callback: (view: ViewDefinition) => boolean) {
//
}
@@ -929,10 +875,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _addViewCore(view: ViewCommon, atIndex?: number) {
if (!view._isAddedToNativeVisualTree) {
let nativeIndex = this._childIndexToNativeChildIndex(atIndex);
view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex);
public _addViewCore(view: ViewBase, atIndex?: number) {
if (view instanceof ViewCommon) {
if (!view._isAddedToNativeVisualTree) {
let nativeIndex = this._childIndexToNativeChildIndex(atIndex);
view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex);
}
}
super._addViewCore(view, atIndex);
@@ -941,12 +889,13 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewCommon) {
// TODO: Change type from ViewCommon to ViewBase. Probably this
// method will need to go to ViewBase class.
// Remove the view from the native visual scene first
this._removeViewFromNativeVisualTree(view);
public _removeViewCore(view: ViewBase) {
if (view instanceof ViewCommon) {
// TODO: Change type from ViewCommon to ViewBase. Probably this
// method will need to go to ViewBase class.
// Remove the view from the native visual scene first
this._removeViewFromNativeVisualTree(view);
}
super._removeViewCore(view);
}
@@ -1057,18 +1006,18 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// }
}
export function getLengthEffectiveValue(density: number, param: Length): number {
export function getLengthEffectiveValue(param: Length): number {
switch (param.unit) {
case "px":
return Math.round(param.value);
default:
case "dip":
return Math.round(density * param.value);
return Math.round(layout.getDisplayDensity() * param.value);
}
}
function getPercentLengthEffectiveValue(prentAvailableLength: number, density: number, param: PercentLength): number {
function getPercentLengthEffectiveValue(prentAvailableLength: number, param: PercentLength): number {
switch (param.unit) {
case "%":
return Math.round(prentAvailableLength * param.value);
@@ -1078,7 +1027,7 @@ function getPercentLengthEffectiveValue(prentAvailableLength: number, density: n
default:
case "dip":
return Math.round(density * param.value);
return Math.round(layout.getDisplayDensity() * param.value);
}
}
@@ -1089,17 +1038,17 @@ function updateChildLayoutParams(child: ViewCommon, parent: ViewCommon, density:
let parentWidthMeasureSize = layout.getMeasureSpecSize(parentWidthMeasureSpec);
let parentWidthMeasureMode = layout.getMeasureSpecMode(parentWidthMeasureSpec);
let parentAvailableWidth = parentWidthMeasureMode === layout.UNSPECIFIED ? -1 : parentWidthMeasureSize;
style.effectiveWidth = getPercentLengthEffectiveValue(parentAvailableWidth, density, style.width);
style.effectiveMarginLeft = getPercentLengthEffectiveValue(parentAvailableWidth, density, style.marginLeft);
style.effectiveMarginRight = getPercentLengthEffectiveValue(parentAvailableWidth, density, style.marginRight);
style.effectiveWidth = getPercentLengthEffectiveValue(parentAvailableWidth, style.width);
style.effectiveMarginLeft = getPercentLengthEffectiveValue(parentAvailableWidth, style.marginLeft);
style.effectiveMarginRight = getPercentLengthEffectiveValue(parentAvailableWidth, style.marginRight);
let parentHeightMeasureSpec = parent._currentHeightMeasureSpec;
let parentHeightMeasureSize = layout.getMeasureSpecSize(parentHeightMeasureSpec);
let parentHeightMeasureMode = layout.getMeasureSpecMode(parentHeightMeasureSpec);
let parentAvailableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
style.effectiveHeight = getPercentLengthEffectiveValue(parentAvailableHeight, density, style.height);
style.effectiveMarginTop = getPercentLengthEffectiveValue(parentAvailableHeight, density, style.marginTop);
style.effectiveMarginBottom = getPercentLengthEffectiveValue(parentAvailableHeight, density, style.marginBottom);
style.effectiveHeight = getPercentLengthEffectiveValue(parentAvailableHeight, style.height);
style.effectiveMarginTop = getPercentLengthEffectiveValue(parentAvailableHeight, style.marginTop);
style.effectiveMarginBottom = getPercentLengthEffectiveValue(parentAvailableHeight, style.marginBottom);
}
interface Length {
@@ -1207,7 +1156,7 @@ isEnabledProperty.register(ViewCommon);
export const isUserInteractionEnabledProperty = new Property<ViewCommon, boolean>({ name: "isUserInteractionEnabled", defaultValue: true, valueConverter: booleanConverter });
isUserInteractionEnabledProperty.register(ViewCommon);
const zeroLength: Length = { value: 0, unit: "px" };
export const zeroLength: Length = { value: 0, unit: "px" };
export function lengthComparer(x: Length, y: Length): boolean {
return x.unit === y.unit && x.value === y.value;
@@ -1216,7 +1165,7 @@ export function lengthComparer(x: Length, y: Length): boolean {
export const minWidthProperty = new CssProperty<Style, Length>({
name: "minWidth", cssName: "min-width", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectiveMinWidth = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectiveMinWidth = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
minWidthProperty.register(Style);
@@ -1224,7 +1173,7 @@ minWidthProperty.register(Style);
export const minHeightProperty = new CssProperty<Style, Length>({
name: "minHeight", cssName: "min-height", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectiveMinHeight = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectiveMinHeight = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
minHeightProperty.register(Style);
@@ -1266,7 +1215,7 @@ paddingProperty.register(Style);
export const paddingLeftProperty = new CssProperty<Style, Length>({
name: "paddingLeft", cssName: "padding-left", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectivePaddingLeft = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectivePaddingLeft = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
paddingLeftProperty.register(Style);
@@ -1274,7 +1223,7 @@ paddingLeftProperty.register(Style);
export const paddingRightProperty = new CssProperty<Style, Length>({
name: "paddingRight", cssName: "padding-right", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectivePaddingRight = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectivePaddingRight = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
paddingRightProperty.register(Style);
@@ -1282,7 +1231,7 @@ paddingRightProperty.register(Style);
export const paddingTopProperty = new CssProperty<Style, Length>({
name: "paddingTop", cssName: "padding-top", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectivePaddingTop = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectivePaddingTop = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
paddingTopProperty.register(Style);
@@ -1290,7 +1239,7 @@ paddingTopProperty.register(Style);
export const paddingBottomProperty = new CssProperty<Style, Length>({
name: "paddingBottom", cssName: "padding-bottom", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
target.effectivePaddingBottom = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
target.effectivePaddingBottom = getLengthEffectiveValue(newValue);
}, valueConverter: Length.parse
});
paddingBottomProperty.register(Style);
@@ -1728,7 +1677,7 @@ borderWidthProperty.register(Style);
export const borderTopWidthProperty = new CssProperty<Style, Length>({
name: "borderTopWidth", cssName: "border-top-width", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
let value = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
let value = getLengthEffectiveValue(newValue);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-top-width should be Non-Negative Finite number. Value: ${value}`);
}
@@ -1742,7 +1691,7 @@ borderTopWidthProperty.register(Style);
export const borderRightWidthProperty = new CssProperty<Style, Length>({
name: "borderRightWidth", cssName: "border-right-width", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
let value = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
let value = getLengthEffectiveValue(newValue);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-right-width should be Non-Negative Finite number. Value: ${value}`);
}
@@ -1756,7 +1705,7 @@ borderRightWidthProperty.register(Style);
export const borderBottomWidthProperty = new CssProperty<Style, Length>({
name: "borderBottomWidth", cssName: "border-bottom-width", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
let value = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
let value = getLengthEffectiveValue(newValue);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-bottom-width should be Non-Negative Finite number. Value: ${value}`);
}
@@ -1770,7 +1719,7 @@ borderBottomWidthProperty.register(Style);
export const borderLeftWidthProperty = new CssProperty<Style, Length>({
name: "borderLeftWidth", cssName: "border-left-width", defaultValue: zeroLength, affectsLayout: isIOS, equalityComparer: lengthComparer,
valueChanged: (target, newValue) => {
let value = getLengthEffectiveValue(layout.getDisplayDensity(), newValue);
let value = getLengthEffectiveValue(newValue);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-left-width should be Non-Negative Finite number. Value: ${value}`);
}
@@ -1970,4 +1919,4 @@ export const fontProperty = new ShorthandProperty<Style>({
]
}
})
fontProperty.register(Style);
fontProperty.register(Style);

View File

@@ -1,23 +1,25 @@
import { PercentLength, Length, Point, CustomLayoutView as CustomLayoutViewDefinition } from "ui/core/view";
import { ad } from "ui/styling/background";
import { ad as androidBackground } from "ui/styling/background";
import {
ViewCommon, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty, visibilityProperty, opacityProperty, minWidthProperty, minHeightProperty,
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty, visibilityProperty, opacityProperty, minWidthProperty, minHeightProperty,
widthProperty, heightProperty, marginLeftProperty, marginTopProperty,
marginRightProperty, marginBottomProperty, horizontalAlignmentProperty, verticalAlignmentProperty,
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
rotateProperty, scaleXProperty, scaleYProperty,
translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty,
layout, Background, GestureTypes, GestureEventData, applyNativeSetters, Property,
Background, GestureTypes, GestureEventData, applyNativeSetters, Property,
traceEnabled, traceWrite, traceCategories, traceNotifyEvent
} from "./view-common";
import { } from "utils/utils";
export * from "./view-common";
let flexbox;
const ANDROID = "_android";
const NATIVE_VIEW = "_nativeView";
const VIEW_GROUP = "_viewGroup";
let density = -1;
// TODO: Move this class into widgets.
@Interfaces([android.view.View.OnTouchListener])
@@ -651,9 +653,8 @@ export class View extends ViewCommon {
if (value instanceof android.graphics.drawable.Drawable) {
this.nativeView.setBackground(value);
} else {
ad.onBackgroundOrBorderPropertyChanged(this);
androidBackground.onBackgroundOrBorderPropertyChanged(this);
}
}
}

View File

@@ -17,6 +17,9 @@ declare module "ui/core/view" {
export * from "ui/core/view-base";
export const zeroLength: Length;
export function getLengthEffectiveValue(param: Length): number;
/**
* Gets a child view by id.
* @param view - The parent (container) view of the view to look for.

View File

@@ -1,5 +1,5 @@
import { AbsoluteLayout as AbsoluteLayoutDefinition } from "ui/layouts/absolute-layout";
import { LayoutBase, View, Property, Length, lengthComparer } from "ui/layouts/layout-base";
import { LayoutBase, View, Property, Length, lengthComparer, zeroLength, getLengthEffectiveValue } from "ui/layouts/layout-base";
export * from "ui/layouts/layout-base";
@@ -52,11 +52,10 @@ export class AbsoluteLayoutBase extends LayoutBase implements AbsoluteLayoutDefi
}
}
export const zeroLenth: Length = { value: 0, unit: "px" };
export const leftProperty = new Property<View, Length>({
name: "left", defaultValue: zeroLenth,
name: "left", defaultValue: zeroLength,
valueChanged: (target, oldValue, newValue) => {
target.effectiveLeft = getLengthEffectiveValue(newValue);
const layout = target.parent;
if (layout instanceof AbsoluteLayoutBase) {
layout.onLeftChanged(target, oldValue, newValue);
@@ -66,8 +65,9 @@ export const leftProperty = new Property<View, Length>({
leftProperty.register(AbsoluteLayoutBase);
export const topProperty = new Property<View, Length>({
name: "top", defaultValue: zeroLenth,
name: "top", defaultValue: zeroLength,
valueChanged: (target, oldValue, newValue) => {
target.effectiveTop = getLengthEffectiveValue(newValue);
const layout = target.parent;
if (layout instanceof AbsoluteLayoutBase) {
layout.onTopChanged(target, oldValue, newValue);

View File

@@ -1,12 +1,39 @@
import { AbsoluteLayoutBase, leftProperty, topProperty, Length, zeroLength } from "./absolute-layout-common";
import { AbsoluteLayoutBase, View, leftProperty, topProperty, Length, zeroLength } from "./absolute-layout-common";
export * from "./absolute-layout-common";
function setNativeProperty(data: PropertyChangeData, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
var view = data.object;
// define native getter and setter for leftProperty.
let leftDescriptor: TypedPropertyDescriptor<Length> = {
enumerable: true,
configurable: true,
writable: true,
get: () => zeroLength,
set: function (this: View, value: Length) {
setNativeProperty(this, (lp) => lp.left = this.effectiveLeft);
}
}
// define native getter and setter for topProperty.
let topDescriptor: TypedPropertyDescriptor<Length> = {
enumerable: true,
configurable: true,
writable: true,
get: () => zeroLength,
set: function (this: View, value: Length) {
setNativeProperty(this, (lp) => lp.top = this.effectiveTop);
}
}
// register native properties on View type.
Object.defineProperties(View, {
[leftProperty.native]: leftDescriptor,
[topProperty.native]: topDescriptor
});
function setNativeProperty(view: View, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
if (view instanceof View) {
var nativeView: android.view.View = view._nativeView;
var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
const nativeView: android.view.View = view._nativeView;
const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
if (lp instanceof org.nativescript.widgets.CommonLayoutParams) {
setter(lp);
nativeView.setLayoutParams(lp);
@@ -14,17 +41,6 @@ function setNativeProperty(data: PropertyChangeData, setter: (lp: org.nativescri
}
}
function setNativeLeftProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.left = data.newValue * utils.layout.getDisplayDensity(); });
}
function setNativeTopProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.top = data.newValue * utils.layout.getDisplayDensity(); });
}
(<PropertyMetadata>common.AbsoluteLayout.leftProperty.metadata).onSetNativeValue = setNativeLeftProperty;
(<PropertyMetadata>common.AbsoluteLayout.topProperty.metadata).onSetNativeValue = setNativeTopProperty;
export class AbsoluteLayout extends AbsoluteLayoutBase {
private _layout: org.nativescript.widgets.AbsoluteLayout;
@@ -40,13 +56,4 @@ export class AbsoluteLayout extends AbsoluteLayoutBase {
public _createUI() {
this._layout = new org.nativescript.widgets.AbsoluteLayout(this._context);
}
-------------------------
This should be defined with Object.DefineProperty on a View type........
get [leftProperty.native](): Length {
return zeroLength;
}
set [leftProperty.native](value: Length) {
return zeroLength;
}
}

View File

@@ -1,49 +1,47 @@
import utils = require("utils/utils");
import common = require("./absolute-layout-common");
import {View} from "ui/core/view";
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";
import { AbsoluteLayoutBase, View, layout, Length } from "./absolute-layout-common";
global.moduleMerge(common, exports);
export * from "./absolute-layout-common";
export class AbsoluteLayout extends common.AbsoluteLayout {
protected onLeftChanged(view: View, oldValue: number, newValue: number) {
export class AbsoluteLayout extends AbsoluteLayoutBase {
onLeftChanged(view: View, oldValue: Length, newValue: Length) {
this.requestLayout();
}
protected onTopChanged(view: View, oldValue: number, newValue: number) {
onTopChanged(view: View, oldValue: Length, newValue: Length) {
this.requestLayout();
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
AbsoluteLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
let measureWidth = 0;
let measureHeight = 0;
let width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
let widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
let height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
let heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
let childMeasureSpec = utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED);
let density = utils.layout.getDisplayDensity();
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const childMeasureSpec = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
this.eachLayoutChild((child, last) => {
let childSize = View.measureChild(this, child, childMeasureSpec, childMeasureSpec);
measureWidth = Math.max(measureWidth, AbsoluteLayout.getLeft(child) * density + childSize.measuredWidth);
measureHeight = Math.max(measureHeight, AbsoluteLayout.getTop(child) * density + childSize.measuredHeight);
measureWidth = Math.max(measureWidth, child.effectiveLeft + childSize.measuredWidth);
measureHeight = Math.max(measureHeight, child.effectiveTop + childSize.measuredHeight);
});
measureWidth += (this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density;
measureHeight += (this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density;
const style = this.style;
measureWidth = Math.max(measureWidth, this.minWidth * density);
measureHeight = Math.max(measureHeight, this.minHeight * density);
measureWidth += style.effectiveBorderLeftWidth + style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderRightWidth;
measureHeight += style.effectiveBorderTopWidth + style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderBottomWidth;
let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
measureWidth = Math.max(measureWidth, style.effectiveMinWidth);
measureHeight = Math.max(measureHeight, style.effectiveMinHeight);
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
@@ -51,21 +49,18 @@ export class AbsoluteLayout extends common.AbsoluteLayout {
public onLayout(left: number, top: number, right: number, bottom: number): void {
super.onLayout(left, top, right, bottom);
let density = utils.layout.getDisplayDensity();
const style = this.style;
this.eachLayoutChild((child, last) => {
let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);
let childWidth = child.getMeasuredWidth();
let childHeight = child.getMeasuredHeight();
const childWidth = child.getMeasuredWidth();
const childHeight = child.getMeasuredHeight();
let childLeft = (this.borderLeftWidth + this.paddingLeft + AbsoluteLayout.getLeft(child)) * density;
let childTop = (this.borderTopWidth + this.paddingTop + AbsoluteLayout.getTop(child)) * density;
let childRight = childLeft + childWidth + (lp.leftMargin + lp.rightMargin) * density;
let childBottom = childTop + childHeight + (lp.topMargin + lp.bottomMargin) * density;
const childLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft + child.effectiveLeft;
const childTop = style.effectiveBorderTopWidth + style.effectivePaddingTop + child.effectiveTop;
const childRight = childLeft + childWidth + style.effectiveMarginLeft + style.effectiveMarginRight;
const childBottom = childTop + childHeight + style.effectiveMarginTop + style.effectiveMarginBottom;
View.layoutChild(this, child, childLeft, childTop, childRight, childBottom);
});
AbsoluteLayout.restoreOriginalParams(this);
}
}

View File

@@ -1,18 +1,5 @@
import definition = require("ui/layouts/dock-layout");
import platform = require("platform");
import {Dock} from "ui/enums";
import {LayoutBase} from "ui/layouts/layout-base";
import {View} from "ui/core/view";
import {PropertyMetadata} from "ui/core/proxy";
import {Property, PropertyChangeData, PropertyMetadataSettings} from "ui/core/dependency-observable";
import {registerSpecialProperty} from "ui/builder/special-properties";
// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
function isDockValid(value: any): boolean {
return value === Dock.left || value === Dock.top || value === Dock.right || value === Dock.bottom;
}
import { DockLayout as DockLayoutDefinition } from "ui/layouts/dock-layout";
import { LayoutBase, View, Property, isIOS, booleanConverter } from "ui/layouts/layout-base";
function validateArgs(element: View): View {
if (!element) {
@@ -21,44 +8,52 @@ function validateArgs(element: View): View {
return element;
}
registerSpecialProperty("dock", (instance, propertyValue) => {
DockLayout.setDock(instance, propertyValue);
});
export * from "ui/layouts/layout-base";
export class DockLayout extends LayoutBase implements definition.DockLayout {
declare module "ui/core/view" {
interface View {
dock: "left" | "top" | "right" | "bottom";
}
}
private static onDockPropertyChanged(data: PropertyChangeData) {
var view = data.object;
if (view instanceof View) {
var layout = view.parent;
if (layout instanceof DockLayout) {
layout.onDockChanged(view, data.oldValue, data.newValue);
}
}
View.prototype.dock = "left";
export class DockLayoutBase extends LayoutBase implements DockLayoutDefinition {
public static getDock(element: View): "left" | "top" | "right" | "bottom" {
return validateArgs(element).dock;
}
public static dockProperty = new Property(
"dock", "DockLayout", new PropertyMetadata(Dock.left, undefined, DockLayout.onDockPropertyChanged, isDockValid));
public static stretchLastChildProperty = new Property(
"stretchLastChild", "DockLayout", new PropertyMetadata(true, AffectsLayout));
public static getDock(element: View): string {
return validateArgs(element)._getValue(DockLayout.dockProperty);
public static setDock(element: View, value: "left" | "top" | "right" | "bottom"): void {
validateArgs(element).dock = value;
}
public static setDock(element: View, value: string): void {
validateArgs(element)._setValue(DockLayout.dockProperty, value);
}
public stretchLastChild: boolean;
get stretchLastChild(): boolean {
return this._getValue(DockLayout.stretchLastChildProperty);
}
set stretchLastChild(value: boolean) {
this._setValue(DockLayout.stretchLastChildProperty, value);
}
protected onDockChanged(view: View, oldValue: number, newValue: number) {
public onDockChanged(view: View, oldValue: "left" | "top" | "right" | "bottom", newValue: "left" | "top" | "right" | "bottom") {
//
}
}
}
export const dockProperty = new Property<View, "left" | "top" | "right" | "bottom">({
name: "dock", defaultValue: "left", valueChanged: (target, oldValue, newValue) => {
if (target instanceof View) {
const layout = target.parent;
if (layout instanceof DockLayoutBase) {
layout.onDockChanged(target, oldValue, newValue);
}
}
}, valueConverter: (v) => {
if (v === "left" || v === "top" || v === "right" || v === "bottom") {
return <"left" | "top" | "right" | "bottom">v;
}
throw new Error(`Invalid value for dock property: ${v}`);
}
});
dockProperty.register(DockLayoutBase);
export const stretchLastChildProperty = new Property<DockLayoutBase, boolean>({
name: "stretchLastChild", defaultValue: true, affectsLayout: isIOS, valueConverter: booleanConverter
});
stretchLastChildProperty.register(DockLayoutBase);

View File

@@ -1,32 +1,36 @@
import common = require("./dock-layout-common");
import {Dock} from "ui/enums";
import {View} from "ui/core/view";
import {PropertyMetadata} from "ui/core/proxy";
import {PropertyChangeData} from "ui/core/dependency-observable";
import { DockLayoutBase, View, dockProperty, stretchLastChildProperty } from "./dock-layout-common";
global.moduleMerge(common, exports);
export * from "./dock-layout-common";
function setNativeDockProperty(data: PropertyChangeData) {
var view = data.object;
if (view instanceof View) {
var nativeView: android.view.View = view._nativeView;
var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
// define native getter and setter for topProperty.
let dockDescriptor: TypedPropertyDescriptor<"left" | "top" | "right" | "bottom"> = {
enumerable: true,
configurable: true,
writable: true,
get: () => "left",
set: function (this: View, value: "left" | "top" | "right" | "bottom") {
const nativeView: android.view.View = this._nativeView;
const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
if (lp instanceof org.nativescript.widgets.CommonLayoutParams) {
switch (data.newValue) {
case Dock.left:
switch (value) {
case "left":
lp.dock = org.nativescript.widgets.Dock.left;
break;
case Dock.top:
case "top":
lp.dock = org.nativescript.widgets.Dock.top;
break;
case Dock.right:
case "right":
lp.dock = org.nativescript.widgets.Dock.right;
break;
case Dock.bottom:
case "bottom":
lp.dock = org.nativescript.widgets.Dock.bottom;
break;
default:
throw new Error("Invalid dock value: " + data.newValue + " on element: " + view);
throw new Error(`Invalid value for dock property: ${value}`);
}
nativeView.setLayoutParams(lp);
@@ -34,17 +38,12 @@ function setNativeDockProperty(data: PropertyChangeData) {
}
}
(<PropertyMetadata>common.DockLayout.dockProperty.metadata).onSetNativeValue = setNativeDockProperty;
// register native properties on View type.
Object.defineProperties(View, {
[dockProperty.native]: dockDescriptor
});
function setNativeStretchLastChildProperty(data: PropertyChangeData) {
let dockLayout = <DockLayout>data.object;
let nativeView = dockLayout._nativeView;
nativeView.setStretchLastChild(data.newValue);
}
(<PropertyMetadata>common.DockLayout.stretchLastChildProperty.metadata).onSetNativeValue = setNativeStretchLastChildProperty;
export class DockLayout extends common.DockLayout {
export class DockLayout extends DockLayoutBase {
private _layout: org.nativescript.widgets.DockLayout;
@@ -59,4 +58,11 @@ export class DockLayout extends common.DockLayout {
public _createUI() {
this._layout = new org.nativescript.widgets.DockLayout(this._context);
}
get [stretchLastChildProperty.native](): boolean {
return false;
}
set [stretchLastChildProperty.native](value: boolean) {
this._layout.setStretchLastChild(value);
}
}

View File

@@ -1,23 +1,10 @@
declare module "ui/layouts/dock-layout" {
import {LayoutBase} from "ui/layouts/layout-base";
import {View} from "ui/core/view";
import {Property} from "ui/core/dependency-observable";
import { LayoutBase, View, Property } from "ui/layouts/layout-base";
/**
* A Layout that arranges its children at its outer edges, and allows its last child to take up the remaining space.
*/
class DockLayout extends LayoutBase {
/**
* Represents the observable property backing the dock property.
*/
public static dockProperty: Property;
/**
* Represents the observable property backing the stretchLastChild property of each DockLayout instance.
*/
public static stretchLastChildProperty: Property;
/**
* Gets the value of the Left property from a given View.
*/
@@ -34,4 +21,14 @@
*/
stretchLastChild: boolean;
}
/**
* Represents the observable property backing the dock property.
*/
export const dockProperty: Property<DockLayout, "left" | "top" | "right" | "bottom">;
/**
* Represents the observable property backing the stretchLastChild property of each DockLayout instance.
*/
export const stretchLastChildProperty: Property<DockLayout, boolean>;
}

View File

@@ -1,65 +1,61 @@
import utils = require("utils/utils");
import common = require("./dock-layout-common");
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";
import {Dock} from "ui/enums";
import {View} from "ui/core/view";
import { DockLayoutBase, View, dockProperty, stretchLastChildProperty, layout } from "./dock-layout-common";
global.moduleMerge(common, exports);
export * from "./dock-layout-common";
export class DockLayout extends common.DockLayout {
export class DockLayout extends DockLayoutBase {
protected onDockChanged(view: View, oldValue: number, newValue: number) {
public onDockChanged(view: View, oldValue: "left" | "top" | "right" | "bottom", newValue: "left" | "top" | "right" | "bottom") {
this.requestLayout();
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
DockLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
var measureWidth = 0;
var measureHeight = 0;
let measureWidth = 0;
let measureHeight = 0;
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
var density = utils.layout.getDisplayDensity();
const style = this.style;
const horizontalPaddingsAndMargins = style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderLeftWidth + style.effectiveBorderRightWidth;
const verticalPaddingsAndMargins = style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderTopWidth + style.effectiveBorderBottomWidth;
let remainingWidth = widthMode === layout.UNSPECIFIED ? Number.MAX_VALUE : width - horizontalPaddingsAndMargins;
let remainingHeight = heightMode === layout.UNSPECIFIED ? Number.MAX_VALUE : height - verticalPaddingsAndMargins;
var remainingWidth = widthMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : width - ((this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density);
var remainingHeight = heightMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : height - ((this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density);
var tempHeight: number = 0;
var tempWidth: number = 0;
var childWidthMeasureSpec: number;
var childHeightMeasureSpec: number;
let tempHeight: number = 0;
let tempWidth: number = 0;
let childWidthMeasureSpec: number;
let childHeightMeasureSpec: number;
this.eachLayoutChild((child, last) => {
if (this.stretchLastChild && last) {
childWidthMeasureSpec = utils.layout.makeMeasureSpec(remainingWidth, widthMode);
childHeightMeasureSpec = utils.layout.makeMeasureSpec(remainingHeight, heightMode);
childWidthMeasureSpec = layout.makeMeasureSpec(remainingWidth, widthMode);
childHeightMeasureSpec = layout.makeMeasureSpec(remainingHeight, heightMode);
}
else {
// Measure children with AT_MOST even if our mode is EXACT
childWidthMeasureSpec = utils.layout.makeMeasureSpec(remainingWidth, widthMode === utils.layout.EXACTLY ? utils.layout.AT_MOST : widthMode);
childHeightMeasureSpec = utils.layout.makeMeasureSpec(remainingHeight, heightMode === utils.layout.EXACTLY ? utils.layout.AT_MOST : heightMode);
childWidthMeasureSpec = layout.makeMeasureSpec(remainingWidth, widthMode === layout.EXACTLY ? layout.AT_MOST : widthMode);
childHeightMeasureSpec = layout.makeMeasureSpec(remainingHeight, heightMode === layout.EXACTLY ? layout.AT_MOST : heightMode);
}
let childSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec);
let dock = DockLayout.getDock(child);
switch (dock) {
case Dock.top:
case Dock.bottom:
switch (child.dock) {
case "top":
case "bottom":
remainingHeight = Math.max(0, remainingHeight - childSize.measuredHeight);
tempHeight += childSize.measuredHeight;
measureWidth = Math.max(measureWidth, tempWidth + childSize.measuredWidth);
measureHeight = Math.max(measureHeight, tempHeight);
break;
case Dock.left:
case Dock.right:
case "left":
case "right":
default:
remainingWidth = Math.max(0, remainingWidth - childSize.measuredWidth);
tempWidth += childSize.measuredWidth;
@@ -69,14 +65,14 @@ export class DockLayout extends common.DockLayout {
}
});
measureWidth += (this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density;
measureHeight += (this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density;
measureWidth += horizontalPaddingsAndMargins;
measureHeight += verticalPaddingsAndMargins;
measureWidth = Math.max(measureWidth, this.minWidth * density);
measureHeight = Math.max(measureHeight, this.minHeight * density);
measureWidth = Math.max(measureWidth, style.effectiveMinWidth);
measureHeight = Math.max(measureHeight, style.effectiveMinHeight);
var widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
var heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
@@ -84,32 +80,34 @@ export class DockLayout extends common.DockLayout {
public onLayout(left: number, top: number, right: number, bottom: number): void {
super.onLayout(left, top, right, bottom);
var density = utils.layout.getDisplayDensity();
const style = this.style;
const horizontalPaddingsAndMargins = style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderLeftWidth + style.effectiveBorderRightWidth;
const verticalPaddingsAndMargins = style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderTopWidth + style.effectiveBorderBottomWidth;
var childLeft = (this.borderLeftWidth + this.paddingLeft) * density;
var childTop = (this.borderTopWidth + this.paddingTop) * density;
let childLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft;
let childTop = style.effectiveBorderTopWidth + style.effectivePaddingTop;
var x = childLeft;
var y = childTop;
let x = childLeft;
let y = childTop;
var remainingWidth = Math.max(0, right - left - ((this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density));
var remainingHeight = Math.max(0, bottom - top - ((this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density));
let remainingWidth = Math.max(0, right - left - horizontalPaddingsAndMargins);
let remainingHeight = Math.max(0, bottom - top - verticalPaddingsAndMargins);
this.eachLayoutChild((child, last) => {
let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);
let childStlye = child.style;
let childWidth = child.getMeasuredWidth() + childStlye.effectiveMarginLeft + childStlye.effectiveMarginRight;
let childHeight = child.getMeasuredHeight() + childStlye.effectiveMarginTop + childStlye.effectiveMarginBottom;
let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density;
let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density;
if (last && this.stretchLastChild) {
// Last child with stretch - give it all the space and return;
View.layoutChild(this, child, x, y, x + remainingWidth, y + remainingHeight);
return;
}
let dock = DockLayout.getDock(child);
switch (dock) {
case Dock.top:
case "top":
childLeft = x;
childTop = y;
childWidth = remainingWidth;
@@ -117,21 +115,21 @@ export class DockLayout extends common.DockLayout {
remainingHeight = Math.max(0, remainingHeight - childHeight);
break;
case Dock.bottom:
case "bottom":
childLeft = x;
childTop = y + remainingHeight - childHeight;
childWidth = remainingWidth;
remainingHeight = Math.max(0, remainingHeight - childHeight);
break;
case Dock.right:
case "right":
childLeft = x + remainingWidth - childWidth;
childTop = y;
childHeight = remainingHeight;
remainingWidth = Math.max(0, remainingWidth - childWidth);
break;
case Dock.left:
case "left":
default:
childLeft = x;
childTop = y;
@@ -143,7 +141,5 @@ export class DockLayout extends common.DockLayout {
View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight);
});
DockLayout.restoreOriginalParams(this);
}
}

View File

@@ -1,20 +1,7 @@
import definition = require("ui/layouts/grid-layout");
import {LayoutBase} from "ui/layouts/layout-base";
import {View, ApplyXmlAttributes} from "ui/core/view";
import {Bindable} from "ui/core/bindable";
import {PropertyMetadata} from "ui/core/proxy";
import {Property, PropertyMetadataSettings, PropertyChangeData} from "ui/core/dependency-observable";
import {registerSpecialProperty} from "ui/builder/special-properties";
import numberUtils = require("../../../utils/number-utils");
import * as typesModule from "utils/types";
var types: typeof typesModule;
function ensureTypes() {
if (!types) {
types = require("utils/types");
}
}
import { GridLayout as GridLayoutDefinition, ItemSpec as ItemSpecDefinition } from "ui/layouts/grid-layout";
import { LayoutBase, View, Bindable, Property } from "ui/layouts/layout-base";
export * from "ui/layouts/layout-base";
function validateArgs(element: View): View {
if (!element) {
throw new Error("element cannot be null or undefinied.");
@@ -22,49 +9,89 @@ function validateArgs(element: View): View {
return element;
}
export module GridUnitType {
export var auto: string = "auto";
export var pixel: string = "pixel";
export var star: string = "star";
declare module "ui/core/view" {
interface View {
row: number;
col: number;
rowSpan: number;
colSpan: number;
}
}
registerSpecialProperty("row", (instance, propertyValue) => {
GridLayout.setRow(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("col", (instance, propertyValue) => {
GridLayout.setColumn(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("colSpan", (instance, propertyValue) => {
GridLayout.setColumnSpan(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("rowSpan", (instance, propertyValue) => {
GridLayout.setRowSpan(instance, !isNaN(+propertyValue) && +propertyValue);
});
View.prototype.row = 0;
View.prototype.col = 0;
View.prototype.rowSpan = 1;
View.prototype.colSpan = 1;
export class ItemSpec extends Bindable implements definition.ItemSpec {
function convertUnitType(value: string): "pixel" | "star" | "auto" {
if (value === "pixel" || value === "star" || value === "auto") {
return <"pixel" | "star" | "auto">value;
}
throw new Error(`Invalid value for unitType: ${value}`);
}
function validateItemSpec(itemSpec: ItemSpec): void {
if (!itemSpec) {
throw new Error("Value cannot be undefined.");
}
if (itemSpec.owner) {
throw new Error("itemSpec is already added to GridLayout.");
}
}
function convertGridLength(value: string): ItemSpec {
if (value === "auto") {
return ItemSpec.create(1, "auto");
}
else if (value.indexOf("*") !== -1) {
const starCount = parseInt(value.replace("*", "") || "1");
return ItemSpec.create(starCount, "star");
}
else if (!isNaN(parseInt(value))) {
return ItemSpec.create(parseInt(value), "pixel");
}
else {
throw new Error(`Cannot parse item spec from string: ${value}`);
}
}
function parseAndAddItemSpecs(value: string, func: (itemSpec: ItemSpec) => void): void {
const arr = value.split(/[\s,]+/);
for (let i = 0, length = arr.length; i < length; i++) {
const str = arr[i].trim();
if (str.length > 0) {
func(convertGridLength(arr[i].trim()));
}
}
}
export class ItemSpec extends Bindable implements ItemSpecDefinition {
private _value: number;
private _unitType: string;
private _unitType: "pixel" | "star" | "auto";
constructor() {
super();
if (arguments.length === 0) {
this._value = 1;
this._unitType = GridUnitType.star;
this._unitType = "star";
}
else if (arguments.length === 2) {
ensureTypes();
if (types.isNumber(arguments[0]) && types.isString(arguments[1])) {
if (arguments[0] < 0 || (arguments[1] !== GridUnitType.auto && arguments[1] !== GridUnitType.star && arguments[1] !== GridUnitType.pixel)) {
throw new Error("Invalid values.");
const value = arguments[0];
const type = arguments[1];
if (typeof value === "number" && typeof type === "string") {
if (value < 0 || isNaN(value) || !isFinite(value)) {
throw new Error(`Value should not be negative, NaN or Infinity: ${value}`);
}
this._value = arguments[0];
this._unitType = arguments[1];
this._value = value;
this._unitType = convertUnitType(type);
}
else {
throw new Error("Arguments must be number and string.");
throw new Error("First argument should be number, second argument should be string.");
}
}
else {
@@ -74,11 +101,11 @@ export class ItemSpec extends Bindable implements definition.ItemSpec {
this.index = -1;
}
public owner: GridLayout;
public owner: GridLayoutBase;
public index: number;
public _actualLength: number = 0;
public static create(value: number, type: string): ItemSpec {
public static create(value: number, type: "pixel" | "star" | "auto"): ItemSpec {
let spec = new ItemSpec();
spec._value = value;
spec._unitType = type;
@@ -88,28 +115,25 @@ export class ItemSpec extends Bindable implements definition.ItemSpec {
public get actualLength(): number {
return this._actualLength;
}
public set actualLength(value: number) {
throw new Error("actualLength is read-only property");
}
public static equals(value1: ItemSpec, value2: ItemSpec): boolean {
return (value1.gridUnitType === value2.gridUnitType) && (value1.value === value2.value) && (value1.owner === value2.owner) && (value1.index === value2.index);
}
get gridUnitType(): string {
get gridUnitType(): "pixel" | "star" | "auto" {
return this._unitType;
}
get isAbsolute(): boolean {
return this._unitType === GridUnitType.pixel;
return this._unitType === "pixel";
}
get isAuto(): boolean {
return this._unitType === GridUnitType.auto;
return this._unitType === "auto";
}
get isStar(): boolean {
return this._unitType === GridUnitType.star;
return this._unitType === "star";
}
get value(): number {
@@ -117,56 +141,44 @@ export class ItemSpec extends Bindable implements definition.ItemSpec {
}
}
export class GridLayout extends LayoutBase implements definition.GridLayout, ApplyXmlAttributes {
export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition {
private _rows: Array<ItemSpec> = new Array<ItemSpec>();
private _cols: Array<ItemSpec> = new Array<ItemSpec>();
public static columnProperty = new Property("Column", "GridLayout",
new PropertyMetadata(0, PropertyMetadataSettings.None, GridLayout.onColumnPropertyChanged, numberUtils.notNegative));
public static columnSpanProperty = new Property("ColumnSpan", "GridLayout",
new PropertyMetadata(1, PropertyMetadataSettings.None, GridLayout.onColumnSpanPropertyChanged, numberUtils.greaterThanZero));
public static rowProperty = new Property("Row", "GridLayout",
new PropertyMetadata(0, PropertyMetadataSettings.None, GridLayout.onRowPropertyChanged, numberUtils.notNegative));
public static rowSpanProperty = new Property("RowSpan", "GridLayout",
new PropertyMetadata(1, PropertyMetadataSettings.None, GridLayout.onRowSpanPropertyChanged, numberUtils.greaterThanZero));
public static getColumn(element: View): number {
return validateArgs(element)._getValue(GridLayout.columnProperty);
return validateArgs(element).col;
}
public static setColumn(element: View, value: number): void {
validateArgs(element)._setValue(GridLayout.columnProperty, value);
validateArgs(element).col = value;
}
public static getColumnSpan(element: View): number {
return validateArgs(element)._getValue(GridLayout.columnSpanProperty);
return validateArgs(element).colSpan;
}
public static setColumnSpan(element: View, value: number): void {
validateArgs(element)._setValue(GridLayout.columnSpanProperty, value);
validateArgs(element).colSpan = value;
}
public static getRow(element: View): number {
return validateArgs(element)._getValue(GridLayout.rowProperty);
return validateArgs(element).row;
}
public static setRow(element: View, value: number): void {
validateArgs(element)._setValue(GridLayout.rowProperty, value);
validateArgs(element).row = value;
}
public static getRowSpan(element: View): number {
return validateArgs(element)._getValue(GridLayout.rowSpanProperty);
return validateArgs(element).rowSpan;
}
public static setRowSpan(element: View, value: number): void {
validateArgs(element)._setValue(GridLayout.rowSpanProperty, value);
validateArgs(element).rowSpan = value;
}
public addRow(itemSpec: ItemSpec) {
GridLayout.validateItemSpec(itemSpec);
validateItemSpec(itemSpec);
itemSpec.owner = this;
this._rows.push(itemSpec);
this._onRowAdded(itemSpec);
@@ -174,7 +186,7 @@ export class GridLayout extends LayoutBase implements definition.GridLayout, App
}
public addColumn(itemSpec: ItemSpec) {
GridLayout.validateItemSpec(itemSpec);
validateItemSpec(itemSpec);
itemSpec.owner = this;
this._cols.push(itemSpec);
this._onColumnAdded(itemSpec);
@@ -233,19 +245,19 @@ export class GridLayout extends LayoutBase implements definition.GridLayout, App
this.invalidate();
}
protected onRowChanged(element: View, oldValue: number, newValue: number) {
public onRowChanged(element: View, oldValue: number, newValue: number) {
this.invalidate();
}
protected onRowSpanChanged(element: View, oldValue: number, newValue: number) {
public onRowSpanChanged(element: View, oldValue: number, newValue: number) {
this.invalidate();
}
protected onColumnChanged(element: View, oldValue: number, newValue: number) {
public onColumnChanged(element: View, oldValue: number, newValue: number) {
this.invalidate();
}
protected onColumnSpanChanged(element: View, oldValue: number, newValue: number) {
public onColumnSpanChanged(element: View, oldValue: number, newValue: number) {
this.invalidate();
}
@@ -280,7 +292,7 @@ export class GridLayout extends LayoutBase implements definition.GridLayout, App
protected get rowsInternal(): Array<ItemSpec> {
return this._rows;
}
protected invalidate(): void {
this.requestLayout();
}
@@ -298,98 +310,61 @@ export class GridLayout extends LayoutBase implements definition.GridLayout, App
return super._applyXmlAttribute(attributeName, attributeValue);
}
private static parseItemSpecs(value: string): Array<ItemSpec> {
var result = new Array<ItemSpec>();
var arr = value.split(/[\s,]+/);
for (var i = 0; i < arr.length; i++) {
let str = arr[i].trim();
if (str.length > 0) {
result.push(GridLayout.convertGridLength(arr[i].trim()));
}
}
return result;
}
private static convertGridLength(value: string): ItemSpec {
if (value === "auto") {
return ItemSpec.create(1, GridUnitType.auto);
}
else if (value.indexOf("*") !== -1) {
var starCount = parseInt(value.replace("*", "") || "1");
return ItemSpec.create(starCount, GridUnitType.star);
}
else if (!isNaN(parseInt(value))) {
return ItemSpec.create(parseInt(value), GridUnitType.pixel);
}
else {
throw new Error("Cannot parse item spec from string: " + value);
}
}
private static onRowPropertyChanged(data: PropertyChangeData): void {
var element = GridLayout.getView(data.object);
var grid = element.parent;
if (grid instanceof GridLayout) {
grid.onRowChanged(element, data.oldValue, data.newValue);
}
}
private static onColumnPropertyChanged(data: PropertyChangeData): void {
var element = GridLayout.getView(data.object);
var grid = element.parent;
if (grid instanceof GridLayout) {
grid.onColumnChanged(element, data.oldValue, data.newValue);
}
}
private static onRowSpanPropertyChanged(data: PropertyChangeData): void {
var element = GridLayout.getView(data.object);
var grid = element.parent;
if (grid instanceof GridLayout) {
grid.onRowSpanChanged(element, data.oldValue, data.newValue);
}
}
private static onColumnSpanPropertyChanged(data: PropertyChangeData): void {
var element = GridLayout.getView(data.object);
var grid = element.parent;
if (grid instanceof GridLayout) {
grid.onColumnSpanChanged(element, data.oldValue, data.newValue);
}
}
private static validateItemSpec(itemSpec: ItemSpec): void {
if (!itemSpec) {
throw new Error("Value cannot be undefined.");
}
if (itemSpec.owner) {
throw new Error("itemSpec is already added to GridLayout.");
}
}
private static getView(object: Object): View {
if (object instanceof View) {
return object;
}
throw new Error("Element is not View or its descendant.");
}
private _setColumns(value: string) {
this.removeColumns();
let columns = GridLayout.parseItemSpecs(value);
for (let i = 0, count = columns.length; i < count; i++) {
this.addColumn(columns[i]);
}
parseAndAddItemSpecs(value, (spec: ItemSpec) => this.addColumn(spec));
}
private _setRows(value: string) {
this.removeRows();
let rows = GridLayout.parseItemSpecs(value);
for (let i = 0, count = rows.length; i < count; i++) {
this.addRow(rows[i]);
}
parseAndAddItemSpecs(value, (spec: ItemSpec) => this.addRow(spec));
}
}
export const columnProperty = new Property<View, number>({
name: "col", defaultValue: 0,
valueChanged: (target, oldValue, newValue) => {
const grid = target.parent;
if (grid instanceof GridLayoutBase) {
grid.onColumnChanged(target, oldValue, newValue);
}
},
valueConverter: (v) => Math.max(0, parseInt(v))
});
columnProperty.register(GridLayoutBase);
export const columnSpanProperty = new Property<View, number>({
name: "colSpan", defaultValue: 1,
valueChanged: (target, oldValue, newValue) => {
const grid = target.parent;
if (grid instanceof GridLayoutBase) {
grid.onColumnSpanChanged(target, oldValue, newValue);
}
},
valueConverter: (v) => Math.max(1, parseInt(v))
});
columnSpanProperty.register(GridLayoutBase);
export const rowProperty = new Property<View, number>({
name: "row", defaultValue: 0,
valueChanged: (target, oldValue, newValue) => {
const grid = target.parent;
if (grid instanceof GridLayoutBase) {
grid.onRowChanged(target, oldValue, newValue);
}
},
valueConverter: (v) => Math.max(0, parseInt(v))
});
rowProperty.register(GridLayoutBase);
export const rowSpanProperty = new Property<View, number>({
name: "rowSpan", defaultValue: 1,
valueChanged: (target, oldValue, newValue) => {
const grid = target.parent;
if (grid instanceof GridLayoutBase) {
grid.onRowSpanChanged(target, oldValue, newValue);
}
},
valueConverter: (v) => Math.max(1, parseInt(v))
});
rowSpanProperty.register(GridLayoutBase);

View File

@@ -1,73 +1,100 @@
import utils = require("utils/utils");
import common = require("./grid-layout-common");
import {View} from "ui/core/view";
import {PropertyMetadata} from "ui/core/proxy";
import {PropertyChangeData} from "ui/core/dependency-observable";
import {
GridLayoutBase, ItemSpec as ItemSpecBase, View, layout,
rowProperty, columnProperty, rowSpanProperty, columnSpanProperty
} from "./grid-layout-common";
global.moduleMerge(common, exports);
export * from "./grid-layout-common";
function setNativeProperty(data: PropertyChangeData, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
let view = data.object;
if (view instanceof View) {
let nativeView: android.view.View = view._nativeView;
var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
if (lp instanceof org.nativescript.widgets.CommonLayoutParams) {
setter(lp);
nativeView.setLayoutParams(lp);
}
function setNativeProperty(view: View, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
let nativeView: android.view.View = view._nativeView;
const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
if (lp instanceof org.nativescript.widgets.CommonLayoutParams) {
setter(lp);
nativeView.setLayoutParams(lp);
}
}
function setNativeRowProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.row = data.newValue; });
// define native getter and setter for rowProperty.
let rowDescriptor: TypedPropertyDescriptor<number> = {
enumerable: true,
configurable: true,
writable: true,
get: () => 0,
set: function (this: View, value: number) {
setNativeProperty(this, (lp) => lp.row = value);
}
}
function setNativeRowSpanProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.rowSpan = data.newValue; });
// define native getter and setter for columnProperty.
let colDescriptor: TypedPropertyDescriptor<number> = {
enumerable: true,
configurable: true,
writable: true,
get: () => 0,
set: function (this: View, value: number) {
setNativeProperty(this, (lp) => lp.column = value);
}
}
function setNativeColumnProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.column = data.newValue; });
// define native getter and setter for rowSpanProperty.
let rowSpanDescriptor: TypedPropertyDescriptor<number> = {
enumerable: true,
configurable: true,
writable: true,
get: () => 1,
set: function (this: View, value: number) {
setNativeProperty(this, (lp) => lp.rowSpan = value);
}
}
function setNativeColumnSpanProperty(data: PropertyChangeData) {
setNativeProperty(data, (lp) => { lp.columnSpan = data.newValue; });
// define native getter and setter for columnSpanProperty.
let colSpanDescriptor: TypedPropertyDescriptor<number> = {
enumerable: true,
configurable: true,
writable: true,
get: () => 1,
set: function (this: View, value: number) {
setNativeProperty(this, (lp) => lp.columnSpan = value);
}
}
(<PropertyMetadata>common.GridLayout.rowProperty.metadata).onSetNativeValue = setNativeRowProperty;
(<PropertyMetadata>common.GridLayout.rowSpanProperty.metadata).onSetNativeValue = setNativeRowSpanProperty;
(<PropertyMetadata>common.GridLayout.columnProperty.metadata).onSetNativeValue = setNativeColumnProperty;
(<PropertyMetadata>common.GridLayout.columnSpanProperty.metadata).onSetNativeValue = setNativeColumnSpanProperty;
// register native properties on View type.
Object.defineProperties(View, {
[rowProperty.native]: rowDescriptor,
[columnProperty.native]: colDescriptor,
[rowSpanProperty.native]: rowSpanDescriptor,
[columnSpanProperty.native]: colSpanDescriptor
});
function createNativeSpec(itemSpec: ItemSpec): org.nativescript.widgets.ItemSpec {
switch (itemSpec.gridUnitType) {
case common.GridUnitType.auto:
case "auto":
return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.auto);
case common.GridUnitType.star:
case "star":
return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.star);
case common.GridUnitType.pixel:
return new org.nativescript.widgets.ItemSpec(itemSpec.value * utils.layout.getDisplayDensity(), org.nativescript.widgets.GridUnitType.pixel);
case "pixel":
return new org.nativescript.widgets.ItemSpec(itemSpec.value * layout.getDisplayDensity(), org.nativescript.widgets.GridUnitType.pixel);
default:
throw new Error("Invalid gridUnitType: " + itemSpec.gridUnitType);
}
}
export class ItemSpec extends common.ItemSpec {
export class ItemSpec extends ItemSpecBase {
nativeSpec: org.nativescript.widgets.ItemSpec;
public get actualLength(): number {
if (this.nativeSpec) {
return Math.round(this.nativeSpec.getActualLength() / utils.layout.getDisplayDensity());
return Math.round(this.nativeSpec.getActualLength() / layout.getDisplayDensity());
}
return 0;
}
}
export class GridLayout extends common.GridLayout {
export class GridLayout extends GridLayoutBase {
private _layout: org.nativescript.widgets.GridLayout;
@@ -81,7 +108,7 @@ export class GridLayout extends common.GridLayout {
public _createUI() {
this._layout = new org.nativescript.widgets.GridLayout(this._context);
// Update native GridLayout
this.getRows().forEach((itemSpec: ItemSpec, index, rows) => { this._onRowAdded(itemSpec); }, this);
this.getColumns().forEach((itemSpec: ItemSpec, index, rows) => { this._onColumnAdded(itemSpec); }, this);
@@ -89,7 +116,7 @@ export class GridLayout extends common.GridLayout {
public _onRowAdded(itemSpec: ItemSpec) {
if (this._layout) {
var nativeSpec = createNativeSpec(itemSpec);
const nativeSpec = createNativeSpec(itemSpec);
itemSpec.nativeSpec = nativeSpec;
this._layout.addRow(nativeSpec);
}
@@ -97,7 +124,7 @@ export class GridLayout extends common.GridLayout {
public _onColumnAdded(itemSpec: ItemSpec) {
if (this._layout) {
var nativeSpec = createNativeSpec(itemSpec);
const nativeSpec = createNativeSpec(itemSpec);
itemSpec.nativeSpec = nativeSpec;
this._layout.addColumn(nativeSpec);
}
@@ -120,4 +147,4 @@ export class GridLayout extends common.GridLayout {
protected invalidate(): void {
// No need to request layout for android because it will be done in the native call.
}
}
}

View File

@@ -1,25 +1,5 @@
declare module "ui/layouts/grid-layout" {
import {LayoutBase} from "ui/layouts/layout-base";
import {View} from "ui/core/view";
import {Property} from "ui/core/dependency-observable";
/**
* GridUnitType enum is used to indicate what kind of value the ItemSpec is holding.
*/
module GridUnitType {
/**
* The value indicates that content should be calculated without constraints.
*/
export var auto: string;
/**
* The value is expressed as a pixel.
*/
export var pixel: string;
/**
* The value is expressed as a weighted proportion of available space.
*/
export var star: string;
}
import { LayoutBase, Property, View } from "ui/layouts/layout-base";
/**
* Defines row/column specific properties that apply to GridLayout elements.
@@ -27,10 +7,7 @@
export class ItemSpec {
constructor();
constructor(value: number, type: string);
constructor(value: number, type: "pixel");
constructor(value: number, type: "star");
constructor(value: number, type: "auto");
constructor(value: number, type: "pixel" | "star" | "auto");
/**
* Gets the actual length of an ItemSpec.
@@ -40,7 +17,7 @@
/**
* Returns unit type of this ItemSpec instance.
*/
gridUnitType: string;
gridUnitType: "pixel" | "star" | "auto";
/**
* Returns true if this ItemSpec instance holds
@@ -71,31 +48,6 @@
*/
export class GridLayout extends LayoutBase {
///**
// * Initializes a new instance of GridLayout.
// * @param options Options to configure this GridLayout instance.
// */
//constructor(options?: Options);
/**
* Represents the observable property backing the column property.
*/
public static columnProperty: Property;
/**
* Represents the observable property backing the columnSpan property.
*/
public static columnSpanProperty: Property;
/**
* Represents the observable property backing the row property.
*/
public static rowProperty: Property;
/**
* Represents the observable property backing the rowSpan property.
*/
public static rowSpanProperty: Property;
/**
* Gets the value of the Column attached property from a given View.
*/
@@ -183,4 +135,24 @@
public _onColumnRemoved(itemSpec: ItemSpec, index: number): void;
//@endprivate
}
}
/**
* Represents the observable property backing the column property.
*/
export const columnProperty: Property<GridLayout, number>;
/**
* Represents the observable property backing the columnSpan property.
*/
export const columnSpanProperty: Property<GridLayout, number>;
/**
* Represents the observable property backing the row property.
*/
export const rowProperty: Property<GridLayout, number>;
/**
* Represents the observable property backing the rowSpan property.
*/
export const rowSpanProperty: Property<GridLayout, number>;
}

View File

@@ -1,11 +1,11 @@
import utils = require("utils/utils");
import common = require("./grid-layout-common");
import {View} from "ui/core/view";
import {HorizontalAlignment, VerticalAlignment} from "ui/enums";
import {
GridLayoutBase, ItemSpec, View, layout,
rowProperty, columnProperty, rowSpanProperty, columnSpanProperty
} from "./grid-layout-common";
global.moduleMerge(common, exports);
export * from "./grid-layout-common";
export class GridLayout extends common.GridLayout {
export class GridLayout extends GridLayoutBase {
private helper: MeasureHelper;
private columnOffsets = new Array<number>();
private rowOffsets = new Array<number>();
@@ -16,20 +16,20 @@ export class GridLayout extends common.GridLayout {
this.helper = new MeasureHelper(this);
}
public _onRowAdded(itemSpec: common.ItemSpec) {
public _onRowAdded(itemSpec: ItemSpec) {
this.helper.rows.push(new ItemGroup(itemSpec));
}
public _onColumnAdded(itemSpec: common.ItemSpec) {
public _onColumnAdded(itemSpec: ItemSpec) {
this.helper.columns.push(new ItemGroup(itemSpec));
}
public _onRowRemoved(itemSpec: common.ItemSpec, index: number) {
public _onRowRemoved(itemSpec: ItemSpec, index: number) {
this.helper.rows[index].children.length = 0;
this.helper.rows.splice(index, 1);
}
public _onColumnRemoved(itemSpec: common.ItemSpec, index: number) {
public _onColumnRemoved(itemSpec: ItemSpec, index: number) {
this.helper.columns[index].children.length = 0;
this.helper.columns.splice(index, 1);
}
@@ -58,11 +58,11 @@ export class GridLayout extends common.GridLayout {
return Math.max(1, Math.min(GridLayout.getRowSpan(view), this.rowsInternal.length - rowIndex));
}
private getColumnSpec(view: View): common.ItemSpec {
private getColumnSpec(view: View): ItemSpec {
return this.columnsInternal[this.getColumnIndex(view)] || this.helper.singleColumn;
}
private getRowSpec(view: View): common.ItemSpec {
private getRowSpec(view: View): ItemSpec {
return this.rowsInternal[this.getRowIndex(view)] || this.helper.singleRow;
}
@@ -101,30 +101,29 @@ export class GridLayout extends common.GridLayout {
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
GridLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
let measureWidth = 0;
let measureHeight = 0;
let width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
let widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
let height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
let heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
let density = utils.layout.getDisplayDensity();
let verticalPadding = (this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density;
let horizontalPadding = (this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density;
const style = this.style;
const horizontalPaddingsAndMargins = style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderLeftWidth + style.effectiveBorderRightWidth;
const verticalPaddingsAndMargins = style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderTopWidth + style.effectiveBorderBottomWidth;
let infinityWidth = widthMode === utils.layout.UNSPECIFIED;
let infinityHeight = heightMode === utils.layout.UNSPECIFIED;
let infinityWidth = widthMode === layout.UNSPECIFIED;
let infinityHeight = heightMode === layout.UNSPECIFIED;
this.helper.width = Math.max(0, width - horizontalPadding);
this.helper.height = Math.max(0, height - verticalPadding);
this.helper.width = Math.max(0, width - horizontalPaddingsAndMargins);
this.helper.height = Math.max(0, height - verticalPaddingsAndMargins);
this.helper.stretchedHorizontally = widthMode === utils.layout.EXACTLY || (this.horizontalAlignment === HorizontalAlignment.stretch && !infinityWidth);
this.helper.stretchedVertically = heightMode === utils.layout.EXACTLY || (this.verticalAlignment === VerticalAlignment.stretch && !infinityHeight);
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);
@@ -141,15 +140,15 @@ export class GridLayout extends common.GridLayout {
this.helper.measure();
// Add in our padding
measureWidth = this.helper.measuredWidth + horizontalPadding;
measureHeight = this.helper.measuredHeight + verticalPadding;
measureWidth = this.helper.measuredWidth + horizontalPaddingsAndMargins;
measureHeight = this.helper.measuredHeight + verticalPaddingsAndMargins;
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, this.minWidth * density);
measureHeight = Math.max(measureHeight, this.minHeight * density);
measureWidth = Math.max(measureWidth, style.effectiveMinWidth);
measureHeight = Math.max(measureHeight, style.effectiveMinHeight);
let widthSizeAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
let heightSizeAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
const widthSizeAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightSizeAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@@ -157,10 +156,10 @@ export class GridLayout extends common.GridLayout {
public onLayout(left: number, top: number, right: number, bottom: number): void {
super.onLayout(left, top, right, bottom);
let density = utils.layout.getDisplayDensity();
const style = this.style;
let paddingLeft = (this.borderLeftWidth + this.paddingLeft) * density;
let paddingTop = (this.borderTopWidth + this.paddingTop) * density;
let paddingLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft;
let paddingTop = style.effectiveBorderTopWidth + style.effectivePaddingTop;
this.columnOffsets.length = 0;
this.rowOffsets.length = 0;
@@ -211,13 +210,11 @@ export class GridLayout extends common.GridLayout {
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);
}
}
GridLayout.restoreOriginalParams(this);
}
}
@@ -237,8 +234,8 @@ class MeasureSpecs {
public measured = false;
public child: View;
private column: common.ItemSpec;
private row: common.ItemSpec
private column: ItemSpec;
private row: ItemSpec
private columnIndex: number = 0;
private rowIndex: number = 0;
@@ -288,19 +285,19 @@ class MeasureSpecs {
this.columnIndex = value;
}
public getRow(): common.ItemSpec {
public getRow(): ItemSpec {
return this.row;
}
public getColumn(): common.ItemSpec {
public getColumn(): ItemSpec {
return this.column;
}
public setRow(value: common.ItemSpec): void {
public setRow(value: ItemSpec): void {
this.row = value;
}
public setColumn(value: common.ItemSpec): void {
public setColumn(value: ItemSpec): void {
this.column = value;
}
}
@@ -308,14 +305,14 @@ class MeasureSpecs {
class ItemGroup {
public length = 0;
public measuredCount = 0;
public rowOrColumn: common.ItemSpec;
public rowOrColumn: ItemSpec;
public children: Array<MeasureSpecs> = new Array<MeasureSpecs>();
public measureToFix = 0;
public currentMeasureToFixCount = 0;
private infinityLength = false;
constructor(spec: common.ItemSpec) {
constructor(spec: ItemSpec) {
this.rowOrColumn = spec;
}
@@ -351,11 +348,11 @@ class ItemGroup {
}
class MeasureHelper {
singleRow: common.ItemSpec;
singleColumn: common.ItemSpec;
singleRow: ItemSpec;
singleColumn: ItemSpec;
grid: GridLayout;
infinity: number = utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED);
infinity: number = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
rows: Array<ItemGroup> = new Array<ItemGroup>();
columns: Array<ItemGroup> = new Array<ItemGroup>();
@@ -384,8 +381,8 @@ class MeasureHelper {
constructor(grid: GridLayout) {
this.grid = grid;
this.singleRow = new common.ItemSpec();
this.singleColumn = new common.ItemSpec();
this.singleRow = new ItemSpec();
this.singleColumn = new ItemSpec();
this.singleRowGroup = new ItemGroup(this.singleRow);
this.singleColumnGroup = new ItemGroup(this.singleColumn);
}
@@ -478,7 +475,7 @@ class MeasureHelper {
}
private static initList(list: Array<ItemGroup>): void {
let density = utils.layout.getDisplayDensity();
let density = layout.getDisplayDensity();
for (let i = 0, size = list.length; i < size; i++) {
let item: ItemGroup = list[i];
item.init(density);
@@ -667,7 +664,7 @@ class MeasureHelper {
return result;
}
public measure(): void {
public measure(): void {
// Measure auto & pixel columns and rows (no spans).
let size = this.columns.length;
for (let i = 0; i < size; i++) {
@@ -763,8 +760,8 @@ class MeasureHelper {
}
private measureChild(measureSpec: MeasureSpecs, isFakeMeasure: boolean): void {
let widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelWidth, utils.layout.EXACTLY);
let heightMeasureSpec = (isFakeMeasure || measureSpec.autoRowsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelHeight, utils.layout.EXACTLY);
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(null, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
let childMeasuredWidth: number = childSize.measuredWidth;
@@ -828,8 +825,8 @@ class MeasureHelper {
measureWidth += columnGroup.length;
}
let widthMeasureSpec = utils.layout.makeMeasureSpec(measureWidth, this.stretchedHorizontally ? utils.layout.EXACTLY : utils.layout.AT_MOST);
let heightMeasureSpec = (measureSpec.autoRowsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelHeight, utils.layout.EXACTLY);
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(null, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
let childMeasuredWidth = childSize.measuredWidth;
@@ -872,8 +869,8 @@ class MeasureHelper {
measureHeight += rowGroup.length;
}
let widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelWidth, utils.layout.EXACTLY);
let heightMeasureSpec = utils.layout.makeMeasureSpec(measureHeight, this.stretchedVertically ? utils.layout.EXACTLY : utils.layout.AT_MOST);
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(null, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
let childMeasuredWidth = childSize.measuredWidth;
@@ -926,11 +923,11 @@ class MeasureHelper {
}
// if (have stars) & (not stretch) - at most
let widthMeasureSpec = utils.layout.makeMeasureSpec(measureWidth,
(measureSpec.starColumnsCount > 0 && !this.stretchedHorizontally) ? utils.layout.AT_MOST : utils.layout.EXACTLY);
let widthMeasureSpec = layout.makeMeasureSpec(measureWidth,
(measureSpec.starColumnsCount > 0 && !this.stretchedHorizontally) ? layout.AT_MOST : layout.EXACTLY);
let heightMeasureSpec = utils.layout.makeMeasureSpec(measureHeight,
(measureSpec.starRowsCount > 0 && !this.stretchedVertically) ? utils.layout.AT_MOST : utils.layout.EXACTLY);
let heightMeasureSpec = layout.makeMeasureSpec(measureHeight,
(measureSpec.starRowsCount > 0 && !this.stretchedVertically) ? layout.AT_MOST : layout.EXACTLY);
let childSize = View.measureChild(null, measureSpec.child, widthMeasureSpec, heightMeasureSpec);
let childMeasuredWidth = childSize.measuredWidth;
@@ -978,4 +975,4 @@ class MeasureHelper {
}
}
}
}
}

View File

@@ -64,20 +64,6 @@
*/
eachLayoutChild(callback: (child: View, isLast: boolean) => void): void;
/**
* Iterates over children and changes their width and height to one calculated from percentage values.
*
* @param widthMeasureSpec Width MeasureSpec of the parent layout.
* @param heightMeasureSpec Height MeasureSpec of the parent layout.
*/
protected static adjustChildrenLayoutParams(layoutBase: LayoutBase, widthMeasureSpec: number, heightMeasureSpec: number): void;
/**
* Iterates over children and restores their original dimensions that were changed for
* percentage values.
*/
protected static restoreOriginalParams(layoutBase: LayoutBase): void;
// /**
// * Gets or sets padding style property.
// */

View File

@@ -1,24 +1,20 @@
import definition = require("ui/layouts/stack-layout");
import platform = require("platform");
import {LayoutBase} from "ui/layouts/layout-base";
import {Orientation} from "ui/enums";
import {PropertyMetadata} from "ui/core/proxy";
import {Property, PropertyMetadataSettings} from "ui/core/dependency-observable";
import { StackLayout as StackLayoutDefinition } from "ui/layouts/stack-layout";
import { LayoutBase, Property, isIOS } from "ui/layouts/layout-base";
// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
export * from "ui/layouts/layout-base";
function validateOrientation(value: any): boolean {
return value === Orientation.vertical || value === Orientation.horizontal;
export class StackLayoutBase extends LayoutBase implements StackLayoutDefinition {
public orientation: "horizontal" | "vertical";
}
export class StackLayout extends LayoutBase implements definition.StackLayout {
public static orientationProperty = new Property("orientation", "StackLayout", new PropertyMetadata(Orientation.vertical, AffectsLayout, undefined, validateOrientation));
export const orientationProperty = new Property<StackLayoutBase, "horizontal" | "vertical">({
name: "orientation", defaultValue: "vertical", affectsLayout: isIOS,
valueConverter: (v) => {
if (v === "horizontal" || v === "vertical") {
return <"horizontal" | "vertical">v;
}
get orientation(): string {
return this._getValue(StackLayout.orientationProperty);
throw new Error(`Invalid orientation value: ${v}`);
}
set orientation(value: string) {
this._setValue(StackLayout.orientationProperty, value);
}
}
});
orientationProperty.register(StackLayoutBase);

View File

@@ -1,19 +1,8 @@
import common = require("./stack-layout-common");
import {Orientation} from "ui/enums";
import {PropertyMetadata} from "ui/core/proxy";
import {PropertyChangeData} from "ui/core/dependency-observable";
import { StackLayoutBase, orientationProperty } from "./stack-layout-common";
global.moduleMerge(common, exports);
export * from "./stack-layout-common";
function setNativeOrientationProperty(data: PropertyChangeData): void {
var stackLayout = <StackLayout>data.object;
var nativeView = stackLayout._nativeView;
nativeView.setOrientation(data.newValue === Orientation.vertical ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal);
}
(<PropertyMetadata>common.StackLayout.orientationProperty.metadata).onSetNativeValue = setNativeOrientationProperty;
export class StackLayout extends common.StackLayout {
export class StackLayout extends StackLayoutBase {
private _layout: org.nativescript.widgets.StackLayout;
get android(): org.nativescript.widgets.StackLayout {
@@ -27,4 +16,11 @@ export class StackLayout extends common.StackLayout {
public _createUI() {
this._layout = new org.nativescript.widgets.StackLayout(this._context);
}
get [orientationProperty.native](): "horizontal" | "vertical" {
return "vertical";
}
set [orientationProperty.native](value: "horizontal" | "vertical") {
this._layout.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal)
}
}

View File

@@ -1,20 +1,19 @@
declare module "ui/layouts/stack-layout" {
import {LayoutBase} from "ui/layouts/layout-base";
import {Property} from "ui/core/dependency-observable";
import { LayoutBase, Property } from "ui/layouts/layout-base";
/**
* A Layout that arranges its children horizontally or vertically. The direction can be set by orientation property.
*/
class StackLayout extends LayoutBase {
/**
* Represents the observable property backing the orientation property of each StackLayout instance.
*/
public static orientationProperty: Property;
/**
* Gets or sets if layout should be horizontal or vertical.
* The default value is vertical.
*/
orientation: string;
orientation: "horizontal" | "vertical";
}
/**
* Represents the observable property backing the orientation property of each StackLayout instance.
*/
export const orientationProperty: Property<StackLayout, "horizontal" | "vertical">;
}

View File

@@ -1,134 +1,128 @@
import common = require("./stack-layout-common");
import utils = require("utils/utils");
import {View} from "ui/core/view";
import {Orientation, VerticalAlignment, HorizontalAlignment} from "ui/enums";
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";
import { StackLayoutBase, orientationProperty, View, layout } from "./stack-layout-common";
global.moduleMerge(common, exports);
export * from "./stack-layout-common";
export class StackLayout extends common.StackLayout {
export class StackLayout extends StackLayoutBase {
private _totalLength = 0;
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
StackLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
var density = utils.layout.getDisplayDensity();
var measureWidth = 0;
var measureHeight = 0;
let measureWidth = 0;
let measureHeight = 0;
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
var isVertical = this.orientation === Orientation.vertical;
var verticalPadding = (this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density;
var horizontalPadding = (this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density;
const isVertical = this.orientation === "vertical";
const style = this.style;
const horizontalPaddingsAndMargins = style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderLeftWidth + style.effectiveBorderRightWidth;
const verticalPaddingsAndMargins = style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderTopWidth + style.effectiveBorderBottomWidth;
var measureSpec: number;
let measureSpec: number;
var mode = isVertical ? heightMode : widthMode;
var remainingLength: number;
let mode = isVertical ? heightMode : widthMode;
let remainingLength: number;
if (mode === utils.layout.UNSPECIFIED) {
measureSpec = utils.layout.UNSPECIFIED;
if (mode === layout.UNSPECIFIED) {
measureSpec = layout.UNSPECIFIED;
remainingLength = 0;
}
else {
measureSpec = utils.layout.AT_MOST;
remainingLength = isVertical ? height - verticalPadding : width - horizontalPadding;
measureSpec = layout.AT_MOST;
remainingLength = isVertical ? height - verticalPaddingsAndMargins : width - horizontalPaddingsAndMargins;
}
var childMeasureSpec: number;
let childMeasureSpec: number;
if (isVertical) {
let childWidth = (widthMode === utils.layout.UNSPECIFIED) ? 0 : width - horizontalPadding;
let childWidth = (widthMode === layout.UNSPECIFIED) ? 0 : width - horizontalPaddingsAndMargins;
childWidth = Math.max(0, childWidth);
childMeasureSpec = utils.layout.makeMeasureSpec(childWidth, widthMode)
childMeasureSpec = layout.makeMeasureSpec(childWidth, widthMode)
}
else {
let childHeight = (heightMode === utils.layout.UNSPECIFIED) ? 0 : height - verticalPadding;
let childHeight = (heightMode === layout.UNSPECIFIED) ? 0 : height - verticalPaddingsAndMargins;
childHeight = Math.max(0, childHeight);
childMeasureSpec = utils.layout.makeMeasureSpec(childHeight, heightMode)
childMeasureSpec = layout.makeMeasureSpec(childHeight, heightMode)
}
var childSize: { measuredWidth: number; measuredHeight: number };
let childSize: { measuredWidth: number; measuredHeight: number };
this.eachLayoutChild((child, last) => {
if (isVertical) {
childSize = View.measureChild(this, child, childMeasureSpec, utils.layout.makeMeasureSpec(remainingLength, measureSpec));
childSize = View.measureChild(this, child, childMeasureSpec, layout.makeMeasureSpec(remainingLength, measureSpec));
measureWidth = Math.max(measureWidth, childSize.measuredWidth);
var viewHeight = childSize.measuredHeight;
let viewHeight = childSize.measuredHeight;
measureHeight += viewHeight;
remainingLength = Math.max(0, remainingLength - viewHeight);
}
else {
childSize = View.measureChild(this, child, utils.layout.makeMeasureSpec(remainingLength, measureSpec), childMeasureSpec);
childSize = View.measureChild(this, child, layout.makeMeasureSpec(remainingLength, measureSpec), childMeasureSpec);
measureHeight = Math.max(measureHeight, childSize.measuredHeight);
var viewWidth = childSize.measuredWidth;
let viewWidth = childSize.measuredWidth;
measureWidth += viewWidth;
remainingLength = Math.max(0, remainingLength - viewWidth);
}
});
measureWidth += horizontalPadding;
measureHeight += verticalPadding;
measureWidth += horizontalPaddingsAndMargins;
measureHeight += verticalPaddingsAndMargins;
measureWidth = Math.max(measureWidth, this.minWidth * density);
measureHeight = Math.max(measureHeight, this.minHeight * density);
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, style.effectiveMinWidth);
measureHeight = Math.max(measureHeight, style.effectiveMinHeight);
this._totalLength = isVertical ? measureHeight : measureWidth;
var widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
var heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
public onLayout(left: number, top: number, right: number, bottom: number): void {
super.onLayout(left, top, right, bottom);
if (this.orientation === Orientation.vertical) {
if (this.orientation === "vertical") {
this.layoutVertical(left, top, right, bottom);
}
else {
this.layoutHorizontal(left, top, right, bottom);
}
StackLayout.restoreOriginalParams(this);
}
private layoutVertical(left: number, top: number, right: number, bottom: number): void {
var density = utils.layout.getDisplayDensity();
var paddingLeft = (this.borderLeftWidth + this.paddingLeft) * density;
var paddingRight = (this.borderRightWidth + this.paddingRight) * density;
var paddingTop = (this.borderTopWidth + this.paddingTop) * density;
var paddingBottom = (this.borderBottomWidth + this.paddingBottom) * density;
const style = this.style;
const paddingLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft;
const paddingTop = style.effectiveBorderTopWidth + style.effectivePaddingTop;
const paddingRight = style.effectiveBorderRightWidth + style.effectivePaddingRight;
const paddingBottom = style.effectiveBorderBottomWidth + style.effectivePaddingBottom;
var childTop: number;
var childLeft: number = paddingLeft;
var childRight = right - left - paddingRight;
let childTop: number;
let childLeft: number = paddingLeft;
let childRight = right - left - paddingRight;
switch (this.verticalAlignment) {
case VerticalAlignment.center:
case VerticalAlignment.middle:
case "center":
case "middle":
childTop = (bottom - top - this._totalLength) / 2 + paddingTop - paddingBottom;
break;
case VerticalAlignment.bottom:
case "bottom":
childTop = bottom - top - this._totalLength + paddingTop - paddingBottom;
break;
case VerticalAlignment.top:
case VerticalAlignment.stretch:
case "top":
case "stretch":
default:
childTop = paddingTop;
break;
}
this.eachLayoutChild((child, last) => {
let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);
let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density;
const childStyle = child.style;
const childHeight = child.getMeasuredHeight() + childStyle.effectiveMarginTop + childStyle.effectiveMarginBottom;
View.layoutChild(this, child, childLeft, childTop, childRight, childTop + childHeight);
childTop += childHeight;
@@ -136,38 +130,38 @@ export class StackLayout extends common.StackLayout {
}
private layoutHorizontal(left: number, top: number, right: number, bottom: number): void {
var density = utils.layout.getDisplayDensity();
var paddingLeft = (this.borderLeftWidth + this.paddingLeft) * density;
var paddingRight = (this.borderRightWidth + this.paddingRight) * density;
var paddingTop = (this.borderTopWidth + this.paddingTop) * density;
var paddingBottom = (this.borderBottomWidth + this.paddingBottom) * density;
const style = this.style;
const paddingLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft;
const paddingTop = style.effectiveBorderTopWidth + style.effectivePaddingTop;
const paddingRight = style.effectiveBorderRightWidth + style.effectivePaddingRight;
const paddingBottom = style.effectiveBorderBottomWidth + style.effectivePaddingBottom;
var childTop: number = paddingTop;
var childLeft: number;
var childBottom = bottom - top - paddingBottom;
let childTop: number = paddingTop;
let childLeft: number;
let childBottom = bottom - top - paddingBottom;
switch (this.horizontalAlignment) {
case HorizontalAlignment.center:
case "center":
childLeft = (right - left - this._totalLength) / 2 + paddingLeft - paddingRight;
break;
case HorizontalAlignment.right:
case "right":
childLeft = right - left - this._totalLength + paddingLeft - paddingRight;
break;
case HorizontalAlignment.left:
case HorizontalAlignment.stretch:
case "left":
case "stretch":
default:
childLeft = paddingLeft;
break;
}
this.eachLayoutChild((child, last) => {
let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);
let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density;
const childStyle = child.style;
const childWidth = child.getMeasuredWidth() + childStyle.effectiveMarginLeft + childStyle.effectiveMarginRight;
View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childBottom);
childLeft += childWidth;
});
}
}
}

View File

@@ -1,44 +1,36 @@
import definition = require("ui/layouts/wrap-layout");
import platform = require("platform");
import {LayoutBase} from "ui/layouts/layout-base";
import {Orientation} from "ui/enums";
import {PropertyMetadata} from "ui/core/proxy";
import {Property, PropertyMetadataSettings} from "ui/core/dependency-observable";
import { WrapLayout as WrapLayoutDefinition } from "ui/layouts/wrap-layout";
import { LayoutBase, Property, isIOS, Length, zeroLength, getLengthEffectiveValue } from "ui/layouts/layout-base";
// on Android we explicitly set propertySettings to None because android will invalidate its layout (so we skip unnecessary native call).
var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
export * from "ui/layouts/layout-base";
function isWidthHeightValid(value: any): boolean {
return (value >= 0.0 && value !== Number.POSITIVE_INFINITY);
export class WrapLayoutBase extends LayoutBase implements WrapLayoutDefinition {
public orientation: "horizontal" | "vertical";
public itemWidth: Length;
public itemHeight: Length;
public effectiveItemWidth: number;
public effectiveItemHeight: number;
}
function isValidOrientation(value: any): boolean {
return value === Orientation.vertical || value === Orientation.horizontal;
}
export const itemWidthProperty = new Property<WrapLayoutBase, Length>({
name: "itemWidth", defaultValue: zeroLength, affectsLayout: isIOS, valueConverter: (v) => Length.parse(v),
valueChanged: (target, oldValue, newValue) => target.effectiveItemWidth = getLengthEffectiveValue(newValue)
});
itemWidthProperty.register(WrapLayoutBase);
export class WrapLayout extends LayoutBase implements definition.WrapLayout {
public static orientationProperty = new Property("orientation", "WrapLayout", new PropertyMetadata(Orientation.horizontal, AffectsLayout, undefined, isValidOrientation));
public static itemWidthProperty = new Property("itemWidth", "WrapLayout", new PropertyMetadata(0, AffectsLayout, undefined, isWidthHeightValid));
public static itemHeightProperty = new Property("itemHeight", "WrapLayout", new PropertyMetadata(0, AffectsLayout, undefined, isWidthHeightValid));
export const itemHeightProperty = new Property<WrapLayoutBase, Length>({
name: "itemHeight", defaultValue: zeroLength, affectsLayout: isIOS, valueConverter: (v) => Length.parse(v),
valueChanged: (target, oldValue, newValue) => target.effectiveItemHeight = getLengthEffectiveValue(newValue)
});
itemHeightProperty.register(WrapLayoutBase);
get orientation(): string {
return this._getValue(WrapLayout.orientationProperty);
}
set orientation(value: string) {
this._setValue(WrapLayout.orientationProperty, value);
}
export const orientationProperty = new Property<WrapLayoutBase, "horizontal" | "vertical">({
name: "orientation", defaultValue: "horizontal", affectsLayout: isIOS,
valueConverter: (v) => {
if (v === "horizontal" || v === "vertical") {
return <"horizontal" | "vertical">v;
}
get itemWidth(): number {
return this._getValue(WrapLayout.itemWidthProperty);
throw new Error(`Invalid orientation value: ${v}`);
}
set itemWidth(value: number) {
this._setValue(WrapLayout.itemWidthProperty, value);
}
get itemHeight(): number {
return this._getValue(WrapLayout.itemHeightProperty);
}
set itemHeight(value: number) {
this._setValue(WrapLayout.itemHeightProperty, value);
}
}
});
orientationProperty.register(WrapLayoutBase);

View File

@@ -1,34 +1,8 @@
import utils = require("utils/utils");
import common = require("./wrap-layout-common");
import {Orientation} from "ui/enums";
import {PropertyMetadata} from "ui/core/proxy";
import {PropertyChangeData} from "ui/core/dependency-observable";
import { WrapLayoutBase, orientationProperty, itemWidthProperty, itemHeightProperty } from "./wrap-layout-common";
global.moduleMerge(common, exports);
export * from "./wrap-layout-common";
function setNativeOrientationProperty(data: PropertyChangeData): void {
var wrapLayout = <WrapLayout>data.object;
var nativeView = wrapLayout._nativeView;
nativeView.setOrientation(data.newValue === Orientation.vertical ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal);
}
function setNativeItemWidthProperty(data: PropertyChangeData): void {
var wrapLayout = <WrapLayout>data.object;
var nativeView = wrapLayout._nativeView;
nativeView.setItemWidth(data.newValue * utils.layout.getDisplayDensity());
}
function setNativeItemHeightProperty(data: PropertyChangeData): void {
var wrapLayout = <WrapLayout>data.object;
var nativeView = wrapLayout._nativeView;
nativeView.setItemHeight(data.newValue * utils.layout.getDisplayDensity());
}
(<PropertyMetadata>common.WrapLayout.orientationProperty.metadata).onSetNativeValue = setNativeOrientationProperty;
(<PropertyMetadata>common.WrapLayout.itemWidthProperty.metadata).onSetNativeValue = setNativeItemWidthProperty;
(<PropertyMetadata>common.WrapLayout.itemHeightProperty.metadata).onSetNativeValue = setNativeItemHeightProperty;
export class WrapLayout extends common.WrapLayout {
export class WrapLayout extends WrapLayoutBase {
private _layout: org.nativescript.widgets.WrapLayout;
get android(): org.nativescript.widgets.WrapLayout {
@@ -42,4 +16,25 @@ export class WrapLayout extends common.WrapLayout {
public _createUI() {
this._layout = new org.nativescript.widgets.WrapLayout(this._context);
}
get [orientationProperty.native](): "horizontal" | "vertical" {
return "vertical";
}
set [orientationProperty.native](value: "horizontal" | "vertical") {
this._layout.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal)
}
get [itemWidthProperty.native](): number {
return 0;
}
set [itemWidthProperty.native](value: number) {
this._layout.setItemWidth(this.effectiveItemWidth);
}
get [itemHeightProperty.native](): number {
return 0;
}
set [itemHeightProperty.native](value: number) {
this._layout.setItemHeight(this.effectiveItemHeight);
}
}

View File

@@ -1,6 +1,5 @@
declare module "ui/layouts/wrap-layout" {
import {LayoutBase} from "ui/layouts/layout-base";
import {Property} from "ui/core/dependency-observable";
import { LayoutBase, Property, Length } from "ui/layouts/layout-base";
/**
* WrapLayout position children in rows or columns depending on orientation property
@@ -8,37 +7,37 @@
*/
class WrapLayout extends LayoutBase {
/**
* Represents the observable property backing the orientation property of each WrapLayout instance.
*/
public static orientationProperty: Property;
/**
* Represents the observable property backing the itemWidth property of each WrapLayout instance.
*/
public static itemWidthProperty: Property;
/**
* Represents the observable property backing the itemHeight property of each WrapLayout instance.
*/
public static itemHeightProperty: Property;
/**
* Gets or sets the flow direction. Default value is horizontal.
* If orientation is horizontal items are arranged in rows, else items are arranged in columns.
*/
orientation: string;
orientation: "horizontal" | "vertical";
/**
* Gets or sets the width used to measure and layout each child.
* Default value is Number.NaN which does not restrict children.
*/
itemWidth: number;
itemWidth: Length;
/**
* Gets or sets the height used to measure and layout each child.
* Default value is Number.NaN which does not restrict children.
*/
itemHeight: number;
itemHeight: Length;
}
/**
* Represents the observable property backing the orientation property of each WrapLayout instance.
*/
export const orientationProperty: Property<WrapLayout, "horizontal" | "vertical">;
/**
* Represents the observable property backing the itemWidth property of each WrapLayout instance.
*/
export const itemWidthProperty: Property<WrapLayout, Length>;
/**
* Represents the observable property backing the itemHeight property of each WrapLayout instance.
*/
export const itemHeightProperty: Property<WrapLayout, Length>;
}

View File

@@ -1,63 +1,60 @@
import utils = require("utils/utils");
import common = require("./wrap-layout-common");
import {View} from "ui/core/view";
import {Orientation} from "ui/enums";
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";
import { WrapLayoutBase, View, orientationProperty, itemWidthProperty, itemHeightProperty, layout } from "./wrap-layout-common";
global.moduleMerge(common, exports);
export * from "./wrap-layout-common";
export class WrapLayout extends common.WrapLayout {
export class WrapLayout extends WrapLayoutBase {
private _lengths: Array<number> = new Array<number>();
private static getChildMeasureSpec(parentMode: number, parentLength: number, itemLength): number {
if (itemLength > 0) {
return utils.layout.makeMeasureSpec(itemLength, utils.layout.EXACTLY);
return layout.makeMeasureSpec(itemLength, layout.EXACTLY);
}
else if (parentMode === utils.layout.UNSPECIFIED) {
return utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED);
else if (parentMode === layout.UNSPECIFIED) {
return layout.makeMeasureSpec(0, layout.UNSPECIFIED);
}
else {
return utils.layout.makeMeasureSpec(parentLength, utils.layout.AT_MOST);
return layout.makeMeasureSpec(parentLength, layout.AT_MOST);
}
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
WrapLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
var measureWidth = 0;
var measureHeight = 0;
let measureWidth = 0;
let measureHeight = 0;
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
var density = utils.layout.getDisplayDensity();
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
var horizontalPadding = (this.borderLeftWidth + this.paddingLeft + this.paddingRight + this.borderRightWidth) * density;
var verticalPadding = (this.borderTopWidth + this.paddingTop + this.paddingBottom + this.borderBottomWidth) * density;
const style = this.style;
const horizontalPaddingsAndMargins = style.effectivePaddingLeft + style.effectivePaddingRight + style.effectiveBorderLeftWidth + style.effectiveBorderRightWidth;
const verticalPaddingsAndMargins = style.effectivePaddingTop + style.effectivePaddingBottom + style.effectiveBorderTopWidth + style.effectiveBorderBottomWidth;
var availableWidth = widthMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : utils.layout.getMeasureSpecSize(widthMeasureSpec) - horizontalPadding;
var availableHeight = heightMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : utils.layout.getMeasureSpecSize(heightMeasureSpec) - verticalPadding;
const availableWidth = widthMode === layout.UNSPECIFIED ? Number.MAX_VALUE : width - horizontalPaddingsAndMargins;
const availableHeight = heightMode === layout.UNSPECIFIED ? Number.MAX_VALUE : height - verticalPaddingsAndMargins;
var childWidthMeasureSpec: number = WrapLayout.getChildMeasureSpec(widthMode, availableWidth, this.itemWidth * density);
var childHeightMeasureSpec: number = WrapLayout.getChildMeasureSpec(heightMode, availableHeight, this.itemHeight * density);
const childWidthMeasureSpec: number = WrapLayout.getChildMeasureSpec(widthMode, availableWidth, this.effectiveItemWidth);
const childHeightMeasureSpec: number = WrapLayout.getChildMeasureSpec(heightMode, availableHeight, this.effectiveItemHeight);
var remainingWidth = availableWidth;
var remainingHeight = availableHeight;
let remainingWidth = availableWidth;
let remainingHeight = availableHeight;
this._lengths.length = 0;
var rowOrColumn = 0;
var maxLength = 0;
var isVertical = this.orientation === Orientation.vertical;
var isVertical = this.orientation === "vertical";
let useItemWidth: boolean = this.itemWidth > 0;
let useItemHeight: boolean = this.itemHeight > 0;
let itemWidth = this.itemWidth;
let itemHeight = this.itemHeight;
let useItemWidth: boolean = this.effectiveItemWidth > 0;
let useItemHeight: boolean = this.effectiveItemHeight > 0;
let itemWidth = this.effectiveItemWidth;
let itemHeight = this.effectiveItemHeight;
this.eachLayoutChild((child, last) => {
var desiredSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec);
const desiredSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec);
let childMeasuredWidth = useItemWidth ? itemWidth : desiredSize.measuredWidth;
let childMeasuredHeight = useItemHeight ? itemHeight : desiredSize.measuredHeight;
let isFirst = this._lengths.length <= rowOrColumn;
@@ -110,14 +107,15 @@ export class WrapLayout extends common.WrapLayout {
});
}
measureWidth += horizontalPadding;
measureHeight += verticalPadding;
measureWidth += horizontalPaddingsAndMargins;
measureHeight += verticalPaddingsAndMargins;
measureWidth = Math.max(measureWidth, this.minWidth * density);
measureHeight = Math.max(measureHeight, this.minHeight * density);
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, style.effectiveMinWidth);
measureHeight = Math.max(measureHeight, style.effectiveMinHeight);
var widthAndState = View.resolveSizeAndState(measureWidth, utils.layout.getMeasureSpecSize(widthMeasureSpec), widthMode, 0);
var heightAndState = View.resolveSizeAndState(measureHeight, utils.layout.getMeasureSpecSize(heightMeasureSpec), heightMode, 0);
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
@@ -125,45 +123,42 @@ export class WrapLayout extends common.WrapLayout {
public onLayout(left: number, top: number, right: number, bottom: number): void {
super.onLayout(left, top, right, bottom);
var isVertical = this.orientation === Orientation.vertical;
const isVertical = this.orientation === "vertical";
const style = this.style;
const paddingLeft = style.effectiveBorderLeftWidth + style.effectivePaddingLeft;
const paddingTop = style.effectiveBorderTopWidth + style.effectivePaddingTop;
const paddingRight = style.effectiveBorderRightWidth + style.effectivePaddingRight;
const paddingBottom = style.effectiveBorderBottomWidth + style.effectivePaddingBottom;
var density = utils.layout.getDisplayDensity();
const topPadding = (this.borderTopWidth + this.paddingTop) * density;
const leftPadding = (this.borderLeftWidth + this.paddingLeft) * density;
const bottomPadding = (this.paddingBottom + this.borderBottomWidth) * density;
const rightPadding = (this.paddingRight + this.borderRightWidth) * density;
var childLeft = leftPadding;
var childTop = topPadding;
var childrenLength: number;
let childLeft = paddingLeft;
let childTop = paddingTop;
let childrenLength: number;
if (isVertical) {
childrenLength = bottom - top - bottomPadding;
childrenLength = bottom - top - paddingBottom;
}
else {
childrenLength = right - left - rightPadding;
childrenLength = right - left - paddingRight;
}
var rowOrColumn = 0;
this.eachLayoutChild((child, last) => {
// Add margins because layoutChild will sustract them.
// * density converts them to device pixels.
let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty);
let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density;
let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density;
const childStyle = child.style;
let childHeight = child.getMeasuredHeight() + childStyle.effectiveMarginTop + childStyle.effectiveMarginBottom;
let childWidth = child.getMeasuredWidth() + childStyle.effectiveMarginLeft + childStyle.effectiveMarginRight;
let length = this._lengths[rowOrColumn];
if (isVertical) {
childWidth = length;
childHeight = this.itemHeight > 0 ? this.itemHeight * density : childHeight;
let isFirst = childTop === topPadding;
childHeight = this.effectiveItemHeight > 0 ? this.effectiveItemHeight : childHeight;
let isFirst = childTop === paddingTop;
if (childTop + childHeight > childrenLength) {
// Move to top.
childTop = topPadding;
childTop = paddingTop;
if (!isFirst) {
// Move to right with current column width.
// Move to right with current column width.
childLeft += length;
}
@@ -175,12 +170,12 @@ export class WrapLayout extends common.WrapLayout {
}
}
else {
childWidth = this.itemWidth > 0 ? this.itemWidth * density : childWidth;
childWidth = this.effectiveItemWidth > 0 ? this.effectiveItemWidth : childWidth;
childHeight = length;
let isFirst = childLeft === leftPadding;
let isFirst = childLeft === paddingLeft;
if (childLeft + childWidth > childrenLength) {
// Move to left.
childLeft = leftPadding;
childLeft = paddingLeft;
if (!isFirst) {
// Move to bottom with current row height.
@@ -206,7 +201,5 @@ export class WrapLayout extends common.WrapLayout {
childLeft += childWidth;
}
});
WrapLayout.restoreOriginalParams(this);
}
}

View File

@@ -3,56 +3,6 @@
export * from "./utils-common";
export module layout {
var density = -1;
var metrics: android.util.DisplayMetrics;
// cache the MeasureSpec constants here, to prevent extensive marshaling calls to and from Java
// TODO: While this boosts the performance it is error-prone in case Google changes these constants
var MODE_SHIFT = 30;
var MODE_MASK = 0x3 << MODE_SHIFT;
var sdkVersion = -1;
var useOldMeasureSpec = false;
export function makeMeasureSpec(size: number, mode: number): number {
if (sdkVersion === -1) {
// check whether the old layout is needed
sdkVersion = ad.getApplicationContext().getApplicationInfo().targetSdkVersion;
useOldMeasureSpec = sdkVersion <= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
}
if (useOldMeasureSpec) {
return size + mode;
}
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
export function getDisplayMetrics(): android.util.DisplayMetrics {
if (!metrics) {
metrics = ad.getApplicationContext().getResources().getDisplayMetrics();
}
return metrics;
}
export function getDisplayDensity(): number {
if (density === -1) {
density = getDisplayMetrics().density;
}
return density;
}
export function toDevicePixels(value: number): number {
return value * getDisplayDensity();
}
export function toDeviceIndependentPixels(value: number): number {
return value / getDisplayDensity();
}
}
// We are using "ad" here to avoid namespace collision with the global android object
export module ad {