This is an automated email from the ASF dual-hosted git repository.
Harbs pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
The following commit(s) were added to refs/heads/develop by this push:
new 2c1e009a2f Add PointUtils with viewport coordinate conversion methods
and corresponding tests
2c1e009a2f is described below
commit 2c1e009a2f761f7f547fbc8d08257fa95beb224b
Author: Harbs <[email protected]>
AuthorDate: Mon Jul 13 10:53:56 2026 +0300
Add PointUtils with viewport coordinate conversion methods and
corresponding tests
---
.../royale/org/apache/royale/utils/PointUtils.as | 86 +++++++
.../src/test/royale/flexUnitTests/CoreTester.as | 1 +
.../test/royale/flexUnitTests/PointUtilsTest.as | 247 +++++++++++++++++++++
3 files changed, 334 insertions(+)
diff --git
a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/PointUtils.as
b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/PointUtils.as
index e502e219e3..e526abe3e5 100644
---
a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/PointUtils.as
+++
b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/PointUtils.as
@@ -163,5 +163,91 @@ package org.apache.royale.utils
return new org.apache.royale.geom.Point(x, y);
}
}
+
+ /**
+ * Converts a point from viewport coordinates to local coordinates.
+ * On JavaScript, viewport coordinates are the coordinate space used
by
+ * MouseEvent.clientX/clientY and Element.getBoundingClientRect().
+ * CSS transforms are not included in the conversion.
+ * Works with IRenderedObject or any object that has an element
property.
+ * Also works with an HTMLElement.
+ *
+ * @param point The point being converted.
+ * @param local The component used as reference for the conversion.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10.2
+ * @playerversion AIR 2.6
+ * @productversion Royale 1.0.0
+ * @royaleignorecoercion HTMLElement
+ * @royaleignorecoercion org.apache.royale.core.IRenderedObject
+ */
+ public static function viewportToLocal(
pt:org.apache.royale.geom.Point, local:Object ):org.apache.royale.geom.Point
+ {
+ COMPILE::SWF
+ {
+ return globalToLocal(pt, local);
+ }
+ COMPILE::JS
+ {
+ var element:HTMLElement;
+ if(local.getBoundingClientRect){
+ element = local as HTMLElement;
+ } else if(local.element){
+ element = local.element as HTMLElement;
+ } else if(local is IRenderedObject){
+ element = (local as IRenderedObject).element;
+ } else {
+ assert(false,"Invalid object used for
PointUtils.viewportToLocal")
+ }
+ var rect:Object = element.getBoundingClientRect();
+ return new org.apache.royale.geom.Point(
+ pt.x - rect.left - element.clientLeft + element.scrollLeft,
+ pt.y - rect.top - element.clientTop + element.scrollTop);
+ }
+ }
+
+ /**
+ * Converts a point from local coordinates to viewport coordinates.
+ * On JavaScript, viewport coordinates are the coordinate space used
by
+ * MouseEvent.clientX/clientY and Element.getBoundingClientRect().
+ * CSS transforms are not included in the conversion.
+ * Works with IRenderedObject or any object that has an element
property.
+ * Also works with an HTMLElement.
+ *
+ * @param point The point being converted.
+ * @param local The component used as reference for the conversion.
+ *
+ * @langversion 3.0
+ * @playerversion Flash 10.2
+ * @playerversion AIR 2.6
+ * @productversion Royale 1.0.0
+ * @royaleignorecoercion HTMLElement
+ * @royaleignorecoercion org.apache.royale.core.IRenderedObject
+ */
+ public static function localToViewport(
pt:org.apache.royale.geom.Point, local:Object ):org.apache.royale.geom.Point
+ {
+ COMPILE::SWF
+ {
+ return localToGlobal(pt, local);
+ }
+ COMPILE::JS
+ {
+ var element:HTMLElement;
+ if(local.getBoundingClientRect){
+ element = local as HTMLElement;
+ } else if(local.element){
+ element = local.element as HTMLElement;
+ } else if(local is IRenderedObject){
+ element = (local as IRenderedObject).element;
+ } else {
+ assert(false,"Invalid object used for
PointUtils.localToViewport")
+ }
+ var rect:Object = element.getBoundingClientRect();
+ return new org.apache.royale.geom.Point(
+ rect.left + element.clientLeft - element.scrollLeft + pt.x,
+ rect.top + element.clientTop - element.scrollTop + pt.y);
+ }
+ }
}
}
diff --git
a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
index d37bb7e970..9fec591ab9 100644
--- a/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
+++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/CoreTester.as
@@ -45,6 +45,7 @@ package flexUnitTests
public var sanitizerTest:SanitizeTest;
public var eventsTest:EventsTest;
public var displayUtilsTest:DisplayUtilsTest;
+ public var pointUtilsTest:PointUtilsTest;
public var objectUtilTests:ObjectUtilsTest;
public var functionalTests:FunctionalTests;
public var taskTests:TaskTests;
diff --git
a/frameworks/projects/Core/src/test/royale/flexUnitTests/PointUtilsTest.as
b/frameworks/projects/Core/src/test/royale/flexUnitTests/PointUtilsTest.as
new file mode 100644
index 0000000000..ae99f8ff7f
--- /dev/null
+++ b/frameworks/projects/Core/src/test/royale/flexUnitTests/PointUtilsTest.as
@@ -0,0 +1,247 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package flexUnitTests
+{
+ import org.apache.royale.core.UIBase;
+ import org.apache.royale.geom.Point;
+ import org.apache.royale.geom.Rectangle;
+ import org.apache.royale.test.asserts.*;
+ import org.apache.royale.utils.DisplayUtils;
+ import org.apache.royale.utils.PointUtils;
+
+ public class PointUtilsTest
+ {
+ private var target:UIBase;
+
+ COMPILE::JS
+ private var fixture:HTMLElement;
+
+ COMPILE::JS
+ private var originalScrollX:Number;
+
+ COMPILE::JS
+ private var originalScrollY:Number;
+
+ [Before]
+ public function setUp():void
+ {
+ COMPILE::JS
+ {
+ originalScrollX = window.pageXOffset;
+ originalScrollY = window.pageYOffset;
+ window.scrollTo(0, 0);
+
+ fixture = document.createElement("div") as HTMLElement;
+ fixture.style.position = "absolute";
+ fixture.style.left = "600px";
+ fixture.style.top = "600px";
+ fixture.style.width = "3000px";
+ fixture.style.height = "3000px";
+ document.body.appendChild(fixture);
+
+ target = new UIBase();
+ target.element.style.position = "absolute";
+ target.element.style.left = "25px";
+ target.element.style.top = "40px";
+ target.element.style.width = "100px";
+ target.element.style.height = "80px";
+ fixture.appendChild(target.element);
+ }
+ }
+
+ [After]
+ public function tearDown():void
+ {
+ COMPILE::JS
+ {
+ window.scrollTo(originalScrollX, originalScrollY);
+ document.body.removeChild(fixture);
+ fixture = null;
+ target = null;
+ }
+ }
+
+ [Test]
+ public function testLocalToViewportAfterDocumentScroll():void
+ {
+ COMPILE::JS
+ {
+ window.scrollTo(200, 300);
+ var rect:Object = target.element.getBoundingClientRect();
+ var result:Point = PointUtils.localToViewport(new Point(10,
15), target);
+
+ assertNear(rect.left + 10, result.x, "Incorrect viewport x");
+ assertNear(rect.top + 15, result.y, "Incorrect viewport y");
+ }
+ }
+
+ [Test]
+ public function testViewportToLocalAfterDocumentScroll():void
+ {
+ COMPILE::JS
+ {
+ window.scrollTo(200, 300);
+ var rect:Object = target.element.getBoundingClientRect();
+ var result:Point = PointUtils.viewportToLocal(new
Point(rect.left + 10, rect.top + 15), target);
+
+ assertNear(10, result.x, "Incorrect local x");
+ assertNear(15, result.y, "Incorrect local y");
+ }
+ }
+
+ [Test]
+ public function
testViewportConversionsAccountForBorderAndElementScroll():void
+ {
+ COMPILE::JS
+ {
+ target.element.style.border = "5px solid transparent";
+ target.element.style.overflow = "scroll";
+ var content:HTMLElement = document.createElement("div") as
HTMLElement;
+ content.style.width = "300px";
+ content.style.height = "300px";
+ target.element.appendChild(content);
+ target.element.scrollLeft = 20;
+ target.element.scrollTop = 30;
+
+ var localPoint:Point = new Point(75, 90);
+ var viewportPoint:Point =
PointUtils.localToViewport(localPoint, target);
+ var result:Point = PointUtils.viewportToLocal(viewportPoint,
target);
+
+ assertNear(localPoint.x, result.x, "Incorrect round-trip x");
+ assertNear(localPoint.y, result.y, "Incorrect round-trip y");
+ var rect:Object = target.element.getBoundingClientRect();
+ assertNear(rect.left + 5 - 20 + localPoint.x, viewportPoint.x,
"Border or horizontal scroll was not applied");
+ assertNear(rect.top + 5 - 30 + localPoint.y, viewportPoint.y,
"Border or vertical scroll was not applied");
+ }
+ }
+
+ [Test]
+ public function testViewportConversionsAcceptHTMLElement():void
+ {
+ COMPILE::JS
+ {
+ var localPoint:Point = new Point(10, 15);
+ var viewportPoint:Point =
PointUtils.localToViewport(localPoint, target.element);
+ var result:Point = PointUtils.viewportToLocal(viewportPoint,
target.element);
+
+ assertPointEquals(localPoint, result, "HTMLElement round
trip");
+ }
+ }
+
+ [Test]
+ public function testViewportConversionsAcceptElementProperty():void
+ {
+ COMPILE::JS
+ {
+ var wrapper:Object = {element: target.element};
+ var localPoint:Point = new Point(10, 15);
+ var viewportPoint:Point =
PointUtils.localToViewport(localPoint, wrapper);
+ var result:Point = PointUtils.viewportToLocal(viewportPoint,
wrapper);
+
+ assertPointEquals(localPoint, result, "Element property round
trip");
+ }
+ }
+
+ [Test]
+ public function testViewportConversionsBetweenElements():void
+ {
+ COMPILE::JS
+ {
+ var destination:UIBase = new UIBase();
+ destination.element.style.position = "absolute";
+ destination.element.style.left = "250px";
+ destination.element.style.top = "300px";
+ destination.element.style.width = "100px";
+ destination.element.style.height = "80px";
+ fixture.appendChild(destination.element);
+
+ window.scrollTo(200, 300);
+ var sourcePoint:Point = new Point(10, 15);
+ var viewportPoint:Point =
PointUtils.localToViewport(sourcePoint, target);
+ var destinationPoint:Point =
PointUtils.viewportToLocal(viewportPoint, destination);
+ var sourceRect:Object = target.element.getBoundingClientRect();
+ var destinationRect:Object =
destination.element.getBoundingClientRect();
+
+ assertNear(sourceRect.left + sourcePoint.x -
destinationRect.left, destinationPoint.x, "Incorrect destination x");
+ assertNear(sourceRect.top + sourcePoint.y -
destinationRect.top, destinationPoint.y, "Incorrect destination y");
+ }
+ }
+
+ [Test]
+ public function testViewportConversionsDoNotMutateInput():void
+ {
+ COMPILE::JS
+ {
+ var localPoint:Point = new Point(10, 15);
+ var viewportPoint:Point =
PointUtils.localToViewport(localPoint, target);
+
+ assertPointEquals(new Point(10, 15), localPoint,
"localToViewport input");
+ assertFalse(localPoint === viewportPoint);
+
+ var viewportCopy:Point = new Point(viewportPoint.x,
viewportPoint.y);
+ var result:Point = PointUtils.viewportToLocal(viewportPoint,
target);
+
+ assertPointEquals(viewportCopy, viewportPoint,
"viewportToLocal input");
+ assertFalse(viewportPoint === result);
+ }
+ }
+
+ [Test]
+ public function testViewportToLocalAcceptsDisplayUtilsBounds():void
+ {
+ COMPILE::JS
+ {
+ window.scrollTo(200, 300);
+ var bounds:Rectangle =
DisplayUtils.getScreenBoundingRect(target);
+ var result:Point = PointUtils.viewportToLocal(bounds.topLeft,
target);
+
+ assertPointEquals(new Point(), result, "DisplayUtils viewport
bounds");
+ }
+ }
+
+ [Test]
+ public function testLegacyConversionsRetainDocumentCoordinates():void
+ {
+ COMPILE::JS
+ {
+ window.scrollTo(200, 300);
+ var localPoint:Point = new Point(10, 15);
+ var documentPoint:Point = PointUtils.localToGlobal(localPoint,
target);
+ var rect:Object = target.element.getBoundingClientRect();
+
+ assertNear(rect.left + window.pageXOffset + localPoint.x,
documentPoint.x, "Incorrect legacy document x");
+ assertNear(rect.top + window.pageYOffset + localPoint.y,
documentPoint.y, "Incorrect legacy document y");
+ assertPointEquals(localPoint,
PointUtils.globalToLocal(documentPoint, target), "Legacy round trip");
+ }
+ }
+
+ COMPILE::JS
+ private function assertPointEquals(expected:Point, actual:Point,
message:String):void
+ {
+ assertNear(expected.x, actual.x, message + " x");
+ assertNear(expected.y, actual.y, message + " y");
+ }
+
+ COMPILE::JS
+ private function assertNear(expected:Number, actual:Number,
message:String):void
+ {
+ assertTrue(Math.abs(expected - actual) < 0.01, message + ":
expected " + expected + " but was " + actual);
+ }
+ }
+}
\ No newline at end of file