From 2bb7ad9d01e1eb72622fe8c3fc6ca705d2574440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Sj=C3=B8gren?= Date: Mon, 2 Dec 2019 10:13:23 +0100 Subject: [PATCH] Implements: Proxy layout properties for the ProxyViewContainer (#8150) * feat(ProxyViewContainer): proxy layout properties to children * update import path * fix(ProxyViewContainer): Layout properties not applied to new children * test(ProxyViewContainer): Add test for layout properties * chore: fix tslint errors --- .../proxy-view-container.ts | 101 +++++++++++++++++- .../proxy-view-container-tests.ts | 29 +++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/nativescript-core/ui/proxy-view-container/proxy-view-container.ts b/nativescript-core/ui/proxy-view-container/proxy-view-container.ts index 411d8f8eb..abc20df11 100644 --- a/nativescript-core/ui/proxy-view-container/proxy-view-container.ts +++ b/nativescript-core/ui/proxy-view-container/proxy-view-container.ts @@ -1,5 +1,8 @@ import { ProxyViewContainer as ProxyViewContainerDefinition } from "."; import { LayoutBase, View, traceEnabled, traceWrite, traceCategories, CSSType } from "../layouts/layout-base"; +import { Property } from "../core/properties/properties"; +import { messageType } from "../../trace/trace"; + /** * Proxy view container that adds all its native children directly to the parent. * To be used as a logical grouping container of views. @@ -11,6 +14,7 @@ import { LayoutBase, View, traceEnabled, traceWrite, traceCategories, CSSType } // * Proxy (with children) is removed form the DOM. In _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent. @CSSType("ProxyViewContainer") export class ProxyViewContainer extends LayoutBase implements ProxyViewContainerDefinition { + private proxiedLayoutProperties = new Set(); constructor() { super(); @@ -62,10 +66,19 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer public _addViewToNativeVisualTree(child: View, atIndex?: number): boolean { if (traceEnabled()) { - traceWrite("ViewContainer._addViewToNativeVisualTree for a child " + child + " ViewContainer.parent: " + this.parent, traceCategories.ViewHierarchy); + traceWrite("ProxyViewContainer._addViewToNativeVisualTree for a child " + child + " ViewContainer.parent: " + this.parent, traceCategories.ViewHierarchy); } super._addViewToNativeVisualTree(child); + layoutProperties.forEach((propName) => { + const proxyPropName = makeProxyPropName(propName); + child[proxyPropName] = child[propName]; + + if (this.proxiedLayoutProperties.has(propName)) { + this._applyLayoutPropertyToChild(child, propName, this[propName]); + } + }); + const parent = this.parent; if (parent instanceof View) { let baseIndex = 0; @@ -147,4 +160,90 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer }); } } + + /** + * Layout property changed, proxy the new value to the child view(s) + */ + public _changedLayoutProperty(propName: string, value: string) { + const numChildren = this._getNativeViewsCount(); + if (numChildren > 1) { + traceWrite("ProxyViewContainer._changeLayoutProperty - you're setting '" + propName + "' for " + this + " with more than one child. Probably this is not what you want, consider wrapping it in a StackLayout ", traceCategories.ViewHierarchy, messageType.error); + } + + this.eachLayoutChild((child) => { + this._applyLayoutPropertyToChild(child, propName, value); + + return true; + }); + + this.proxiedLayoutProperties.add(propName); + } + + /** + * Apply the layout property to the child view. + */ + private _applyLayoutPropertyToChild(child: View, propName: string, value: any) { + const proxyPropName = makeProxyPropName(propName); + if (proxyPropName in child) { + if (child[propName] !== child[proxyPropName]) { + // Value was set directly on the child view, don't override. + if (traceEnabled()) { + traceWrite("ProxyViewContainer._applyLayoutPropertyToChild child " + child + " has its own value [" + child[propName] + "] for [" + propName + "]", traceCategories.ViewHierarchy); + } + + return; + } + } + + child[propName] = value; + child[proxyPropName] = value; + } +} + +// Layout propeties to be proxyed to the child views +const layoutProperties = [ + // AbsoluteLayout + "left", + "top", + + // DockLayout + "dock", + + // FlexLayout + "flexDirection", + "flexWrap", + "justifyContent", + "alignItems", + "alignContent", + "order", + "flexGrow", + "flexShrink", + "flexWrapBefore", + "alignSelf", + "flexFlow", + "flex", + + // GridLayout + "column", + "columnSpan", + "col", + "colSpan", + "row", + "rowSpan", +]; + +// Override the inherited layout properties +for (const name of layoutProperties) { + const proxyProperty = new Property({ + name, + valueChanged(target, oldValue, value) { + target._changedLayoutProperty(name, value); + } + }); + + proxyProperty.register(ProxyViewContainer); +} + +function makeProxyPropName(propName) { + return `_proxy:${propName}`; } diff --git a/tests/app/ui/proxy-view-container/proxy-view-container-tests.ts b/tests/app/ui/proxy-view-container/proxy-view-container-tests.ts index 278519cda..e890abb16 100644 --- a/tests/app/ui/proxy-view-container/proxy-view-container-tests.ts +++ b/tests/app/ui/proxy-view-container/proxy-view-container-tests.ts @@ -43,6 +43,35 @@ export function test_children_immediately_registered_in_parent_grid_layout() { helper.buildUIAndRunTest(outer, testAction); } +export function test_proxy_layout_properties() { + const outer = new GridLayout(); + const proxy = new ProxyViewContainer(); + + function testAction(views: Array) { + outer.addChild(proxy); + + const btn = createBtn("1"); + proxy.addChild(btn); + + proxy.row = 1; + TKUnit.assertEqual(proxy.row, btn.row, "Proxy row value to existing child"); + + const btn2 = createBtn("2"); + proxy.addChild(btn2); + TKUnit.assertEqual(proxy.row, btn2.row, "Proxy row value to new child"); + + proxy.removeChild(btn2); + + btn.row = 0; + TKUnit.assertNotEqual(proxy.row, btn.row, "Child value changed"); + + proxy.row = 1; + TKUnit.assertNotEqual(proxy.row, btn.row, "Changed child value not overridden"); + } + + helper.buildUIAndRunTest(outer, testAction); +} + export function test_children_registered_in_parent_grid_layout_on_attach() { const outer = new GridLayout(); const proxy = new ProxyViewContainer();