diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 8ac0fcb0a..17e30383e 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -527,6 +527,62 @@
+
+
+ absolute-layout.d.ts
+
+
+ absolute-layout.d.ts
+
+
+ absolute-layout.d.ts
+
+
+ dock-layout.d.ts
+
+
+ dock-layout.d.ts
+
+
+ dock-layout.d.ts
+
+
+ grid-layout.d.ts
+
+
+ grid-layout.d.ts
+
+
+ grid-layout.d.ts
+
+
+ layout.d.ts
+
+
+ layout.d.ts
+
+
+ layout-base.d.ts
+
+
+
+ stack-layout.d.ts
+
+
+ stack-layout.d.ts
+
+
+ stack-layout.d.ts
+
+
+ wrap-layout.d.ts
+
+
+ wrap-layout.d.ts
+
+
+ wrap-layout.d.ts
+
repeater.d.ts
@@ -588,26 +644,11 @@
image-cache.d.ts
-
- absolute-layout.d.ts
-
-
- dock-layout.d.ts
-
-
- grid-layout.d.ts
-
-
- layout.d.ts
-
-
- stack-layout.d.ts
-
wrap-layout.d.ts
diff --git a/apps/tests/pages/page9.ts b/apps/tests/pages/page9.ts
index 19babcf96..80cdf5741 100644
--- a/apps/tests/pages/page9.ts
+++ b/apps/tests/pages/page9.ts
@@ -3,12 +3,9 @@ import slider = require("ui/slider");
import imageSource = require("image-source");
import gridModule = require("ui/layouts/grid-layout");
import enums = require("ui/enums");
-
+import img = require("ui/image");
+var Image = img.Image;
export function createPage() {
- var StackLayout = require("ui/layouts/stack-layout").StackLayout;
- var Image = require("ui/image").Image;
-
- var stack = new StackLayout();
var grid = new gridModule.GridLayout();
grid.addColumn(new gridModule.ItemSpec(1, gridModule.GridUnitType.auto));
@@ -17,24 +14,16 @@ export function createPage() {
grid.addRow(new gridModule.ItemSpec(1, gridModule.GridUnitType.auto));
grid.addRow(new gridModule.ItemSpec(1, gridModule.GridUnitType.star));
- var sldr = new slider.Slider();
- gridModule.GridLayout.setColumnSpan(sldr, 2);
- sldr.maxValue = 500;
-
- stack.addChild(sldr);
- stack.addChild(grid);
-
var image = new Image();
image.stretch = enums.Stretch.fill;
- image.verticalAlignment = 2;
- image.horizontalAlignment = 1;
+ image.verticalAlignment = enums.VerticalAlignment.bottom;
+ image.horizontalAlignment = enums.HorizontalAlignment.center;
- image.source = imageSource.fromFile(__dirname + "test.png");
+ image.imageSource = imageSource.fromFile(__dirname + "/test.png");
grid.addChild(image);
var page = new pages.Page();
- page.content = stack;
+ page.content = grid;
page.css = "GridLayout { background-color: pink } image { background-color: green }";
return page;
-}
-//export var Page = page;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/apps/ui-tests-app/layouts/myview.ts b/apps/ui-tests-app/layouts/myview.ts
index 9e434a7d9..9c159f087 100644
--- a/apps/ui-tests-app/layouts/myview.ts
+++ b/apps/ui-tests-app/layouts/myview.ts
@@ -1,7 +1,7 @@
import observable = require("data/observable");
import enums = require("ui/enums");
import view = require("ui/core/view");
-import layouts = require("ui/layouts/layout");
+import layouts = require("ui/layouts/layout-base");
export class ViewModel extends observable.Observable {
@@ -67,7 +67,7 @@ export class ViewModel extends observable.Observable {
public onVisibile(args: { eventName: string, object: any }): void {
var view: view.View = args.object;
- var layout = view.parent;
+ var layout = view.parent;
var child = layout.getViewById("collapse");
child.visibility = enums.Visibility.visible;
diff --git a/ui/content-view/content-view.ts b/ui/content-view/content-view.ts
index d8ec56e29..53ceae932 100644
--- a/ui/content-view/content-view.ts
+++ b/ui/content-view/content-view.ts
@@ -47,6 +47,7 @@ export class ContentView extends view.CustomLayoutView implements definition.Con
}
}
+ // This method won't be called in Android because we use the native android layout.
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
var result = view.View.measureChild(this, this.content, widthMeasureSpec, heightMeasureSpec);
@@ -66,6 +67,7 @@ export class ContentView extends view.CustomLayoutView implements definition.Con
this.setMeasuredDimension(widthAndState, heightAndState);
}
+ // This method won't be called in Android because we use the native android layout.
public onLayout(left: number, top: number, right: number, bottom: number): void {
view.View.layoutChild(this, this.content, 0, 0, right - left, bottom - top);
}
diff --git a/ui/core/dependency-observable.ts b/ui/core/dependency-observable.ts
index 22e50c72c..ef8d13b12 100644
--- a/ui/core/dependency-observable.ts
+++ b/ui/core/dependency-observable.ts
@@ -68,7 +68,7 @@ export class PropertyMetadata implements definition.PropertyMetadata {
equalityComparer?: definition.PropertyEqualityComparer) {
this._defaultValue = defaultValue;
this._options = options;
- if (types.isUndefined(this._options)) {
+ if (types.isNullOrUndefined(this._options)) {
this._options = PropertyMetadataSettings.None;
}
this._onChanged = onChanged;
diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts
index e79f02477..bc83e1d43 100644
--- a/ui/core/view-common.ts
+++ b/ui/core/view-common.ts
@@ -128,6 +128,7 @@ export class View extends proxy.ProxyObject implements definition.View {
private _requestedVisualState: string;
private _isLoaded: boolean;
private _isLayoutValid: boolean = false;
+
public _domId: number;
public _isAddedToNativeVisualTree = false;
@@ -293,41 +294,6 @@ export class View extends proxy.ProxyObject implements definition.View {
this.style.marginBottom = value;
}
- get padding(): string {
- return this.style.padding;
- }
- set padding(value: string) {
- this.style.padding = value;
- }
-
- get paddingLeft(): number {
- return this.style.paddingLeft;
- }
- set paddingLeft(value: number) {
- this.style.paddingLeft = value;
- }
-
- get paddingTop(): number {
- return this.style.paddingTop;
- }
- set paddingTop(value: number) {
- this.style.paddingTop = value;
- }
-
- get paddingRight(): number {
- return this.style.paddingRight;
- }
- set paddingRight(value: number) {
- this.style.paddingRight = value;
- }
-
- get paddingBottom(): number {
- return this.style.paddingBottom;
- }
- set paddingBottom(value: number) {
- this.style.paddingBottom = value;
- }
-
get horizontalAlignment(): string {
return this.style.horizontalAlignment;
}
@@ -510,11 +476,11 @@ export class View extends proxy.ProxyObject implements definition.View {
}
public getMeasuredWidth(): number {
- return this._measuredWidth;
+ return this._measuredWidth & utils.layout.MEASURED_SIZE_MASK;
}
public getMeasuredHeight(): number {
- return this._measuredHeight;
+ return this._measuredHeight & utils.layout.MEASURED_SIZE_MASK;
}
public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void {
diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts
index f745ad301..94e30ade3 100644
--- a/ui/core/view.android.ts
+++ b/ui/core/view.android.ts
@@ -32,16 +32,13 @@ function onIsUserInteractionEnabledPropertyChanged(data: dependencyObservable.Pr
(viewCommon.View.isUserInteractionEnabledProperty.metadata).onSetNativeValue = onIsUserInteractionEnabledPropertyChanged;
export var NativeViewGroup = (android.view.ViewGroup).extend({
- get owner() {
- return this[OWNER];
- },
onMeasure: function (widthMeasureSpec, heightMeasureSpec) {
- var owner: viewDefinition.View = this.owner;
+ var owner: viewDefinition.View = this[OWNER];
owner.onMeasure(widthMeasureSpec, heightMeasureSpec);
this.setMeasuredDimension(owner.getMeasuredWidth(), owner.getMeasuredHeight());
},
onLayout: function (changed: boolean, left: number, top: number, right: number, bottom: number): void {
- var owner: viewDefinition.View = this.owner;
+ var owner: viewDefinition.View = this[OWNER];
owner.onLayout(left, top, right, bottom);
}
});
@@ -239,6 +236,14 @@ export class View extends viewCommon.View {
return this.android;
}
+ get isLayoutValid(): boolean {
+ if (this._nativeView) {
+ return !this._nativeView.isLayoutRequested();
+ }
+
+ return false;
+ }
+
public layoutNativeView(left: number, top: number, right: number, bottom: number): void {
if (this._nativeView) {
this._nativeView.layout(left, top, right, bottom);
@@ -287,8 +292,8 @@ export class View extends viewCommon.View {
}
public focus(): boolean {
- if (this.android) {
- return this.android.requestFocus();
+ if (this._nativeView) {
+ return this._nativeView.requestFocus();
}
return false;
@@ -328,15 +333,9 @@ export class CustomLayoutView extends View implements viewDefinition.CustomLayou
}
public _createUI() {
- this._viewGroup = new NativeViewGroup(this._context);
- this._viewGroup[OWNER] = this;
+ this._viewGroup = new org.nativescript.widgets.ContentLayout(this._context);
}
- //public _onDetached(force?: boolean) {
- // delete this._viewGroup[OWNER];
- // super._onDetached(force);
- //}
-
public _addViewToNativeVisualTree(child: View): boolean {
super._addViewToNativeVisualTree(child);
@@ -356,46 +355,4 @@ export class CustomLayoutView extends View implements viewDefinition.CustomLayou
trace.notifyEvent(child, "childInLayoutRemovedFromNativeVisualTree");
}
}
-
- public measure(widthMeasureSpec: number, heightMeasureSpec: number): void {
- this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
-
- var view = this._nativeView;
- if (view) {
- var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
- var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
-
- var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
- var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
-
- trace.write(this + " :measure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
- view.measure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- public layout(left: number, top: number, right: number, bottom: number): void {
- this._setCurrentLayoutBounds(left, top, right, bottom);
-
- var view = this._nativeView;
- if (view) {
- this.layoutNativeView(left, top, right, bottom);
- trace.write(this + " :layout: " + left + ", " + top + ", " + (right - left) + ", " + (bottom - top), trace.categories.Layout);
- }
- }
-
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
- // Don't call super because it will trigger measure again.
-
- var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
- var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
-
- var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
- var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
- trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
- }
-
- public onLayout(left: number, top: number, right: number, bottom: number): void {
- // Don't call super because it will trigger layout again.
- trace.write(this + " :onLayout: " + left + ", " + top + ", " + (right - left) + ", " + (bottom - top), trace.categories.Layout);
- }
}
\ No newline at end of file
diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts
index 94ca5efce..c4772c36c 100644
--- a/ui/core/view.d.ts
+++ b/ui/core/view.d.ts
@@ -205,31 +205,6 @@ declare module "ui/core/view" {
*/
marginBottom: number;
- /**
- * Gets or sets padding style property.
- */
- padding: string;
-
- /**
- * Specify the left padding of this view.
- */
- paddingLeft: number;
-
- /**
- * Specify the top padding of this view.
- */
- paddingTop: number;
-
- /**
- * Specify the right padding of this view.
- */
- paddingRight: number;
-
- /**
- * Specify the bottom padding of this view.
- */
- paddingBottom: number;
-
/**
* Gets or sets the alignment of this view within its parent along the Horizontal axis.
*/
diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts
index 5f22fa455..b926fc91f 100644
--- a/ui/core/view.ios.ts
+++ b/ui/core/view.ios.ts
@@ -85,13 +85,18 @@ export class View extends viewCommon.View {
// For UILabel and UIImage.
view.userInteractionEnabled = true;
}
+
+ get isLayoutRequested(): boolean {
+ return (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
+ }
public requestLayout(): void {
super.requestLayout();
this._privateFlags |= PFLAG_FORCE_LAYOUT;
- if (this.parent) {
- this.parent.requestLayout();
+ var parent = this.parent;
+ if (parent && !parent.isLayoutRequested) {
+ parent.requestLayout();
}
}
@@ -236,7 +241,7 @@ export class CustomLayoutView extends View {
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
- // Don't call super because it will trigger measure again.
+ // Don't call super because it will set MeasureDimension. This method must be overriden and calculate its measuredDimensions.
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
diff --git a/ui/frame/frame-common.ts b/ui/frame/frame-common.ts
index eef4bfff3..9d9c494c7 100644
--- a/ui/frame/frame-common.ts
+++ b/ui/frame/frame-common.ts
@@ -327,25 +327,6 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
return 0;
}
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
- var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
- var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
-
- var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
- var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
-
- var result = view.View.measureChild(this, this.currentPage, widthMeasureSpec, utils.layout.makeMeasureSpec(height - this.navigationBarHeight, heightMode));
-
- var widthAndState = view.View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
- var heightAndState = view.View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
-
- this.setMeasuredDimension(widthAndState, heightAndState);
- }
-
- public onLayout(left: number, top: number, right: number, bottom: number): void {
- view.View.layoutChild(this, this.currentPage, 0, this.navigationBarHeight, right - left, bottom - top);
- }
-
// We don't need to put Page as visual child. Don't call super.
public _addViewToNativeVisualTree(child: view.View): boolean {
return true;
diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts
index 782de2f24..e0e8f7c01 100644
--- a/ui/frame/frame.android.ts
+++ b/ui/frame/frame.android.ts
@@ -373,8 +373,7 @@ var NativeActivity = {
this.androidFrame.setActivity(this);
// Create and set content container.
- var root = new view.NativeViewGroup(this);
- root[OWNER] = this.frame;
+ var root = new org.nativescript.widgets.ContentLayout(this);
this.androidFrame.rootViewGroup = root;
this.androidFrame.rootViewGroup.setId(this.frame.containerViewId);
diff --git a/ui/image/image-common.ts b/ui/image/image-common.ts
index 233d36265..096a44617 100644
--- a/ui/image/image-common.ts
+++ b/ui/image/image-common.ts
@@ -3,9 +3,7 @@ import view = require("ui/core/view");
import proxy = require("ui/core/proxy");
import imageSource = require("image-source");
import definition = require("ui/image");
-import trace = require("trace");
import enums = require("ui/enums");
-import utils = require("utils/utils");
import types = require("utils/types");
var SRC = "src";
@@ -49,43 +47,18 @@ function onSrcPropertyChanged(data: dependencyObservable.PropertyChangeData) {
export class Image extends view.View implements definition.Image {
- public static srcProperty = new dependencyObservable.Property(
- SRC,
- IMAGE,
- new proxy.PropertyMetadata(
- undefined,
- dependencyObservable.PropertyMetadataSettings.None,
- onSrcPropertyChanged
- )
- );
+ public static srcProperty = new dependencyObservable.Property(SRC, IMAGE,
+ new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None, onSrcPropertyChanged));
- public static imageSourceProperty = new dependencyObservable.Property(
- IMAGE_SOURCE,
- IMAGE,
- new proxy.PropertyMetadata(
- undefined,
- // None on purpose. for iOS we trigger it manually if needed. Android layout handles it.
- dependencyObservable.PropertyMetadataSettings.None
- )
- );
+ // None on purpose. for iOS we trigger it manually if needed. Android layout handles it.
+ public static imageSourceProperty = new dependencyObservable.Property(IMAGE_SOURCE, IMAGE,
+ new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None));
- public static isLoadingProperty = new dependencyObservable.Property(
- ISLOADING,
- IMAGE,
- new proxy.PropertyMetadata(
- false,
- dependencyObservable.PropertyMetadataSettings.None
- )
- );
+ public static isLoadingProperty = new dependencyObservable.Property(ISLOADING, IMAGE,
+ new proxy.PropertyMetadata(false, dependencyObservable.PropertyMetadataSettings.None));
- public static stretchProperty = new dependencyObservable.Property(
- STRETCH,
- IMAGE,
- new proxy.PropertyMetadata(
- enums.Stretch.aspectFit,
- dependencyObservable.PropertyMetadataSettings.AffectsLayout
- )
- );
+ public static stretchProperty = new dependencyObservable.Property(STRETCH, IMAGE,
+ new proxy.PropertyMetadata(enums.Stretch.aspectFit, dependencyObservable.PropertyMetadataSettings.AffectsLayout));
constructor(options?: definition.Options) {
super(options);
@@ -119,75 +92,4 @@ export class Image extends view.View implements definition.Image {
public _setNativeImage(nativeImage: any) {
//
}
-
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
-
- // We don't call super because we measure native view with specific size.
- var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
- var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
-
- var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
- var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
- trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
-
- var nativeWidth = this.imageSource ? this.imageSource.width : 0;
- var nativeHeight = this.imageSource ? this.imageSource.height : 0;
-
- var measureWidth = Math.max(nativeWidth, this.minWidth);
- var measureHeight = Math.max(nativeHeight, this.minHeight);
-
- var finiteWidth: boolean = widthMode !== utils.layout.UNSPECIFIED;
- var finiteHeight: boolean = heightMode !== utils.layout.UNSPECIFIED;
-
- if (nativeWidth !== 0 && nativeHeight !== 0 && (finiteWidth || finiteHeight)) {
- var scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch);
- var resultW = Math.floor(nativeWidth * scale.width);
- var resultH = Math.floor(nativeHeight * scale.height);
-
- measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
- measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
-
- trace.write("Image stretch: " + this.stretch +
- ", nativeWidth: " + nativeWidth +
- ", nativeHeight: " + nativeHeight, trace.categories.Layout);
- }
-
- var widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0);
- var heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0);
-
- this.setMeasuredDimension(widthAndState, heightAndState);
- }
-
- private static computeScaleFactor(measureWidth: number, measureHeight: number, widthIsFinite: boolean, heightIsFinite: boolean, nativeWidth: number, nativeHeight: number, imageStretch: string): { width: number; height: number } {
- var scaleW = 1;
- var scaleH = 1;
-
- if ((imageStretch === enums.Stretch.aspectFill || imageStretch === enums.Stretch.aspectFit || imageStretch === enums.Stretch.fill) &&
- (widthIsFinite || heightIsFinite)) {
-
- scaleW = (nativeWidth > 0) ? measureWidth / nativeWidth : 0;
- scaleH = (nativeHeight > 0) ? measureHeight / nativeHeight : 0;
-
- if (!widthIsFinite) {
- scaleW = scaleH;
- }
- else if (!heightIsFinite) {
- scaleH = scaleW;
- }
- else {
- // No infinite dimensions.
- switch (imageStretch) {
- case enums.Stretch.aspectFit:
- scaleH = scaleW < scaleH ? scaleW : scaleH;
- scaleW = scaleH;
- break;
- case enums.Stretch.aspectFill:
- scaleH = scaleW > scaleH ? scaleW : scaleH;
- scaleW = scaleH;
- break;
- }
- }
- }
- return { width: scaleW, height: scaleH };
- }
}
diff --git a/ui/image/image.android.ts b/ui/image/image.android.ts
index 6bb66de94..a3faa8a8b 100644
--- a/ui/image/image.android.ts
+++ b/ui/image/image.android.ts
@@ -42,14 +42,14 @@ function onImageSourcePropertyChanged(data: dependencyObservable.PropertyChangeD
(imageCommon.Image.stretchProperty.metadata).onSetNativeValue = onStretchPropertyChanged;
export class Image extends imageCommon.Image {
- private _android: android.widget.ImageView;
+ private _android: org.nativescript.widgets.ImageView;
- get android(): android.widget.ImageView {
+ get android(): org.nativescript.widgets.ImageView {
return this._android;
}
public _createUI() {
- this._android = new android.widget.ImageView(this._context);
+ this._android = new org.nativescript.widgets.ImageView(this._context);
}
public _setNativeImage(nativeImage: any) {
diff --git a/ui/image/image.ios.ts b/ui/image/image.ios.ts
index c1a5f5cde..54bb91752 100644
--- a/ui/image/image.ios.ts
+++ b/ui/image/image.ios.ts
@@ -3,6 +3,9 @@ import dependencyObservable = require("ui/core/dependency-observable");
import proxy = require("ui/core/proxy");
import definition = require("ui/image");
import enums = require("ui/enums");
+import utils = require("utils/utils");
+import trace = require("trace");
+import view = require("ui/core/view");
global.moduleMerge(imageCommon, exports);
@@ -59,4 +62,75 @@ export class Image extends imageCommon.Image {
this.requestLayout();
}
}
+
+ public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
+
+ // We don't call super because we measure native view with specific size.
+ var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
+ var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
+
+ var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
+ var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
+ trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
+
+ var nativeWidth = this.imageSource ? this.imageSource.width : 0;
+ var nativeHeight = this.imageSource ? this.imageSource.height : 0;
+
+ var measureWidth = Math.max(nativeWidth, this.minWidth);
+ var measureHeight = Math.max(nativeHeight, this.minHeight);
+
+ var finiteWidth: boolean = widthMode !== utils.layout.UNSPECIFIED;
+ var finiteHeight: boolean = heightMode !== utils.layout.UNSPECIFIED;
+
+ if (nativeWidth !== 0 && nativeHeight !== 0 && (finiteWidth || finiteHeight)) {
+ var scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch);
+ var resultW = Math.floor(nativeWidth * scale.width);
+ var resultH = Math.floor(nativeHeight * scale.height);
+
+ measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
+ measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
+
+ trace.write("Image stretch: " + this.stretch +
+ ", nativeWidth: " + nativeWidth +
+ ", nativeHeight: " + nativeHeight, trace.categories.Layout);
+ }
+
+ var widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0);
+ var heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0);
+
+ this.setMeasuredDimension(widthAndState, heightAndState);
+ }
+
+ private static computeScaleFactor(measureWidth: number, measureHeight: number, widthIsFinite: boolean, heightIsFinite: boolean, nativeWidth: number, nativeHeight: number, imageStretch: string): { width: number; height: number } {
+ var scaleW = 1;
+ var scaleH = 1;
+
+ if ((imageStretch === enums.Stretch.aspectFill || imageStretch === enums.Stretch.aspectFit || imageStretch === enums.Stretch.fill) &&
+ (widthIsFinite || heightIsFinite)) {
+
+ scaleW = (nativeWidth > 0) ? measureWidth / nativeWidth : 0;
+ scaleH = (nativeHeight > 0) ? measureHeight / nativeHeight : 0;
+
+ if (!widthIsFinite) {
+ scaleW = scaleH;
+ }
+ else if (!heightIsFinite) {
+ scaleH = scaleW;
+ }
+ else {
+ // No infinite dimensions.
+ switch (imageStretch) {
+ case enums.Stretch.aspectFit:
+ scaleH = scaleW < scaleH ? scaleW : scaleH;
+ scaleW = scaleH;
+ break;
+ case enums.Stretch.aspectFill:
+ scaleH = scaleW > scaleH ? scaleW : scaleH;
+ scaleW = scaleH;
+ break;
+ }
+ }
+ }
+ return { width: scaleW, height: scaleH };
+ }
}
\ No newline at end of file
diff --git a/ui/layouts/absolute-layout/absolute-layout-common.ts b/ui/layouts/absolute-layout/absolute-layout-common.ts
new file mode 100644
index 000000000..cbbb89b18
--- /dev/null
+++ b/ui/layouts/absolute-layout/absolute-layout-common.ts
@@ -0,0 +1,69 @@
+import layouts = require("ui/layouts/layout-base");
+import definition = require("ui/layouts/absolute-layout");
+import dependencyObservable = require("ui/core/dependency-observable");
+import view = require("ui/core/view");
+import proxy = require("ui/core/proxy");
+
+function validateArgs(element: view.View): view.View {
+ if (!element) {
+ throw new Error("element cannot be null or undefinied.");
+ }
+ return element;
+}
+
+export class AbsoluteLayout extends layouts.LayoutBase implements definition.AbsoluteLayout {
+
+ private static isValid(value: number): boolean {
+ return isFinite(value);
+ }
+
+ private static onLeftPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var layout = uiView.parent;
+ if (layout instanceof AbsoluteLayout) {
+ layout.onLeftChanged(uiView, data.oldValue, data.newValue);
+ }
+ }
+ }
+
+ private static onTopPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var layout = uiView.parent;
+ if (layout instanceof AbsoluteLayout) {
+ layout.onTopChanged(uiView, data.oldValue, data.newValue);
+ }
+ }
+ }
+
+ public static leftProperty = new dependencyObservable.Property("left", "AbsoluteLayout",
+ new proxy.PropertyMetadata(0, undefined, AbsoluteLayout.onLeftPropertyChanged, AbsoluteLayout.isValid));
+
+ public static topProperty = new dependencyObservable.Property("top", "AbsoluteLayout",
+ new proxy.PropertyMetadata(0, undefined, AbsoluteLayout.onTopPropertyChanged, AbsoluteLayout.isValid));
+
+ public static getLeft(element: view.View): number {
+ return validateArgs(element)._getValue(AbsoluteLayout.leftProperty);
+ }
+
+ public static setLeft(element: view.View, value: number): void {
+ validateArgs(element)._setValue(AbsoluteLayout.leftProperty, value);
+ }
+
+ public static getTop(element: view.View): number {
+ return validateArgs(element)._getValue(AbsoluteLayout.topProperty);
+ }
+
+ public static setTop(element: view.View, value: number): void {
+ validateArgs(element)._setValue(AbsoluteLayout.topProperty, value);
+ }
+
+ protected onLeftChanged(view: view.View, oldValue: number, newValue: number) {
+ //
+ }
+
+ protected onTopChanged(view: view.View, oldValue: number, newValue: number) {
+ //
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/absolute-layout/absolute-layout.android.ts b/ui/layouts/absolute-layout/absolute-layout.android.ts
new file mode 100644
index 000000000..4dff79878
--- /dev/null
+++ b/ui/layouts/absolute-layout/absolute-layout.android.ts
@@ -0,0 +1,52 @@
+import utils = require("utils/utils");
+import view = require("ui/core/view");
+import common = require("ui/layouts/absolute-layout/absolute-layout-common");
+import dependencyObservable = require("ui/core/dependency-observable");
+import proxy = require("ui/core/proxy");
+
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
+
+function setNativeProperty(data: dependencyObservable.PropertyChangeData, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
+
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var nativeView: android.view.View = uiView._nativeView;
+
+ var lp = nativeView.getLayoutParams();
+ if (!(lp instanceof org.nativescript.widgets.CommonLayoutParams)) {
+ lp = new org.nativescript.widgets.CommonLayoutParams();
+ }
+ setter(lp);
+ nativeView.setLayoutParams(lp);
+ }
+}
+
+function setNativeLeftProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.left = data.newValue * utils.layout.getDisplayDensity(); });
+}
+
+function setNativeTopProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.top = data.newValue * utils.layout.getDisplayDensity(); });
+}
+
+(common.AbsoluteLayout.leftProperty.metadata).onSetNativeValue = setNativeLeftProperty;
+(common.AbsoluteLayout.topProperty.metadata).onSetNativeValue = setNativeTopProperty;
+
+export class AbsoluteLayout extends common.AbsoluteLayout {
+
+ private _layout: org.nativescript.widgets.AbsoluteLayout;
+
+ get android(): org.nativescript.widgets.AbsoluteLayout {
+ return this._layout;
+ }
+
+ get _nativeView(): org.nativescript.widgets.AbsoluteLayout {
+ return this._layout;
+ }
+
+ public _createUI() {
+ this._layout = new org.nativescript.widgets.AbsoluteLayout(this._context);
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/absolute-layout/absolute-layout.d.ts b/ui/layouts/absolute-layout/absolute-layout.d.ts
index 541950f18..401c9a2f1 100644
--- a/ui/layouts/absolute-layout/absolute-layout.d.ts
+++ b/ui/layouts/absolute-layout/absolute-layout.d.ts
@@ -1,12 +1,12 @@
declare module "ui/layouts/absolute-layout" {
- import layout = require("ui/layouts/layout");
+ import layout = require("ui/layouts/layout-base");
import view = require("ui/core/view");
import dependencyObservable = require("ui/core/dependency-observable");
/**
* A layout that lets you specify exact locations (left/top coordinates) of its children.
*/
- class AbsoluteLayout extends layout.Layout {
+ class AbsoluteLayout extends layout.LayoutBase {
/**
* Represents the observable property backing the left property.
diff --git a/ui/layouts/absolute-layout/absolute-layout.ts b/ui/layouts/absolute-layout/absolute-layout.ios.ts
similarity index 59%
rename from ui/layouts/absolute-layout/absolute-layout.ts
rename to ui/layouts/absolute-layout/absolute-layout.ios.ts
index cd311b673..bb150ad0a 100644
--- a/ui/layouts/absolute-layout/absolute-layout.ts
+++ b/ui/layouts/absolute-layout/absolute-layout.ios.ts
@@ -1,56 +1,19 @@
-import layouts = require("ui/layouts/layout");
-import definition = require("ui/layouts/absolute-layout");
-import utils = require("utils/utils");
-import dependencyObservable = require("ui/core/dependency-observable");
+import utils = require("utils/utils");
import view = require("ui/core/view");
-import numberUtils = require("utils/number-utils");
+import common = require("ui/layouts/absolute-layout/absolute-layout-common");
-function onPropertyChanged(data: dependencyObservable.PropertyChangeData) {
- var uiView = data.object;
- if (uiView instanceof view.View) {
- var layout = uiView.parent;
- if (layout instanceof AbsoluteLayout) {
- layout.requestLayout();
- }
- }
-}
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
-export class AbsoluteLayout extends layouts.Layout implements definition.AbsoluteLayout {
+export class AbsoluteLayout extends common.AbsoluteLayout {
- public static leftProperty = new dependencyObservable.Property("left", "AbsoluteLayout",
- new dependencyObservable.PropertyMetadata(0, undefined, onPropertyChanged, numberUtils.isFiniteNumber));
-
- public static topProperty = new dependencyObservable.Property("top", "AbsoluteLayout",
- new dependencyObservable.PropertyMetadata(0, undefined, onPropertyChanged, numberUtils.isFiniteNumber));
-
- public static getLeft(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
-
- return element._getValue(AbsoluteLayout.leftProperty);
+ protected onLeftChanged(view: view.View, oldValue: number, newValue: number) {
+ this.requestLayout();
}
- public static setLeft(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(AbsoluteLayout.leftProperty, value);
- }
-
- public static getTop(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
-
- return element._getValue(AbsoluteLayout.topProperty);
- }
-
- public static setTop(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(AbsoluteLayout.topProperty, value);
+ protected onTopChanged(view: view.View, oldValue: number, newValue: number) {
+ this.requestLayout();
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
diff --git a/ui/layouts/dock-layout/dock-layout-common.ts b/ui/layouts/dock-layout/dock-layout-common.ts
new file mode 100644
index 000000000..9cc6f628f
--- /dev/null
+++ b/ui/layouts/dock-layout/dock-layout-common.ts
@@ -0,0 +1,58 @@
+import layouts = require("ui/layouts/layout-base");
+import definition = require("ui/layouts/dock-layout");
+import dependencyObservable = require("ui/core/dependency-observable");
+import view = require("ui/core/view");
+import enums = require("ui/enums");
+import proxy = require("ui/core/proxy");
+
+// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
+var AffectsLayout = global.android ? dependencyObservable.PropertyMetadataSettings.None : dependencyObservable.PropertyMetadataSettings.AffectsLayout;
+
+function isDockValid(value: any): boolean {
+ return value === enums.Dock.left || value === enums.Dock.top || value === enums.Dock.right || value === enums.Dock.bottom;
+}
+
+function validateArgs(element: view.View): view.View {
+ if (!element) {
+ throw new Error("element cannot be null or undefinied.");
+ }
+ return element;
+}
+
+export class DockLayout extends layouts.LayoutBase implements definition.DockLayout {
+
+ private static onDockPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var layout = uiView.parent;
+ if (layout instanceof DockLayout) {
+ layout.onDockChanged(uiView, data.oldValue, data.newValue);
+ }
+ }
+ }
+
+ public static dockProperty = new dependencyObservable.Property(
+ "dock", "DockLayout", new proxy.PropertyMetadata(enums.Dock.left, undefined, DockLayout.onDockPropertyChanged, isDockValid));
+
+ public static stretchLastChildProperty = new dependencyObservable.Property(
+ "stretchLastChild", "DockLayout", new proxy.PropertyMetadata(true, AffectsLayout));
+
+ public static getDock(element: view.View): string {
+ return validateArgs(element)._getValue(DockLayout.dockProperty);
+ }
+
+ public static setDock(element: view.View, value: string): void {
+ validateArgs(element)._setValue(DockLayout.dockProperty, value);
+ }
+
+ get stretchLastChild(): boolean {
+ return this._getValue(DockLayout.stretchLastChildProperty);
+ }
+ set stretchLastChild(value: boolean) {
+ this._setValue(DockLayout.stretchLastChildProperty, value);
+ }
+
+ protected onDockChanged(view: view.View, oldValue: number, newValue: number) {
+ //
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/dock-layout/dock-layout.android.ts b/ui/layouts/dock-layout/dock-layout.android.ts
new file mode 100644
index 000000000..42dee5c4e
--- /dev/null
+++ b/ui/layouts/dock-layout/dock-layout.android.ts
@@ -0,0 +1,69 @@
+import definition = require("ui/layouts/dock-layout");
+import dependencyObservable = require("ui/core/dependency-observable");
+import view = require("ui/core/view");
+import enums = require("ui/enums");
+import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/dock-layout/dock-layout-common");
+
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
+
+function setNativeDockProperty(data: dependencyObservable.PropertyChangeData) {
+
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var nativeView: android.view.View = uiView._nativeView;
+
+ var lp = nativeView.getLayoutParams();
+ if (!(lp instanceof org.nativescript.widgets.CommonLayoutParams)) {
+ lp = new org.nativescript.widgets.CommonLayoutParams();
+ }
+
+ switch (data.newValue) {
+ case enums.Dock.left:
+ lp.dock = org.nativescript.widgets.Dock.left;
+ break;
+ case enums.Dock.top:
+ lp.dock = org.nativescript.widgets.Dock.top;
+ break;
+ case enums.Dock.right:
+ lp.dock = org.nativescript.widgets.Dock.right;
+ break;
+ case enums.Dock.bottom:
+ lp.dock = org.nativescript.widgets.Dock.bottom;
+ break;
+ default:
+ throw new Error("Invalid dock value: " + data.newValue + " on element: " + uiView);
+ }
+
+ nativeView.setLayoutParams(lp);
+ }
+}
+
+(common.DockLayout.dockProperty.metadata).onSetNativeValue = setNativeDockProperty;
+
+export class DockLayout extends common.DockLayout {
+
+ static setNativeStretchLastChildProperty(data: dependencyObservable.PropertyChangeData) {
+ var dockLayout = data.object;
+ var nativeView = dockLayout._nativeView;
+ nativeView.setStretchLastChild(data.newValue);
+ }
+
+ private _layout: org.nativescript.widgets.DockLayout;
+
+ get android(): org.nativescript.widgets.DockLayout {
+ return this._layout;
+ }
+
+ get _nativeView(): org.nativescript.widgets.DockLayout {
+ return this._layout;
+ }
+
+ public _createUI() {
+ this._layout = new org.nativescript.widgets.DockLayout(this._context);
+ }
+}
+
+(common.DockLayout.stretchLastChildProperty.metadata).onSetNativeValue = DockLayout.setNativeStretchLastChildProperty;
\ No newline at end of file
diff --git a/ui/layouts/dock-layout/dock-layout.d.ts b/ui/layouts/dock-layout/dock-layout.d.ts
index 028cefcc1..46a29c6a3 100644
--- a/ui/layouts/dock-layout/dock-layout.d.ts
+++ b/ui/layouts/dock-layout/dock-layout.d.ts
@@ -1,12 +1,12 @@
declare module "ui/layouts/dock-layout" {
import view = require("ui/core/view");
- import layout = require("ui/layouts/layout");
+ import layout = require("ui/layouts/layout-base");
import dependencyObservable = require("ui/core/dependency-observable");
/**
* A Layout that arranges its children at its outer edges, and allows its last child to take up the remaining space.
*/
- class DockLayout extends layout.Layout {
+ class DockLayout extends layout.LayoutBase {
/**
* Represents the observable property backing the dock property.
diff --git a/ui/layouts/dock-layout/dock-layout.ts b/ui/layouts/dock-layout/dock-layout.ios.ts
similarity index 77%
rename from ui/layouts/dock-layout/dock-layout.ts
rename to ui/layouts/dock-layout/dock-layout.ios.ts
index a30158733..1965f470f 100644
--- a/ui/layouts/dock-layout/dock-layout.ts
+++ b/ui/layouts/dock-layout/dock-layout.ios.ts
@@ -1,53 +1,12 @@
-import layouts = require("ui/layouts/layout");
-import definition = require("ui/layouts/dock-layout");
-import utils = require("utils/utils");
-import dependencyObservable = require("ui/core/dependency-observable");
+import utils = require("utils/utils");
import view = require("ui/core/view");
import enums = require("ui/enums");
-import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/dock-layout/dock-layout-common");
-function isDockValid(value: any): boolean {
- return value === enums.Dock.left || value === enums.Dock.top || value === enums.Dock.right || value === enums.Dock.bottom;
-}
+export class DockLayout extends common.DockLayout {
-function onDockPropertyChanged(data: dependencyObservable.PropertyChangeData) {
- var uiView = data.object;
- if (uiView instanceof view.View) {
- var layout = (uiView).parent;
- if (layout instanceof DockLayout) {
- layout.requestLayout();
- }
- }
-}
-
-export class DockLayout extends layouts.Layout implements definition.DockLayout {
-
- public static dockProperty = new dependencyObservable.Property(
- "dock", "DockLayout", new dependencyObservable.PropertyMetadata(enums.Dock.left, undefined, onDockPropertyChanged, isDockValid));
-
- public static stretchLastChildProperty = new dependencyObservable.Property(
- "stretchLastChild", "DockLayout", new proxy.PropertyMetadata(true, dependencyObservable.PropertyMetadataSettings.AffectsLayout));
-
- public static getDock(element: view.View): string {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
-
- return element._getValue(DockLayout.dockProperty);
- }
-
- public static setDock(element: view.View, value: string): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(DockLayout.dockProperty, value);
- }
-
- get stretchLastChild(): boolean {
- return this._getValue(DockLayout.stretchLastChildProperty);
- }
- set stretchLastChild(value: boolean) {
- this._setValue(DockLayout.stretchLastChildProperty, value);
+ protected onDockChanged(view: view.View, oldValue: number, newValue: number) {
+ this.requestLayout();
}
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
diff --git a/ui/layouts/grid-layout/grid-layout-common.ts b/ui/layouts/grid-layout/grid-layout-common.ts
new file mode 100644
index 000000000..7c2c1a6b3
--- /dev/null
+++ b/ui/layouts/grid-layout/grid-layout-common.ts
@@ -0,0 +1,370 @@
+import layouts = require("ui/layouts/layout-base");
+import definition = require("ui/layouts/grid-layout");
+import utils = require("utils/utils");
+import dependencyObservable = require("ui/core/dependency-observable");
+import enums = require("ui/enums");
+import view = require("ui/core/view");
+import bindable = require("ui/core/bindable");
+import types = require("utils/types");
+import numberUtils = require("utils/number-utils");
+import proxy = require("ui/core/proxy");
+
+function validateArgs(element: view.View): view.View {
+ if (!element) {
+ throw new Error("element cannot be null or undefinied.");
+ }
+ return element;
+}
+
+export module GridUnitType {
+ export var auto: string = "auto";
+ export var pixel: string = "pixel";
+ export var star: string = "star";
+}
+
+export class ItemSpec extends bindable.Bindable implements definition.ItemSpec {
+
+ private _value: number;
+ private _unitType: string;
+
+ constructor() {
+ super();
+
+ if (arguments.length === 0) {
+ this._value = 1;
+ this._unitType = GridUnitType.star;
+
+ }
+ else if (arguments.length === 2) {
+ 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.");
+ }
+ this._value = arguments[0];
+ this._unitType = arguments[1];
+ }
+ else {
+ throw new Error("Arguments must be number and string.");
+ }
+ }
+ else {
+ throw new Error("ItemSpec expects 0 or 2 arguments");
+ }
+
+ this.index = -1;
+ }
+
+ public owner: GridLayout;
+ public index: number;
+ public _actualLength: number = 0;
+
+ 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 {
+ return this._unitType;
+ }
+
+ get isAbsolute(): boolean {
+ return this._unitType === GridUnitType.pixel;
+ }
+
+ get isAuto(): boolean {
+ return this._unitType === GridUnitType.auto;
+ }
+
+ get isStar(): boolean {
+ return this._unitType === GridUnitType.star;
+ }
+
+ get value(): number {
+ return this._value;
+ }
+}
+
+export class GridLayout extends layouts.LayoutBase implements definition.GridLayout, view.ApplyXmlAttributes {
+ private _rows: Array = new Array();
+ private _cols: Array = new Array();
+ protected _singleRow: ItemSpec = new ItemSpec();
+ protected _singleColumn: ItemSpec = new ItemSpec();
+
+ public static columnProperty = new dependencyObservable.Property("Column", "GridLayout",
+ new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, GridLayout.onColumnPropertyChanged, numberUtils.notNegative));
+
+ public static columnSpanProperty = new dependencyObservable.Property("ColumnSpan", "GridLayout",
+ new proxy.PropertyMetadata(1, dependencyObservable.PropertyMetadataSettings.None, GridLayout.onColumnSpanPropertyChanged, numberUtils.greaterThanZero));
+
+ public static rowProperty = new dependencyObservable.Property("Row", "GridLayout",
+ new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, GridLayout.onRowPropertyChanged, numberUtils.notNegative));
+
+ public static rowSpanProperty = new dependencyObservable.Property("RowSpan", "GridLayout",
+ new proxy.PropertyMetadata(1, dependencyObservable.PropertyMetadataSettings.None, GridLayout.onRowSpanPropertyChanged, numberUtils.greaterThanZero));
+
+ public static getColumn(element: view.View): number {
+ return validateArgs(element)._getValue(GridLayout.columnProperty);
+ }
+
+ public static setColumn(element: view.View, value: number): void {
+ validateArgs(element)._setValue(GridLayout.columnProperty, value);
+ }
+
+ public static getColumnSpan(element: view.View): number {
+ return validateArgs(element)._getValue(GridLayout.columnSpanProperty);
+ }
+
+ public static setColumnSpan(element: view.View, value: number): void {
+ validateArgs(element)._setValue(GridLayout.columnSpanProperty, value);
+ }
+
+ public static getRow(element: view.View): number {
+ return validateArgs(element)._getValue(GridLayout.rowProperty);
+ }
+
+ public static setRow(element: view.View, value: number): void {
+ validateArgs(element)._setValue(GridLayout.rowProperty, value);
+ }
+
+ public static getRowSpan(element: view.View): number {
+ return validateArgs(element)._getValue(GridLayout.rowSpanProperty);
+ }
+
+ public static setRowSpan(element: view.View, value: number): void {
+ validateArgs(element)._setValue(GridLayout.rowSpanProperty, value);
+ }
+
+ constructor() {
+ super();
+ this._singleRow.index = 0
+ this._singleColumn.index = 0;
+ }
+
+ public addRow(itemSpec: ItemSpec) {
+ GridLayout.validateItemSpec(itemSpec);
+ itemSpec.owner = this;
+ this._rows.push(itemSpec);
+ this.onRowAdded(itemSpec);
+ }
+
+ public addColumn(itemSpec: ItemSpec) {
+ GridLayout.validateItemSpec(itemSpec);
+ itemSpec.owner = this;
+ this._cols.push(itemSpec);
+ this.onColumnAdded(itemSpec);
+ }
+
+ public removeRow(itemSpec: ItemSpec): void {
+ if (!itemSpec) {
+ throw new Error("Value is null.");
+ }
+
+ var index = this._rows.indexOf(itemSpec);
+ if (itemSpec.owner !== this || index < 0) {
+ throw new Error("Row is not child of this GridLayout");
+ }
+
+ itemSpec.index = -1;
+ this._rows.splice(index, 1);
+ this.onRowRemoved(itemSpec, index);
+ }
+
+ public removeColumn(itemSpec: ItemSpec): void {
+ if (!itemSpec) {
+ throw new Error("Value is null.");
+ }
+
+ var index = this._cols.indexOf(itemSpec);
+ if (itemSpec.owner !== this || index < 0) {
+ throw new Error("Column is not child of this GridLayout");
+ }
+
+ itemSpec.index = -1;
+ this._cols.splice(index, 1);
+ this.onColumnRemoved(itemSpec, index);
+ }
+
+ protected onRowChanged(element: view.View, oldValue: number, newValue: number) {
+ //
+ }
+
+ protected onRowSpanChanged(element: view.View, oldValue: number, newValue: number) {
+ //
+ }
+
+ protected onColumnChanged(element: view.View, oldValue: number, newValue: number) {
+ //
+ }
+
+ protected onColumnSpanChanged(element: view.View, oldValue: number, newValue: number) {
+ //
+ }
+
+ protected onRowAdded(itemSpec: ItemSpec) {
+ //
+ }
+
+ protected onColumnAdded(itemSpec: ItemSpec) {
+ //
+ }
+
+ protected onRowRemoved(itemSpec: ItemSpec, index: number) {
+ //
+ }
+
+ protected onColumnRemoved(itemSpec: ItemSpec, index: number) {
+ //
+ }
+
+ public getColumns(): Array {
+ return this._cols.slice();
+ }
+
+ public getRows(): Array {
+ return this._rows.slice();
+ }
+
+ protected getColumn(view: view.View): ItemSpec {
+ if (this._cols.length === 0) {
+ return this._singleColumn;
+ }
+
+ var columnIndex = Math.min(GridLayout.getColumn(view), this._cols.length - 1);
+ return this._cols[columnIndex];
+ }
+
+ protected getRow(view: view.View): ItemSpec {
+ if (this._rows.length === 0) {
+ return this._singleRow;
+ }
+
+ var columnIndex = Math.min(GridLayout.getRow(view), this._rows.length - 1);
+ return this._rows[columnIndex];
+ }
+
+ protected getColumnSpan(view: view.View, columnIndex: number): number {
+ if (this._cols.length === 0) {
+ return 1;
+ }
+
+ return Math.min(GridLayout.getColumnSpan(view), this._cols.length - columnIndex);
+ }
+
+ protected getRowSpan(view: view.View, rowIndex: number): number {
+ if (this._rows.length === 0) {
+ return 1;
+ }
+
+ return Math.min(GridLayout.getRowSpan(view), this._rows.length - rowIndex);
+ }
+
+ protected invalidate(): void {
+ //
+ }
+
+ applyXmlAttribute(attributeName: string, attributeValue: any): boolean {
+ if (attributeName === "columns") {
+ this.setColumns(attributeValue);
+ return true;
+ }
+ else if (attributeName === "rows") {
+ this.setRows(attributeValue);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static parseItemSpecs(value: string): Array {
+ var result = new Array();
+ var arr = value.split(",");
+ for (var i = 0; i < arr.length; i++) {
+ result.push(GridLayout.convertGridLength(arr[i].trim()));
+ }
+
+ return result;
+ }
+
+ private static convertGridLength(value: string): ItemSpec {
+
+ if (value === "auto") {
+ return new definition.ItemSpec(1, definition.GridUnitType.auto);
+ }
+ else if (value.indexOf("*") !== -1) {
+ var starCount = parseInt(value.replace("*", "") || "1");
+ return new definition.ItemSpec(starCount, definition.GridUnitType.star);
+ }
+ else if (!isNaN(parseInt(value))) {
+ return new definition.ItemSpec(parseInt(value), definition.GridUnitType.pixel);
+ }
+ else {
+ throw new Error("Cannot parse item spec from string: " + value);
+ }
+ }
+
+ private static onRowPropertyChanged(data: dependencyObservable.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: dependencyObservable.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: dependencyObservable.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: dependencyObservable.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.View {
+ if (object instanceof view.View) {
+ return object;
+ }
+
+ throw new Error("Element is not View or its descendant.");
+ }
+
+ private setColumns(value: string) {
+ this._cols = GridLayout.parseItemSpecs(value);
+ this.invalidate();
+ }
+
+ private setRows(value: string) {
+ this._rows = GridLayout.parseItemSpecs(value);
+ this.invalidate();
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/grid-layout/grid-layout.android.ts b/ui/layouts/grid-layout/grid-layout.android.ts
new file mode 100644
index 000000000..c420b1725
--- /dev/null
+++ b/ui/layouts/grid-layout/grid-layout.android.ts
@@ -0,0 +1,111 @@
+import definition = require("ui/layouts/grid-layout");
+import utils = require("utils/utils");
+import dependencyObservable = require("ui/core/dependency-observable");
+import enums = require("ui/enums");
+import view = require("ui/core/view");
+import bindable = require("ui/core/bindable");
+import types = require("utils/types");
+import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/grid-layout/grid-layout-common");
+
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
+
+
+function setNativeProperty(data: dependencyObservable.PropertyChangeData, setter: (lp: org.nativescript.widgets.CommonLayoutParams) => void) {
+
+ var uiView = data.object;
+ if (uiView instanceof view.View) {
+ var nativeView: android.view.View = uiView._nativeView;
+
+ var lp = nativeView.getLayoutParams();
+ if (!(lp instanceof org.nativescript.widgets.CommonLayoutParams)) {
+ lp = new org.nativescript.widgets.CommonLayoutParams();
+ }
+ setter(lp);
+ nativeView.setLayoutParams(lp);
+ }
+}
+
+function setNativeRowProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.row = data.newValue; });
+}
+
+function setNativeRowSpanProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.rowSpan = data.newValue; });
+}
+
+function setNativeColumnProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.column = data.newValue; });
+}
+
+function setNativeColumnSpanProperty(data: dependencyObservable.PropertyChangeData) {
+ setNativeProperty(data, (lp) => { lp.columnSpan = data.newValue; });
+}
+
+(common.GridLayout.rowProperty.metadata).onSetNativeValue = setNativeRowProperty;
+(common.GridLayout.rowSpanProperty.metadata).onSetNativeValue = setNativeRowSpanProperty;
+(common.GridLayout.columnProperty.metadata).onSetNativeValue = setNativeColumnProperty;
+(common.GridLayout.columnSpanProperty.metadata).onSetNativeValue = setNativeColumnSpanProperty;
+
+function createNativeSpec(itemSpec: common.ItemSpec): org.nativescript.widgets.ItemSpec {
+ switch (itemSpec.gridUnitType) {
+ case common.GridUnitType.auto:
+ return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.auto);
+
+ case common.GridUnitType.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);
+
+ default:
+ throw new Error("Invalid gridUnitType: " + itemSpec.gridUnitType);
+ }
+}
+
+export class GridLayout extends common.GridLayout {
+
+ private _layout: org.nativescript.widgets.GridLayout;
+
+ get android(): org.nativescript.widgets.GridLayout {
+ return this._layout;
+ }
+
+ get _nativeView(): org.nativescript.widgets.GridLayout {
+ return this._layout;
+ }
+
+ public _createUI() {
+ this._layout = new org.nativescript.widgets.GridLayout(this._context);
+
+ // Update native GridLayout
+ this.getRows().forEach((itemSpec, index, rows) => { this.onRowAdded(itemSpec); }, this);
+ this.getColumns().forEach((itemSpec, index, rows) => { this.onColumnAdded(itemSpec); }, this);
+ }
+
+ protected onRowAdded(itemSpec: common.ItemSpec) {
+ if (this._layout) {
+ this._layout.addRow(createNativeSpec(itemSpec));
+ }
+ }
+
+ protected onColumnAdded(itemSpec: common.ItemSpec) {
+ if (this._layout) {
+ this._layout.addColumn(createNativeSpec(itemSpec));
+ }
+ }
+
+ protected onRowRemoved(itemSpec: common.ItemSpec, index: number) {
+ if (this._layout) {
+ this._layout.removeRowAt(index);
+ }
+ }
+
+ protected onColumnRemoved(itemSpec: common.ItemSpec, index: number) {
+ if (this._layout) {
+ this._layout.removeColumnAt(index);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/grid-layout/grid-layout.d.ts b/ui/layouts/grid-layout/grid-layout.d.ts
index 2b2e95e0c..af688603f 100644
--- a/ui/layouts/grid-layout/grid-layout.d.ts
+++ b/ui/layouts/grid-layout/grid-layout.d.ts
@@ -1,5 +1,5 @@
declare module "ui/layouts/grid-layout" {
- import layout = require("ui/layouts/layout");
+ import layout = require("ui/layouts/layout-base");
import view = require("ui/core/view");
/**
@@ -68,7 +68,7 @@
/**
* Defines a rectangular layout area that consists of columns and rows.
*/
- export class GridLayout extends layout.Layout {
+ export class GridLayout extends layout.LayoutBase {
///**
// * Initializes a new instance of GridLayout.
diff --git a/ui/layouts/grid-layout/grid-layout.ts b/ui/layouts/grid-layout/grid-layout.ios.ts
similarity index 73%
rename from ui/layouts/grid-layout/grid-layout.ts
rename to ui/layouts/grid-layout/grid-layout.ios.ts
index df8bc5727..6cc34df8c 100644
--- a/ui/layouts/grid-layout/grid-layout.ts
+++ b/ui/layouts/grid-layout/grid-layout.ios.ts
@@ -1,295 +1,54 @@
-import layouts = require("ui/layouts/layout");
-import definition = require("ui/layouts/grid-layout");
-import utils = require("utils/utils");
-import dependencyObservable = require("ui/core/dependency-observable");
+import utils = require("utils/utils");
import enums = require("ui/enums");
import view = require("ui/core/view");
-import bindable = require("ui/core/bindable");
-import types = require("utils/types");
-import numberUtils = require("utils/number-utils");
+import common = require("ui/layouts/grid-layout/grid-layout-common");
-export module GridUnitType {
- export var auto: string = "auto";
- export var pixel: string = "pixel";
- export var star: string = "star";
-}
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
-export class ItemSpec extends bindable.Bindable implements definition.ItemSpec {
-
- private _value: number;
- private _unitType: string;
-
- constructor() {
- super();
-
- if (arguments.length === 0) {
- this._value = 1;
- this._unitType = GridUnitType.star;
-
- }
- else if (arguments.length === 2) {
- 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.");
- }
- this._value = arguments[0];
- this._unitType = arguments[1];
- }
- else {
- throw new Error("Arguments must be number and string.");
- }
- }
- else {
- throw new Error("ItemSpec expects 0 or 2 arguments");
- }
-
- this.index = -1;
- }
-
- public owner: GridLayout;
- public index: number;
- public _actualLength: number = 0;
-
- 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 {
- return this._unitType;
- }
-
- get isAbsolute(): boolean {
- return this._unitType === GridUnitType.pixel;
- }
-
- get isAuto(): boolean {
- return this._unitType === GridUnitType.auto;
- }
-
- get isStar(): boolean {
- return this._unitType === GridUnitType.star;
- }
-
- get value(): number {
- return this._value;
- }
-}
-
-export class GridLayout extends layouts.Layout implements definition.GridLayout, view.ApplyXmlAttributes {
- private _rows: Array = new Array();
- private _cols: Array = new Array();
- private _singleRow: ItemSpec = new ItemSpec();
- private _singleColumn: ItemSpec = new ItemSpec();
+export class GridLayout extends common.GridLayout {
private _isValid: boolean = false;
-
private helper: MeasureHelper;
- public static columnProperty = new dependencyObservable.Property("Column", "GridLayout",
- new dependencyObservable.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, GridLayout.attachedPropertyChanged, numberUtils.notNegative));
- public static columnSpanProperty = new dependencyObservable.Property("ColumnSpan", "GridLayout",
- new dependencyObservable.PropertyMetadata(1, dependencyObservable.PropertyMetadataSettings.None, GridLayout.attachedPropertyChanged, numberUtils.greaterThanZero));
-
- public static rowProperty = new dependencyObservable.Property("Row", "GridLayout",
- new dependencyObservable.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, GridLayout.attachedPropertyChanged, numberUtils.notNegative));
- public static rowSpanProperty = new dependencyObservable.Property("RowSpan", "GridLayout",
- new dependencyObservable.PropertyMetadata(1, dependencyObservable.PropertyMetadataSettings.None, GridLayout.attachedPropertyChanged, numberUtils.greaterThanZero));
-
- public static getColumn(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
+ protected onRowAdded(itemSpec: common.ItemSpec) {
+ this.invalidate();
}
- return element._getValue(GridLayout.columnProperty);
- }
-
- public static setColumn(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(GridLayout.columnProperty, value);
- }
-
- public static getColumnSpan(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- return element._getValue(GridLayout.columnSpanProperty);
- }
-
- public static setColumnSpan(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(GridLayout.columnSpanProperty, value);
- }
-
- public static getRow(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- return element._getValue(GridLayout.rowProperty);
- }
-
- public static setRow(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(GridLayout.rowProperty, value);
- }
-
- public static getRowSpan(element: view.View): number {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- return element._getValue(GridLayout.rowSpanProperty);
- }
-
- public static setRowSpan(element: view.View, value: number): void {
- if (!element) {
- throw new Error("element cannot be null or undefinied.");
- }
- element._setValue(GridLayout.rowSpanProperty, value);
- }
-
- private static attachedPropertyChanged(data: dependencyObservable.PropertyChangeData): void {
- if (data.object instanceof view.View) {
- var element = data.object;
- if (!element) {
- throw new Error("Element is not View.");
- }
-
- var grid = element.parent;
- if (grid instanceof GridLayout) {
- grid.invalidate();
- }
- }
- }
-
- 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.");
- }
- }
-
- constructor() {
- super();
- this._singleRow.index = 0
- this._singleColumn.index = 0;
- }
-
- public addRow(itemSpec: ItemSpec) {
- GridLayout.validateItemSpec(itemSpec);
- itemSpec.owner = this;
- this._rows.push(itemSpec);
+ protected onColumnAdded(itemSpec: common.ItemSpec) {
this.invalidate();
}
- public addColumn(itemSpec: ItemSpec) {
- GridLayout.validateItemSpec(itemSpec);
- itemSpec.owner = this;
- this._cols.push(itemSpec);
+ protected onRowRemoved(itemSpec: common.ItemSpec, index: number) {
this.invalidate();
}
- public removeColumn(itemSpec: ItemSpec): void {
- if (!itemSpec) {
- throw new Error("Value is null.");
- }
-
- var index = this._cols.indexOf(itemSpec);
- if (itemSpec.owner !== this || index < 0) {
- throw new Error("Column is not child of this GridLayout");
- }
-
- itemSpec.index = -1;
- this._cols.splice(index, 1);
+ protected onColumnRemoved(itemSpec: common.ItemSpec, index: number) {
this.invalidate();
}
- public removeRow(itemSpec: ItemSpec): void {
- if (!itemSpec) {
- throw new Error("Value is null.");
- }
-
- var index = this._rows.indexOf(itemSpec);
- if (itemSpec.owner !== this || index < 0) {
- throw new Error("Row is not child of this GridLayout");
- }
-
- itemSpec.index = -1;
- this._rows.splice(index, 1);
+ protected onRowChanged(element: view.View, oldValue: number, newValue: number) {
this.invalidate();
}
- public getColumns(): Array {
- return this._cols.slice();
- }
-
- public getRows(): Array {
- return this._rows.slice();
- }
-
- private setColumns(value: string) {
- this._cols = GridLayout.parseItemSpecs(value);
+ protected onRowSpanChanged(element: view.View, oldValue: number, newValue: number) {
this.invalidate();
}
- private setRows(value: string) {
- this._rows = GridLayout.parseItemSpecs(value);
+ protected onColumnChanged(element: view.View, oldValue: number, newValue: number) {
this.invalidate();
}
- public invalidate(): void {
+ protected onColumnSpanChanged(element: view.View, oldValue: number, newValue: number) {
+ this.invalidate();
+ }
+
+ protected invalidate(): void {
this._isValid = false;
this.requestLayout();
}
- private getColumn(view: view.View): ItemSpec {
- if (this._cols.length === 0) {
- return this._singleColumn;
- }
-
- var columnIndex = Math.min(GridLayout.getColumn(view), this._cols.length - 1);
- return this._cols[columnIndex];
- }
-
- private getRow(view: view.View): ItemSpec {
- if (this._rows.length === 0) {
- return this._singleRow;
- }
-
- var columnIndex = Math.min(GridLayout.getRow(view), this._rows.length - 1);
- return this._rows[columnIndex];
- }
-
- private getColumnSpan(view: view.View, columnIndex: number): number {
- if (this._cols.length === 0) {
- return 1;
- }
-
- return Math.min(GridLayout.getColumnSpan(view), this._cols.length - columnIndex);
- }
-
- private getRowSpan(view: view.View, rowIndex: number): number {
- if (this._rows.length === 0) {
- return 1;
- }
-
- return Math.min(GridLayout.getRowSpan(view), this._rows.length - rowIndex);
- }
-
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -306,17 +65,20 @@ export class GridLayout extends layouts.Layout implements definition.GridLayout,
var infinityWidth = widthMode === utils.layout.UNSPECIFIED;
var infinityHeight = heightMode === utils.layout.UNSPECIFIED;
- var column: ItemSpec;
+ var column: common.ItemSpec;
var columnGroup: ColumnGroup;
- var row: ItemSpec;
+ var row: common.ItemSpec;
var rowGroup: RowGroup;
+
+ var rows = this.getRows();
+ var cols = this.getColumns();
if (!this._isValid) {
- this._rows.forEach((value: ItemSpec, index: number, array: ItemSpec[]) => {
+ rows.forEach((value: common.ItemSpec, index: number, array: common.ItemSpec[]) => {
value.index = index;
});
- this._cols.forEach((value: ItemSpec, index: number, array: ItemSpec[]) => {
+ cols.forEach((value: common.ItemSpec, index: number, array: common.ItemSpec[]) => {
value.index = index;
});
@@ -327,8 +89,8 @@ export class GridLayout extends layouts.Layout implements definition.GridLayout,
this.helper.infinityWidth = infinityWidth;
this.helper.infinityHeight = infinityHeight;
- for (i = 0; i < this._cols.length; i++) {
- column = this._cols[i];
+ for (i = 0; i < cols.length; i++) {
+ column = cols[i];
columnGroup = new ColumnGroup(column, this.helper);
this.helper.columns.push(columnGroup);
if (column.isAbsolute) {
@@ -336,8 +98,8 @@ export class GridLayout extends layouts.Layout implements definition.GridLayout,
}
}
- for (i = 0; i < this._rows.length; i++) {
- row = this._rows[i];
+ for (i = 0; i < rows.length; i++) {
+ row = rows[i];
rowGroup = new RowGroup(row, this.helper);
this.helper.rows.push(rowGroup);
if (row.isAbsolute) {
@@ -345,11 +107,11 @@ export class GridLayout extends layouts.Layout implements definition.GridLayout,
}
}
- if (this._rows.length === 0) {
+ if (rows.length === 0) {
this.helper.rows.push(new RowGroup(this._singleRow, this.helper));
}
- if (this._cols.length === 0) {
+ if (cols.length === 0) {
this.helper.columns.push(new ColumnGroup(this._singleColumn, this.helper));
}
}
@@ -445,44 +207,6 @@ export class GridLayout extends layouts.Layout implements definition.GridLayout,
}
}
}
-
- _applyXmlAttribute(attributeName: string, attributeValue: any): boolean {
- if (attributeName === "columns") {
- this.setColumns(attributeValue);
- return true;
- }
- else if (attributeName === "rows") {
- this.setRows(attributeValue);
- return true;
- }
-
- return super._applyXmlAttribute(attributeName, attributeValue);
- }
-
- private static parseItemSpecs(value: string): Array {
- var result = new Array();
- var arr = value.split(",");
- for (var i = 0; i < arr.length; i++) {
- result.push(GridLayout.convertGridLength(arr[i].trim()));
- }
-
- return result;
- }
-
- private static convertGridLength(value: string): ItemSpec {
-
- if (value === "auto") {
- return new definition.ItemSpec(1, definition.GridUnitType.auto);
- } else if (value.indexOf("*") !== -1) {
- var starCount = parseInt(value.replace("*", "") || "1");
- return new definition.ItemSpec(starCount, definition.GridUnitType.star);
- } else if (!isNaN(parseInt(value))) {
- return new definition.ItemSpec(parseInt(value), definition.GridUnitType.pixel);
- }
- else {
- throw new Error("Cannot parse item spec from string: " + value);
- }
- }
}
class MeasureSpecs {
@@ -503,8 +227,8 @@ class MeasureSpecs {
constructor(
public child: view.View,
- public column: ItemSpec,
- public row: ItemSpec,
+ public column: common.ItemSpec,
+ public row: common.ItemSpec,
columnSpan?: number,
rowSpan?: number) {
// cannot have zero colSpan.
@@ -546,14 +270,14 @@ class MeasureSpecs {
class ColumnGroup {
width: number = 0;
measuredCount = 0;
- column: ItemSpec;
+ column: common.ItemSpec;
children: Array = new Array();
owner: MeasureHelper;
public measureToFix: number = 0;
public currentMeasureToFixCount: number = 0;
- constructor(column: ItemSpec, owner: MeasureHelper) {
+ constructor(column: common.ItemSpec, owner: MeasureHelper) {
this.owner = owner;
this.column = column;
}
@@ -587,14 +311,14 @@ class ColumnGroup {
class RowGroup {
height: number = 0;
measuredCount = 0;
- row: ItemSpec;
+ row: common.ItemSpec;
children: Array = new Array();
owner: MeasureHelper;
public measureToFix: number = 0;
public currentMeasureToFixCount: number = 0;
- constructor(row: ItemSpec, owner: MeasureHelper) {
+ constructor(row: common.ItemSpec, owner: MeasureHelper) {
this.row = row;
this.owner = owner;
}
diff --git a/ui/layouts/layout-base.d.ts b/ui/layouts/layout-base.d.ts
new file mode 100644
index 000000000..316e54cc2
--- /dev/null
+++ b/ui/layouts/layout-base.d.ts
@@ -0,0 +1,60 @@
+declare module "ui/layouts/layout-base" {
+ import view = require("ui/core/view");
+ import dependencyObservable = require("ui/core/dependency-observable");
+
+ /**
+ * Base class for all views that supports children positioning.
+ */
+ export class LayoutBase extends view.CustomLayoutView {
+
+ public static clipToBoundsProperty: dependencyObservable.Property;
+
+ /**
+ * Returns the number of children in this Layout.
+ */
+ getChildrenCount(): number;
+
+ /**
+ * Returns the view at the specified position.
+ * @param index The position at which to get the child from.
+ */
+ getChildAt(index: number): view.View;
+
+ /**
+ * Adds the view to children array.
+ * @param view The view to be added to the end of the children array.
+ */
+ addChild(view: view.View);
+
+ /**
+ * Removes the specified view from the children array.
+ * @param view The view to remove from the children array.
+ */
+ removeChild(view: view.View);
+
+ /**
+ * Gets or sets padding style property.
+ */
+ padding: string;
+
+ /**
+ * Specify the bottom padding of this layout.
+ */
+ paddingBottom: number;
+
+ /**
+ * Specify the left padding of this layout.
+ */
+ paddingLeft: number;
+
+ /**
+ * Specify the right padding of this layout.
+ */
+ paddingRight: number;
+
+ /**
+ * Specify the top padding of this layout.
+ */
+ paddingTop: number;
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/layout.ts b/ui/layouts/layout-base.ts
similarity index 69%
rename from ui/layouts/layout.ts
rename to ui/layouts/layout-base.ts
index c22ee5e01..525cbd493 100644
--- a/ui/layouts/layout.ts
+++ b/ui/layouts/layout-base.ts
@@ -1,31 +1,12 @@
-import definition = require("ui/layouts/layout");
+import definition = require("ui/layouts/layout-base");
import view = require("ui/core/view");
import dependencyObservable = require("ui/core/dependency-observable");
import proxy = require("ui/core/proxy");
-function onClipToBoundsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
- var nativeView = (data.object)._nativeView;
- if (!nativeView) {
- return;
- }
- var value = data.newValue;
+export class LayoutBase extends view.CustomLayoutView implements definition.LayoutBase, view.AddChildFromBuilder {
- if (nativeView instanceof UIView) {
- (nativeView).clipsToBounds = value;
- }
- else if (nativeView instanceof android.view.ViewGroup) {
- (nativeView).setClipChildren(value);
- }
-}
-
-var clipToBoundsProperty = new dependencyObservable.Property(
- "clipToBounds",
- "Layout",
- new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None, onClipToBoundsPropertyChanged)
- );
-
-export class Layout extends view.CustomLayoutView implements definition.Layout, view.AddChildFromBuilder {
- public static clipToBoundsProperty = clipToBoundsProperty;
+ public static clipToBoundsProperty = new dependencyObservable.Property("clipToBounds", "LayoutBase",
+ new proxy.PropertyMetadata(true, dependencyObservable.PropertyMetadataSettings.None, LayoutBase.onClipToBoundsPropertyChanged));
private _subViews: Array = new Array();
@@ -88,6 +69,13 @@ export class Layout extends view.CustomLayoutView implements definition.Layout,
}
}
+ get padding(): string {
+ return this.style.padding;
+ }
+ set padding(value: string) {
+ this.style.padding = value;
+ }
+
public get paddingTop(): number {
return this.style.paddingTop;
}
@@ -116,10 +104,12 @@ export class Layout extends view.CustomLayoutView implements definition.Layout,
this.style.paddingLeft = value;
}
- get clipToBounds(): boolean {
- return this._getValue(Layout.clipToBoundsProperty);
+ protected onClipToBoundsChanged(oldValue: boolean, newValue: boolean) {
+ //
+ }
+
+ private static onClipToBoundsPropertyChanged(data: dependencyObservable.PropertyChangeData): void {
+ var layout = data.object;
+ layout.onClipToBoundsChanged(data.oldValue, data.newValue);
}
- set clipToBounds(value: boolean) {
- this._setValue(Layout.clipToBoundsProperty, value);
- }
-}
+}
\ No newline at end of file
diff --git a/ui/layouts/layout.android.ts b/ui/layouts/layout.android.ts
new file mode 100644
index 000000000..ef95d1f44
--- /dev/null
+++ b/ui/layouts/layout.android.ts
@@ -0,0 +1,72 @@
+import definition = require("ui/layouts/layout");
+import view = require("ui/core/view");
+import layoutBase = require("ui/layouts/layout-base");
+import trace = require("trace");
+import utils = require("utils/utils");
+import proxy = require("ui/core/proxy");
+
+var OWNER = "_owner";
+
+export class Layout extends layoutBase.LayoutBase implements definition.Layout {
+ private _viewGroup: android.view.ViewGroup;
+
+ get android(): android.view.ViewGroup {
+ return this._viewGroup;
+ }
+
+ get _nativeView(): android.view.ViewGroup {
+ return this._viewGroup;
+ }
+
+ public _createUI() {
+ this._viewGroup = new view.NativeViewGroup(this._context);
+ this._viewGroup[OWNER] = this;
+ }
+
+ public _onDetached(force?: boolean) {
+ delete this._viewGroup[OWNER];
+ super._onDetached(force);
+ }
+
+ public measure(widthMeasureSpec: number, heightMeasureSpec: number): void {
+ this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
+
+ var view = this._nativeView;
+ if (view) {
+ var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
+ var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
+
+ var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
+ var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
+
+ trace.write(this + " :measure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
+ view.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ public layout(left: number, top: number, right: number, bottom: number): void {
+ this._setCurrentLayoutBounds(left, top, right, bottom);
+
+ var view = this._nativeView;
+ if (view) {
+ this.layoutNativeView(left, top, right, bottom);
+ trace.write(this + " :layout: " + left + ", " + top + ", " + (right - left) + ", " + (bottom - top), trace.categories.Layout);
+ }
+ }
+
+ public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
+ // Don't call super because it will trigger measure again.
+
+ var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
+ var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
+
+ var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
+ var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
+ trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
+ }
+
+ public onLayout(left: number, top: number, right: number, bottom: number): void {
+ // Don't call super because it will trigger layout again.
+ trace.write(this + " :onLayout: " + left + ", " + top + ", " + (right - left) + ", " + (bottom - top), trace.categories.Layout);
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/layout.d.ts b/ui/layouts/layout.d.ts
index b30e85dda..011f74b9c 100644
--- a/ui/layouts/layout.d.ts
+++ b/ui/layouts/layout.d.ts
@@ -1,65 +1,11 @@
declare module "ui/layouts/layout" {
import view = require("ui/core/view");
+ import layoutBase = require("ui/layouts/layout-base");
/**
- * Base class for all views that supports children positioning.
+ * Base class for all views that supports children positioning in cross platform manner.
*/
- export class Layout extends view.View {
-
- /**
- * Returns the number of children in this Layout.
- */
- getChildrenCount(): number;
-
- /**
- * Returns the view at the specified position.
- * @param index The position at which to get the child from.
- */
- getChildAt(index: number): view.View;
-
- /**
- * Returns the position of the child view
- * @param child The child view that we are looking for.
- */
- getChildIndex(child: view.View): number;
-
- /**
- * Adds the view to children array.
- * @param view The view to be added to the end of the children array.
- */
- addChild(view: view.View);
-
- /**
- * Inserts the view to children array at the specified index.
- * @param atIndex The insertion index.
- * @param view The view to be added to the end of the children array.
- */
- insertChild(atIndex: number, child: view.View);
-
- /**
- * Removes the specified view from the children array.
- * @param view The view to remove from the children array.
- */
- removeChild(view: view.View);
-
- /**
- * Specify the bottom padding of this layout.
- */
- paddingBottom: number;
-
- /**
- * Specify the left padding of this layout.
- */
- paddingLeft: number;
-
- /**
- * Specify the right padding of this layout.
- */
- paddingRight: number;
-
- /**
- * Specify the top padding of this layout.
- */
- paddingTop: number;
+ export class Layout extends layoutBase.LayoutBase {
+ //
}
-}
+}
\ No newline at end of file
diff --git a/ui/layouts/layout.ios.ts b/ui/layouts/layout.ios.ts
new file mode 100644
index 000000000..618c4597c
--- /dev/null
+++ b/ui/layouts/layout.ios.ts
@@ -0,0 +1,41 @@
+import definition = require("ui/layouts/layout");
+import layoutBase = require("ui/layouts/layout-base");
+import trace = require("trace");
+import utils = require("utils/utils");
+
+export class Layout extends layoutBase.LayoutBase implements definition.Layout {
+
+ private _view: UIView;
+
+ constructor() {
+ super();
+
+ this._view = new UIView();
+ this._view.autoresizesSubviews = false;
+ }
+
+ get ios(): UIView {
+ return this._view;
+ }
+
+ get _nativeView(): UIView {
+ return this._view;
+ }
+
+ public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
+ // Don't call super because it will measure the native element.
+
+ var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
+ var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
+
+ var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
+ var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
+ trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout);
+ }
+
+ protected onClipToBoundsChanged(oldValue: boolean, newValue: boolean): void {
+ if (this._nativeView) {
+ this._nativeView.clipsToBounds = newValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/stack-layout/stack-layout-common.ts b/ui/layouts/stack-layout/stack-layout-common.ts
new file mode 100644
index 000000000..da71dc3d1
--- /dev/null
+++ b/ui/layouts/stack-layout/stack-layout-common.ts
@@ -0,0 +1,25 @@
+import layouts = require("ui/layouts/layout-base");
+import definition = require("ui/layouts/stack-layout");
+import dependencyObservable = require("ui/core/dependency-observable");
+import enums = require("ui/enums");
+import proxy = require("ui/core/proxy");
+
+// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
+var AffectsLayout = global.android ? dependencyObservable.PropertyMetadataSettings.None : dependencyObservable.PropertyMetadataSettings.AffectsLayout;
+
+function validateOrientation(value: any): boolean {
+ return value === enums.Orientation.vertical || value === enums.Orientation.horizontal;
+}
+
+export class StackLayout extends layouts.LayoutBase implements definition.StackLayout {
+
+ public static orientationProperty = new dependencyObservable.Property("orientation","StackLayout",
+ new proxy.PropertyMetadata(enums.Orientation.vertical, AffectsLayout, undefined, validateOrientation));
+
+ get orientation(): string {
+ return this._getValue(StackLayout.orientationProperty);
+ }
+ set orientation(value: string) {
+ this._setValue(StackLayout.orientationProperty, value);
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/stack-layout/stack-layout.android.ts b/ui/layouts/stack-layout/stack-layout.android.ts
new file mode 100644
index 000000000..e92ea19a1
--- /dev/null
+++ b/ui/layouts/stack-layout/stack-layout.android.ts
@@ -0,0 +1,38 @@
+import dependencyObservable = require("ui/core/dependency-observable");
+import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/stack-layout/stack-layout-common");
+import enums = require("ui/enums");
+
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
+
+export class StackLayout extends common.StackLayout {
+
+ static setNativeOrientationProperty(data: dependencyObservable.PropertyChangeData): void {
+ var stackLayout = data.object;
+ var nativeView = stackLayout._nativeView;
+ if (data.newValue === enums.Orientation.vertical) {
+ nativeView.setOrientation(org.nativescript.widgets.Orientation.vertical);
+ }
+ else {
+ nativeView.setOrientation(org.nativescript.widgets.Orientation.horzontal);
+ }
+ }
+
+ private _layout: org.nativescript.widgets.StackLayout;
+
+ get android(): org.nativescript.widgets.StackLayout {
+ return this._layout;
+ }
+
+ get _nativeView(): org.nativescript.widgets.StackLayout {
+ return this._layout;
+ }
+
+ public _createUI() {
+ this._layout = new org.nativescript.widgets.StackLayout(this._context);
+ }
+}
+
+(common.StackLayout.orientationProperty.metadata).onSetNativeValue = StackLayout.setNativeOrientationProperty;
\ No newline at end of file
diff --git a/ui/layouts/stack-layout/stack-layout.d.ts b/ui/layouts/stack-layout/stack-layout.d.ts
index 09d5e1e7e..9ce0486e5 100644
--- a/ui/layouts/stack-layout/stack-layout.d.ts
+++ b/ui/layouts/stack-layout/stack-layout.d.ts
@@ -1,11 +1,11 @@
declare module "ui/layouts/stack-layout" {
- import layout = require("ui/layouts/layout");
+ import layout = require("ui/layouts/layout-base");
import dependencyObservable = require("ui/core/dependency-observable");
/**
* A Layout that arranges its children horizontally or vertically. The direction can be set by orientation property.
*/
- class StackLayout extends layout.Layout {
+ class StackLayout extends layout.LayoutBase {
/**
* Represents the observable property backing the orientation property of each StackLayout instance.
diff --git a/ui/layouts/stack-layout/stack-layout.ts b/ui/layouts/stack-layout/stack-layout.ios.ts
similarity index 88%
rename from ui/layouts/stack-layout/stack-layout.ts
rename to ui/layouts/stack-layout/stack-layout.ios.ts
index 0da139aa0..79c91ec8b 100644
--- a/ui/layouts/stack-layout/stack-layout.ts
+++ b/ui/layouts/stack-layout/stack-layout.ios.ts
@@ -1,35 +1,16 @@
-import layouts = require("ui/layouts/layout");
-import definition = require("ui/layouts/stack-layout");
-import utils = require("utils/utils");
-import dependencyObservable = require("ui/core/dependency-observable");
+import utils = require("utils/utils");
import enums = require("ui/enums");
-import proxy = require("ui/core/proxy");
import view = require("ui/core/view");
+import common = require("ui/layouts/stack-layout/stack-layout-common");
-function validateOrientation(value: any): boolean {
- return value === enums.Orientation.vertical || value === enums.Orientation.horizontal;
-}
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
-export var orientationProperty = new dependencyObservable.Property(
- "orientation",
- "StackLayout",
- new proxy.PropertyMetadata(enums.Orientation.vertical,
- dependencyObservable.PropertyMetadataSettings.AffectsLayout,
- undefined,
- validateOrientation)
- );
-
-export class StackLayout extends layouts.Layout implements definition.StackLayout {
+export class StackLayout extends common.StackLayout {
private _totalLength = 0;
- get orientation(): string {
- return this._getValue(orientationProperty);
- }
- set orientation(value: string) {
- this._setValue(orientationProperty, value);
- }
-
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
var density = utils.layout.getDisplayDensity();
diff --git a/ui/layouts/wrap-layout/wrap-layout-common.ts b/ui/layouts/wrap-layout/wrap-layout-common.ts
new file mode 100644
index 000000000..f9c1579dc
--- /dev/null
+++ b/ui/layouts/wrap-layout/wrap-layout-common.ts
@@ -0,0 +1,49 @@
+import layouts = require("ui/layouts/layout-base");
+import definition = require("ui/layouts/wrap-layout");
+import dependencyObservable = require("ui/core/dependency-observable");
+import enums = require("ui/enums");
+import proxy = require("ui/core/proxy");
+
+// on Android we explicitly set propertySettings to None because android will invalidate its layout (so we skip unnecessary native call).
+var AffectsLayout = global.android ? dependencyObservable.PropertyMetadataSettings.None : dependencyObservable.PropertyMetadataSettings.AffectsLayout;
+
+function isWidthHeightValid(value: any): boolean {
+ return (value >= 0.0 && value !== Number.POSITIVE_INFINITY);
+}
+
+function isValidOrientation(value: any): boolean {
+ return value === enums.Orientation.vertical || value === enums.Orientation.horizontal;
+}
+
+export class WrapLayout extends layouts.LayoutBase implements definition.WrapLayout {
+
+ public static orientationProperty = new dependencyObservable.Property("orientation", "WrapLayout",
+ new proxy.PropertyMetadata(enums.Orientation.horizontal, AffectsLayout, undefined, isValidOrientation));
+
+ public static itemWidthProperty = new dependencyObservable.Property("itemWidth", "WrapLayout",
+ new proxy.PropertyMetadata(0, AffectsLayout, undefined, isWidthHeightValid));
+
+ public static itemHeightProperty = new dependencyObservable.Property("itemHeight", "WrapLayout",
+ new proxy.PropertyMetadata(0, AffectsLayout, undefined, isWidthHeightValid));
+
+ get orientation(): string {
+ return this._getValue(WrapLayout.orientationProperty);
+ }
+ set orientation(value: string) {
+ this._setValue(WrapLayout.orientationProperty, value);
+ }
+
+ get itemWidth(): number {
+ return this._getValue(WrapLayout.itemWidthProperty);
+ }
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/ui/layouts/wrap-layout/wrap-layout.android.ts b/ui/layouts/wrap-layout/wrap-layout.android.ts
new file mode 100644
index 000000000..0aa0f0585
--- /dev/null
+++ b/ui/layouts/wrap-layout/wrap-layout.android.ts
@@ -0,0 +1,46 @@
+import dependencyObservable = require("ui/core/dependency-observable");
+import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/wrap-layout/wrap-layout-common");
+
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
+
+export class WrapLayout extends common.WrapLayout {
+
+ static setNativeOrientationProperty(data: dependencyObservable.PropertyChangeData): void {
+ var wrapLayout = data.object;
+ var nativeView = wrapLayout._nativeView;
+ nativeView.setOrientation(data.newValue);
+ }
+
+ static setNativeItemWidthProperty(data: dependencyObservable.PropertyChangeData): void {
+ var wrapLayout = data.object;
+ var nativeView = wrapLayout._nativeView;
+ nativeView.setItemWidth(data.newValue);
+ }
+
+ static setNativeItemHeightProperty(data: dependencyObservable.PropertyChangeData): void {
+ var wrapLayout = data.object;
+ var nativeView = wrapLayout._nativeView;
+ nativeView.setItemHeight(data.newValue);
+ }
+
+ private _layout: org.nativescript.widgets.WrapLayout;
+
+ get android(): org.nativescript.widgets.WrapLayout {
+ return this._layout;
+ }
+
+ get _nativeView(): org.nativescript.widgets.WrapLayout {
+ return this._layout;
+ }
+
+ public _createUI() {
+ this._layout = new org.nativescript.widgets.WrapLayout(this._context);
+ }
+}
+
+(common.WrapLayout.orientationProperty.metadata).onSetNativeValue = WrapLayout.setNativeOrientationProperty;
+(common.WrapLayout.orientationProperty.metadata).onSetNativeValue = WrapLayout.setNativeItemWidthProperty;
+(common.WrapLayout.orientationProperty.metadata).onSetNativeValue = WrapLayout.setNativeItemHeightProperty;
\ No newline at end of file
diff --git a/ui/layouts/wrap-layout/wrap-layout.d.ts b/ui/layouts/wrap-layout/wrap-layout.d.ts
index 156569499..eabab3642 100644
--- a/ui/layouts/wrap-layout/wrap-layout.d.ts
+++ b/ui/layouts/wrap-layout/wrap-layout.d.ts
@@ -1,12 +1,12 @@
declare module "ui/layouts/wrap-layout" {
- import layout = require("ui/layouts/layout");
+ import layout = require("ui/layouts/layout-base");
import dependencyObservable = require("ui/core/dependency-observable");
/**
* WrapLayout position children in rows or columns depending on orientation property
* until space is filled and then wraps them on new row or column.
*/
- class WrapLayout extends layout.Layout {
+ class WrapLayout extends layout.LayoutBase {
/**
* Represents the observable property backing the orientation property of each WrapLayout instance.
diff --git a/ui/layouts/wrap-layout/wrap-layout.ts b/ui/layouts/wrap-layout/wrap-layout.ios.ts
similarity index 78%
rename from ui/layouts/wrap-layout/wrap-layout.ts
rename to ui/layouts/wrap-layout/wrap-layout.ios.ts
index 3194658dc..ce70f661c 100644
--- a/ui/layouts/wrap-layout/wrap-layout.ts
+++ b/ui/layouts/wrap-layout/wrap-layout.ios.ts
@@ -1,73 +1,20 @@
-import layouts = require("ui/layouts/layout");
-import definition = require("ui/layouts/wrap-layout");
-import utils = require("utils/utils");
-import dependencyObservable = require("ui/core/dependency-observable");
+import utils = require("utils/utils");
import view = require("ui/core/view");
import enums = require("ui/enums");
+import dependencyObservable = require("ui/core/dependency-observable");
import proxy = require("ui/core/proxy");
+import common = require("ui/layouts/wrap-layout/wrap-layout-common");
-function isWidthHeightValid(value: any): boolean {
- return isNaN(value) || (value >= 0.0 && value !== Number.POSITIVE_INFINITY);
-}
+// merge the exports of the common file with the exports of this file
+declare var exports;
+require("utils/module-merge").merge(common, exports);
-function isValidOrientation(value: any): boolean {
- return value === enums.Orientation.vertical || value === enums.Orientation.horizontal;
-}
-
-export class WrapLayout extends layouts.Layout implements definition.WrapLayout {
+export class WrapLayout extends common.WrapLayout {
private _lenghts: Array;
- public static orientationProperty = new dependencyObservable.Property(
- "orientation",
- "WrapLayout",
- new proxy.PropertyMetadata(enums.Orientation.horizontal,
- dependencyObservable.PropertyMetadataSettings.AffectsLayout,
- undefined,
- isValidOrientation)
- );
-
- public static itemWidthProperty = new dependencyObservable.Property(
- "itemWidth",
- "WrapLayout",
- new proxy.PropertyMetadata(Number.NaN,
- dependencyObservable.PropertyMetadataSettings.AffectsLayout,
- undefined,
- isWidthHeightValid)
- );
-
- public static itemHeightProperty = new dependencyObservable.Property(
- "itemHeight",
- "WrapLayout",
- new proxy.PropertyMetadata(Number.NaN,
- dependencyObservable.PropertyMetadataSettings.AffectsLayout,
- undefined,
- isWidthHeightValid)
- );
-
- get orientation(): string {
- return this._getValue(WrapLayout.orientationProperty);
- }
- set orientation(value: string) {
- this._setValue(WrapLayout.orientationProperty, value);
- }
-
- get itemWidth(): number {
- return this._getValue(WrapLayout.itemWidthProperty);
- }
- 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);
- }
-
private static getChildMeasureSpec(parentMode: number, parentLength: number, itemLength): number {
- if (!isNaN(itemLength)) {
+ if (itemLength > 0) {
return utils.layout.makeMeasureSpec(itemLength, utils.layout.EXACTLY);
}
else if (parentMode === utils.layout.UNSPECIFIED) {
diff --git a/ui/scroll-view/scroll-view.android.ts b/ui/scroll-view/scroll-view.android.ts
index 4cf4a887d..33fe51e4c 100644
--- a/ui/scroll-view/scroll-view.android.ts
+++ b/ui/scroll-view/scroll-view.android.ts
@@ -1,5 +1,4 @@
import dependencyObservable = require("ui/core/dependency-observable");
-import view = require("ui/core/view");
import definition = require("ui/scroll-view");
import contentView = require("ui/content-view");
import common = require("ui/scroll-view/scroll-view-common");
@@ -8,97 +7,12 @@ import enums = require("ui/enums");
global.moduleMerge(common, exports);
-var OWNER = "_owner";
-var STATE = "_scrollViewState";
-
common.orientationProperty.metadata.onValueChanged = function scrollViewOrientationChanged(data: dependencyObservable.PropertyChangeData) {
(data.object)._onOrientationChanged(data.oldValue, data.newValue);
}
-interface ScrollViewState {
- scrollX: number;
- scrollY: number;
-}
-
-function onMeasureScrollView(widthMeasureSpec: number, heightMeasureSpec: number) {
- var owner: view.View = this.owner;
- owner.onMeasure(widthMeasureSpec, heightMeasureSpec);
- this.setMeasuredDimension(owner.getMeasuredWidth(), owner.getMeasuredHeight());
-}
-
-function onLayoutScrollView(changed: boolean, left: number, top: number, right: number, bottom: number) {
- var owner: view.View = this.owner;
- owner.onLayout(left, top, right, bottom);
-
- // Restore scroll state on the first layout after native instance is recreated.
- var state: ScrollViewState = owner[STATE];
- if (state) {
- this.scrollTo(state.scrollX, state.scrollY);
- delete owner[STATE];
- }
-}
-
-function onSaveInstanceStateNative(): android.os.Parcelable {
- var state: ScrollViewState = {
- scrollX: this.getScrollX(),
- scrollY: this.getScrollY()
- }
- this.owner[STATE] = state;
- return this.super.onSaveInstanceState();
-}
-
-class NativeVerticalScrollView extends android.widget.ScrollView {
- constructor(ctx) {
- super(ctx);
- return global.__native(this);
- }
-
- get owner() {
- return this[OWNER];
- }
-
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
- onMeasureScrollView.apply(this, [widthMeasureSpec, heightMeasureSpec]);
- }
-
- public onLayout(changed: boolean, left: number, top: number, right: number, bottom: number) {
- onLayoutScrollView.apply(this, [changed, left, top, right, bottom]);
- }
-
- public onSaveInstanceState() {
- onSaveInstanceStateNative.apply(this, []);
- }
-};
-
-class NativeHorizontalScrollView extends android.widget.HorizontalScrollView {
- constructor(ctx) {
- super(ctx);
- return global.__native(this);
- }
-
- get owner() {
- return this[OWNER];
- }
-
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
- onMeasureScrollView.apply(this, [widthMeasureSpec, heightMeasureSpec]);
- }
-
- public onLayout(changed: boolean, left: number, top: number, right: number, bottom: number) {
- onLayoutScrollView.apply(this, [changed, left, top, right, bottom]);
- }
-
- public onSaveInstanceState() {
- onSaveInstanceStateNative.apply(this, []);
- }
-};
-
export class ScrollView extends contentView.ContentView implements definition.ScrollView {
- private _android: android.widget.FrameLayout;
- private _contentMeasuredWidth: number = 0;
- private _contentMeasuredHeight: number = 0;
-
- private _scrollableLength: number = 0;
+ private _android: org.nativescript.widgets.VerticalScrollView | org.nativescript.widgets.HorizontalScrollView;
private _androidViewId: number;
get android(): android.view.ViewGroup {
@@ -137,7 +51,7 @@ export class ScrollView extends contentView.ContentView implements definition.Sc
return 0;
}
- return this._scrollableLength;
+ return this._android.getScrollableLength();
}
get scrollableHeight(): number {
@@ -145,19 +59,18 @@ export class ScrollView extends contentView.ContentView implements definition.Sc
return 0;
}
- return this._scrollableLength;
+ return this._android.getScrollableLength();
}
public scrollToVerticalOffset(value: number, animated: boolean) {
if (this._android && this.orientation === enums.Orientation.vertical) {
value *= utils.layout.getDisplayDensity();
- var scrollView: android.widget.ScrollView = (this._android);
if (animated) {
- scrollView.smoothScrollTo(0, value);
+ this._android.smoothScrollTo(0, value);
}
else {
- scrollView.scrollTo(0, value);
+ this._android.scrollTo(0, value);
}
}
}
@@ -166,30 +79,28 @@ export class ScrollView extends contentView.ContentView implements definition.Sc
if (this._android && this.orientation === enums.Orientation.horizontal) {
value *= utils.layout.getDisplayDensity();
- var scrollView: android.widget.HorizontalScrollView = (this._android);
if (animated) {
- scrollView.smoothScrollTo(value, 0);
+ this._android.smoothScrollTo(value, 0);
}
else {
- scrollView.scrollTo(value, 0);
+ this._android.scrollTo(value, 0);
}
}
}
public _createUI() {
if (this.orientation === enums.Orientation.horizontal) {
- this._android = new NativeHorizontalScrollView(this._context);
+ this._android = new org.nativescript.widgets.HorizontalScrollView(this._context);
}
else {
- this._android = new NativeVerticalScrollView(this._context);
+ this._android = new org.nativescript.widgets.VerticalScrollView(this._context);
}
if (!this._androidViewId) {
this._androidViewId = android.view.View.generateViewId();
}
- this._android.setId(this._androidViewId);
- this._android[OWNER] = this;
+ this._android.setId(this._androidViewId);
}
public _onOrientationChanged(oldValue: string, newValue: string) {
@@ -205,56 +116,4 @@ export class ScrollView extends contentView.ContentView implements definition.Sc
}
}
}
-
- public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
- // Don't call measure because it will measure content twice.
- var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
- var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
-
- var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
- var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
-
- var density = utils.layout.getDisplayDensity();
- var child = this.content
- if (!child) {
- this._scrollableLength = 0;
- this._contentMeasuredWidth = this.minWidth * density;
- this._contentMeasuredHeight = this.minHeight * density;
- }
- else {
- var childSize: { measuredWidth: number; measuredHeight: number };
- if (this.orientation === enums.Orientation.vertical) {
- childSize = view.View.measureChild(this, child, widthMeasureSpec, utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED));
- }
- else {
- childSize = view.View.measureChild(this, child, utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED), heightMeasureSpec);
- }
-
- this._contentMeasuredWidth = Math.max(childSize.measuredWidth, this.minWidth * density);
- this._contentMeasuredHeight = Math.max(childSize.measuredHeight, this.minHeight * density);
- }
-
- var widthAndState = view.View.resolveSizeAndState(this._contentMeasuredWidth, width, widthMode, 0);
- var heightAndState = view.View.resolveSizeAndState(this._contentMeasuredHeight, height, heightMode, 0);
-
- this.setMeasuredDimension(widthAndState, heightAndState);
- }
-
- public onLayout(left: number, top: number, right: number, bottom: number): void {
-
- var width = (right - left);
- var height = (bottom - top);
-
- if (this.orientation === enums.Orientation.horizontal) {
- this._scrollableLength = this._contentMeasuredWidth - width;
- view.View.layoutChild(this, this.content, 0, 0, Math.max(this._contentMeasuredWidth, width), height);
- }
- else {
- this._scrollableLength = this._contentMeasuredHeight - height;
- view.View.layoutChild(this, this.content, 0, 0, width, Math.max(this._contentMeasuredHeight, height));
- }
-
- this._scrollableLength /= utils.layout.getDisplayDensity();
- this._scrollableLength = Math.max(0, this._scrollableLength);
- }
}
\ No newline at end of file
diff --git a/ui/styling/style.ts b/ui/styling/style.ts
index 6b5b08375..baf5cd679 100644
--- a/ui/styling/style.ts
+++ b/ui/styling/style.ts
@@ -23,6 +23,216 @@ var _handlersCache = {};
// classes like Frame that does not need to handle styling properties.
var noStylingClasses = {};
+// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
+var AffectsLayout = global.android ? dependencyObservable.PropertyMetadataSettings.None : dependencyObservable.PropertyMetadataSettings.AffectsLayout;
+
+export interface Thickness {
+ left: number;
+ top: number;
+ right: number;
+ bottom: number;
+}
+
+export interface CommonLayoutParams {
+ width: number;
+ height: number;
+
+ leftMargin: number;
+ topMargin: number;
+ rightMargin: number;
+ bottomMargin: number;
+
+ horizontalAlignment: string;
+ verticalAlignment: string;
+}
+
+function parseThickness(value: any): Thickness {
+ var result: Thickness = { top: 0, right: 0, bottom: 0, left: 0 };
+ if (types.isString(value)) {
+ var arr = value.split(/[ ,]+/);
+ var top = parseInt(arr[0]);
+ top = isNaN(top) ? 0 : top;
+
+ var right = parseInt(arr[1]);
+ right = isNaN(right) ? top : right;
+
+ var bottom = parseInt(arr[2]);
+ bottom = isNaN(bottom) ? top : bottom;
+
+ var left = parseInt(arr[3]);
+ left = isNaN(left) ? right : left;
+
+ result.top = top;
+ result.right = right;
+ result.bottom = bottom;
+ result.left = left;
+
+ } else if (types.isNumber(value)) {
+ result.top = result.right = result.bottom = result.left = value;
+ }
+
+ return result;
+}
+
+function layoutParamsComparer(x: CommonLayoutParams, y: CommonLayoutParams): boolean {
+ return x.width === y.width
+ && x.height === y.height
+ && x.leftMargin === y.leftMargin
+ && x.topMargin === y.topMargin
+ && x.rightMargin === y.rightMargin
+ && x.bottomMargin === y.bottomMargin
+ && x.horizontalAlignment === y.horizontalAlignment
+ && x.verticalAlignment === y.verticalAlignment
+}
+
+function onLayoutParamsChanged(data: observable.PropertyChangeData) {
+ var style =