diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 0c7ef0322..7568e227f 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -89,6 +89,7 @@ modal-page.xml + @@ -726,6 +727,8 @@ + + @@ -2087,6 +2090,7 @@ + @@ -2149,7 +2153,7 @@ False - + \ No newline at end of file diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts index c5f896464..4d9da1006 100644 --- a/apps/tests/testRunner.ts +++ b/apps/tests/testRunner.ts @@ -25,6 +25,7 @@ export function isRunningOnEmulator(): boolean { } export var allTests = {}; +allTests["PROXY-VIEW-CONTAINER"] = require("./ui/proxy-view-container/proxy-view-container-tests") allTests["SCROLL-VIEW"] = require("./ui/scroll-view/scroll-view-tests"); allTests["ACTION-BAR"] = require("./ui/action-bar/action-bar-tests"); allTests["XML-DECLARATION"] = require("./xml-declaration/xml-declaration-tests"); diff --git a/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts b/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts new file mode 100644 index 000000000..1d2601331 --- /dev/null +++ b/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts @@ -0,0 +1,160 @@ +import TKUnit = require("../../TKUnit"); +import helper = require("../helper"); +import viewModule = require("ui/core/view"); +import observable = require("data/observable"); +import color = require("color"); +import platform = require("platform"); + +import {ProxyViewContainer} from "ui/proxy-view-container"; +import {View, Button, StackLayout} from "ui"; + +export function test_add_children_to_attached_proxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + function testAction(views: Array) { + outer.addChild(createBtn("1")); + outer.addChild(proxy); + proxy.addChild(createBtn("2")); + proxy.addChild(createBtn("3")); + proxy.addChild(createBtn("4")); + + outer.addChild(createBtn("5")); + + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_add_children_to_detached_proxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + function testAction(views: Array) { + outer.addChild(createBtn("1")); + + proxy.addChild(createBtn("2")); + proxy.addChild(createBtn("3")); + proxy.addChild(createBtn("4")); + outer.addChild(proxy); + + outer.addChild(createBtn("5")); + + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_remove_proxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + outer.addChild(createBtn("1")); + + outer.addChild(proxy); + proxy.addChild(createBtn("2")); + proxy.addChild(createBtn("3")); + proxy.addChild(createBtn("4")); + + outer.addChild(createBtn("5")); + + function testAction(views: Array) { + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + outer.removeChild(proxy); + assertNativeChildren(outer, ["1", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_remove_child_of_attached_proxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + outer.addChild(createBtn("1")); + + outer.addChild(proxy); + proxy.addChild(createBtn("2")); + var testBtn = createBtn("3") + proxy.addChild(testBtn); + proxy.addChild(createBtn("4")); + + outer.addChild(createBtn("5")); + + function testAction(views: Array) { + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + proxy.removeChild(testBtn); + assertNativeChildren(outer, ["1", "2", "4", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_insert_inside_porxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + outer.addChild(createBtn("1")); + + outer.addChild(proxy); + proxy.addChild(createBtn("2")); + proxy.addChild(createBtn("4")); + + outer.addChild(createBtn("5")); + + function testAction(views: Array) { + assertNativeChildren(outer, ["1", "2", "4", "5"]); + proxy.insertChild(createBtn("3"), 1); + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_insert_after_porxy() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + outer.addChild(createBtn("1")); + + outer.addChild(proxy); + proxy.addChild(createBtn("2")); + proxy.addChild(createBtn("3")); + proxy.addChild(createBtn("4")); + + function testAction(views: Array) { + assertNativeChildren(outer, ["1", "2", "3", "4"]); + outer.insertChild(createBtn("5"), 2); + assertNativeChildren(outer, ["1", "2", "3", "4", "5"]); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +function createBtn(text: string): Button { + var b = new Button(); + b.text = text; + return b; +} + +function assertNativeChildren(stack: StackLayout, arr: Array) { + if (stack.android) { + let android: org.nativescript.widgets.StackLayout = stack.android; + TKUnit.assertEqual(android.getChildCount(), arr.length, "Native children"); + for (let i = 0; i < arr.length; i++) { + let nativeBtn = android.getChildAt(i); + TKUnit.assertEqual(nativeBtn.getText(), arr[i]); + } + } else if (stack.ios) { + let ios: UIView = stack.ios; + TKUnit.assertEqual(ios.subviews.count, arr.length, "Native children"); + for (let i = 0; i < arr.length; i++) { + let nativeBtn = ios.subviews[i]; + TKUnit.assertEqual(nativeBtn.titleLabel.text, arr[i]); + } + } else { + TKUnit.assert(false, "No native view to assert"); + } +} diff --git a/tsconfig.json b/tsconfig.json index d7bf4287a..db6ff7be7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -265,6 +265,7 @@ "apps/tests/ui/page/test-page-module.ts", "apps/tests/ui/placeholder/placeholder-tests.ts", "apps/tests/ui/progress/progress-tests.ts", + "apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts", "apps/tests/ui/repeater/repeater-tests.ts", "apps/tests/ui/repeater/repeaterItems-bindingToGestures.ts", "apps/tests/ui/scroll-view/scroll-view-tests.ts", @@ -594,6 +595,8 @@ "ui/progress/progress.android.ts", "ui/progress/progress.d.ts", "ui/progress/progress.ios.ts", + "ui/proxy-view-container/proxy-view-container.d.ts", + "ui/proxy-view-container/proxy-view-container.ts", "ui/repeater/repeater.d.ts", "ui/repeater/repeater.ts", "ui/scroll-view/scroll-view-common.ts", diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index e8701b9f3..cb6e4e4fd 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -22,7 +22,7 @@ registerSpecialProperty("class", (instance: definition.View, propertyValue: stri instance.className = propertyValue; }); -function getEventOrGestureName(name: string) : string { +function getEventOrGestureName(name: string): string { return name.indexOf("on") === 0 ? name.substr(2, name.length - 2) : name; } @@ -190,7 +190,7 @@ export class View extends ProxyObject implements definition.View { observe(type: gestures.GestureTypes, callback: (args: gestures.GestureEventData) => void, thisArg?: any): void { if (!this._gestureObservers[type]) { this._gestureObservers[type] = []; - } + } this._gestureObservers[type].push(gestures.observe(this, type, callback, thisArg)); } @@ -926,6 +926,26 @@ export class View extends ProxyObject implements definition.View { // } + _childIndexToNativeChildIndex(index?: number): number { + return index; + } + + _getNativeViewsCount(): number { + return this._isAddedToNativeVisualTree ? 1 : 0; + } + + _eachLayoutView(callback: (View) => void): void { + return callback(this); + } + + _addToSuperview(superview: any, index?: number): boolean { + // IOS specific + return false; + } + _removeFromSuperview(): void { + // IOS specific + } + /** * 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. @@ -954,7 +974,8 @@ export class View extends ProxyObject implements definition.View { view.style._inheritStyleProperties(); if (!view._isAddedToNativeVisualTree) { - view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, atIndex); + var nativeIndex = this._childIndexToNativeChildIndex(atIndex); + view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex); } // TODO: Discuss this. diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts index a95f66ec4..e10a1dc0f 100644 --- a/ui/core/view.d.ts +++ b/ui/core/view.d.ts @@ -445,7 +445,7 @@ declare module "ui/core/view" { * Sets in-line CSS string as style. * @param style - In-line CSS string. */ - public setInlineStyle(style: string) : void; + public setInlineStyle(style: string): void; public getGestureObservers(type: gestures.GestureTypes): Array; @@ -497,6 +497,14 @@ declare module "ui/core/view" { _removeView(view: View); _context: any /* android.content.Context */; + _childIndexToNativeChildIndex(index?: number): number; + _getNativeViewsCount(): number; + + _eachLayoutView(callback: (View) => void): void; + + _addToSuperview(superview: any, index?: number): boolean; + _removeFromSuperview(); + public _applyXmlAttribute(attribute: string, value: any): boolean; // TODO: Implement logic for stripping these lines out diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts index c4725f048..b4dc36f59 100644 --- a/ui/core/view.ios.ts +++ b/ui/core/view.ios.ts @@ -1,4 +1,5 @@ -import viewCommon = require("./view-common"); +import types = require("utils/types"); +import viewCommon = require("./view-common"); import trace = require("trace"); import utils = require("utils/utils"); import dependencyObservable = require("ui/core/dependency-observable"); @@ -265,6 +266,26 @@ export class View extends viewCommon.View { if (this._cachedFrame) { this._setNativeViewFrame(this._nativeView, this._cachedFrame); } + } + + public _addToSuperview(superview: any, atIndex?: number): boolean { + if (superview && this._nativeView) { + if (types.isNullOrUndefined(atIndex) || atIndex >= superview.subviews.count) { + superview.addSubview(this._nativeView); + } else { + superview.insertSubviewAtIndex(this._nativeView, atIndex); + } + + return true; + } + + return false; + } + + public _removeFromSuperview() { + if (this._nativeView) { + this._nativeView.removeFromSuperview(); + } } } @@ -290,30 +311,15 @@ export class CustomLayoutView extends View { } public _addViewToNativeVisualTree(child: View, atIndex: number): boolean { - super._addViewToNativeVisualTree(child); + super._addViewToNativeVisualTree(child, atIndex); - if (this._nativeView && child._nativeView) { - var types = require("utils/types"); - - if (types.isNullOrUndefined(atIndex) || atIndex >= this._nativeView.subviews.count) { - this._nativeView.addSubview(child._nativeView); - } - else { - this._nativeView.insertSubviewAtIndex(child._nativeView, atIndex); - } - - return true; - } - - return false; + return child._addToSuperview(this._nativeView, atIndex); } public _removeViewFromNativeVisualTree(child: View): void { super._removeViewFromNativeVisualTree(child); - if (child._nativeView) { - child._nativeView.removeFromSuperview(); - } + child._removeFromSuperview(); } } diff --git a/ui/layouts/absolute-layout/absolute-layout.ios.ts b/ui/layouts/absolute-layout/absolute-layout.ios.ts index d95b29001..2722dbe4f 100644 --- a/ui/layouts/absolute-layout/absolute-layout.ios.ts +++ b/ui/layouts/absolute-layout/absolute-layout.ios.ts @@ -30,16 +30,11 @@ export class AbsoluteLayout extends common.AbsoluteLayout { let childMeasureSpec = utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED); let density = utils.layout.getDisplayDensity(); - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let childSize = View.measureChild(this, child, childMeasureSpec, childMeasureSpec); measureWidth = Math.max(measureWidth, AbsoluteLayout.getLeft(child) * density + childSize.measuredWidth); measureHeight = Math.max(measureHeight, AbsoluteLayout.getTop(child) * density + childSize.measuredHeight); - } + }); measureWidth += (this.paddingLeft + this.paddingRight) * density; measureHeight += (this.paddingTop + this.paddingBottom) * density; @@ -57,12 +52,7 @@ export class AbsoluteLayout extends common.AbsoluteLayout { super.onLayout(left, top, right, bottom); let density = utils.layout.getDisplayDensity(); - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); let childWidth = child.getMeasuredWidth(); @@ -74,7 +64,7 @@ export class AbsoluteLayout extends common.AbsoluteLayout { let childBottom = childTop + childHeight + (lp.topMargin + lp.bottomMargin) * density; View.layoutChild(this, child, childLeft, childTop, childRight, childBottom); - } + }); AbsoluteLayout.restoreOriginalParams(this); } diff --git a/ui/layouts/dock-layout/dock-layout.ios.ts b/ui/layouts/dock-layout/dock-layout.ios.ts index c5a7dbcad..9088dec79 100644 --- a/ui/layouts/dock-layout/dock-layout.ios.ts +++ b/ui/layouts/dock-layout/dock-layout.ios.ts @@ -35,13 +35,8 @@ export class DockLayout extends common.DockLayout { var childWidthMeasureSpec: number; var childHeightMeasureSpec: number; - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - - if (this.stretchLastChild && i === (count - 1)) { + this.eachLayoutChild((child, last) => { + if (this.stretchLastChild && last) { childWidthMeasureSpec = utils.layout.makeMeasureSpec(remainingWidth, widthMode); childHeightMeasureSpec = utils.layout.makeMeasureSpec(remainingHeight, heightMode); } @@ -72,7 +67,7 @@ export class DockLayout extends common.DockLayout { measureHeight = Math.max(measureHeight, tempHeight + childSize.measuredHeight); break; } - } + }); measureWidth += (this.paddingLeft + this.paddingRight) * density; measureHeight += (this.paddingTop + this.paddingBottom) * density; @@ -100,24 +95,18 @@ export class DockLayout extends common.DockLayout { var remainingWidth = Math.max(0, right - left - ((this.paddingLeft + this.paddingRight) * density)); var remainingHeight = Math.max(0, bottom - top - ((this.paddingTop + this.paddingBottom) * density)); - var count = this.getChildrenCount(); - var childToStretch = null; - if (count > 0 && this.stretchLastChild) { - count--; - childToStretch = this.getChildAt(count); - } - - for (let i = 0; i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density; let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density; - + + if (last && this.stretchLastChild) { + // Last child with stretch - give it all the space and return; + View.layoutChild(this, child, x, y, x + remainingWidth, y + remainingHeight); + return; + } + let dock = DockLayout.getDock(child); switch (dock) { case Dock.top: @@ -153,11 +142,7 @@ export class DockLayout extends common.DockLayout { } View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight); - } - - if (childToStretch) { - View.layoutChild(this, childToStretch, x, y, x + remainingWidth, y + remainingHeight); - } + }); DockLayout.restoreOriginalParams(this); } diff --git a/ui/layouts/grid-layout/grid-layout.ios.ts b/ui/layouts/grid-layout/grid-layout.ios.ts index 890de3206..f0bd9c0d4 100644 --- a/ui/layouts/grid-layout/grid-layout.ios.ts +++ b/ui/layouts/grid-layout/grid-layout.ios.ts @@ -145,16 +145,11 @@ export class GridLayout extends common.GridLayout { this.helper.clearMeasureSpecs(); this.helper.init(); - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let measureSpecs = this.map.get(child); this.updateMeasureSpecs(child, measureSpecs); this.helper.addMeasureSpec(measureSpecs); - } + }); this.helper.measure(); diff --git a/ui/layouts/layout-base.d.ts b/ui/layouts/layout-base.d.ts index 950ef00a0..346019148 100644 --- a/ui/layouts/layout-base.d.ts +++ b/ui/layouts/layout-base.d.ts @@ -50,6 +50,12 @@ */ removeChildren(): void; + /** + * Calls the callback for each child that should be laid out. + * @param callback The callback + */ + eachLayoutChild(callback: (child: view.View, isLast: boolean) => void): void; + /** * Iterates over children and changes their width and height to one calculated from percentage values. * diff --git a/ui/layouts/layout-base.ts b/ui/layouts/layout-base.ts index eda56c429..97822bae8 100644 --- a/ui/layouts/layout-base.ts +++ b/ui/layouts/layout-base.ts @@ -1,4 +1,5 @@ import definition = require("ui/layouts/layout-base"); +import types = require("utils/types"); import view = require("ui/core/view"); import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); @@ -41,13 +42,13 @@ export class LayoutBase extends view.CustomLayoutView implements definition.Layo public addChild(child: view.View): void { // TODO: Do we need this method since we have the core logic in the View implementation? - this._addView(child); this._subViews.push(child); + this._addView(child); } public insertChild(child: view.View, atIndex: number): void { - this._addView(child, atIndex); this._subViews.splice(atIndex, 0, child); + this._addView(child, atIndex); } public removeChild(child: view.View): void { @@ -64,19 +65,6 @@ export class LayoutBase extends view.CustomLayoutView implements definition.Layo } } - public _eachChildView(callback: (child: view.View) => boolean): void { - var i; - var length = this._subViews.length; - var retVal: boolean; - - for (i = 0; i < length; i++) { - retVal = callback(this._subViews[i]); - if (retVal === false) { - break; - } - } - } - get padding(): string { return this.style.padding; } @@ -127,6 +115,52 @@ export class LayoutBase extends view.CustomLayoutView implements definition.Layo } } + public _childIndexToNativeChildIndex(index?: number): number { + if (types.isUndefined(index)) { + return undefined; + } + var result = 0; + for (let i = 0; i < index && i < this._subViews.length; i++) { + result += this._subViews[i]._getNativeViewsCount(); + } + return result; + } + + public _eachChildView(callback: (child: view.View) => boolean): void { + var i; + var length = this._subViews.length; + var retVal: boolean; + + for (i = 0; i < length; i++) { + retVal = callback(this._subViews[i]); + if (retVal === false) { + break; + } + } + } + + public eachLayoutChild(callback: (child: view.View, isLast: boolean) => void): void { + var index = 0; + var lastChild: view.View = null; + + this._eachChildView((cv) => { + cv._eachLayoutView((lv) => { + if (lastChild && lastChild._isVisible) { + callback(lastChild, false); + } + + lastChild = lv; + }); + + return true; + }); + + if (lastChild && lastChild._isVisible) { + callback(lastChild, true); + } + + } + private static onClipToBoundsPropertyChanged(data: dependencyObservable.PropertyChangeData): void { var layout = data.object; layout.onClipToBoundsChanged(data.oldValue, data.newValue); diff --git a/ui/layouts/stack-layout/stack-layout.ios.ts b/ui/layouts/stack-layout/stack-layout.ios.ts index 6f3a4d7ac..cc27d003a 100644 --- a/ui/layouts/stack-layout/stack-layout.ios.ts +++ b/ui/layouts/stack-layout/stack-layout.ios.ts @@ -54,12 +54,8 @@ export class StackLayout extends common.StackLayout { } var childSize: { measuredWidth: number; measuredHeight: number }; - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } + this.eachLayoutChild((child, last) => { if (isVertical) { childSize = View.measureChild(this, child, childMeasureSpec, utils.layout.makeMeasureSpec(remainingLength, measureSpec)); measureWidth = Math.max(measureWidth, childSize.measuredWidth); @@ -74,7 +70,7 @@ export class StackLayout extends common.StackLayout { measureWidth += viewWidth; remainingLength = Math.max(0, remainingLength - viewWidth); } - } + }); measureWidth += horizontalPadding; measureHeight += verticalPadding; @@ -129,18 +125,13 @@ export class StackLayout extends common.StackLayout { break; } - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density; View.layoutChild(this, child, childLeft, childTop, childRight, childTop + childHeight); childTop += childHeight; - } + }) } private layoutHorizontal(left: number, top: number, right: number, bottom: number): void { @@ -170,17 +161,12 @@ export class StackLayout extends common.StackLayout { break; } - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density; View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childBottom); childLeft += childWidth; - } + }); } } \ No newline at end of file diff --git a/ui/layouts/wrap-layout/wrap-layout.ios.ts b/ui/layouts/wrap-layout/wrap-layout.ios.ts index 6a0712ffb..01b7e4247 100644 --- a/ui/layouts/wrap-layout/wrap-layout.ios.ts +++ b/ui/layouts/wrap-layout/wrap-layout.ios.ts @@ -56,12 +56,7 @@ export class WrapLayout extends common.WrapLayout { let itemWidth = this.itemWidth; let itemHeight = this.itemHeight; - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { var desiredSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec); let childMeasuredWidth = useItemWidth ? itemWidth : desiredSize.measuredWidth; let childMeasuredHeight = useItemHeight ? itemHeight : desiredSize.measuredHeight; @@ -99,7 +94,7 @@ export class WrapLayout extends common.WrapLayout { else { this._lengths[rowOrColumn] = Math.max(this._lengths[rowOrColumn], isVertical ? childMeasuredWidth : childMeasuredHeight); } - } + }); if (isVertical) { measureHeight = Math.max(maxLength, measureHeight); @@ -144,12 +139,7 @@ export class WrapLayout extends common.WrapLayout { } var rowOrColumn = 0; - for (let i = 0, count = this.getChildrenCount(); i < count; i++) { - let child = this.getChildAt(i); - if (!child._isVisible) { - continue; - } - + this.eachLayoutChild((child, last) => { // Add margins because layoutChild will sustract them. // * density converts them to device pixels. let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); @@ -203,7 +193,7 @@ export class WrapLayout extends common.WrapLayout { // Move next child Left position to right. childLeft += childWidth; } - } + }); WrapLayout.restoreOriginalParams(this); } diff --git a/ui/proxy-view-container/package.json b/ui/proxy-view-container/package.json new file mode 100644 index 000000000..7d943751f --- /dev/null +++ b/ui/proxy-view-container/package.json @@ -0,0 +1,2 @@ +{ "name" : "proxy-view-container", + "main" : "proxy-view-container.js" } diff --git a/ui/proxy-view-container/proxy-view-container.d.ts b/ui/proxy-view-container/proxy-view-container.d.ts new file mode 100644 index 000000000..2495eddd2 --- /dev/null +++ b/ui/proxy-view-container/proxy-view-container.d.ts @@ -0,0 +1,6 @@ +declare module "ui/proxy-view-container" { + import layout = require("ui/layouts/layout-base"); + + export class ProxyViewContainer extends layout.LayoutBase { + } +} diff --git a/ui/proxy-view-container/proxy-view-container.ts b/ui/proxy-view-container/proxy-view-container.ts new file mode 100644 index 000000000..446122cd3 --- /dev/null +++ b/ui/proxy-view-container/proxy-view-container.ts @@ -0,0 +1,111 @@ +import types = require("utils/types"); +import view = require("ui/core/view"); +import definition = require("ui/proxy-view-container"); +import trace = require("trace"); +import layout = require("ui/layouts/layout-base"); +/** + * Proxy view container that adds all its native children directly to the parent. + * To be used as a logical grouping container of views. + */ +// Cases to cover: +// * Child is added to the attached proxy. Handled in _addViewToNativeVisualTree. +// * Proxy (with children) is added to the DOM. +// - IOS: Handled in _addToSuperview - when the proxy is added, it adds all its children to the new parent. +// - Android: _onAttached calls _addViewToNativeVisualTree recursively when the proxy is added to the parent. +// * Child is removed from attached proxy. Handled in _removeViewFromNativeVisualTree. +// * Proxy (with children) is removed form the DOM. +// - IOS: Handled in _removeFromSuperview - when the proxy is removed, it removes all its children from its parent. +// - Android: _onDetached calls _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent. +export class ProxyViewContainer extends layout.LayoutBase implements definition.ProxyViewContainer { + // No native view for proxy container. + get ios(): any { + return null; + } + + get android(): any { + return null; + } + + get _nativeView(): any { + return null; + } + + public _createUI() { + // + } + + public _getNativeViewsCount(): number { + let result = 0; + this._eachChildView((cv) => { + result += cv._getNativeViewsCount(); + return true; + }); + + return result; + } + + public _eachLayoutView(callback: (View) => void): void { + this._eachChildView((cv) => { + cv._eachLayoutView(callback); + return true; + }); + } + + public _addViewToNativeVisualTree(child: view.View, atIndex?: number): boolean { + trace.write("ViewContainer._addViewToNativeVisualTree for a child " + view + " ViewContainer.parent: " + this.parent, trace.categories.ViewHierarchy); + super._addViewToNativeVisualTree(child); + + var parent = this.parent; + if (parent) { + let baseIndex = 0; + let insideIndex = 0; + if (parent instanceof layout.LayoutBase) { + // Get my index in parent and convert it to native index. + baseIndex = parent._childIndexToNativeChildIndex(parent.getChildIndex(this)); + } + + if (types.isDefined(atIndex)) { + insideIndex = this._childIndexToNativeChildIndex(atIndex); + } else { + // Add last; + insideIndex = this._getNativeViewsCount(); + } + + trace.write("ProxyViewContainer._addViewToNativeVisualTree at: " + atIndex + " base: " + baseIndex + " additional: " + insideIndex, trace.categories.ViewHierarchy); + return parent._addViewToNativeVisualTree(child, baseIndex + insideIndex); + } + + return false; + } + + public _removeViewFromNativeVisualTree(child: view.View): void { + trace.write("ProxyViewContainer._removeViewFromNativeVisualTree for a child " + view + " ViewContainer.parent: " + this.parent, trace.categories.ViewHierarchy); + super._removeViewFromNativeVisualTree(child); + + var parent = this.parent; + if (parent) { + return parent._removeViewFromNativeVisualTree(child); + } + } + + public _addToSuperview(superview: any, atIndex?: number): boolean { + var index = 0; + this._eachChildView((cv) => { + if (!cv._isAddedToNativeVisualTree) { + cv._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(cv, index++); + } + return true; + }); + + return true; + } + + public _removeFromSuperview() { + this._eachChildView((cv) => { + if (cv._isAddedToNativeVisualTree) { + this._removeViewFromNativeVisualTree(cv); + } + return true; + }); + } +}