From fe3d176dd67294234d3b838f20cd42c80cf76599 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Tue, 15 Mar 2016 16:41:57 +0200 Subject: [PATCH 1/5] Add public instance methods to View class for getting locations and sizes Resolves #1760 --- apps/tests/ui/view/view-tests-common.ts | 85 ++++++++++++++++++++++--- ui/core/view-common.ts | 33 ++++++++++ ui/core/view.android.ts | 26 ++++++++ ui/core/view.d.ts | 61 +++++++++++++++++- ui/core/view.ios.ts | 26 ++++++++ 5 files changed, 222 insertions(+), 9 deletions(-) diff --git a/apps/tests/ui/view/view-tests-common.ts b/apps/tests/ui/view/view-tests-common.ts index 61f1f19ce..5a70d3b9b 100644 --- a/apps/tests/ui/view/view-tests-common.ts +++ b/apps/tests/ui/view/view-tests-common.ts @@ -3,7 +3,7 @@ import viewModule = require("ui/core/view"); import frame = require("ui/frame"); import page = require("ui/page"); import button = require("ui/button"); -import label = require("ui/label"); +import labelModule = require("ui/label"); import types = require("utils/types"); import helper = require("../../ui/helper"); import color = require("color"); @@ -14,6 +14,7 @@ import observable = require("data/observable"); import bindable = require("ui/core/bindable"); import definition = require("./view-tests"); import enums = require("ui/enums"); +import absoluteLayoutModule = require("ui/layouts/absolute-layout"); export var test_eachDescendant = function () { var test = function (views: Array) { @@ -625,7 +626,7 @@ export var test_binding_style_opacity = function () { } function _createLabelWithBorder(): viewModule.View { - var lbl = new label.Label(); + var lbl = new labelModule.Label(); lbl.borderRadius = 10; lbl.borderWidth = 2; lbl.borderColor = new color.Color("#FF0000"); @@ -635,7 +636,7 @@ function _createLabelWithBorder(): viewModule.View { } export var testIsVisible = function () { - var lbl = new label.Label(); + var lbl = new labelModule.Label(); helper.buildUIAndRunTest(lbl, function (views: Array) { TKUnit.assertEqual(lbl.visibility, enums.Visibility.visible); @@ -652,7 +653,7 @@ export var testIsVisible = function () { } export var testSetInlineStyle = function () { - var lbl = new label.Label(); + var lbl = new labelModule.Label(); var expectedColor = "#ff0000"; var expectedBackgroundColor = "#ff0000"; @@ -667,7 +668,7 @@ export var testSetInlineStyle = function () { export var testBorderWidth = function () { helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array) { - var lbl = views[0]; + var lbl = views[0]; var expectedValue = lbl.borderWidth; var actualValue = definition.getNativeBorderWidth(lbl); TKUnit.assertEqual(actualValue, expectedValue); @@ -676,7 +677,7 @@ export var testBorderWidth = function () { export var testCornerRadius = function () { helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array) { - var lbl = views[0]; + var lbl = views[0]; var expectedValue = lbl.borderRadius; var actualValue = definition.getNativeCornerRadius(lbl); TKUnit.assertEqual(actualValue, expectedValue); @@ -685,14 +686,14 @@ export var testCornerRadius = function () { export var testBorderColor = function () { helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array) { - var lbl = views[0]; + var lbl = views[0]; TKUnit.assertEqual(definition.checkNativeBorderColor(lbl), true, "BorderColor not applied correctly!"); }); } export var testBackgroundColor = function () { helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array) { - var lbl = views[0]; + var lbl = views[0]; TKUnit.assertEqual(definition.checkNativeBackgroundColor(lbl), true, "BackgroundColor not applied correctly!"); }); } @@ -710,4 +711,72 @@ export var testBackgroundImage = function () { export function test_automation_text_default_value() { let view = new button.Button(); TKUnit.assertTrue(view.automationText === undefined, "AutomationText default value should be UNDEFINED."); +} + +export var test_getLocationInWindow_IsUndefinedWhenNotInTheVisualTree = function () { + var label = new labelModule.Label(); + TKUnit.assertNull(label.getLocationInWindow()); +} + +export var test_getLocationOnScreen_IsUndefinedWhenNotInTheVisualTree = function () { + var label = new labelModule.Label(); + TKUnit.assertNull(label.getLocationOnScreen()); +} + +var delta = 0.1; +export var test_getLocationRelativeToOtherView = function () { + var a1 = new absoluteLayoutModule.AbsoluteLayout(); + a1.width = 200; + a1.height = 200; + a1.backgroundColor = new color.Color("red"); + + var a2 = new absoluteLayoutModule.AbsoluteLayout(); + a2.width = 100; + a2.height = 100; + absoluteLayoutModule.AbsoluteLayout.setLeft(a2, 11); + absoluteLayoutModule.AbsoluteLayout.setTop(a2, 12); + a2.backgroundColor = new color.Color("green"); + + var label = new labelModule.Label(); + label.text = "label"; + label.id = "label"; + label.width = 70; + label.height = 30; + absoluteLayoutModule.AbsoluteLayout.setLeft(label, 13); + absoluteLayoutModule.AbsoluteLayout.setTop(label, 14); + a2.backgroundColor = new color.Color("yellow"); + + a2.addChild(label); + a1.addChild(a2); + + helper.buildUIAndRunTest(a1, function (views: Array) { + frame.topmost().requestLayout(); + TKUnit.wait(0.1); + + var labelInA2 = label.getLocationRelativeTo(a2); + var labelInA1 = label.getLocationRelativeTo(a1); + var a2InA1 = a2.getLocationRelativeTo(a1); + + TKUnit.assertAreClose(labelInA2.x, 13, delta); + TKUnit.assertAreClose(labelInA2.y, 14, delta); + + TKUnit.assertAreClose(labelInA1.x, 24, delta); + TKUnit.assertAreClose(labelInA1.y, 26, delta); + + TKUnit.assertAreClose(a2InA1.x, 11, delta); + TKUnit.assertAreClose(a2InA1.y, 12, delta); + }); +} + +export var test_getActualSize = function () { + var label = new labelModule.Label(); + label.width = 100; + label.height = 200; + helper.buildUIAndRunTest(label, function (views: Array) { + frame.topmost().requestLayout(); + TKUnit.wait(0.1); + var actualSize = label.getActualSize(); + TKUnit.assertAreClose(actualSize.width, 100, delta); + TKUnit.assertAreClose(actualSize.height, 200, delta); + }); } \ No newline at end of file diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index d812361aa..9d15cc10b 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -1140,6 +1140,39 @@ export class View extends ProxyObject implements definition.View { return undefined; } + public getLocationInWindow(): definition.Point { + return undefined; + } + + public getLocationOnScreen(): definition.Point { + return undefined; + } + + public getLocationRelativeTo(otherView: definition.View): definition.Point { + var my = this.getLocationInWindow(); + var other = otherView.getLocationInWindow(); + if (!my || !other) { + return undefined; + } + + return { + x: my.x - other.x, + y: my.y - other.y + } + } + + public getActualSize(): definition.Size { + var currentBounds = this._getCurrentLayoutBounds(); + if (!currentBounds) { + return undefined; + } + + return { + width: utils.layout.toDeviceIndependentPixels(currentBounds.right - currentBounds.left), + height: utils.layout.toDeviceIndependentPixels(currentBounds.bottom - currentBounds.top), + } + } + public animate(animation: any): Promise { return this.createAnimation(animation).play(); } diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts index 4002efbab..93b13caea 100644 --- a/ui/core/view.android.ts +++ b/ui/core/view.android.ts @@ -363,6 +363,32 @@ export class View extends viewCommon.View { return false; } + public getLocationInWindow(): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.getWindowToken()) { + return undefined; + } + + var nativeArray = (Array).create("int", 2); + this._nativeView.getLocationInWindow(nativeArray); + return { + x: utils.layout.toDeviceIndependentPixels(nativeArray[0]), + y: utils.layout.toDeviceIndependentPixels(nativeArray[1]), + } + } + + public getLocationOnScreen(): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.getWindowToken()) { + return undefined; + } + + var nativeArray = (Array).create("int", 2); + this._nativeView.getLocationOnScreen(nativeArray); + return { + x: utils.layout.toDeviceIndependentPixels(nativeArray[0]), + y: utils.layout.toDeviceIndependentPixels(nativeArray[1]), + } + } + public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number { var result = size; switch (specMode) { diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts index c7da2cc7e..db4911afe 100644 --- a/ui/core/view.d.ts +++ b/ui/core/view.d.ts @@ -32,6 +32,38 @@ declare module "ui/core/view" { export function isEventOrGesture(name: string, view: View): boolean; + /** + * The Point interface describes a two dimensional location. + * It has two properties x and y, representing the x and y coordinate of the location. + */ + export interface Point { + /** + * Represents the x coordinate of the location. + */ + x: number; + + /** + * Represents the y coordinate of the location. + */ + y: number; + } + + /** + * The Size interface describes abstract dimensions in two dimensional space. + * It has two properties width and height, representing the width and height values of the size. + */ + export interface Size { + /** + * Represents the width of the size. + */ + width: number; + + /** + * Represents the height of the size. + */ + height: number; + } + /** * Defines interface for an optional parameter used to create a view. */ @@ -433,7 +465,7 @@ declare module "ui/core/view" { /** * Returns the child view with the specified id. */ - getViewById(id: string): T; + public getViewById(id: string): T; /** * Tries to focus the view. @@ -483,9 +515,36 @@ declare module "ui/core/view" { */ on(event: "unloaded", callback: (args: observable.EventData) => void, thisArg?: any); + /** + * Animates one or more properties of the view based on the supplied options. + */ public animate(options: animation.AnimationDefinition): Promise; + + /** + * Creates an Animation object based on the supplied options. + */ public createAnimation(options: animation.AnimationDefinition): animation.Animation; + /** + * Returns the location of this view in the window coordinate system. + */ + public getLocationInWindow(): Point; + + /** + * Returns the location of this view in the screen coordinate system. + */ + public getLocationOnScreen(): Point; + + /** + * Returns the location of this view in the otherView's coordinate system. + */ + public getLocationRelativeTo(otherView: View): Point; + + /** + * Returns the actual size of the view in device-independent pixels. + */ + public getActualSize(): Size; + // Lifecycle events onLoaded(): void; onUnloaded(): void; diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts index dd1754b09..6c411913e 100644 --- a/ui/core/view.ios.ts +++ b/ui/core/view.ios.ts @@ -1,5 +1,6 @@ import types = require("utils/types"); import viewCommon = require("./view-common"); +import viewDefinition = require("ui/core/view"); import trace = require("trace"); import utils = require("utils/utils"); import dependencyObservable = require("ui/core/dependency-observable"); @@ -252,6 +253,31 @@ export class View extends viewCommon.View { return false; } + public getLocationInWindow(): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.window) { + return undefined; + } + + var pointInWindow = this._nativeView.convertPointToView(this._nativeView.bounds.origin, null); + return { + x: utils.layout.toDeviceIndependentPixels(pointInWindow.x), + y: utils.layout.toDeviceIndependentPixels(pointInWindow.y), + } + } + + public getLocationOnScreen(): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.window) { + return undefined; + } + + var pointInWindow = this._nativeView.convertPointToView(this._nativeView.bounds.origin, null); + var pointOnScreen = this._nativeView.window.convertPointToWindow(pointInWindow, null); + return { + x: utils.layout.toDeviceIndependentPixels(pointOnScreen.x), + y: utils.layout.toDeviceIndependentPixels(pointOnScreen.y), + } + } + private _onSizeChanged() { this.style._sizeChanged(); } From 220cc627d2d0368690810c38c3accde5b917e52b Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Mon, 21 Mar 2016 11:36:12 +0200 Subject: [PATCH 2/5] Increase unit test delta to 0.5 --- apps/tests/ui/view/view-tests-common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tests/ui/view/view-tests-common.ts b/apps/tests/ui/view/view-tests-common.ts index 5a70d3b9b..ddf475b99 100644 --- a/apps/tests/ui/view/view-tests-common.ts +++ b/apps/tests/ui/view/view-tests-common.ts @@ -723,7 +723,7 @@ export var test_getLocationOnScreen_IsUndefinedWhenNotInTheVisualTree = function TKUnit.assertNull(label.getLocationOnScreen()); } -var delta = 0.1; +var delta = 0.5; export var test_getLocationRelativeToOtherView = function () { var a1 = new absoluteLayoutModule.AbsoluteLayout(); a1.width = 200; From d80bc5fc811313b9d085a3fb0eb6016bf021d54d Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Mon, 21 Mar 2016 13:05:17 +0200 Subject: [PATCH 3/5] Eliminate rounding errors --- apps/tests/ui/view/view-tests-common.ts | 2 +- ui/core/view-common.ts | 11 +---------- ui/core/view.android.ts | 17 +++++++++++++++++ ui/core/view.ios.ts | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/tests/ui/view/view-tests-common.ts b/apps/tests/ui/view/view-tests-common.ts index ddf475b99..5a70d3b9b 100644 --- a/apps/tests/ui/view/view-tests-common.ts +++ b/apps/tests/ui/view/view-tests-common.ts @@ -723,7 +723,7 @@ export var test_getLocationOnScreen_IsUndefinedWhenNotInTheVisualTree = function TKUnit.assertNull(label.getLocationOnScreen()); } -var delta = 0.5; +var delta = 0.1; export var test_getLocationRelativeToOtherView = function () { var a1 = new absoluteLayoutModule.AbsoluteLayout(); a1.width = 200; diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index 9d15cc10b..3d9ed55af 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -1149,16 +1149,7 @@ export class View extends ProxyObject implements definition.View { } public getLocationRelativeTo(otherView: definition.View): definition.Point { - var my = this.getLocationInWindow(); - var other = otherView.getLocationInWindow(); - if (!my || !other) { - return undefined; - } - - return { - x: my.x - other.x, - y: my.y - other.y - } + return undefined; } public getActualSize(): definition.Size { diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts index 93b13caea..b8eb91672 100644 --- a/ui/core/view.android.ts +++ b/ui/core/view.android.ts @@ -389,6 +389,23 @@ export class View extends viewCommon.View { } } + public getLocationRelativeTo(otherView: viewDefinition.View): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.getWindowToken() || + !otherView._nativeView || !otherView._nativeView.getWindowToken() || + this._nativeView.getWindowToken() !== otherView._nativeView.getWindowToken()) { + return undefined; + } + + var myArray = (Array).create("int", 2); + this._nativeView.getLocationOnScreen(myArray); + var otherArray = (Array).create("int", 2); + otherView._nativeView.getLocationOnScreen(otherArray); + return { + x: utils.layout.toDeviceIndependentPixels(myArray[0] - otherArray[0]), + y: utils.layout.toDeviceIndependentPixels(myArray[1] - otherArray[1]), + } + } + public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number { var result = size; switch (specMode) { diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts index 6c411913e..644862cf1 100644 --- a/ui/core/view.ios.ts +++ b/ui/core/view.ios.ts @@ -278,6 +278,21 @@ export class View extends viewCommon.View { } } + public getLocationRelativeTo(otherView: viewDefinition.View): viewDefinition.Point { + if (!this._nativeView || !this._nativeView.window || + !otherView._nativeView || !otherView._nativeView.window || + this._nativeView.window !== otherView._nativeView.window) { + return undefined; + } + + var myPointInWindow = this._nativeView.convertPointToView(this._nativeView.bounds.origin, null); + var otherPointInWindow = otherView._nativeView.convertPointToView(otherView._nativeView.bounds.origin, null); + return { + x: utils.layout.toDeviceIndependentPixels(myPointInWindow.x - otherPointInWindow.x), + y: utils.layout.toDeviceIndependentPixels(myPointInWindow.y - otherPointInWindow.y), + } + } + private _onSizeChanged() { this.style._sizeChanged(); } From 15f94ce9e3e5d0b8b457ff9c41687c0f56ff5653 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Mon, 21 Mar 2016 13:27:17 +0200 Subject: [PATCH 4/5] Increase delta again --- apps/tests/ui/view/view-tests-common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tests/ui/view/view-tests-common.ts b/apps/tests/ui/view/view-tests-common.ts index 5a70d3b9b..ddf475b99 100644 --- a/apps/tests/ui/view/view-tests-common.ts +++ b/apps/tests/ui/view/view-tests-common.ts @@ -723,7 +723,7 @@ export var test_getLocationOnScreen_IsUndefinedWhenNotInTheVisualTree = function TKUnit.assertNull(label.getLocationOnScreen()); } -var delta = 0.1; +var delta = 0.5; export var test_getLocationRelativeToOtherView = function () { var a1 = new absoluteLayoutModule.AbsoluteLayout(); a1.width = 200; From bec22271e175468e0e29d853765889dc9d8a006b Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Mon, 21 Mar 2016 13:57:36 +0200 Subject: [PATCH 5/5] More adjustments --- apps/tests/ui/view/view-tests-common.ts | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/tests/ui/view/view-tests-common.ts b/apps/tests/ui/view/view-tests-common.ts index ddf475b99..0a3080656 100644 --- a/apps/tests/ui/view/view-tests-common.ts +++ b/apps/tests/ui/view/view-tests-common.ts @@ -723,7 +723,7 @@ export var test_getLocationOnScreen_IsUndefinedWhenNotInTheVisualTree = function TKUnit.assertNull(label.getLocationOnScreen()); } -var delta = 0.5; +var delta = 1; export var test_getLocationRelativeToOtherView = function () { var a1 = new absoluteLayoutModule.AbsoluteLayout(); a1.width = 200; @@ -733,8 +733,8 @@ export var test_getLocationRelativeToOtherView = function () { var a2 = new absoluteLayoutModule.AbsoluteLayout(); a2.width = 100; a2.height = 100; - absoluteLayoutModule.AbsoluteLayout.setLeft(a2, 11); - absoluteLayoutModule.AbsoluteLayout.setTop(a2, 12); + absoluteLayoutModule.AbsoluteLayout.setLeft(a2, 10); + absoluteLayoutModule.AbsoluteLayout.setTop(a2, 10); a2.backgroundColor = new color.Color("green"); var label = new labelModule.Label(); @@ -742,8 +742,8 @@ export var test_getLocationRelativeToOtherView = function () { label.id = "label"; label.width = 70; label.height = 30; - absoluteLayoutModule.AbsoluteLayout.setLeft(label, 13); - absoluteLayoutModule.AbsoluteLayout.setTop(label, 14); + absoluteLayoutModule.AbsoluteLayout.setLeft(label, 10); + absoluteLayoutModule.AbsoluteLayout.setTop(label, 10); a2.backgroundColor = new color.Color("yellow"); a2.addChild(label); @@ -757,14 +757,14 @@ export var test_getLocationRelativeToOtherView = function () { var labelInA1 = label.getLocationRelativeTo(a1); var a2InA1 = a2.getLocationRelativeTo(a1); - TKUnit.assertAreClose(labelInA2.x, 13, delta); - TKUnit.assertAreClose(labelInA2.y, 14, delta); + TKUnit.assertAreClose(labelInA2.x, 10, delta, "labelInA2.x"); + TKUnit.assertAreClose(labelInA2.y, 10, delta, "labelInA2.y"); - TKUnit.assertAreClose(labelInA1.x, 24, delta); - TKUnit.assertAreClose(labelInA1.y, 26, delta); + TKUnit.assertAreClose(labelInA1.x, 20, delta, "labelInA1.x"); + TKUnit.assertAreClose(labelInA1.y, 20, delta, "labelInA1.y"); - TKUnit.assertAreClose(a2InA1.x, 11, delta); - TKUnit.assertAreClose(a2InA1.y, 12, delta); + TKUnit.assertAreClose(a2InA1.x, 10, delta, "a2InA1.x"); + TKUnit.assertAreClose(a2InA1.y, 10, delta, "a2InA1.y"); }); } @@ -776,7 +776,7 @@ export var test_getActualSize = function () { frame.topmost().requestLayout(); TKUnit.wait(0.1); var actualSize = label.getActualSize(); - TKUnit.assertAreClose(actualSize.width, 100, delta); - TKUnit.assertAreClose(actualSize.height, 200, delta); + TKUnit.assertAreClose(actualSize.width, 100, delta, "actualSize.width"); + TKUnit.assertAreClose(actualSize.height, 200, delta, "actualSize.height"); }); } \ No newline at end of file