Merge pull request #1483 from NativeScript/feature/proxy-container-layout-fix

Fixed ios proxy view blocks layout requests
This commit is contained in:
Alexander Vakrilov
2016-02-04 10:30:06 +02:00
7 changed files with 140 additions and 16 deletions

View File

@ -6,7 +6,7 @@ import color = require("color");
import platform = require("platform"); import platform = require("platform");
import {ProxyViewContainer} from "ui/proxy-view-container"; 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() { export function test_add_children_to_attached_proxy() {
var outer = new StackLayout(); var outer = new StackLayout();
@ -161,6 +161,105 @@ export function test_insert_after_porxy() {
helper.buildUIAndRunTest(outer, testAction); 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<viewModule.View>) {
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);
}
export function test_proxy_iniside_page() {
var proxy = new ProxyViewContainer();
proxy.addChild(createBtn("1"));
function testAction(views: Array<viewModule.View>) {
var page = <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<viewModule.View>) {
var page = <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<viewModule.View>) {
var page = <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<TabViewItem>();
// items.push(new TabViewItem({ title: "tab with proxy", view: proxy }));
// tab.items = items;
// function testAction(views: Array<viewModule.View>) {
// var page = <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<viewModule.View>) {
// var page = <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;
}, timeoutSec || 1);
}
function createBtn(text: string): Button { function createBtn(text: string): Button {
var b = new Button(); var b = new Button();
b.text = text; b.text = text;

View File

@ -22,7 +22,7 @@ export class Border extends contentView.ContentView implements definition.Border
var density = utils.layout.getDisplayDensity(); var density = utils.layout.getDisplayDensity();
var borderSize = (2 * this.borderWidth) * density; 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(width - borderSize, widthMode),
utils.layout.makeMeasureSpec(height - borderSize, heightMode)); 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 { public onLayout(left: number, top: number, right: number, bottom: number): void {
var density = utils.layout.getDisplayDensity(); var density = utils.layout.getDisplayDensity();
var borderSize = this.borderWidth * density; 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);
} }
} }

View File

@ -24,5 +24,7 @@ declare module "ui/content-view" {
//@endprivate //@endprivate
_addChildFromBuilder(name: string, value: any): void; _addChildFromBuilder(name: string, value: any): void;
layoutView: view.View;
} }
} }

View File

@ -23,6 +23,24 @@ export class ContentView extends view.CustomLayoutView implements definition.Con
this._onContentChanged(oldView, value); 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 { get _childrenCount(): number {
if (this._content) { if (this._content) {
return 1; 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. // This method won't be called in Android because we use the native android layout.
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { 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 width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(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. // 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 { 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);
} }
} }

View File

@ -276,7 +276,7 @@ export class Page extends pageCommon.Page {
let heightSpec = utils.layout.makeMeasureSpec(height - actionBarHeight - statusBarHeight, heightMode); let heightSpec = utils.layout.makeMeasureSpec(height - actionBarHeight - statusBarHeight, heightMode);
// Measure content with height - navigationBarHeight. Here we could use actionBarSize.measuredHeight probably. // 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 measureWidth = Math.max(actionBarWidth, result.measuredWidth, this.minWidth);
let measureHeight = Math.max(result.measuredHeight + actionBarHeight, this.minHeight); let measureHeight = Math.max(result.measuredHeight + actionBarHeight, this.minHeight);
@ -307,7 +307,7 @@ export class Page extends pageCommon.Page {
statusBarHeight = 0; 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 { public _addViewToNativeVisualTree(view: View): boolean {

View File

@ -18,18 +18,23 @@ import {LayoutBase} from "ui/layouts/layout-base";
// - Android: _onDetached calls _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent. // - Android: _onDetached calls _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent.
export class ProxyViewContainer extends LayoutBase implements definition.ProxyViewContainer { export class ProxyViewContainer extends LayoutBase implements definition.ProxyViewContainer {
// No native view for proxy container. // No native view for proxy container.
get ios(): any { get ios(): any {
return null; return null;
} }
get android(): any { get android(): any {
return null; return null;
} }
get _nativeView(): any { get _nativeView(): any {
return null; return null;
} }
get isLayoutRequested(): boolean {
// Always return false so all layout requests from children bubble up.
return false;
}
public _createUI() { public _createUI() {
// //
} }
@ -70,7 +75,7 @@ export class ProxyViewContainer extends LayoutBase implements definition.ProxyVi
// Add last; // Add last;
insideIndex = this._getNativeViewsCount(); insideIndex = this._getNativeViewsCount();
} }
trace.write("ProxyViewContainer._addViewToNativeVisualTree at: " + atIndex + " base: " + baseIndex + " additional: " + insideIndex, trace.categories.ViewHierarchy); trace.write("ProxyViewContainer._addViewToNativeVisualTree at: " + atIndex + " base: " + baseIndex + " additional: " + insideIndex, trace.categories.ViewHierarchy);
return parent._addViewToNativeVisualTree(child, baseIndex + insideIndex); return parent._addViewToNativeVisualTree(child, baseIndex + insideIndex);
} }

View File

@ -109,7 +109,7 @@ export class ScrollView extends common.ScrollView implements definition.ScrollVi
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
var density = utils.layout.getDisplayDensity(); var density = utils.layout.getDisplayDensity();
var child = this.content var child = this.layoutView;
if (!child) { if (!child) {
this._contentMeasuredWidth = this.minWidth * density; this._contentMeasuredWidth = this.minWidth * density;
this._contentMeasuredHeight = this.minHeight * density; this._contentMeasuredHeight = this.minHeight * density;
@ -140,10 +140,10 @@ export class ScrollView extends common.ScrollView implements definition.ScrollVi
var height = (bottom - top); var height = (bottom - top);
if (this.orientation === enums.Orientation.horizontal) { 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 { 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));
} }
} }
} }