From 57f15abf278d0a5db273b2937899be1f0c7da027 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Wed, 3 Feb 2016 15:38:48 +0200 Subject: [PATCH 1/2] Fixed ios proxy view blocks layout requests --- .../proxy-view-container-tests.ts | 26 +++++++++++++++++++ .../proxy-view-container.ts | 17 +++++++----- 2 files changed, 37 insertions(+), 6 deletions(-) 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 index 1bd9095d4..22b1ab976 100644 --- a/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts +++ b/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts @@ -161,6 +161,32 @@ export function test_insert_after_porxy() { helper.buildUIAndRunTest(outer, testAction); } +export function test_proxy_does_not_stop_request_layout_bubble() { + var outer = new StackLayout(); + var proxy = new ProxyViewContainer(); + + outer.addChild(createBtn("1")); + outer.addChild(proxy); + var btn = createBtn("2"); + proxy.addChild(btn); + + function testAction(views: Array) { + assertNativeChildren(outer, ["1", "2"]); + waitUntilElementLayoutIsValid(outer); + TKUnit.assert(outer.isLayoutValid, "outer container isLayoutValid should be true"); + btn.requestLayout(); + TKUnit.assertFalse(outer.isLayoutValid, "outer container isLayoutValid should be invalidated here"); + }; + + helper.buildUIAndRunTest(outer, testAction); +} + +function waitUntilElementLayoutIsValid(view: View, timeoutSec?: number): void { + TKUnit.waitUntilReady(() => { + return view.isLayoutValid; + }, timeoutSec || 1); +} + function createBtn(text: string): Button { var b = new Button(); b.text = text; diff --git a/ui/proxy-view-container/proxy-view-container.ts b/ui/proxy-view-container/proxy-view-container.ts index 4ab99ef19..b7e0f5edd 100644 --- a/ui/proxy-view-container/proxy-view-container.ts +++ b/ui/proxy-view-container/proxy-view-container.ts @@ -18,18 +18,23 @@ import {LayoutBase} from "ui/layouts/layout-base"; // - Android: _onDetached calls _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent. export class ProxyViewContainer extends LayoutBase implements definition.ProxyViewContainer { // No native view for proxy container. - get ios(): any { - return null; + get ios(): any { + return null; } - + get android(): any { return null; } - + get _nativeView(): any { return null; } - + + get isLayoutRequested(): boolean { + // Always return false so all layout requests from children bubble up. + return false; + } + public _createUI() { // } @@ -70,7 +75,7 @@ export class ProxyViewContainer extends LayoutBase implements definition.ProxyVi // Add last; insideIndex = this._getNativeViewsCount(); } - + trace.write("ProxyViewContainer._addViewToNativeVisualTree at: " + atIndex + " base: " + baseIndex + " additional: " + insideIndex, trace.categories.ViewHierarchy); return parent._addViewToNativeVisualTree(child, baseIndex + insideIndex); } From 4a8ec625065e6ba76fa88ed33384963f71dbc235 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Wed, 3 Feb 2016 18:07:25 +0200 Subject: [PATCH 2/2] Handle the case whr proxy is inside ContentView --- .../proxy-view-container-tests.ts | 75 ++++++++++++++++++- ui/border/border.ts | 4 +- ui/content-view/content-view.d.ts | 2 + ui/content-view/content-view.ts | 22 +++++- ui/page/page.ios.ts | 4 +- ui/scroll-view/scroll-view.ios.ts | 6 +- 6 files changed, 103 insertions(+), 10 deletions(-) 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 index 22b1ab976..2d7f361e4 100644 --- a/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts +++ b/apps/tests/ui/proxy-view-container/proxy-view-container-tests.ts @@ -6,7 +6,7 @@ import color = require("color"); import platform = require("platform"); import {ProxyViewContainer} from "ui/proxy-view-container"; -import {View, Button, LayoutBase, StackLayout, GridLayout} from "ui"; +import {View, Button, LayoutBase, StackLayout, GridLayout, Page, ScrollView, TabView, TabViewItem} from "ui"; export function test_add_children_to_attached_proxy() { var outer = new StackLayout(); @@ -181,6 +181,79 @@ export function test_proxy_does_not_stop_request_layout_bubble() { helper.buildUIAndRunTest(outer, testAction); } +export function test_proxy_iniside_page() { + var proxy = new ProxyViewContainer(); + proxy.addChild(createBtn("1")); + + function testAction(views: Array) { + var page = views[1]; + waitUntilElementLayoutIsValid(page); + }; + + helper.buildUIAndRunTest(proxy, testAction); +} + +export function test_proxy_iniside_scroll_view() { + var scroll = new ScrollView(); + scroll.content = proxy; + + var proxy = new ProxyViewContainer(); + proxy.addChild(createBtn("1")); + + function testAction(views: Array) { + var page = views[1]; + waitUntilElementLayoutIsValid(page); + }; + + helper.buildUIAndRunTest(scroll, testAction); +} + +export function test_proxy_iniside_border() { + var scroll = new ScrollView(); + scroll.content = proxy; + + var proxy = new ProxyViewContainer(); + proxy.addChild(createBtn("1")); + + function testAction(views: Array) { + var page = views[1]; + waitUntilElementLayoutIsValid(page); + }; + + helper.buildUIAndRunTest(scroll, testAction); +} + +// TODO: Proxy as a direct child to of TabItem is not supported. Not sure if we want to support it. +//export function test_proxy_iniside_tab() { +// var proxy = new ProxyViewContainer(); +// proxy.addChild(createBtn("1")); + +// var tab = new TabView(); +// var items = new Array(); +// items.push(new TabViewItem({ title: "tab with proxy", view: proxy })); +// tab.items = items; + +// function testAction(views: Array) { +// var page = views[1]; +// waitUntilElementLayoutIsValid(page); +// }; + +// helper.buildUIAndRunTest(tab, tab); +//} + +// TODO: Proxy as a direct child to of ActionBar is not supported. Not sure if we want to support it. +//export function test_proxy_iniside_actionBar() { +// function testAction(views: Array) { +// var page = views[1]; +// var proxy = new ProxyViewContainer(); +// proxy.addChild(createBtn("1")); +// page.actionBar.titleView = proxy; +// waitUntilElementLayoutIsValid(page); +// }; + +// helper.buildUIAndRunTest(createBtn("hi"), testAction); +//} + function waitUntilElementLayoutIsValid(view: View, timeoutSec?: number): void { TKUnit.waitUntilReady(() => { return view.isLayoutValid; diff --git a/ui/border/border.ts b/ui/border/border.ts index 6ef647bc8..3dfbce78c 100644 --- a/ui/border/border.ts +++ b/ui/border/border.ts @@ -22,7 +22,7 @@ export class Border extends contentView.ContentView implements definition.Border var density = utils.layout.getDisplayDensity(); var borderSize = (2 * this.borderWidth) * density; - var result = viewModule.View.measureChild(this, this.content, + var result = viewModule.View.measureChild(this, this.layoutView, utils.layout.makeMeasureSpec(width - borderSize, widthMode), utils.layout.makeMeasureSpec(height - borderSize, heightMode)); @@ -35,6 +35,6 @@ export class Border extends contentView.ContentView implements definition.Border public onLayout(left: number, top: number, right: number, bottom: number): void { var density = utils.layout.getDisplayDensity(); var borderSize = this.borderWidth * density; - viewModule.View.layoutChild(this, this.content, borderSize, borderSize, right - left - borderSize, bottom - top - borderSize); + viewModule.View.layoutChild(this, this.layoutView, borderSize, borderSize, right - left - borderSize, bottom - top - borderSize); } } \ No newline at end of file diff --git a/ui/content-view/content-view.d.ts b/ui/content-view/content-view.d.ts index 12dbb8378..45ae645bc 100644 --- a/ui/content-view/content-view.d.ts +++ b/ui/content-view/content-view.d.ts @@ -24,5 +24,7 @@ declare module "ui/content-view" { //@endprivate _addChildFromBuilder(name: string, value: any): void; + + layoutView: view.View; } } \ No newline at end of file diff --git a/ui/content-view/content-view.ts b/ui/content-view/content-view.ts index 7e794aec8..898d4b47f 100644 --- a/ui/content-view/content-view.ts +++ b/ui/content-view/content-view.ts @@ -23,6 +23,24 @@ export class ContentView extends view.CustomLayoutView implements definition.Con this._onContentChanged(oldView, value); } + get layoutView(): view.View { + var result: view.View; + + if (this._content) { + let first = true; + this._content._eachLayoutView((child) => { + if (first) { + first = false; + result = child; + } else { + throw new Error("More than one layout child inside a ContentView"); + } + }); + } + + return result; + } + get _childrenCount(): number { if (this._content) { return 1; @@ -49,7 +67,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); + var result = view.View.measureChild(this, this.layoutView, widthMeasureSpec, heightMeasureSpec); var width = utils.layout.getMeasureSpecSize(widthMeasureSpec); var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec); @@ -69,6 +87,6 @@ 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 onLayout(left: number, top: number, right: number, bottom: number): void { - view.View.layoutChild(this, this.content, 0, 0, right - left, bottom - top); + view.View.layoutChild(this, this.layoutView, 0, 0, right - left, bottom - top); } } \ No newline at end of file diff --git a/ui/page/page.ios.ts b/ui/page/page.ios.ts index e7eb31034..f5cacaf67 100644 --- a/ui/page/page.ios.ts +++ b/ui/page/page.ios.ts @@ -276,7 +276,7 @@ export class Page extends pageCommon.Page { let heightSpec = utils.layout.makeMeasureSpec(height - actionBarHeight - statusBarHeight, heightMode); // Measure content with height - navigationBarHeight. Here we could use actionBarSize.measuredHeight probably. - let result = View.measureChild(this, this.content, widthMeasureSpec, heightSpec); + let result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightSpec); let measureWidth = Math.max(actionBarWidth, result.measuredWidth, this.minWidth); let measureHeight = Math.max(result.measuredHeight + actionBarHeight, this.minHeight); @@ -307,7 +307,7 @@ export class Page extends pageCommon.Page { statusBarHeight = 0; } - View.layoutChild(this, this.content, 0, navigationBarHeight + statusBarHeight, right - left, bottom - top); + View.layoutChild(this, this.layoutView, 0, navigationBarHeight + statusBarHeight, right - left, bottom - top); } public _addViewToNativeVisualTree(view: View): boolean { diff --git a/ui/scroll-view/scroll-view.ios.ts b/ui/scroll-view/scroll-view.ios.ts index 25ee328cb..52cfca93a 100644 --- a/ui/scroll-view/scroll-view.ios.ts +++ b/ui/scroll-view/scroll-view.ios.ts @@ -109,7 +109,7 @@ export class ScrollView extends common.ScrollView implements definition.ScrollVi var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); var density = utils.layout.getDisplayDensity(); - var child = this.content + var child = this.layoutView; if (!child) { this._contentMeasuredWidth = this.minWidth * density; this._contentMeasuredHeight = this.minHeight * density; @@ -140,10 +140,10 @@ export class ScrollView extends common.ScrollView implements definition.ScrollVi var height = (bottom - top); if (this.orientation === enums.Orientation.horizontal) { - view.View.layoutChild(this, this.content, 0, 0, Math.max(this._contentMeasuredWidth, width), height); + view.View.layoutChild(this, this.layoutView, 0, 0, Math.max(this._contentMeasuredWidth, width), height); } else { - view.View.layoutChild(this, this.content, 0, 0, width, Math.max(this._contentMeasuredHeight, height)); + view.View.layoutChild(this, this.layoutView, 0, 0, width, Math.max(this._contentMeasuredHeight, height)); } } }