diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 72a63583b..ee9f7bcc8 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -80,6 +80,7 @@ data-binding.xml + modal-page.xml diff --git a/apps/tests/layouts/absolute-layout-tests.ts b/apps/tests/layouts/absolute-layout-tests.ts index 8c28fb3f6..8fadba0d0 100644 --- a/apps/tests/layouts/absolute-layout-tests.ts +++ b/apps/tests/layouts/absolute-layout-tests.ts @@ -99,6 +99,27 @@ export class AbsoluteLayoutTest extends testModule.UITestbtn).width = "50%"; + (btn).height = "50%"; + btn.margin = "10%"; + layout.addChild(btn); + + this.waitUntilTestElementLayoutIsValid(); + + // AbsoluteLayout measures with 0/UNSPECIFIED so we cannot support percents in it. + layoutHelper.assertMeasure(btn, 100, 100); + layoutHelper.assertLayout(btn, 20, 20, btn.getMeasuredWidth(), btn.getMeasuredHeight()); + + TKUnit.assertEqual(btn.getMeasuredWidth(), 100, "Button MeasuredWidth incorrect"); + TKUnit.assertEqual(btn.getMeasuredHeight(), 100, "Button MeasuredHeight incorrect"); + } } export function createTestCase(): AbsoluteLayoutTest { diff --git a/apps/tests/layouts/common-layout-tests.ts b/apps/tests/layouts/common-layout-tests.ts new file mode 100644 index 000000000..c7693eea0 --- /dev/null +++ b/apps/tests/layouts/common-layout-tests.ts @@ -0,0 +1,86 @@ +import TKUnit = require("../TKUnit"); +import layoutHelper = require("./layout-helper"); +import enums = require("ui/enums"); +import testModule = require("../ui-test"); +import {LayoutBase} from "ui/layouts/layout-base"; +import {widthProperty} from "ui/styling/style" + +export function percent_support_test(test: testModule.UITest) { + let layout: LayoutBase = test.testView; + layout.removeChildren(); + layout.width = layoutHelper.dp(200); + layout.height = layoutHelper.dp(200); + + let btn = new layoutHelper.MyButton(); + btn.horizontalAlignment = enums.HorizontalAlignment.left; + btn.verticalAlignment = enums.VerticalAlignment.top; + (btn).width = "50%"; + (btn).height = "50%"; + btn.margin = "10%"; + btn.text = "1"; + layout.addChild(btn); + + test.waitUntilTestElementLayoutIsValid(); + + TKUnit.assertEqual(btn.getMeasuredWidth(), 100, "Button MeasuredWidth incorrect"); + TKUnit.assertEqual(btn.getMeasuredHeight(), 100, "Button MeasuredHeight incorrect"); + + let bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "TopLeft layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "TopLeft layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "TopLeft layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "TopLeft layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.center; + btn.verticalAlignment = enums.VerticalAlignment.center; + test.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 50, "Center layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 50, "Center layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 150, "Center layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 150, "Center layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.stretch; + btn.verticalAlignment = enums.VerticalAlignment.stretch; + test.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 50, "Stretch layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 50, "Stretch layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 150, "Stretch layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 150, "Stretch layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.right; + btn.verticalAlignment = enums.VerticalAlignment.bottom; + test.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 200 - 100 - 20, "BottomRight layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 200 - 100 - 20, "BottomRight layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 200 - 20, "BottomRight layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 200 - 20, "BottomRight layout BOTTOM incorrect"); + + //reset values. + btn.height = Number.NaN; + (btn.style)._resetValue(widthProperty); + btn.margin = "0"; + btn.horizontalAlignment = enums.HorizontalAlignment.stretch; + btn.verticalAlignment = enums.VerticalAlignment.stretch; + btn.height = Number.NaN; + + TKUnit.assertEqual(btn.marginLeft, 0, "marginLeft"); + TKUnit.assertEqual(btn.marginTop, 0, "marginTop"); + TKUnit.assertEqual(btn.marginRight, 0, "marginRight"); + TKUnit.assertEqual(btn.marginBottom, 0, "marginBottom"); + TKUnit.assert(isNaN(btn.width), "width"); + TKUnit.assert(isNaN(btn.height), "height"); + + test.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 0, "Reset Stretch layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 0, "Reset Stretch layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 200, "Reset Stretch layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 200, "Reset Stretch layout BOTTOM incorrect"); +} \ No newline at end of file diff --git a/apps/tests/layouts/dock-layout-tests.ts b/apps/tests/layouts/dock-layout-tests.ts index 4e7e69dc8..141a7bade 100644 --- a/apps/tests/layouts/dock-layout-tests.ts +++ b/apps/tests/layouts/dock-layout-tests.ts @@ -6,6 +6,7 @@ import helper = require("./layout-helper"); import navHelper = require("../ui/helper"); import testModule = require("../ui-test"); import layoutHelper = require("./layout-helper"); +import commonTests = require("./common-layout-tests"); // // # DockLayout @@ -190,6 +191,10 @@ export class DockLayoutTest extends testModule.UITest { // ``` // } + + public test_percent_support() { + commonTests.percent_support_test(this); + } } export function createTestCase(): DockLayoutTest { diff --git a/apps/tests/layouts/grid-layout-tests.ts b/apps/tests/layouts/grid-layout-tests.ts index 625891517..e4fd393c4 100644 --- a/apps/tests/layouts/grid-layout-tests.ts +++ b/apps/tests/layouts/grid-layout-tests.ts @@ -11,6 +11,7 @@ import enums = require("ui/enums"); import testModule = require("../ui-test"); import layoutHelper = require("./layout-helper"); import platform = require("platform"); +import commonTests = require("./common-layout-tests"); var DELTA = 1; @@ -641,6 +642,10 @@ export class GridLayoutTest extends testModule.UITest { // ``` // } + + public test_percent_support() { + commonTests.percent_support_test(this); + } } export function createTestCase(): GridLayoutTest { diff --git a/apps/tests/layouts/stack-layout-tests.ts b/apps/tests/layouts/stack-layout-tests.ts index 8ec4bab73..279fbd94c 100644 --- a/apps/tests/layouts/stack-layout-tests.ts +++ b/apps/tests/layouts/stack-layout-tests.ts @@ -8,6 +8,7 @@ import enums = require("ui/enums"); import utils = require("utils/utils"); import testModule = require("../ui-test"); import layoutHelper = require("./layout-helper"); +import commonTests = require("./common-layout-tests"); export class StackLayoutTest extends testModule.UITest { @@ -204,6 +205,113 @@ export class StackLayoutTest extends testModule.UITest { // } + + private setup_percent(): layoutHelper.MyButton { + let layout = this.testView; + layout.removeChildren(); + layout.width = layoutHelper.dp(200); + layout.height = layoutHelper.dp(200); + + let btn = new layoutHelper.MyButton(); + btn.horizontalAlignment = enums.HorizontalAlignment.left; + btn.verticalAlignment = enums.VerticalAlignment.top; + (btn).width = "50%"; + (btn).height = "50%"; + btn.margin = "10%"; + btn.text = "1"; + layout.addChild(btn); + return btn; + } + + public test_percent_support_vertical() { + let btn = this.setup_percent(); + + this.waitUntilTestElementLayoutIsValid(); + + TKUnit.assertEqual(btn.getMeasuredWidth(), 100, "Button MeasuredWidth incorrect"); + TKUnit.assertEqual(btn.getMeasuredHeight(), 100, "Button MeasuredHeight incorrect"); + + let bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "TopLeft layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "TopLeft layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "TopLeft layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "TopLeft layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.center; + btn.verticalAlignment = enums.VerticalAlignment.center; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 50, "Center layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "Center layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 150, "Center layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "Center layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.stretch; + btn.verticalAlignment = enums.VerticalAlignment.stretch; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 50, "Stretch layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "Stretch layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 150, "Stretch layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "Stretch layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.right; + btn.verticalAlignment = enums.VerticalAlignment.bottom; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 80, "BottomRight layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "BottomRight layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 180, "BottomRight layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "BottomRight layout BOTTOM incorrect"); + } + + public test_percent_support_horizontal() { + let btn = this.setup_percent(); + this.testView.orientation = enums.Orientation.horizontal; + this.waitUntilTestElementLayoutIsValid(); + + TKUnit.assertEqual(btn.getMeasuredWidth(), 100, "Button MeasuredWidth incorrect"); + TKUnit.assertEqual(btn.getMeasuredHeight(), 100, "Button MeasuredHeight incorrect"); + + let bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "TopLeft layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "TopLeft layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "TopLeft layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "TopLeft layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.center; + btn.verticalAlignment = enums.VerticalAlignment.center; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "Center layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 50, "Center layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "Center layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 150, "Center layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.stretch; + btn.verticalAlignment = enums.VerticalAlignment.stretch; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "Stretch layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 50, "Stretch layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "Stretch layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 150, "Stretch layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.right; + btn.verticalAlignment = enums.VerticalAlignment.bottom; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "BottomRight layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 80, "BottomRight layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "BottomRight layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 180, "BottomRight layout BOTTOM incorrect"); + } } export function createTestCase(): StackLayoutTest { diff --git a/apps/tests/layouts/wrap-layout-tests.ts b/apps/tests/layouts/wrap-layout-tests.ts index 23f042e13..7d0c9868d 100644 --- a/apps/tests/layouts/wrap-layout-tests.ts +++ b/apps/tests/layouts/wrap-layout-tests.ts @@ -1,9 +1,9 @@ import TKUnit = require("../TKUnit"); -import viewModule = require("ui/core/view"); -import labelModule = require("ui/label"); +import {Label} from "ui/label"; import helper = require("../ui/helper"); import layoutHelper = require("./layout-helper"); import testModule = require("../ui-test"); +import commonTests = require("./common-layout-tests"); // // # WrapLayout @@ -47,7 +47,7 @@ export class WrapLayoutTest extends testModule.UITestbtn).width = "50%"; + (btn).height = "50%"; + btn.margin = "10%"; + btn.text = "1"; + layout.addChild(btn); + + this.waitUntilTestElementLayoutIsValid(); + + TKUnit.assertEqual(btn.getMeasuredWidth(), 100, "Button MeasuredWidth incorrect"); + TKUnit.assertEqual(btn.getMeasuredHeight(), 100, "Button MeasuredHeight incorrect"); + + let bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "TopLeft layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "TopLeft layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "TopLeft layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "TopLeft layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.center; + btn.verticalAlignment = enums.VerticalAlignment.center; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "Center layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "Center layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "Center layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "Center layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.stretch; + btn.verticalAlignment = enums.VerticalAlignment.stretch; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "Stretch layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "Stretch layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "Stretch layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "Stretch layout BOTTOM incorrect"); + + btn.horizontalAlignment = enums.HorizontalAlignment.right; + btn.verticalAlignment = enums.VerticalAlignment.bottom; + this.waitUntilTestElementLayoutIsValid(); + + bounds = btn._getCurrentLayoutBounds(); + TKUnit.assertEqual(bounds.left, 20, "BottomRight layout LEFT incorrect"); + TKUnit.assertEqual(bounds.top, 20, "BottomRight layout TOP incorrect"); + TKUnit.assertEqual(bounds.right, 120, "BottomRight layout RIGHT incorrect"); + TKUnit.assertEqual(bounds.bottom, 120, "BottomRight layout BOTTOM incorrect"); + } } export function createTestCase(): WrapLayoutTest { diff --git a/org.nativescript.widgets.d.ts b/org.nativescript.widgets.d.ts index da1b342d8..6dfbc4998 100644 --- a/org.nativescript.widgets.d.ts +++ b/org.nativescript.widgets.d.ts @@ -4,6 +4,14 @@ export class CommonLayoutParams extends android.widget.FrameLayout.LayoutParams { constructor(); + public widthPercent: number; + public heightPercent: number; + + public topMarginPercent: number; + public leftMarginPercent: number; + public bottomMarginPercent: number; + public rightMarginPercent: number; + public left: number; public top: number; diff --git a/trace/trace.ts b/trace/trace.ts index 4398c4e36..7d8173771 100644 --- a/trace/trace.ts +++ b/trace/trace.ts @@ -38,10 +38,7 @@ export function setCategories(categories: string) { export function addCategories(categories: string) { var split = categories.split(","); - _categories = {}; - - var i; - for (i = 0; i < split.length; i++) { + for (let i = 0; i < split.length; i++) { _categories[split[i].trim()] = true; } } diff --git a/tsconfig.json b/tsconfig.json index 97427c6cf..93f0a4aa5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -169,6 +169,7 @@ "apps/tests/http-tests.ts", "apps/tests/image-source-tests.ts", "apps/tests/layouts/absolute-layout-tests.ts", + "apps/tests/layouts/common-layout-tests.ts", "apps/tests/layouts/dock-layout-tests.ts", "apps/tests/layouts/grid-layout-tests.ts", "apps/tests/layouts/layout-helper.android.ts", diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index 9a5f69105..eb4a7373c 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -15,6 +15,7 @@ import color = require("color"); import animationModule = require("ui/animation"); import observable = require("data/observable"); import {registerSpecialProperty} from "ui/builder/special-properties"; +import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style"; registerSpecialProperty("class", (instance: definition.View, propertyValue: string) => { instance.className = propertyValue; @@ -727,6 +728,7 @@ export class View extends proxy.ProxyObject implements definition.View { } var density = utils.layout.getDisplayDensity(); + let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); var childTop: number; var childLeft: number; @@ -735,35 +737,40 @@ export class View extends proxy.ProxyObject implements definition.View { var childHeight = child.getMeasuredHeight(); var vAlignment: string; - if (!isNaN(child.height) && child.verticalAlignment === enums.VerticalAlignment.stretch) { + if (lp.height >= 0 && child.verticalAlignment === enums.VerticalAlignment.stretch) { vAlignment = enums.VerticalAlignment.center; } else { vAlignment = child.verticalAlignment; } + let marginTop = lp.topMargin; + let marginBottom = lp.bottomMargin; + let marginLeft = lp.leftMargin; + let marginRight = lp.rightMargin; + switch (vAlignment) { case enums.VerticalAlignment.top: - childTop = top + child.marginTop * density; + childTop = top + marginTop * density; break; case enums.VerticalAlignment.center || enums.VerticalAlignment.middle: - childTop = top + (bottom - top - childHeight + (child.marginTop - child.marginBottom) * density) / 2; + childTop = top + (bottom - top - childHeight + (marginTop - marginBottom) * density) / 2; break; case enums.VerticalAlignment.bottom: - childTop = bottom - childHeight - (child.marginBottom * density); + childTop = bottom - childHeight - (marginBottom * density); break; case enums.VerticalAlignment.stretch: default: - childTop = top + child.marginTop * density; - childHeight = bottom - top - (child.marginTop + child.marginBottom) * density; + childTop = top + marginTop * density; + childHeight = bottom - top - (marginTop + marginBottom) * density; break; } var hAlignment: string; - if (!isNaN(child.width) && child.horizontalAlignment === enums.HorizontalAlignment.stretch) { + if (lp.width >= 0 && child.horizontalAlignment === enums.HorizontalAlignment.stretch) { hAlignment = enums.HorizontalAlignment.center; } else { @@ -772,21 +779,21 @@ export class View extends proxy.ProxyObject implements definition.View { switch (hAlignment) { case enums.HorizontalAlignment.left: - childLeft = left + child.marginLeft * density; + childLeft = left + marginLeft * density; break; case enums.HorizontalAlignment.center: - childLeft = left + (right - left - childWidth + (child.marginLeft - child.marginRight) * density) / 2; + childLeft = left + (right - left - childWidth + (marginLeft - marginRight) * density) / 2; break; case enums.HorizontalAlignment.right: - childLeft = right - childWidth - child.marginRight * density; + childLeft = right - childWidth - (marginRight * density); break; case enums.HorizontalAlignment.stretch: default: - childLeft = left + child.marginLeft * density; - childWidth = right - left - (child.marginLeft + child.marginRight) * density; + childLeft = left + marginLeft * density; + childWidth = right - left - (marginLeft + marginRight) * density; break; } @@ -795,7 +802,7 @@ export class View extends proxy.ProxyObject implements definition.View { childLeft = Math.round(childLeft); childTop = Math.round(childTop); - trace.write(parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, trace.categories.Layout); + trace.write(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, trace.categories.Layout); child.layout(childLeft, childTop, childRight, childBottom); } @@ -810,19 +817,22 @@ export class View extends proxy.ProxyObject implements definition.View { var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); - trace.write(parent + " :measureChild: " + child + " " + utils.layout.getMode(widthMode) + " " + width + ", " + utils.layout.getMode(heightMode) + " " + height, trace.categories.Layout); var childWidthMeasureSpec = View.getMeasureSpec(child, width, widthMode, true); var childHeightMeasureSpec = View.getMeasureSpec(child, height, heightMode, false); + trace.write(child.parent + " :measureChild: " + child + " " + utils.layout.measureSpecToString(childWidthMeasureSpec) + ", " + utils.layout.measureSpecToString(childHeightMeasureSpec), trace.categories.Layout); + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); measureWidth = child.getMeasuredWidth(); measureHeight = child.getMeasuredHeight(); var density = utils.layout.getDisplayDensity(); + let lp: CommonLayoutParams = child.style._getValue(style.nativeLayoutParamsProperty); + // Convert to pixels. - measureWidth = Math.round(measureWidth + (child.marginLeft + child.marginRight) * density); - measureHeight = Math.round(measureHeight + (child.marginTop + child.marginBottom) * density); + measureWidth = Math.round(measureWidth + (lp.leftMargin + lp.rightMargin) * density); + measureHeight = Math.round(measureHeight + (lp.topMargin + lp.bottomMargin) * density); } return { measuredWidth: measureWidth, measuredHeight: measureHeight }; @@ -830,9 +840,11 @@ export class View extends proxy.ProxyObject implements definition.View { private static getMeasureSpec(view: View, parentLength: number, parentSpecMode: number, horizontal: boolean): number { + let lp: CommonLayoutParams = view.style._getValue(style.nativeLayoutParamsProperty); + var density = utils.layout.getDisplayDensity(); - var margins = horizontal ? view.marginLeft + view.marginRight : view.marginTop + view.marginBottom; - margins = Math.floor(margins * density); + var margins = horizontal ? lp.leftMargin + lp.rightMargin : lp.topMargin + lp.bottomMargin; + margins = Math.round(margins * density); var resultSize = 0; var resultMode = 0; @@ -840,10 +852,10 @@ export class View extends proxy.ProxyObject implements definition.View { var measureLength = Math.max(0, parentLength - margins); // Convert to pixels. - var childLength = Math.floor((horizontal ? view.width : view.height) * density); + var childLength = Math.round((horizontal ? lp.width : lp.height) * density); // We want a specific size... let be it. - if (!isNaN(childLength)) { + if (childLength >= 0) { if (parentSpecMode !== utils.layout.UNSPECIFIED) { resultSize = Math.min(parentLength, childLength); } diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts index ede1427e8..71b4e3e0d 100644 --- a/ui/core/view.d.ts +++ b/ui/core/view.d.ts @@ -367,24 +367,47 @@ declare module "ui/core/view" { /** * Called from layout when this view should assign a size and position to each of its children. Derived classes with children should override this method and call layout on each of their children. - * @param left Left position, relative to parent - * @param top Top position, relative to parent - * @param right Right position, relative to parent + * @param left Left position, relative to parent + * @param top Top position, relative to parent + * @param right Right position, relative to parent * @param bottom Bottom position, relative to parent */ public onLayout(left: number, top: number, right: number, bottom: number): void; /** * This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time. - * @param measuredWidth The measured width of this view. May be a complex bit mask as defined by MEASURED_SIZE_MASK and MEASURED_STATE_TOO_SMALL. + * @param measuredWidth The measured width of this view. May be a complex bit mask as defined by MEASURED_SIZE_MASK and MEASURED_STATE_TOO_SMALL. * @param measuredHeight The measured height of this view. May be a complex bit mask as defined by MEASURED_SIZE_MASK and MEASURED_STATE_TOO_SMALL. */ public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void; + /** + * Called from onLayout when native view position is about to be changed. + * @param parent This parameter is not used. You can pass null. + * @param left Left position, relative to parent + * @param top Top position, relative to parent + * @param right Right position, relative to parent + * @param bottom Bottom position, relative to parent + */ public layoutNativeView(left: number, top: number, right: number, bottom: number): void; + /** + * Measure a child by taking into account its margins and a given measureSpecs. + * @param parent This parameter is not used. You can pass null. + * @param child The view to be measured. + * @param measuredWidth The measured width that the parent layout specifies for this view. + * @param measuredHeight The measured height that the parent layout specifies for this view. + */ public static measureChild(parent: View, child: View, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number }; - + + /** + * Layout a child by taking into account its margins, horizontal and vertical alignments and a given bounds. + * @param parent This parameter is not used. You can pass null. + * @param left Left position, relative to parent + * @param top Top position, relative to parent + * @param right Right position, relative to parent + * @param bottom Bottom position, relative to parent + */ public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void; /** diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index 78ac97190..e31b97d4a 100644 --- a/ui/frame/frame.android.ts +++ b/ui/frame/frame.android.ts @@ -30,9 +30,9 @@ var PageFragmentBody = (android.app.Fragment).extend({ }, onCreateView: function (inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { - trace.write(`PageFragmentBody.onCreateView(${inflater}, ${container}, ${savedInstanceState})`, trace.categories.NativeLifecycle); var entry = this.entry; var page = entry.resolvedPage; + trace.write(`PageFragmentBody.onCreateView(${inflater}, ${page}, ${savedInstanceState})`, trace.categories.NativeLifecycle); if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { this.super.getFragmentManager().beginTransaction().hide(this).commit(); page._onAttached(this.getActivity()); diff --git a/ui/layouts/absolute-layout/absolute-layout.ios.ts b/ui/layouts/absolute-layout/absolute-layout.ios.ts index 202b2d72b..33c9b3257 100644 --- a/ui/layouts/absolute-layout/absolute-layout.ios.ts +++ b/ui/layouts/absolute-layout/absolute-layout.ios.ts @@ -1,6 +1,7 @@ import utils = require("utils/utils"); import view = require("ui/core/view"); import common = require("./absolute-layout-common"); +import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style"; global.moduleMerge(common, exports); @@ -15,6 +16,7 @@ export class AbsoluteLayout extends common.AbsoluteLayout { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + AbsoluteLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); var measureWidth = 0; @@ -29,14 +31,13 @@ export class AbsoluteLayout extends common.AbsoluteLayout { var childMeasureSpec = utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED); var density = utils.layout.getDisplayDensity(); - var count = this.getChildrenCount(); - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childSize = view.View.measureChild(this, child, childMeasureSpec, childMeasureSpec); + let childSize = view.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); } @@ -57,22 +58,25 @@ export class AbsoluteLayout extends common.AbsoluteLayout { super.onLayout(left, top, right, bottom); var density = utils.layout.getDisplayDensity(); - var count = this.getChildrenCount(); - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childWidth = child.getMeasuredWidth(); - var childHeight = child.getMeasuredHeight(); + let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); - var childLeft = (this.paddingLeft + AbsoluteLayout.getLeft(child)) * density; - var childTop = (this.paddingTop + AbsoluteLayout.getTop(child)) * density; - var childRight = childLeft + childWidth + (child.marginLeft + child.marginRight) * density; - var childBottom = childTop + childHeight + (child.marginTop + child.marginBottom) * density; + let childWidth = child.getMeasuredWidth(); + let childHeight = child.getMeasuredHeight(); + + let childLeft = (this.paddingLeft + AbsoluteLayout.getLeft(child)) * density; + let childTop = (this.paddingTop + AbsoluteLayout.getTop(child)) * density; + let childRight = childLeft + childWidth + (lp.leftMargin + lp.rightMargin) * density; + let childBottom = childTop + childHeight + (lp.topMargin + lp.bottomMargin) * density; view.View.layoutChild(this, child, childLeft, childTop, childRight, childBottom); } + + AbsoluteLayout.restoreOriginalParams(this); } -} +} \ No newline at end of file diff --git a/ui/layouts/dock-layout/dock-layout.ios.ts b/ui/layouts/dock-layout/dock-layout.ios.ts index ad485e9de..b851b91bd 100644 --- a/ui/layouts/dock-layout/dock-layout.ios.ts +++ b/ui/layouts/dock-layout/dock-layout.ios.ts @@ -2,6 +2,7 @@ import view = require("ui/core/view"); import enums = require("ui/enums"); import common = require("./dock-layout-common"); +import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style"; global.moduleMerge(common, exports); @@ -12,6 +13,7 @@ export class DockLayout extends common.DockLayout { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + DockLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); var measureWidth = 0; @@ -32,10 +34,10 @@ export class DockLayout extends common.DockLayout { var tempWidth: number = 0; var childWidthMeasureSpec: number; var childHeightMeasureSpec: number; - var count = this.getChildrenCount(); - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } @@ -49,9 +51,8 @@ export class DockLayout extends common.DockLayout { childHeightMeasureSpec = utils.layout.makeMeasureSpec(remainingHeight, heightMode === utils.layout.EXACTLY ? utils.layout.AT_MOST : heightMode); } - var childSize = view.View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec); - - var dock = DockLayout.getDock(child); + let childSize = view.View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec); + let dock = DockLayout.getDock(child); switch (dock) { case enums.Dock.top: @@ -106,16 +107,18 @@ export class DockLayout extends common.DockLayout { childToStretch = this.getChildAt(count); } - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0; i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childWidth = child.getMeasuredWidth() + (child.marginLeft + child.marginRight) * density; - var childHeight = child.getMeasuredHeight() + (child.marginTop + child.marginBottom) * density; + let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); - var dock = DockLayout.getDock(child); + let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density; + let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density; + + let dock = DockLayout.getDock(child); switch (dock) { case enums.Dock.top: childLeft = x; @@ -148,11 +151,14 @@ export class DockLayout extends common.DockLayout { remainingWidth = Math.max(0, remainingWidth - childWidth); break; } + view.View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight); } if (childToStretch) { view.View.layoutChild(this, childToStretch, x, y, x + remainingWidth, y + remainingHeight); } + + DockLayout.restoreOriginalParams(this); } -} +} \ No newline at end of file diff --git a/ui/layouts/grid-layout/grid-layout.ios.ts b/ui/layouts/grid-layout/grid-layout.ios.ts index e7cbcd25e..091615591 100644 --- a/ui/layouts/grid-layout/grid-layout.ios.ts +++ b/ui/layouts/grid-layout/grid-layout.ios.ts @@ -1,7 +1,8 @@ import utils = require("utils/utils"); -import enums = require("ui/enums"); -import view = require("ui/core/view"); +import {HorizontalAlignment, VerticalAlignment} from "ui/enums"; +import {View} from "ui/core/view"; import common = require("./grid-layout-common"); +import {CommonLayoutParams} from "ui/styling/style"; global.moduleMerge(common, exports); @@ -26,19 +27,19 @@ export class GridLayout extends common.GridLayout { this.invalidate(); } - protected onRowChanged(element: view.View, oldValue: number, newValue: number) { + protected onRowChanged(element: View, oldValue: number, newValue: number) { this.invalidate(); } - protected onRowSpanChanged(element: view.View, oldValue: number, newValue: number) { + protected onRowSpanChanged(element: View, oldValue: number, newValue: number) { this.invalidate(); } - protected onColumnChanged(element: view.View, oldValue: number, newValue: number) { + protected onColumnChanged(element: View, oldValue: number, newValue: number) { this.invalidate(); } - protected onColumnSpanChanged(element: view.View, oldValue: number, newValue: number) { + protected onColumnSpanChanged(element: View, oldValue: number, newValue: number) { this.invalidate(); } @@ -48,6 +49,7 @@ export class GridLayout extends common.GridLayout { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + GridLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); let width = utils.layout.getMeasureSpecSize(widthMeasureSpec); @@ -109,7 +111,7 @@ export class GridLayout extends common.GridLayout { let childrenCount = this.getChildrenCount(); for (let i = 0; i < childrenCount; i++) { let child = this.getChildAt(i); - if (!child || !child._isVisible) { + if (!child._isVisible) { continue; } @@ -131,8 +133,8 @@ export class GridLayout extends common.GridLayout { measureWidth = Math.max(measureWidth, this.minWidth * density); measureHeight = Math.max(measureHeight, this.minHeight * density); - let widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0); - let heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0); + let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0); + let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0); this.setMeasuredDimension(widthAndState, heightAndState); } @@ -189,9 +191,11 @@ export class GridLayout extends common.GridLayout { let childBottom = rowOffsets[measureSpec.rowIndex + measureSpec.rowSpan]; // No need to include margins in the width, height - view.View.layoutChild(this, measureSpec.child, childLeft, childTop, childRight, childBottom); + View.layoutChild(this, measureSpec.child, childLeft, childTop, childRight, childBottom); } } + + GridLayout.restoreOriginalParams(this); } } @@ -212,7 +216,7 @@ class MeasureSpecs { public measured: boolean = false; constructor( - public child: view.View, + public child: View, public column: common.ItemSpec, public row: common.ItemSpec, columnSpan?: number, @@ -351,11 +355,11 @@ class MeasureHelper { rowStarValue: number; get horizontalStretch(): boolean { - return this.grid.horizontalAlignment === enums.HorizontalAlignment.stretch && !this.infinityWidth; + return this.grid.horizontalAlignment === HorizontalAlignment.stretch && !this.infinityWidth; } get verticalStretch(): boolean { - return this.grid.verticalAlignment === enums.VerticalAlignment.stretch && !this.infinityHeight; + return this.grid.verticalAlignment === VerticalAlignment.stretch && !this.infinityHeight; } get columnsFixed(): boolean { @@ -630,7 +634,7 @@ class MeasureHelper { let widthMeasureSpec: number = (measureSpec.autoColumnsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelWidth, utils.layout.EXACTLY); let heightMeasureSpec: number = (isFakeMeasure || measureSpec.autoRowsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelHeight, utils.layout.EXACTLY); - let childSize = view.View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); + let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); let columnSpanEnd = measureSpec.columnIndex + measureSpec.columnSpan; let rowSpanEnd = measureSpec.rowIndex + measureSpec.rowSpan; @@ -694,7 +698,7 @@ class MeasureHelper { let widthMeasureSpec = utils.layout.makeMeasureSpec(measureWidth, this.horizontalStretch ? utils.layout.EXACTLY : utils.layout.AT_MOST); let heightMeasureSpec: number = (measureSpec.autoRowsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelHeight, utils.layout.EXACTLY); - let childSize = view.View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); + let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); this.updateColumnGroupWidth(measureSpec, childSize.measuredWidth); @@ -739,7 +743,7 @@ class MeasureHelper { let widthMeasureSpec: number = (measureSpec.autoColumnsCount > 0) ? this.infinity : utils.layout.makeMeasureSpec(measureSpec.pixelWidth, utils.layout.EXACTLY); let heightMeasureSpec = utils.layout.makeMeasureSpec(measureHeight, this.verticalStretch ? utils.layout.EXACTLY : utils.layout.AT_MOST); - let childSize = view.View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); + let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); // Distribute width over auto columns if (measureSpec.autoColumnsCount > 0) { @@ -798,7 +802,7 @@ class MeasureHelper { let widthMeasureSpec = utils.layout.makeMeasureSpec(measureWidth, (measureSpec.starColumnsCount > 0 && !this.horizontalStretch) ? utils.layout.AT_MOST : utils.layout.EXACTLY); let heightMeasureSpec = utils.layout.makeMeasureSpec(measureHeight, (measureSpec.starRowsCount > 0 && !this.verticalStretch) ? utils.layout.AT_MOST : utils.layout.EXACTLY); - let childSize = view.View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); + let childSize = View.measureChild(this.grid, measureSpec.child, widthMeasureSpec, heightMeasureSpec); this.updateColumnGroupWidth(measureSpec, childSize.measuredWidth); this.updateRowGroupHeight(measureSpec, childSize.measuredHeight); @@ -830,7 +834,6 @@ class MeasureHelper { } private updateColumnGroupWidth(measureSpec: MeasureSpecs, remainingSpace: number): void { - // Distribute width over star columns if (!this.horizontalStretch) { let columnIndex = measureSpec.columnIndex; diff --git a/ui/layouts/layout-base.d.ts b/ui/layouts/layout-base.d.ts index 388bc5390..950ef00a0 100644 --- a/ui/layouts/layout-base.d.ts +++ b/ui/layouts/layout-base.d.ts @@ -50,6 +50,20 @@ */ removeChildren(): void; + /** + * Iterates over children and changes their width and height to one calculated from percentage values. + * + * @param widthMeasureSpec Width MeasureSpec of the parent layout. + * @param heightMeasureSpec Height MeasureSpec of the parent layout. + */ + protected static adjustChildrenLayoutParams(layoutBase: LayoutBase, widthMeasureSpec: number, heightMeasureSpec: number): void; + + /** + * Iterates over children and restores their original dimensions that were changed for + * percentage values. + */ + protected static restoreOriginalParams(layoutBase: LayoutBase): void; + /** * Gets or sets padding style property. */ diff --git a/ui/layouts/layout-base.ts b/ui/layouts/layout-base.ts index f72613418..eda56c429 100644 --- a/ui/layouts/layout-base.ts +++ b/ui/layouts/layout-base.ts @@ -2,6 +2,8 @@ import view = require("ui/core/view"); import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); +import utils = require("utils/utils"); +import style = require("ui/styling/style"); export class LayoutBase extends view.CustomLayoutView implements definition.LayoutBase, view.AddChildFromBuilder { @@ -129,4 +131,72 @@ export class LayoutBase extends view.CustomLayoutView implements definition.Layo var layout = data.object; layout.onClipToBoundsChanged(data.oldValue, data.newValue); } -} + + protected static adjustChildrenLayoutParams(layoutBase: LayoutBase, widthMeasureSpec: number, heightMeasureSpec: number): void { + let availableWidth = utils.layout.getMeasureSpecSize(widthMeasureSpec); + let widthSpec = utils.layout.getMeasureSpecMode(widthMeasureSpec); + + let availableHeight = utils.layout.getMeasureSpecSize(heightMeasureSpec); + let heightSpec = utils.layout.getMeasureSpecMode(heightMeasureSpec); + + for (let i = 0, count = layoutBase.getChildrenCount(); i < count; i++) { + let child = layoutBase.getChildAt(i); + let lp: style.CommonLayoutParams = child.style._getValue(style.nativeLayoutParamsProperty); + + if (widthSpec !== utils.layout.UNSPECIFIED) { + if (lp.widthPercent > 0) { + lp.width = Math.round(availableWidth * lp.widthPercent); + } + + if (lp.leftMarginPercent > 0) { + lp.leftMargin = Math.round(availableWidth * lp.leftMarginPercent); + } + + if (lp.rightMarginPercent > 0) { + lp.rightMargin = Math.round(availableWidth * lp.rightMarginPercent); + } + } + + if (heightSpec !== utils.layout.UNSPECIFIED) { + if (lp.heightPercent > 0) { + lp.height = Math.round(availableHeight * lp.heightPercent); + } + + if (lp.topMarginPercent > 0) { + lp.topMargin = Math.round(availableHeight * lp.topMarginPercent); + } + + if (lp.bottomMarginPercent > 0) { + lp.bottomMargin = Math.round(availableHeight * lp.bottomMarginPercent); + } + } + } + } + + protected static restoreOriginalParams(layoutBase: LayoutBase): void { + for (let i = 0, count = layoutBase.getChildrenCount(); i < count; i++) { + let child = layoutBase.getChildAt(i); + let lp: style.CommonLayoutParams = child.style._getValue(style.nativeLayoutParamsProperty); + + if (lp.widthPercent > 0) { + lp.width = -1; + } + + if (lp.heightPercent > 0) { + lp.height = -1; + } + if (lp.leftMarginPercent > 0) { + lp.leftMargin = 0; + } + if (lp.topMarginPercent > 0) { + lp.topMargin = 0; + } + if (lp.rightMarginPercent > 0) { + lp.rightMargin = 0; + } + if (lp.bottomMarginPercent > 0) { + lp.bottomMargin = 0; + } + } + } +} \ No newline at end of file diff --git a/ui/layouts/stack-layout/stack-layout.ios.ts b/ui/layouts/stack-layout/stack-layout.ios.ts index eecc52257..191ac4704 100644 --- a/ui/layouts/stack-layout/stack-layout.ios.ts +++ b/ui/layouts/stack-layout/stack-layout.ios.ts @@ -1,7 +1,8 @@ import utils = require("utils/utils"); -import enums = require("ui/enums"); -import view = require("ui/core/view"); +import {Orientation, VerticalAlignment, HorizontalAlignment} from "ui/enums"; +import {View} from "ui/core/view"; import common = require("./stack-layout-common"); +import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style"; global.moduleMerge(common, exports); @@ -10,6 +11,7 @@ export class StackLayout extends common.StackLayout { private _totalLength = 0; public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + StackLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); var density = utils.layout.getDisplayDensity(); @@ -22,12 +24,10 @@ export class StackLayout extends common.StackLayout { var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); - var isVertical = this.orientation === enums.Orientation.vertical; + var isVertical = this.orientation === Orientation.vertical; var verticalPadding = (this.paddingTop + this.paddingBottom) * density; var horizontalPadding = (this.paddingLeft + this.paddingRight) * density; - var count = this.getChildrenCount(); - var measureSpec: number; var mode = isVertical ? heightMode : widthMode; @@ -44,32 +44,32 @@ export class StackLayout extends common.StackLayout { var childMeasureSpec: number; if (isVertical) { - var childWidth = (widthMode === utils.layout.UNSPECIFIED) ? 0 : width - horizontalPadding; + let childWidth = (widthMode === utils.layout.UNSPECIFIED) ? 0 : width - horizontalPadding; childWidth = Math.max(0, childWidth); childMeasureSpec = utils.layout.makeMeasureSpec(childWidth, widthMode) } else { - var childHeight = (heightMode === utils.layout.UNSPECIFIED) ? 0 : height - verticalPadding; + let childHeight = (heightMode === utils.layout.UNSPECIFIED) ? 0 : height - verticalPadding; childHeight = Math.max(0, childHeight); childMeasureSpec = utils.layout.makeMeasureSpec(childHeight, heightMode) } var childSize: { measuredWidth: number; measuredHeight: number }; - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } if (isVertical) { - childSize = view.View.measureChild(this, child, childMeasureSpec, utils.layout.makeMeasureSpec(remainingLength, measureSpec)); + childSize = View.measureChild(this, child, childMeasureSpec, utils.layout.makeMeasureSpec(remainingLength, measureSpec)); measureWidth = Math.max(measureWidth, childSize.measuredWidth); var viewHeight = childSize.measuredHeight; measureHeight += viewHeight; remainingLength = Math.max(0, remainingLength - viewHeight); } else { - childSize = view.View.measureChild(this, child, utils.layout.makeMeasureSpec(remainingLength, measureSpec), childMeasureSpec); + childSize = View.measureChild(this, child, utils.layout.makeMeasureSpec(remainingLength, measureSpec), childMeasureSpec); measureHeight = Math.max(measureHeight, childSize.measuredHeight); var viewWidth = childSize.measuredWidth; measureWidth += viewWidth; @@ -85,20 +85,22 @@ export class StackLayout extends common.StackLayout { this._totalLength = isVertical ? measureHeight : measureWidth; - var widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0); - var heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0); + var widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0); + var heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0); this.setMeasuredDimension(widthAndState, heightAndState); } public onLayout(left: number, top: number, right: number, bottom: number): void { super.onLayout(left, top, right, bottom); - if (this.orientation === enums.Orientation.vertical) { + if (this.orientation === Orientation.vertical) { this.layoutVertical(left, top, right, bottom); } else { this.layoutHorizontal(left, top, right, bottom); } + + StackLayout.restoreOriginalParams(this); } private layoutVertical(left: number, top: number, right: number, bottom: number): void { @@ -114,30 +116,31 @@ export class StackLayout extends common.StackLayout { var childRight = right - left - paddingRight; switch (this.verticalAlignment) { - case enums.VerticalAlignment.center || enums.VerticalAlignment.middle: + case VerticalAlignment.center || VerticalAlignment.middle: childTop = (bottom - top - this._totalLength) / 2 + paddingTop - paddingBottom; break; - case enums.VerticalAlignment.bottom: + case VerticalAlignment.bottom: childTop = bottom - top - this._totalLength + paddingTop - paddingBottom; break; - case enums.VerticalAlignment.top: - case enums.VerticalAlignment.stretch: + case VerticalAlignment.top: + case VerticalAlignment.stretch: default: childTop = paddingTop; break; } - var count = this.getChildrenCount(); - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childHeight = child.getMeasuredHeight() + (child.marginTop + child.marginBottom) * density; - view.View.layoutChild(this, child, childLeft, childTop, childRight, childTop + childHeight); + 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; } } @@ -155,31 +158,32 @@ export class StackLayout extends common.StackLayout { var childBottom = bottom - top - paddingBottom; switch (this.horizontalAlignment) { - case enums.HorizontalAlignment.center: + case HorizontalAlignment.center: childLeft = (right - left - this._totalLength) / 2 + paddingLeft - paddingRight; break; - case enums.HorizontalAlignment.right: + case HorizontalAlignment.right: childLeft = right - left - this._totalLength + paddingLeft - paddingRight; break; - case enums.HorizontalAlignment.left: - case enums.HorizontalAlignment.stretch: + case HorizontalAlignment.left: + case HorizontalAlignment.stretch: default: childLeft = paddingLeft; break; } - var count = this.getChildrenCount(); - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childWidth = child.getMeasuredWidth() + (child.marginLeft + child.marginRight) * density;; - view.View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childBottom); + 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 4d07734d5..cc3e5983c 100644 --- a/ui/layouts/wrap-layout/wrap-layout.ios.ts +++ b/ui/layouts/wrap-layout/wrap-layout.ios.ts @@ -1,13 +1,14 @@ import utils = require("utils/utils"); -import view = require("ui/core/view"); -import enums = require("ui/enums"); import common = require("./wrap-layout-common"); +import {View} from "ui/core/view"; +import {Orientation} from "ui/enums"; +import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style"; global.moduleMerge(common, exports); export class WrapLayout extends common.WrapLayout { - private _lengths: Array; + private _lengths: Array = new Array(); private static getChildMeasureSpec(parentMode: number, parentLength: number, itemLength): number { if (itemLength > 0) { @@ -22,6 +23,7 @@ export class WrapLayout extends common.WrapLayout { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { + WrapLayout.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); var measureWidth = 0; @@ -33,8 +35,6 @@ export class WrapLayout extends common.WrapLayout { var height = utils.layout.getMeasureSpecSize(heightMeasureSpec); var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec); - var count = this.getChildrenCount(); - var density = utils.layout.getDisplayDensity(); var childWidthMeasureSpec: number = WrapLayout.getChildMeasureSpec(widthMode, width, this.itemWidth * density); var childHeightMeasureSpec: number = WrapLayout.getChildMeasureSpec(heightMode, height, this.itemHeight * density); @@ -42,18 +42,19 @@ export class WrapLayout extends common.WrapLayout { var remainingWidth = widthMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : width - ((this.paddingLeft + this.paddingRight) * density); var remainingHeight = heightMode === utils.layout.UNSPECIFIED ? Number.MAX_VALUE : height - ((this.paddingTop + this.paddingBottom) * density); - this._lengths = [0]; + this._lengths.length = 0; + var rowOrColumn = 0; var maxLength = 0; - var i: number = 0; - var isVertical = this.orientation === enums.Orientation.vertical; - for (i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + var isVertical = this.orientation === Orientation.vertical; + + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } - var childSize = view.View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec); + var childSize = View.measureChild(this, child, childWidthMeasureSpec, childHeightMeasureSpec); if (isVertical) { if (childSize.measuredHeight > remainingHeight) { rowOrColumn++; @@ -64,7 +65,6 @@ export class WrapLayout extends common.WrapLayout { } else { remainingHeight -= childSize.measuredHeight; - this._lengths[rowOrColumn] = Math.max(this._lengths[rowOrColumn], childSize.measuredWidth); measureHeight += childSize.measuredHeight; } } @@ -78,23 +78,29 @@ export class WrapLayout extends common.WrapLayout { } else { remainingWidth -= childSize.measuredWidth; - this._lengths[rowOrColumn] = Math.max(this._lengths[rowOrColumn], childSize.measuredHeight); measureWidth += childSize.measuredWidth; } } + + if (this._lengths.length <= rowOrColumn) { + this._lengths[rowOrColumn] = isVertical ? childSize.measuredWidth: childSize.measuredHeight; + } + else { + this._lengths[rowOrColumn] = Math.max(this._lengths[rowOrColumn], isVertical ? childSize.measuredWidth : childSize.measuredHeight); + } } if (isVertical) { measureHeight = Math.max(maxLength, measureHeight); - for (i = 0; i < this._lengths.length; i++) { - measureWidth += this._lengths[i]; - } + this._lengths.forEach((value, index, array) => { + measureWidth += value; + }); } else { measureWidth = Math.max(maxLength, measureWidth); - for (i = 0; i < this._lengths.length; i++) { - measureHeight += this._lengths[i]; - } + this._lengths.forEach((value, index, array) => { + measureHeight += value; + }); } measureWidth += (this.paddingLeft + this.paddingRight) * density; @@ -103,8 +109,8 @@ export class WrapLayout extends common.WrapLayout { measureWidth = Math.max(measureWidth, this.minWidth * density); measureHeight = Math.max(measureHeight, this.minHeight * density); - var widthAndState = view.View.resolveSizeAndState(measureWidth, width, widthMode, 0); - var heightAndState = view.View.resolveSizeAndState(measureHeight, height, heightMode, 0); + var widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0); + var heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0); this.setMeasuredDimension(widthAndState, heightAndState); } @@ -112,10 +118,9 @@ export class WrapLayout extends common.WrapLayout { public onLayout(left: number, top: number, right: number, bottom: number): void { super.onLayout(left, top, right, bottom); - var isVertical = this.orientation === enums.Orientation.vertical; + var isVertical = this.orientation === Orientation.vertical; var density = utils.layout.getDisplayDensity(); - var count = this.getChildrenCount(); var childLeft = this.paddingLeft * density; var childTop = this.paddingTop * density; @@ -128,21 +133,21 @@ export class WrapLayout extends common.WrapLayout { } var rowOrColumn = 0; - for (var i = 0; i < count; i++) { - var child = this.getChildAt(i); - if (!child || !child._isVisible) { + for (let i = 0, count = this.getChildrenCount(); i < count; i++) { + let child = this.getChildAt(i); + if (!child._isVisible) { continue; } // Add margins because layoutChild will sustract them. // * density converts them to device pixels. - var childWidth = child.getMeasuredWidth() + (child.marginLeft + child.marginRight) * density; - var childHeight = child.getMeasuredHeight() + (child.marginTop + child.marginBottom) * density; + let lp: CommonLayoutParams = child.style._getValue(nativeLayoutParamsProperty); - var length = this._lengths[rowOrColumn]; - if (isVertical) { - - + let childWidth = child.getMeasuredWidth() + (lp.leftMargin + lp.rightMargin) * density; + let childHeight = child.getMeasuredHeight() + (lp.topMargin + lp.bottomMargin) * density; + + let length = this._lengths[rowOrColumn]; + if (isVertical) { childWidth = length; childHeight = this.itemHeight > 0 ? this.itemHeight * density : childHeight; if (childTop + childHeight > childrenLength) { @@ -177,7 +182,7 @@ export class WrapLayout extends common.WrapLayout { } } - view.View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight); + View.layoutChild(this, child, childLeft, childTop, childLeft + childWidth, childTop + childHeight); if (isVertical) { // Move next child Top position to bottom. @@ -188,5 +193,7 @@ export class WrapLayout extends common.WrapLayout { childLeft += childWidth; } } + + WrapLayout.restoreOriginalParams(this); } -} +} \ No newline at end of file diff --git a/ui/styling/style.d.ts b/ui/styling/style.d.ts index d98e5189a..c1bf42371 100644 --- a/ui/styling/style.d.ts +++ b/ui/styling/style.d.ts @@ -18,11 +18,19 @@ declare module "ui/styling/style" { width: number; height: number; + widthPercent: number; + heightPercent: number; + leftMargin: number; topMargin: number; rightMargin: number; bottomMargin: number; + leftMarginPercent: number; + topMarginPercent: number; + rightMarginPercent: number; + bottomMarginPercent: number; + horizontalAlignment: string; verticalAlignment: string; } @@ -66,7 +74,7 @@ declare module "ui/styling/style" { public opacity: number; public whiteSpace: string; - new(parentView: View); + constructor(parentView: View); public _beginUpdate(); public _endUpdate(); diff --git a/ui/styling/style.ts b/ui/styling/style.ts index acd771cb5..c07c7b177 100644 --- a/ui/styling/style.ts +++ b/ui/styling/style.ts @@ -13,6 +13,7 @@ import utils = require("utils/utils"); import font = require("ui/styling/font"); import background = require("ui/styling/background"); import platform = require("platform"); +import definition = require("ui/styling/style"); // key is the property id and value is Dictionary; var _registeredHandlers = Array(); @@ -26,28 +27,67 @@ var noStylingClasses = {}; // on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call). var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout; -export interface Thickness { - left: number; - top: number; - right: number; - bottom: number; +interface ThicknessValue { + left: Object; + top: Object; + right: Object; + bottom: Object; } -export interface CommonLayoutParams { - width: number; - height: number; - - leftMargin: number; - topMargin: number; - rightMargin: number; - bottomMargin: number; - - horizontalAlignment: string; - verticalAlignment: string; +interface PercentHelper { + value: number; + isPercent: boolean; + isError: boolean; } -function parseThickness(value: any): Thickness { - var result: Thickness = { top: 0, right: 0, bottom: 0, left: 0 }; +function parseMargin(value: any): ThicknessValue { + if (types.isString(value)) { + let arr = (value).split(/[ ,]+/); + + let top: Object; + let right: Object; + let bottom: Object; + let left: Object; + + if (arr.length === 1) { + top = right = bottom = left = arr[0]; + } + else if (arr.length === 2) { + top = bottom = arr[0]; + right = left = arr[1]; + } + else if (arr.length === 4) { + top = arr[0]; + right = arr[1]; + bottom = arr[2]; + left = arr[3]; + } + else { + throw new Error("Invalid value for margin: " + value); + } + + return { + top: top, + right: right, + bottom: bottom, + left: left + } + } + else if (types.isNumber(value)) { + return { + top: value, + right: value, + bottom: value, + left: value + } + } + else { + return value; + } +} + +function parseThickness(value: any): definition.Thickness { + var result: definition.Thickness = { top: 0, right: 0, bottom: 0, left: 0 }; if (types.isString(value)) { var arr = value.split(/[ ,]+/); var top = parseInt(arr[0]); @@ -77,7 +117,7 @@ function parseThickness(value: any): Thickness { return result; } -function layoutParamsComparer(x: CommonLayoutParams, y: CommonLayoutParams): boolean { +function layoutParamsComparer(x: definition.CommonLayoutParams, y: definition.CommonLayoutParams): boolean { return x.width === y.width && x.height === y.height && x.leftMargin === y.leftMargin @@ -86,17 +126,61 @@ function layoutParamsComparer(x: CommonLayoutParams, y: CommonLayoutParams): boo && x.bottomMargin === y.bottomMargin && x.horizontalAlignment === y.horizontalAlignment && x.verticalAlignment === y.verticalAlignment + && x.widthPercent === y.widthPercent + && x.heightPercent === y.heightPercent + && x.leftMarginPercent === y.leftMarginPercent + && x.topMarginPercent === y.topMarginPercent + && x.rightMarginPercent === y.rightMarginPercent + && x.bottomMarginPercent === y.bottomMarginPercent } function onLayoutParamsChanged(data: PropertyChangeData) { - var style =