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

Reply via email to