diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 5344348f3..7acaf01a7 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -217,13 +217,19 @@ + + + layout-helper.d.ts + - + + layout-helper.d.ts + diff --git a/android17.d.ts b/android17.d.ts index 2166aee83..a037485ed 100644 --- a/android17.d.ts +++ b/android17.d.ts @@ -117006,7 +117006,12 @@ declare module android { ContentDescription: string; MinimumHeight: number; DrawingCacheQuality: number; - toString(): string; + toString(): string; + + protected onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void; + + protected onLayout(changed: boolean, left: number, top: number, right: number, bottom: number): void; + /** * Returns the size of the vertical faded edges used to indicate that more content in this view is visible. */ diff --git a/apps/tests/layouts/layout-helper.android.ts b/apps/tests/layouts/layout-helper.android.ts new file mode 100644 index 000000000..359de09b9 --- /dev/null +++ b/apps/tests/layouts/layout-helper.android.ts @@ -0,0 +1,120 @@ +import button = require("ui/button"); +import utils = require("utils/utils"); +import TKUnit = require("../TKUnit"); + +var DELTA = 0.1; + +class NativeButton extends android.widget.Button { + + private owner: MyButton; + + constructor(context: android.content.Context, owner: MyButton) { + super(context); + this.owner = owner; + return global.__native(this); + } + + protected onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + this.owner._measureWidth = utils.layout.getMeasureSpecSize(widthMeasureSpec); + this.owner._measureHeight = utils.layout.getMeasureSpecSize(heightMeasureSpec); + this.owner.measureCount++; + } + + protected onLayout(changed: boolean, left: number, top: number, right: number, bottom: number): void { + this.owner._layoutLeft = left; + this.owner._layoutTop = top; + this.owner._layoutWidth = right - left; + this.owner._layoutHeight = bottom - top; + + super.onLayout(changed, left, top, right, bottom); + this.owner.arrangeCount++; + } +} + +export class MyButton extends button.Button { + public measureCount: number = 0; + public arrangeCount: number = 0; + + _layoutLeft; + _layoutTop; + _layoutWidth; + _layoutHeight; + + _measureWidth; + _measureHeight; + + public get measured(): boolean { + return this.measureCount > 0; + } + + public get arranged(): boolean { + return this.arrangeCount > 0; + } + + private _layout: NativeButton; + + get android(): NativeButton { + return this._layout; + } + + get _nativeView(): NativeButton { + return this._layout; + } + + public _createUI() { + this._layout = new NativeButton(this._context, this); + } + + get measureHeight(): number { + return this._measureHeight; + } + + get measureWidth(): number { + return this._measureWidth; + } + + get layoutWidth(): number { + return this._layoutWidth; + } + + get layoutHeight(): number { + return this._layoutHeight; + } + + get layoutLeft(): number { + return this._layoutLeft; + } + + get layoutTop(): number { + return this._layoutTop; + } +} + +export function assertMeasure(btn: MyButton, width: number, height: number, name?: string) { + var density = utils.layout.getDisplayDensity(); + + var delta = Math.floor(density) !== density ? 1.1 : DELTA; + name = name ? "[" + name + "]" : ""; + + TKUnit.assertAreClose(Math.floor(btn.measureWidth / density), width, delta, name + "width"); + TKUnit.assertAreClose(Math.floor(btn.measureHeight / density), height, delta, name + "height"); +} + +export function assertLayout(btn: MyButton, left: number, top: number, width: number, height: number, name?: string): void { + var density = utils.layout.getDisplayDensity(); + + var delta = Math.floor(density) !== density ? 1.1 : DELTA; + name = name ? "[" + name + "]" : ""; + + TKUnit.assertAreClose(Math.floor(btn.layoutLeft / density), left, delta, name + "left"); + TKUnit.assertAreClose(Math.floor(btn.layoutTop / density), top, delta, name + "top"); + TKUnit.assertAreClose(Math.floor(btn.layoutWidth / density), width, delta, name + "width"); + TKUnit.assertAreClose(Math.floor(btn.layoutHeight / density), height, delta, name + "height"); +} + +export function dip(value: number): number { + var density = utils.layout.getDisplayDensity(); + return Math.floor(value * density); +} \ No newline at end of file diff --git a/apps/tests/layouts/layout-helper.ts b/apps/tests/layouts/layout-helper.ios.ts similarity index 100% rename from apps/tests/layouts/layout-helper.ts rename to apps/tests/layouts/layout-helper.ios.ts diff --git a/apps/tests/layouts/stack-layout-tests.ts b/apps/tests/layouts/stack-layout-tests.ts index a63484767..b4dde9a90 100644 --- a/apps/tests/layouts/stack-layout-tests.ts +++ b/apps/tests/layouts/stack-layout-tests.ts @@ -217,7 +217,7 @@ export function test_insertChildAtPosition() { let newChild = new Button(); newChild.text = 'in-between'; - rootLayout.insertChild(1, newChild); + rootLayout.insertChild(newChild, 1); assertChildTexts("btn1|in-between|btn2", rootLayout, "button inserted at correct location"); } diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index bc83e1d43..00d0701da 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -777,7 +777,7 @@ export class View extends proxy.ProxyObject implements definition.View { * Core logic for adding a child view to this instance. Used by the framework to handle lifecycle events more centralized. Do not outside the UI Stack implementation. * // TODO: Think whether we need the base Layout routine. */ - public _addView(view: View) { + public _addView(view: View, atIndex?: number) { if (!view) { throw new Error("Expecting a valid View instance."); } @@ -787,7 +787,7 @@ export class View extends proxy.ProxyObject implements definition.View { } view._parent = this; - this._addViewCore(view); + this._addViewCore(view, atIndex); trace.write("called _addView on view " + this._domId + " for a child " + view._domId, trace.categories.ViewHierarchy); } @@ -795,13 +795,13 @@ export class View extends proxy.ProxyObject implements definition.View { /** * Method is intended to be overridden by inheritors and used as "protected" */ - public _addViewCore(view: View) { + public _addViewCore(view: View, atIndex?: number) { this._propagateInheritableProperties(view); view.style._inheritStyleProperties(); if (!view._isAddedToNativeVisualTree) { - view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view); + view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, atIndex); } // TODO: Discuss this. @@ -874,7 +874,7 @@ export class View extends proxy.ProxyObject implements definition.View { /** * Method is intended to be overridden by inheritors and used as "protected". */ - public _addViewToNativeVisualTree(view: View): boolean { + public _addViewToNativeVisualTree(view: View, atIndex?: number): boolean { if (view._isAddedToNativeVisualTree) { throw new Error("Child already added to the native visual tree."); } diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts index 94e30ade3..22e9ec740 100644 --- a/ui/core/view.android.ts +++ b/ui/core/view.android.ts @@ -5,6 +5,7 @@ import utils = require("utils/utils"); import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); import gestures = require("ui/gestures"); +import types = require("utils/types"); global.moduleMerge(viewCommon, exports); @@ -336,11 +337,16 @@ export class CustomLayoutView extends View implements viewDefinition.CustomLayou this._viewGroup = new org.nativescript.widgets.ContentLayout(this._context); } - public _addViewToNativeVisualTree(child: View): boolean { + public _addViewToNativeVisualTree(child: View, atIndex?: number): boolean { super._addViewToNativeVisualTree(child); if (this._nativeView && child._nativeView) { - this._nativeView.addView(child._nativeView); + if (types.isNullOrUndefined(atIndex) || atIndex >= this._nativeView.getChildCount()) { + this._nativeView.addView(child._nativeView); + } + else { + this._nativeView.addView(child._nativeView, atIndex); + } return true; } diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts index c4772c36c..92f4021d7 100644 --- a/ui/core/view.d.ts +++ b/ui/core/view.d.ts @@ -387,7 +387,7 @@ declare module "ui/core/view" { onUnloaded(): void; isLoaded: boolean; - _addView(view: View); + _addView(view: View, atIndex?: number); _propagateInheritableProperties(view: View) _inheritProperties(parentView: View) _removeView(view: View); @@ -407,7 +407,7 @@ declare module "ui/core/view" { /** * Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise. */ - _addViewToNativeVisualTree(view: View): boolean; + _addViewToNativeVisualTree(view: View, atIndex?: number): boolean; _removeViewFromNativeVisualTree(view: View): void; _eachChildView(callback: (child: View) => boolean); diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts index b926fc91f..1e1e4127d 100644 --- a/ui/core/view.ios.ts +++ b/ui/core/view.ios.ts @@ -4,6 +4,7 @@ import utils = require("utils/utils"); import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); import background = require("ui/styling/background"); +import types = require("utils/types"); global.moduleMerge(viewCommon, exports); @@ -251,11 +252,17 @@ export class CustomLayoutView extends View { trace.write(this + " :onMeasure: " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout); } - public _addViewToNativeVisualTree(child: View): boolean { + public _addViewToNativeVisualTree(child: View, atIndex: number): boolean { super._addViewToNativeVisualTree(child); if (this._nativeView && child._nativeView) { - this._nativeView.addSubview(child._nativeView); + if (types.isNullOrUndefined(atIndex) || atIndex >= this._nativeView.subviews.count) { + this._nativeView.addSubview(child._nativeView); + } + else { + this._nativeView.insertSubviewAtIndex(child._nativeView, atIndex); + } + return true; } diff --git a/ui/layouts/layout-base.d.ts b/ui/layouts/layout-base.d.ts index 316e54cc2..f36d88968 100644 --- a/ui/layouts/layout-base.d.ts +++ b/ui/layouts/layout-base.d.ts @@ -20,12 +20,25 @@ */ 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 view The view to be added to the end of the children array. + * @param atIndex The insertion index. + */ + insertChild(child: view.View, atIndex: number); + /** * Removes the specified view from the children array. * @param view The view to remove from the children array. diff --git a/ui/layouts/layout-base.ts b/ui/layouts/layout-base.ts index 108f43c48..66b29d44b 100644 --- a/ui/layouts/layout-base.ts +++ b/ui/layouts/layout-base.ts @@ -43,8 +43,8 @@ export class LayoutBase extends view.CustomLayoutView implements definition.Layo this._subViews.push(child); } - public insertChild(atIndex: number, child: view.View) { - this._addView(child); + public insertChild(child: view.View, atIndex: number) { + this._addView(child, atIndex); this._subViews.splice(atIndex, 0, child); } diff --git a/ui/styling/style.ts b/ui/styling/style.ts index 7cf4de96c..afbc73cf1 100644 --- a/ui/styling/style.ts +++ b/ui/styling/style.ts @@ -188,6 +188,38 @@ function onBackgroundImagePropertyChanged(data: observable.PropertyChangeData) { } } +function onBackgroundColorPropertyChanged(data: observable.PropertyChangeData) { + var style =