1 /* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package test.javafx.scene; 27 28 import test.javafx.scene.shape.TestUtils; 29 import test.javafx.scene.shape.CircleTest; 30 import com.sun.javafx.geom.BoxBounds; 31 import com.sun.javafx.geom.PickRay; 32 import com.sun.javafx.geom.transform.Affine2D; 33 import com.sun.javafx.geom.transform.Affine3D; 34 import com.sun.javafx.geom.transform.BaseTransform; 35 import com.sun.javafx.geom.transform.Translate2D; 36 import test.com.sun.javafx.pgstub.StubStage; 37 import test.com.sun.javafx.pgstub.StubToolkit; 38 import com.sun.javafx.scene.DirtyBits; 39 import com.sun.javafx.scene.NodeHelper; 40 import com.sun.javafx.scene.input.PickResultChooser; 41 import com.sun.javafx.scene.shape.RectangleHelper; 42 import com.sun.javafx.sg.prism.NGGroup; 43 import com.sun.javafx.sg.prism.NGNode; 44 import com.sun.javafx.sg.prism.NGRectangle; 45 import test.com.sun.javafx.test.objects.TestScene; 46 import test.com.sun.javafx.test.objects.TestStage; 47 import com.sun.javafx.tk.Toolkit; 48 import com.sun.javafx.util.Utils; 49 import javafx.beans.property.*; 50 import javafx.geometry.BoundingBox; 51 import javafx.geometry.Bounds; 52 import javafx.geometry.NodeOrientation; 53 import javafx.geometry.Point2D; 54 import javafx.geometry.Point3D; 55 import javafx.scene.effect.DropShadow; 56 import javafx.scene.effect.Effect; 57 import javafx.scene.shape.*; 58 import javafx.scene.transform.Rotate; 59 import javafx.scene.transform.Transform; 60 import org.junit.Rule; 61 import org.junit.Test; 62 import org.junit.rules.ExpectedException; 63 64 import java.lang.reflect.Method; 65 import java.util.Comparator; 66 import javafx.scene.Group; 67 import javafx.scene.Node; 68 import javafx.scene.NodeShim; 69 import javafx.scene.ParallelCamera; 70 import javafx.scene.ParentShim; 71 import javafx.scene.PerspectiveCamera; 72 import javafx.scene.Scene; 73 import javafx.scene.SceneShim; 74 import javafx.scene.SubScene; 75 import javafx.scene.layout.AnchorPane; 76 import javafx.scene.transform.Affine; 77 import javafx.scene.transform.Scale; 78 import javafx.scene.transform.Shear; 79 import javafx.scene.transform.Translate; 80 import javafx.stage.Stage; 81 82 import static org.junit.Assert.*; 83 import org.junit.Ignore; 84 /** 85 * Tests various aspects of Node. 86 * 87 */ 88 public class NodeTest { 89 @Rule 90 public ExpectedException thrown = ExpectedException.none(); 91 92 // Things to test: 93 // When parent is changed, should cursor on toolkit change as well if 94 // the current node has the mouse over it and didn't explicitly set a 95 // cursor?? 96 97 // Test CSS integration 98 99 // Events: 100 // Events should *not* be delivered to invisible nodes as per the 101 // specification for visible 102 103 // A Node must lose focus when it is no longer visible 104 105 // A node made invisible must cause the cursor to be updated 106 107 // Setting the cursor should override the parent cursor when hover 108 // (test that this happens both when the node already has hover set and 109 // when hover is changed to true) 110 111 // Setting the cursor to null should revert to parent cursor when hover 112 // (test that this happens both when the node already has hover set and 113 // when hover is changed to true) 114 115 // Clip: 116 // Test setting/clearing the clip affects the bounds 117 // Test changing bounds / smooth / etc on clip updates bounds of clipped Node 118 119 // Effect: 120 // Test setting/clearing the effect affects the bounds 121 // Test changing state on Effect updates bounds of Node 122 123 // Test that a disabled Group affects the disabled property of child nodes 124 125 // Test contains, intersects methods 126 // Test parentToLocal/localToStage/etc 127 128 // Test computeCompleteBounds 129 // (other bounds test situtations explicitly tested in BoundsTest) 130 131 // Test transforms end up setting the correct matrix on the peer 132 // In particular, test that pivots are taken correctly into account 133 134 // Test hover is updated when mouse enters 135 // Test hover is updated when mouse exists 136 // Test hover is updated when mouse was over but a higher node then 137 // turns on blocks mouse 138 // Test hover is updated when node moves out from under the cursor 139 // TODO most of these cases cannot be handled until/unless we update 140 // the list of nodes under the cursor on pulse events 141 142 // Test pressed is updated when mouse is pressed 143 // Test pressed is updated when mouse is released 144 // TODO shoudl pressed obey the semantics of a button that is armed & pressed? 145 // Or should "armed" be put on Node? What to do here? 146 147 // Test various onMouseXXX event handlers 148 149 // Test onKeyXXX handlers 150 151 // Test focused is updated? 152 // Test nodes which are not focusable are not focused! 153 // Test focus... (SHOULD NOT DEPEND ON KEY LISTENERS BEING INSTALLED!!) 154 155 // Test that clip is taken into account for both "contains" and 156 // "intersects". See http://javafx-jira.kenai.com/browse/RT-646 157 158 159 160 /*************************************************************************** 161 * * 162 * Basic Node Tests * 163 * * 164 **************************************************************************/ 165 166 // TODO disable this because it depends on TestNode 167 // @Test public void testPeerNotifiedOfVisibilityChanges() { 168 // Rectangle rect = new Rectangle(); 169 // Node peer = rect.impl_getPGNode(); 170 // assertEquals(peer.visible, rect.visible); 171 // 172 // rect.visible = false; 173 // assertEquals(peer.visible, rect.visible); 174 // 175 // rect.visible = true; 176 // assertEquals(peer.visible, rect.visible); 177 // } 178 179 /*************************************************************************** 180 * * 181 * Testing Node Bounds * 182 * * 183 **************************************************************************/ 184 185 // TODO disable this because it depends on TestNode 186 // public function testContainsCallsPeer():Void { 187 // var rect = Rectangle { }; 188 // var peer = rect.impl_getPGNode() as TestNode; 189 // peer.numTimesContainsCalled = 0; 190 // 191 // rect.contains(0, 0); 192 // assertEquals(1, peer.numTimesContainsCalled); 193 // 194 // rect.contains(Point2D { x:10, y:10 }); 195 // assertEquals(2, peer.numTimesContainsCalled); 196 // } 197 198 // TODO disable this because it depends on TestNode 199 // public function testIntersectsCallsPeer():Void { 200 // var rect = Rectangle { }; 201 // var peer = rect.impl_getPGNode() as TestNode; 202 // peer.numTimesIntersectsCalled = 0; 203 // 204 // rect.intersects(0, 0, 10, 10); 205 // assertEquals(1, peer.numTimesIntersectsCalled); 206 // 207 // rect.intersects(BoundingBox { minX:10, minY:10, width:100, height:100 }); 208 // assertEquals(2, peer.numTimesIntersectsCalled); 209 // } 210 211 /*************************************************************************** 212 * * 213 * Testing Node transforms * 214 * * 215 **************************************************************************/ 216 217 /** 218 * Tests that the function which converts a com.sun.javafx.geom.Point2D 219 * in parent coords to local coords works properly. 220 */ 221 @Test public void testParentToLocalGeomPoint() { 222 Rectangle rect = new Rectangle(); 223 rect.setTranslateX(10); 224 rect.setTranslateY(10); 225 rect.setWidth(100); 226 rect.setHeight(100); 227 rect.getTransforms().clear(); 228 rect.getTransforms().addAll(Transform.scale(2, 2), Transform.translate(30, 30)); 229 230 Point2D pt = new Point2D(0, 0); 231 pt = rect.parentToLocal(pt); 232 assertEquals(new Point2D(-35, -35), pt); 233 } 234 235 // TODO need to test with some observableArrayList of transforms which cannot be 236 // cleanly inverted so that we can test that code path 237 238 @Test public void testLocalToParentGeomPoint() { 239 Rectangle rect = new Rectangle(); 240 rect.setTranslateX(10); 241 rect.setTranslateY(10); 242 rect.setWidth(100); 243 rect.setHeight(100); 244 rect.getTransforms().clear(); 245 rect.getTransforms().addAll(Transform.scale(2, 2), Transform.translate(30, 30)); 246 247 Point2D pt = new Point2D(0, 0); 248 pt = rect.localToParent(pt); 249 assertEquals(new Point2D(70, 70), pt); 250 } 251 252 @Test public void testPickingNodeDirectlyNoTransforms() { 253 Rectangle rect = new Rectangle(); 254 rect.setX(10); 255 rect.setY(10); 256 rect.setWidth(100); 257 rect.setHeight(100); 258 259 // needed since picking doesn't work unless rooted in a scene and visible 260 Scene scene = new Scene(new Group()); 261 ParentShim.getChildren(scene.getRoot()).add(rect); 262 263 PickResultChooser res = new PickResultChooser(); 264 rect.impl_pickNode(new PickRay(50, 50, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res); 265 assertSame(rect, res.getIntersectedNode()); 266 res = new PickResultChooser(); 267 rect.impl_pickNode(new PickRay(0, 0, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res); 268 assertNull(res.getIntersectedNode()); 269 } 270 271 @Test public void testPickingNodeDirectlyWithTransforms() { 272 Rectangle rect = new Rectangle(); 273 rect.setTranslateX(10); 274 rect.setTranslateY(10); 275 rect.setWidth(100); 276 rect.setHeight(100); 277 278 // needed since picking doesn't work unless rooted in a scene and visible 279 Scene scene = new Scene(new Group()); 280 ParentShim.getChildren(scene.getRoot()).add(rect); 281 282 PickResultChooser res = new PickResultChooser(); 283 rect.impl_pickNode(new PickRay(50, 50, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res); 284 assertSame(rect, res.getIntersectedNode()); 285 res = new PickResultChooser(); 286 rect.impl_pickNode(new PickRay(0, 0, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res); 287 assertNull(res.getIntersectedNode()); 288 } 289 290 @Test public void testEffectSharedOnNodes() { 291 Effect effect = new DropShadow(); 292 Rectangle node = new Rectangle(); 293 node.setEffect(effect); 294 295 Rectangle node2 = new Rectangle(); 296 node2.setEffect(effect); 297 298 assertEquals(effect, node.getEffect()); 299 assertEquals(effect, node2.getEffect()); 300 } 301 302 public static void testBooleanPropertyPropagation( 303 final Node node, 304 final String propertyName, 305 final boolean initialValue, 306 final boolean newValue) throws Exception { 307 308 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 309 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 310 final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString(); 311 final String getterName = new StringBuilder("is").append(propertyNameBuilder).toString(); 312 313 final Class<? extends Node> nodeClass = node.getClass(); 314 final Method setter = nodeClass.getMethod(setterName, boolean.class); 315 final Method getter = nodeClass.getMethod(getterName); 316 317 final NGNode peer = NodeHelper.getPeer(node); 318 final Class<? extends NGNode> impl_class = peer.getClass(); 319 final Method impl_getter = impl_class.getMethod(getterName); 320 321 322 // 1. Create test scene 323 final Scene scene = new Scene(new Group()); 324 ParentShim.getChildren(scene.getRoot()).add(node); 325 326 // 2. Initial setup 327 setter.invoke(node, initialValue); 328 NodeHelper.syncPeer(node); 329 assertEquals(initialValue, getter.invoke(node)); 330 assertEquals(initialValue, impl_getter.invoke(peer)); 331 332 // 3. Change value of the property 333 setter.invoke(node, newValue); 334 335 // 4. Check that the property value has changed but has not propagated to PGNode 336 assertEquals(newValue, getter.invoke(node)); 337 assertEquals(initialValue, impl_getter.invoke(peer)); 338 339 // 5. Propagate the property value to PGNode 340 NodeHelper.syncPeer(node); 341 342 // 6. Check that the value has been propagated to PGNode 343 assertEquals(newValue, impl_getter.invoke(peer)); 344 } 345 346 347 public static void testFloatPropertyPropagation( 348 final Node node, 349 final String propertyName, 350 final float initialValue, 351 final float newValue) throws Exception { 352 353 testFloatPropertyPropagation(node, propertyName, propertyName, initialValue, newValue); 354 } 355 356 public static void syncNode(Node node) { 357 NodeShim.updateBounds(node); 358 NodeHelper.syncPeer(node); 359 } 360 361 public static void assertBooleanPropertySynced( 362 final Node node, 363 final String propertyName, 364 final String pgPropertyName, 365 final boolean value) throws Exception { 366 367 final Scene scene = new Scene(new Group(), 500, 500); 368 369 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 370 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 371 final String getterName = new StringBuilder("is").append(propertyNameBuilder).toString(); 372 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 373 Boolean defaultValue = (Boolean)getterMethod.invoke(node); 374 BooleanProperty v = new SimpleBooleanProperty(defaultValue); 375 376 Method modelMethod = node.getClass().getMethod( 377 propertyName + "Property", 378 new Class[]{}); 379 BooleanProperty model = (BooleanProperty)modelMethod.invoke(node); 380 model.bind(v); 381 382 ParentShim.getChildren(scene.getRoot()).add(node); 383 384 NodeTest.syncNode(node); 385 assertEquals(defaultValue, TestUtils.getBooleanValue(node, pgPropertyName)); 386 387 v.set(value); 388 NodeTest.syncNode(node); 389 assertEquals(value, TestUtils.getBooleanValue(node, pgPropertyName)); 390 } 391 392 public static void assertIntPropertySynced( 393 final Node node, 394 final String propertyName, 395 final String pgPropertyName, 396 final int value) throws Exception { 397 398 final Scene scene = new Scene(new Group(), 500, 500); 399 400 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 401 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 402 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 403 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 404 Integer defaultValue = (Integer)getterMethod.invoke(node); 405 IntegerProperty v = new SimpleIntegerProperty(defaultValue); 406 407 Method modelMethod = node.getClass().getMethod( 408 propertyName + "Property", 409 new Class[]{}); 410 IntegerProperty model = (IntegerProperty)modelMethod.invoke(node); 411 model.bind(v); 412 413 ParentShim.getChildren(scene.getRoot()).add(node); 414 415 NodeTest.syncNode(node); 416 assertTrue(numbersEquals(defaultValue, 417 (Number)TestUtils.getObjectValue(node, pgPropertyName))); 418 419 v.set(value); 420 NodeTest.syncNode(node); 421 assertTrue(numbersEquals(new Integer(value), 422 (Number)TestUtils.getObjectValue(node, pgPropertyName))); 423 } 424 425 public static boolean numbersEquals(Number expected, Number value) { 426 return numbersEquals(expected, value, 0.001); 427 } 428 429 public static boolean numbersEquals(Number expected, Number value, double delta) { 430 boolean res = (Math.abs(expected.doubleValue() - value.doubleValue()) < delta); 431 if (!res) { 432 System.err.println("expected=" + expected + ", value=" + value); 433 } 434 return res; 435 } 436 437 public static void assertDoublePropertySynced( 438 final Node node, 439 final String propertyName, 440 final String pgPropertyName, 441 final double value) throws Exception { 442 443 final Scene scene = new Scene(new Group(), 500, 500); 444 445 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 446 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 447 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 448 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 449 Double defaultValue = (Double)getterMethod.invoke(node); 450 DoubleProperty v = new SimpleDoubleProperty(defaultValue); 451 452 Method modelMethod = node.getClass().getMethod( 453 propertyName + "Property", 454 new Class[]{}); 455 DoubleProperty model = (DoubleProperty)modelMethod.invoke(node); 456 model.bind(v); 457 458 ParentShim.getChildren(scene.getRoot()).add(node); 459 460 NodeTest.syncNode(node); 461 assertTrue(numbersEquals(defaultValue, 462 (Number)TestUtils.getObjectValue(node, pgPropertyName))); 463 464 v.set(value); 465 NodeTest.syncNode(node); 466 assertTrue(numbersEquals(new Double(value), 467 (Number)TestUtils.getObjectValue(node, pgPropertyName))); 468 } 469 470 471 public static void assertObjectPropertySynced( 472 final Node node, 473 final String propertyName, 474 final String pgPropertyName, 475 final Object value) throws Exception { 476 477 final Scene scene = new Scene(new Group(), 500, 500); 478 479 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 480 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 481 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 482 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 483 Object defaultValue = getterMethod.invoke(node); 484 ObjectProperty v = new SimpleObjectProperty(defaultValue); 485 486 Method modelMethod = node.getClass().getMethod( 487 propertyName + "Property", 488 new Class[]{}); 489 ObjectProperty model = (ObjectProperty)modelMethod.invoke(node); 490 model.bind(v); 491 492 ParentShim.getChildren(scene.getRoot()).add(node); 493 494 NodeTest.syncNode(node); 495 // sometimes enum is used on node but int on PGNode 496 Object result1 = TestUtils.getObjectValue(node, pgPropertyName); 497 if (result1 instanceof Integer) { 498 assertTrue(((Enum)defaultValue).ordinal() == ((Integer)result1).intValue()); 499 } else { 500 assertEquals(defaultValue, TestUtils.getObjectValue(node, pgPropertyName)); 501 } 502 503 v.set(value); 504 NodeTest.syncNode(node); 505 506 Object result2 = TestUtils.getObjectValue(node, pgPropertyName); 507 if (result2 instanceof Integer) { 508 assertTrue(((Enum)value).ordinal() == ((Integer)result2).intValue()); 509 } else { 510 assertEquals(value, TestUtils.getObjectValue(node, pgPropertyName)); 511 } 512 } 513 514 515 516 public static void assertObjectProperty_AsStringSynced( 517 final Node node, 518 final String propertyName, 519 final String pgPropertyName, 520 final Object value) throws Exception { 521 522 final Scene scene = new Scene(new Group(), 500, 500); 523 524 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 525 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 526 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 527 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 528 Object defaultValue = getterMethod.invoke(node); 529 ObjectProperty v = new SimpleObjectProperty(defaultValue); 530 531 Method modelMethod = node.getClass().getMethod( 532 propertyName + "Property", 533 new Class[]{}); 534 ObjectProperty model = (ObjectProperty)modelMethod.invoke(node); 535 model.bind(v); 536 537 ParentShim.getChildren(scene.getRoot()).add(node); 538 539 NodeTest.syncNode(node); 540 assertEquals( 541 defaultValue.toString(), 542 TestUtils.getObjectValue(node, pgPropertyName).toString()); 543 544 v.set(value); 545 NodeTest.syncNode(node); 546 547 assertEquals( 548 value.toString(), 549 TestUtils.getObjectValue(node, pgPropertyName).toString()); 550 } 551 552 public static void assertStringPropertySynced( 553 final Node node, 554 final String propertyName, 555 final String pgPropertyName, 556 final String value) throws Exception { 557 558 final Scene scene = new Scene(new Group(), 500, 500); 559 560 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 561 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 562 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 563 Method getterMethod = node.getClass().getMethod(getterName, new Class[]{}); 564 String defaultValue = (String)getterMethod.invoke(node); 565 StringProperty v = new SimpleStringProperty(defaultValue); 566 567 Method modelMethod = node.getClass().getMethod( 568 propertyName + "Property", 569 new Class[]{}); 570 StringProperty model = (StringProperty)modelMethod.invoke(node); 571 model.bind(v); 572 573 ParentShim.getChildren(scene.getRoot()).add(node); 574 575 NodeTest.syncNode(node); 576 assertEquals( 577 defaultValue, 578 TestUtils.getStringValue(node, pgPropertyName)); 579 580 v.set(value); 581 NodeTest.syncNode(node); 582 583 assertEquals( 584 value, 585 TestUtils.getStringValue(node, pgPropertyName)); 586 } 587 588 public static void testFloatPropertyPropagation( 589 final Node node, 590 final String propertyName, 591 final String pgPropertyName, 592 final float initialValue, 593 final float newValue) throws Exception { 594 595 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 596 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 597 598 final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName); 599 pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0))); 600 601 final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString(); 602 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 603 final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString(); 604 605 final Class<? extends Node> nodeClass = node.getClass(); 606 final Method setter = nodeClass.getMethod(setterName, float.class); 607 final Method getter = nodeClass.getMethod(getterName); 608 609 final NGNode peer = NodeHelper.getPeer(node); 610 final Class<? extends NGNode> impl_class = peer.getClass(); 611 final Method impl_getter = impl_class.getMethod(pgGetterName); 612 613 614 // 1. Create test scene 615 final Scene scene = new Scene(new Group()); 616 ParentShim.getChildren(scene.getRoot()).add(node); 617 618 // 2. Initial setup 619 setter.invoke(node, initialValue); 620 NodeHelper.syncPeer(node); 621 assertEquals(initialValue, (Float) getter.invoke(node), 1e-100); 622 assertEquals(initialValue, (Float) impl_getter.invoke(peer), 1e-100); 623 624 // 3. Change value of the property 625 setter.invoke(node, newValue); 626 627 // 4. Check that the property value has changed but has not propagated to PGNode 628 assertEquals(newValue, (Float) getter.invoke(node), 1e-100); 629 assertEquals(initialValue, (Float) impl_getter.invoke(peer), 1e-100); 630 631 // 5. Propagate the property value to PGNode 632 NodeHelper.syncPeer(node); 633 634 // 6. Check that the value has been propagated to PGNode 635 assertEquals(newValue, (Float) impl_getter.invoke(peer), 1e-100); 636 } 637 638 public static void testDoublePropertyPropagation( 639 final Node node, 640 final String propertyName, 641 final double initialValue, 642 final double newValue) throws Exception { 643 644 testDoublePropertyPropagation(node, propertyName, propertyName, initialValue, newValue); 645 } 646 647 648 public static void testDoublePropertyPropagation( 649 final Node node, 650 final String propertyName, 651 final String pgPropertyName, 652 final double initialValue, 653 final double newValue) throws Exception { 654 655 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 656 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 657 658 final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName); 659 pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0))); 660 661 final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString(); 662 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 663 final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString(); 664 665 final Class<? extends Node> nodeClass = node.getClass(); 666 final Method setter = nodeClass.getMethod(setterName, double.class); 667 final Method getter = nodeClass.getMethod(getterName); 668 669 final NGNode peer = NodeHelper.getPeer(node); 670 final Class<? extends NGNode> impl_class = peer.getClass(); 671 final Method impl_getter = impl_class.getMethod(pgGetterName); 672 673 674 // 1. Create test scene 675 final Scene scene = new Scene(new Group()); 676 ParentShim.getChildren(scene.getRoot()).add(node); 677 678 // 2. Initial setup 679 setter.invoke(node, initialValue); 680 NodeHelper.syncPeer(node); 681 assertEquals(initialValue, (Double) getter.invoke(node), 1e-100); 682 assertEquals((float) initialValue, (Float) impl_getter.invoke(peer), 1e-100); 683 684 // 3. Change value of the property 685 setter.invoke(node, newValue); 686 687 // 4. Check that the property value has changed but has not propagated to PGNode 688 assertEquals(newValue, (Double) getter.invoke(node), 1e-100); 689 assertEquals((float) initialValue, (Float) impl_getter.invoke(peer), 1e-100); 690 691 // 5. Propagate the property value to PGNode 692 NodeHelper.syncPeer(node); 693 694 // 6. Check that the value has been propagated to PGNode 695 assertEquals((float) newValue, (Float) impl_getter.invoke(peer), 1e-100); 696 } 697 698 public interface ObjectValueConvertor { 699 Object toSg(Object pgValue); 700 } 701 702 public static final Comparator DEFAULT_OBJ_COMPARATOR = 703 (sgValue, pgValue) -> { 704 assertEquals(sgValue, pgValue); 705 return 0; 706 }; 707 708 public static void testObjectPropertyPropagation( 709 final Node node, 710 final String propertyName, 711 final Object initialValue, 712 final Object newValue) throws Exception { 713 714 testObjectPropertyPropagation(node, propertyName, propertyName, initialValue, newValue); 715 } 716 717 public static void testObjectPropertyPropagation( 718 final Node node, 719 final String propertyName, 720 final String pgPropertyName, 721 final Object initialValue, 722 final Object newValue) throws Exception { 723 testObjectPropertyPropagation(node, propertyName, pgPropertyName, 724 initialValue, newValue, DEFAULT_OBJ_COMPARATOR); 725 } 726 727 public static void testObjectPropertyPropagation( 728 final Node node, 729 final String propertyName, 730 final String pgPropertyName, 731 final Object initialValue, 732 final Object newValue, 733 final ObjectValueConvertor convertor) throws Exception { 734 testObjectPropertyPropagation( 735 node, propertyName, pgPropertyName, 736 initialValue, newValue, 737 (sgValue, pgValue) -> { 738 assertEquals(sgValue, convertor.toSg(pgValue)); 739 return 0; 740 } 741 ); 742 } 743 744 public static void testObjectPropertyPropagation( 745 final Node node, 746 final String propertyName, 747 final String pgPropertyName, 748 final Object initialValue, 749 final Object newValue, 750 final Comparator comparator) throws Exception { 751 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 752 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 753 754 final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName); 755 pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0))); 756 757 final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString(); 758 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 759 final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString(); 760 761 final Class<? extends Node> nodeClass = node.getClass(); 762 final Method getter = nodeClass.getMethod(getterName); 763 final Method setter = nodeClass.getMethod(setterName, getter.getReturnType()); 764 765 final NGNode peer = NodeHelper.getPeer(node); 766 final Class<? extends NGNode> impl_class = peer.getClass(); 767 final Method impl_getter = impl_class.getMethod(pgGetterName); 768 769 770 // 1. Create test scene 771 final Scene scene = new Scene(new Group()); 772 ParentShim.getChildren(scene.getRoot()).add(node); 773 774 // 2. Initial setup 775 setter.invoke(node, initialValue); 776 NodeHelper.syncPeer(node); 777 assertEquals(initialValue, getter.invoke(node)); 778 assertEquals(0, comparator.compare(initialValue, 779 impl_getter.invoke(peer))); 780 781 // 3. Change value of the property 782 setter.invoke(node, newValue); 783 784 // 4. Check that the property value has changed but has not propagated to PGNode 785 assertEquals(newValue, getter.invoke(node)); 786 assertEquals(0, comparator.compare(initialValue, 787 impl_getter.invoke(peer))); 788 789 // 5. Propagate the property value to PGNode 790 NodeHelper.syncPeer(node); 791 792 // 6. Check that the value has been propagated to PGNode 793 assertEquals(0, comparator.compare(newValue, 794 impl_getter.invoke(peer))); 795 } 796 797 798 public static void testIntPropertyPropagation( 799 final Node node, 800 final String propertyName, 801 final int initialValue, 802 final int newValue) throws Exception { 803 804 testIntPropertyPropagation(node, propertyName, propertyName, initialValue, newValue); 805 } 806 807 808 public static void testIntPropertyPropagation( 809 final Node node, 810 final String propertyName, 811 final String pgPropertyName, 812 final int initialValue, 813 final int newValue) throws Exception { 814 815 final StringBuilder propertyNameBuilder = new StringBuilder(propertyName); 816 propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0))); 817 818 final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName); 819 pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0))); 820 821 final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString(); 822 final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString(); 823 final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString(); 824 825 final Class<? extends Node> nodeClass = node.getClass(); 826 final Method getter = nodeClass.getMethod(getterName); 827 final Method setter = nodeClass.getMethod(setterName, getter.getReturnType()); 828 829 final NGNode peer = NodeHelper.getPeer(node); 830 final Class<? extends NGNode> impl_class = peer.getClass(); 831 final Method impl_getter = impl_class.getMethod(pgGetterName); 832 833 834 // 1. Create test scene 835 final Scene scene = new Scene(new Group()); 836 ParentShim.getChildren(scene.getRoot()).add(node); 837 838 // 2. Initial setup 839 setter.invoke(node, initialValue); 840 assertEquals(initialValue, getter.invoke(node)); 841 NodeHelper.syncPeer(node); 842 assertEquals(initialValue, ((Number) impl_getter.invoke(peer)).intValue()); 843 844 // 3. Change value of the property 845 setter.invoke(node, newValue); 846 847 // 4. Check that the property value has changed but has not propagated to PGNode 848 assertEquals(newValue, getter.invoke(node)); 849 assertEquals(initialValue, ((Number) impl_getter.invoke(peer)).intValue()); 850 851 // 5. Propagate the property value to PGNode 852 NodeHelper.syncPeer(node); 853 854 // 6. Check that the value has been propagated to PGNode 855 assertEquals(newValue, ((Number) impl_getter.invoke(peer)).intValue()); 856 } 857 858 public static void callSyncPGNode(final Node node) { 859 NodeHelper.syncPeer(node); 860 } 861 862 @Test 863 public void testToFront() { 864 Rectangle rect1 = new Rectangle(); 865 Rectangle rect2 = new Rectangle(); 866 Group g = new Group(); 867 868 Scene scene = new Scene(g); 869 ParentShim.getChildren(g).add(rect1); 870 ParentShim.getChildren(g).add(rect2); 871 872 rect1.toFront(); 873 rect2.toFront(); 874 875 // toFront should not remove rectangle from scene 876 assertEquals(scene, rect2.getScene()); 877 assertEquals(scene, rect1.getScene()); 878 // test corect order of scene content 879 assertEquals(rect2, ParentShim.getChildren(g).get(1)); 880 assertEquals(rect1, ParentShim.getChildren(g).get(0)); 881 882 rect1.toFront(); 883 assertEquals(scene, rect2.getScene()); 884 assertEquals(scene, rect1.getScene()); 885 assertEquals(rect1, ParentShim.getChildren(g).get(1)); 886 assertEquals(rect2, ParentShim.getChildren(g).get(0)); 887 } 888 889 @Test 890 public void testClip() { 891 Rectangle rect1 = new Rectangle(); 892 Rectangle rect2 = new Rectangle(); 893 rect1.setClip(rect2); 894 895 Scene scene = new Scene(new Group()); 896 ParentShim.getChildren(scene.getRoot()).add(rect1); 897 assertEquals(rect2, rect1.getClip()); 898 assertEquals(scene, rect2.getScene()); 899 900 } 901 902 @Test 903 public void testInvalidClip() { 904 Rectangle rectA = new Rectangle(300, 300); 905 Rectangle clip1 = new Rectangle(10, 10); 906 Rectangle clip2 = new Rectangle(100, 100); 907 clip2.setClip(rectA); 908 rectA.setClip(clip1); 909 assertEquals(rectA.getClip(), clip1); 910 thrown.expect(IllegalArgumentException.class); 911 try { 912 rectA.setClip(clip2); 913 } catch (final IllegalArgumentException e) { 914 assertNotSame(rectA.getClip(), clip2); 915 throw e; 916 } 917 } 918 919 @Test public void testProperties() { 920 Rectangle node = new Rectangle(); 921 javafx.collections.ObservableMap<Object, Object> properties = node.getProperties(); 922 923 /* If we ask for it, we should get it. 924 */ 925 assertNotNull(properties); 926 927 /* What we put in, we should get out. 928 */ 929 properties.put("MyKey", "MyValue"); 930 assertEquals("MyValue", properties.get("MyKey")); 931 932 /* If we ask for it again, we should get the same thing. 933 */ 934 javafx.collections.ObservableMap<Object, Object> properties2 = node.getProperties(); 935 assertEquals(properties2, properties); 936 937 /* What we put in to the other one, we should get out of this one because 938 * they should be the same thing. 939 */ 940 assertEquals("MyValue", properties2.get("MyKey")); 941 } 942 943 public static boolean isDirty(Node node, DirtyBits[] dbs) { 944 for(DirtyBits db:dbs) { 945 if (!NodeShim.isDirty(node, db)) { 946 System.out.printf("@NodeTest:check dirty: %s [%d]\n",db,db.ordinal()); 947 return false; 948 } 949 } 950 return true; 951 } 952 953 @Test 954 public void testDefaultValueForViewOrderIsZeroWhenReadFromGetter() { 955 final Node node = new Rectangle(); 956 assertEquals(0, node.getViewOrder(), .005); 957 } 958 959 @Test 960 public void testDefaultValueForViewOrderIsZeroWhenReadFromProperty() { 961 final Node node = new Rectangle(); 962 assertEquals(0, node.viewOrderProperty().get(), .005); 963 } 964 965 @Test 966 public void settingViewOrderThroughSetterShouldAffectBothGetterAndProperty() { 967 final Node node = new Rectangle(); 968 node.setViewOrder(.5); 969 assertEquals(.5, node.getViewOrder(), .005); 970 assertEquals(.5, node.viewOrderProperty().get(), .005); 971 } 972 973 @Test 974 public void settingViewOrderThroughPropertyShouldAffectBothGetterAndProperty() { 975 final Node node = new Rectangle(); 976 node.viewOrderProperty().set(.5); 977 assertEquals(.5, node.getViewOrder(), .005); 978 assertEquals(.5, node.viewOrderProperty().get(), .005); 979 } 980 981 @Test 982 public void testDefaultValueForOpacityIsOneWhenReadFromGetter() { 983 final Node node = new Rectangle(); 984 assertEquals(1, node.getOpacity(), .005); 985 } 986 987 @Test 988 public void testDefaultValueForOpacityIsOneWhenReadFromProperty() { 989 final Node node = new Rectangle(); 990 assertEquals(1, node.opacityProperty().get(), .005); 991 } 992 993 @Test 994 public void settingOpacityThroughSetterShouldAffectBothGetterAndProperty() { 995 final Node node = new Rectangle(); 996 node.setOpacity(.5); 997 assertEquals(.5, node.getOpacity(), .005); 998 assertEquals(.5, node.opacityProperty().get(), .005); 999 } 1000 1001 @Test 1002 public void settingOpacityThroughPropertyShouldAffectBothGetterAndProperty() { 1003 final Node node = new Rectangle(); 1004 node.opacityProperty().set(.5); 1005 assertEquals(.5, node.getOpacity(), .005); 1006 assertEquals(.5, node.opacityProperty().get(), .005); 1007 } 1008 1009 @Test 1010 public void testDefaultValueForVisibleIsTrueWhenReadFromGetter() { 1011 final Node node = new Rectangle(); 1012 assertTrue(node.isVisible()); 1013 } 1014 1015 @Test 1016 public void testDefaultValueForVisibleIsTrueWhenReadFromProperty() { 1017 final Node node = new Rectangle(); 1018 assertTrue(node.visibleProperty().get()); 1019 } 1020 1021 @Test 1022 public void settingVisibleThroughSetterShouldAffectBothGetterAndProperty() { 1023 final Node node = new Rectangle(); 1024 node.setVisible(false); 1025 assertFalse(node.isVisible()); 1026 assertFalse(node.visibleProperty().get()); 1027 } 1028 1029 @Test 1030 public void settingVisibleThroughPropertyShouldAffectBothGetterAndProperty() { 1031 final Node node = new Rectangle(); 1032 node.visibleProperty().set(false); 1033 assertFalse(node.isVisible()); 1034 assertFalse(node.visibleProperty().get()); 1035 } 1036 1037 @Test 1038 public void testDefaultStyleIsEmptyString() { 1039 final Node node = new Rectangle(); 1040 assertEquals("", node.getStyle()); 1041 assertEquals("", node.styleProperty().get()); 1042 node.setStyle(null); 1043 assertEquals("", node.styleProperty().get()); 1044 assertEquals("", node.getStyle()); 1045 } 1046 1047 @Test 1048 public void testSynchronizationOfInvisibleNodes() { 1049 final Group g = new Group(); 1050 final Circle c = new CircleTest.StubCircle(50); 1051 final NGGroup sg = NodeHelper.getPeer(g); 1052 final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c); 1053 ParentShim.getChildren(g).add(c); 1054 1055 syncNode(g); 1056 syncNode(c); 1057 assertFalse(sg.getChildren().isEmpty()); 1058 assertEquals(50.0, sc.getRadius(), 0.01); 1059 1060 g.setVisible(false); 1061 1062 syncNode(g); 1063 syncNode(c); 1064 assertFalse(sg.isVisible()); 1065 1066 final Rectangle r = new Rectangle(); 1067 ParentShim.getChildren(g).add(r); 1068 c.setRadius(100); 1069 1070 syncNode(g); 1071 syncNode(c); 1072 // Group with change in children will always be synced even if it is invisible 1073 assertEquals(2, sg.getChildren().size()); 1074 assertEquals(50.0, sc.getRadius(), 0.01); 1075 1076 g.setVisible(true); 1077 1078 syncNode(g); 1079 syncNode(c); 1080 assertEquals(2, sg.getChildren().size()); 1081 assertEquals(100.0, sc.getRadius(), 0.01); 1082 1083 } 1084 1085 @Test 1086 public void testSynchronizationOfInvisibleNodes_2() { 1087 final Group g = new Group(); 1088 final Circle c = new CircleTest.StubCircle(50); 1089 1090 Scene s = new Scene(g); 1091 Stage st = new Stage(); 1092 st.show(); 1093 st.setScene(s); 1094 1095 final NGGroup sg = NodeHelper.getPeer(g); 1096 final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c); 1097 1098 ParentShim.getChildren(g).add(c); 1099 1100 SceneShim.scenePulseListener_pulse(s); 1101 1102 g.setVisible(false); 1103 1104 SceneShim.scenePulseListener_pulse(s); 1105 1106 assertFalse(sg.isVisible()); 1107 assertTrue(sc.isVisible()); 1108 1109 c.setCenterX(10); // Make the circle dirty. It won't be synchronized as it is practically invisible (through the parent) 1110 1111 SceneShim.scenePulseListener_pulse(s); 1112 1113 c.setVisible(false); // As circle is invisible and dirty, this won't trigger a synchronization 1114 1115 SceneShim.scenePulseListener_pulse(s); 1116 1117 assertFalse(sg.isVisible()); 1118 assertTrue(sc.isVisible()); // This has not been synchronized, as it's not necessary 1119 // The rendering will stop at the Group, which is invisible 1120 1121 g.setVisible(true); 1122 1123 SceneShim.scenePulseListener_pulse(s); 1124 1125 assertTrue(sg.isVisible()); 1126 assertFalse(sc.isVisible()); // Now the group is visible again, we need to synchronize also 1127 // the Circle 1128 } 1129 1130 @Test 1131 public void testSynchronizationOfInvisibleNodes_2_withClip() { 1132 final Group g = new Group(); 1133 final Circle c = new CircleTest.StubCircle(50); 1134 1135 Scene s = new Scene(g); 1136 Stage st = new Stage(); 1137 st.show(); 1138 st.setScene(s); 1139 1140 final NGGroup sg = NodeHelper.getPeer(g); 1141 final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c); 1142 1143 g.setClip(c); 1144 1145 SceneShim.scenePulseListener_pulse(s); 1146 1147 g.setVisible(false); 1148 1149 SceneShim.scenePulseListener_pulse(s); 1150 1151 assertFalse(sg.isVisible()); 1152 assertTrue(sc.isVisible()); 1153 1154 c.setCenterX(10); // Make the circle dirty. It won't be synchronized as it is practically invisible (through the parent) 1155 1156 SceneShim.scenePulseListener_pulse(s); 1157 1158 c.setVisible(false); // As circle is invisible and dirty, this won't trigger a synchronization 1159 1160 SceneShim.scenePulseListener_pulse(s); 1161 1162 assertFalse(sg.isVisible()); 1163 assertTrue(sc.isVisible()); // This has not been synchronized, as it's not necessary 1164 // The rendering will stop at the Group, which is invisible 1165 1166 g.setVisible(true); 1167 1168 SceneShim.scenePulseListener_pulse(s); 1169 1170 assertTrue(sg.isVisible()); 1171 assertFalse(sc.isVisible()); // Now the group is visible again, we need to synchronize also 1172 // the Circle 1173 } 1174 1175 @Test 1176 public void testLocalToScreen() { 1177 Rectangle rect = new Rectangle(); 1178 1179 rect.setTranslateX(10); 1180 rect.setTranslateY(20); 1181 1182 TestScene scene = new TestScene(new Group(rect)); 1183 final TestStage testStage = new TestStage(""); 1184 testStage.setX(100); 1185 testStage.setY(200); 1186 scene.set_window(testStage); 1187 Point2D p = rect.localToScreen(new Point2D(1, 2)); 1188 assertEquals(111.0, p.getX(), 0.0001); 1189 assertEquals(222.0, p.getY(), 0.0001); 1190 Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4)); 1191 assertEquals(111.0, b.getMinX(), 0.0001); 1192 assertEquals(222.0, b.getMinY(), 0.0001); 1193 assertEquals(3.0, b.getWidth(), 0.0001); 1194 assertEquals(4.0, b.getHeight(), 0.0001); 1195 } 1196 1197 @Test 1198 public void testLocalToScreen3D() { 1199 Box box = new Box(10, 10, 10); 1200 1201 box.setTranslateX(10); 1202 box.setTranslateY(20); 1203 1204 TestScene scene = new TestScene(new Group(box)); 1205 scene.setCamera(new PerspectiveCamera()); 1206 final TestStage testStage = new TestStage(""); 1207 testStage.setX(100); 1208 testStage.setY(200); 1209 scene.set_window(testStage); 1210 1211 Point2D p = box.localToScreen(new Point3D(1, 2, -5)); 1212 assertEquals(111.42, p.getX(), 0.1); 1213 assertEquals(223.14, p.getY(), 0.1); 1214 Bounds b = box.localToScreen(new BoundingBox(1, 2, -5, 1, 2, 10)); 1215 assertEquals(110.66, b.getMinX(), 0.1); 1216 assertEquals(221.08, b.getMinY(), 0.1); 1217 assertEquals(1.88, b.getWidth(), 0.1); 1218 assertEquals(4.3, b.getHeight(), 0.1); 1219 assertEquals(0.0, b.getDepth(), 0.0001); 1220 } 1221 1222 @Test 1223 public void testScreenToLocal() { 1224 Rectangle rect = new Rectangle(); 1225 1226 rect.setTranslateX(10); 1227 rect.setTranslateY(20); 1228 1229 TestScene scene = new TestScene(new Group(rect)); 1230 final TestStage testStage = new TestStage(""); 1231 testStage.setX(100); 1232 testStage.setY(200); 1233 scene.set_window(testStage); 1234 1235 assertEquals(new Point2D(1, 2), rect.screenToLocal(new Point2D(111, 222))); 1236 assertEquals(new BoundingBox(1, 2, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4))); 1237 } 1238 1239 @Test 1240 public void testLocalToScreenWithTranslatedCamera() { 1241 Rectangle rect = new Rectangle(); 1242 1243 rect.setTranslateX(10); 1244 rect.setTranslateY(20); 1245 1246 ParallelCamera cam = new ParallelCamera(); 1247 TestScene scene = new TestScene(new Group(rect, cam)); 1248 scene.setCamera(cam); 1249 final TestStage testStage = new TestStage(""); 1250 testStage.setX(100); 1251 testStage.setY(200); 1252 cam.setTranslateX(30); 1253 cam.setTranslateY(20); 1254 scene.set_window(testStage); 1255 1256 Point2D p = rect.localToScreen(new Point2D(1, 2)); 1257 assertEquals(81.0, p.getX(), 0.0001); 1258 assertEquals(202.0, p.getY(), 0.0001); 1259 Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4)); 1260 assertEquals(81.0, b.getMinX(), 0.0001); 1261 assertEquals(202.0, b.getMinY(), 0.0001); 1262 assertEquals(3.0, b.getWidth(), 0.0001); 1263 assertEquals(4.0, b.getHeight(), 0.0001); 1264 } 1265 1266 @Test 1267 public void testScreenToLocalWithTranslatedCamera() { 1268 Rectangle rect = new Rectangle(); 1269 1270 rect.setTranslateX(10); 1271 rect.setTranslateY(20); 1272 1273 ParallelCamera cam = new ParallelCamera(); 1274 TestScene scene = new TestScene(new Group(rect, cam)); 1275 scene.setCamera(cam); 1276 final TestStage testStage = new TestStage(""); 1277 testStage.setX(100); 1278 testStage.setY(200); 1279 cam.setTranslateX(30); 1280 cam.setTranslateY(20); 1281 scene.set_window(testStage); 1282 1283 assertEquals(new Point2D(31, 22), rect.screenToLocal(new Point2D(111, 222))); 1284 assertEquals(new BoundingBox(31, 22, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4))); 1285 } 1286 1287 @Test 1288 public void testLocalToScreenInsideSubScene() { 1289 Rectangle rect = new Rectangle(); 1290 rect.setTranslateX(4); 1291 rect.setTranslateY(9); 1292 SubScene subScene = new SubScene(new Group(rect), 100, 100); 1293 subScene.setTranslateX(6); 1294 subScene.setTranslateY(11); 1295 1296 TestScene scene = new TestScene(new Group(subScene)); 1297 final TestStage testStage = new TestStage(""); 1298 testStage.setX(100); 1299 testStage.setY(200); 1300 scene.set_window(testStage); 1301 1302 Point2D p = rect.localToScreen(new Point2D(1, 2)); 1303 assertEquals(111.0, p.getX(), 0.0001); 1304 assertEquals(222.0, p.getY(), 0.0001); 1305 Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4)); 1306 assertEquals(111.0, b.getMinX(), 0.0001); 1307 assertEquals(222.0, b.getMinY(), 0.0001); 1308 assertEquals(3.0, b.getWidth(), 0.0001); 1309 assertEquals(4.0, b.getHeight(), 0.0001); 1310 } 1311 1312 @Test 1313 public void testScreenToLocalInsideSubScene() { 1314 Rectangle rect = new Rectangle(); 1315 rect.setTranslateX(4); 1316 rect.setTranslateY(9); 1317 SubScene subScene = new SubScene(new Group(rect), 100, 100); 1318 subScene.setTranslateX(6); 1319 subScene.setTranslateY(11); 1320 1321 TestScene scene = new TestScene(new Group(subScene)); 1322 final TestStage testStage = new TestStage(""); 1323 testStage.setX(100); 1324 testStage.setY(200); 1325 scene.set_window(testStage); 1326 1327 assertEquals(new Point2D(1, 2), rect.screenToLocal(new Point2D(111, 222))); 1328 assertEquals(new BoundingBox(1, 2, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4))); 1329 } 1330 1331 @Test 1332 public void test2DLocalToScreenOn3DRotatedSubScene() { 1333 Rectangle rect = new Rectangle(); 1334 rect.setTranslateX(5); 1335 rect.setTranslateY(10); 1336 SubScene subScene = new SubScene(new Group(rect), 100, 100); 1337 subScene.setTranslateX(5); 1338 subScene.setTranslateY(10); 1339 subScene.setRotationAxis(Rotate.Y_AXIS); 1340 subScene.setRotate(40); 1341 1342 TestScene scene = new TestScene(new Group(subScene)); 1343 scene.setCamera(new PerspectiveCamera()); 1344 final TestStage testStage = new TestStage(""); 1345 testStage.setX(100); 1346 testStage.setY(200); 1347 scene.set_window(testStage); 1348 1349 Point2D p = rect.localToScreen(new Point2D(1, 2)); 1350 assertEquals(124.36, p.getX(), 0.1); 1351 assertEquals(226.0, p.getY(), 0.1); 1352 Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4)); 1353 assertEquals(124.36, b.getMinX(), 0.1); 1354 assertEquals(225.75, b.getMinY(), 0.1); 1355 assertEquals(1.85, b.getWidth(), 0.1); 1356 assertEquals(3.76, b.getHeight(), 0.1); 1357 } 1358 1359 @Test 1360 public void test2DScreenToLocalTo3DRotatedSubScene() { 1361 Rectangle rect = new Rectangle(); 1362 rect.setTranslateX(5); 1363 rect.setTranslateY(10); 1364 SubScene subScene = new SubScene(new Group(rect), 100, 100); 1365 subScene.setTranslateX(5); 1366 subScene.setTranslateY(10); 1367 subScene.setRotationAxis(Rotate.Y_AXIS); 1368 subScene.setRotate(40); 1369 1370 TestScene scene = new TestScene(new Group(subScene)); 1371 scene.setCamera(new PerspectiveCamera()); 1372 final TestStage testStage = new TestStage(""); 1373 testStage.setX(100); 1374 testStage.setY(200); 1375 scene.set_window(testStage); 1376 1377 Point2D p = rect.screenToLocal(new Point2D(124.36, 226.0)); 1378 assertEquals(1, p.getX(), 0.1); 1379 assertEquals(2, p.getY(), 0.1); 1380 Bounds b = rect.screenToLocal(new BoundingBox(124.36, 225.75, 1.85, 3.76)); 1381 assertEquals(1, b.getMinX(), 0.1); 1382 assertEquals(1.72, b.getMinY(), 0.1); 1383 assertEquals(3, b.getWidth(), 0.1); 1384 assertEquals(4.52, b.getHeight(), 0.1); 1385 } 1386 1387 @Test 1388 public void testScreenToLocalWithNonInvertibleTransform() { 1389 Rectangle rect = new Rectangle(); 1390 1391 rect.setScaleX(0.0); 1392 1393 TestScene scene = new TestScene(new Group(rect)); 1394 final TestStage testStage = new TestStage(""); 1395 testStage.setX(100); 1396 testStage.setY(200); 1397 scene.set_window(testStage); 1398 1399 assertNull(rect.screenToLocal(new Point2D(111, 222))); 1400 assertNull(rect.screenToLocal(new BoundingBox(111, 222, 3, 4))); 1401 } 1402 1403 @Test 1404 public void testScreenToLocalInsideNonInvertibleSubScene() { 1405 Rectangle rect = new Rectangle(); 1406 rect.setTranslateX(4); 1407 rect.setTranslateY(9); 1408 SubScene subScene = new SubScene(new Group(rect), 100, 100); 1409 subScene.setScaleX(0.0); 1410 1411 TestScene scene = new TestScene(new Group(subScene)); 1412 final TestStage testStage = new TestStage(""); 1413 testStage.setX(100); 1414 testStage.setY(200); 1415 scene.set_window(testStage); 1416 1417 assertNull(rect.screenToLocal(new Point2D(111, 222))); 1418 assertNull(rect.screenToLocal(new BoundingBox(111, 222, 3, 4))); 1419 } 1420 1421 @Test 1422 public void testRootMirroringWithTranslate() { 1423 final Group rootGroup = new Group(); 1424 rootGroup.setTranslateX(20); 1425 rootGroup.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); 1426 final Scene scene = new Scene(rootGroup, 200, 200); 1427 1428 final Point2D trPoint = scene.getRoot().localToScene(0, 0); 1429 assertEquals(180, trPoint.getX(), 0.1); 1430 } 1431 1432 1433 @Test 1434 public void testLayoutXYTriggersParentSizeChange() { 1435 final Group rootGroup = new Group(); 1436 final Group subGroup = new Group(); 1437 ParentShim.getChildren(rootGroup).add(subGroup); 1438 1439 Rectangle r = new Rectangle(50,50); 1440 r.setManaged(false); 1441 Rectangle staticR = new Rectangle(1,1); 1442 ParentShim.getChildren(subGroup).addAll(r, staticR); 1443 1444 assertEquals(50,subGroup.getLayoutBounds().getWidth(), 1e-10); 1445 assertEquals(50,subGroup.getLayoutBounds().getHeight(), 1e-10); 1446 1447 r.setLayoutX(50); 1448 1449 rootGroup.layout(); 1450 1451 assertEquals(100,subGroup.getLayoutBounds().getWidth(), 1e-10); 1452 assertEquals(50,subGroup.getLayoutBounds().getHeight(), 1e-10); 1453 1454 } 1455 1456 @Test 1457 public void testLayoutXYWontBreakLayout() { 1458 final Group rootGroup = new Group(); 1459 final AnchorPane pane = new AnchorPane(); 1460 ParentShim.getChildren(rootGroup).add(pane); 1461 1462 Rectangle r = new Rectangle(50,50); 1463 ParentShim.getChildren(pane).add(r); 1464 1465 AnchorPane.setLeftAnchor(r, 10d); 1466 AnchorPane.setTopAnchor(r, 10d); 1467 1468 rootGroup.layout(); 1469 1470 assertEquals(10, r.getLayoutX(), 1e-10); 1471 assertEquals(10, r.getLayoutY(), 1e-10); 1472 1473 r.setLayoutX(50); 1474 1475 assertEquals(50, r.getLayoutX(), 1e-10); 1476 assertEquals(10, r.getLayoutY(), 1e-10); 1477 1478 rootGroup.layout(); 1479 1480 assertEquals(10, r.getLayoutX(), 1e-10); 1481 assertEquals(10, r.getLayoutY(), 1e-10); 1482 1483 } 1484 1485 @Test 1486 public void clipShouldUpdateAfterParentVisibilityChange() { 1487 1488 final Group root = new Group(); 1489 Scene scene = new Scene(root, 300, 300); 1490 1491 final Group parent = new Group(); 1492 parent.setVisible(false); 1493 1494 final Circle circle = new Circle(100, 100, 100); 1495 ParentShim.getChildren(parent).add(circle); 1496 1497 final Rectangle clip = new StubRect(100, 100); 1498 circle.setClip(clip); 1499 1500 ParentShim.getChildren(root).add(parent); 1501 parent.setVisible(true); 1502 1503 Stage stage = new Stage(); 1504 stage.setScene(scene); 1505 stage.show(); 1506 1507 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1508 1509 clip.setWidth(300); 1510 1511 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1512 1513 assertEquals(300, ((MockNGRect) NodeHelper.getPeer(clip)).w, 1e-10); 1514 } 1515 1516 @Test 1517 public void untransformedNodeShouldSyncIdentityTransform() { 1518 final Node node = createTestRect(); 1519 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1520 assertSame(BaseTransform.IDENTITY_TRANSFORM, 1521 ((MockNGRect) NodeHelper.getPeer(node)).t); 1522 } 1523 1524 @Test 1525 public void nodeTransfomedByIdentitiesShouldSyncIdentityTransform() { 1526 final Node node = createTestRect(); 1527 node.setRotationAxis(Rotate.X_AXIS); 1528 node.getTransforms().add(new Translate()); 1529 node.getTransforms().add(new Scale()); 1530 node.getTransforms().add(new Affine()); 1531 node.getTransforms().add(new Rotate(0, Rotate.Y_AXIS)); 1532 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1533 assertSame(BaseTransform.IDENTITY_TRANSFORM, 1534 ((MockNGRect) NodeHelper.getPeer(node)).t); 1535 } 1536 1537 @Test 1538 public void translatedNodeShouldSyncTranslateTransform1() { 1539 final Node node = createTestRect(); 1540 node.setTranslateX(30); 1541 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1542 assertSame(Translate2D.class, 1543 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1544 } 1545 1546 @Test 1547 public void translatedNodeShouldSyncTranslateTransform2() { 1548 final Node node = createTestRect(); 1549 node.getTransforms().add(new Translate(20, 10)); 1550 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1551 assertSame(Translate2D.class, 1552 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1553 } 1554 1555 @Test 1556 public void multitranslatedNodeShouldSyncTranslateTransform() { 1557 final Node node = createTestRect(); 1558 node.setTranslateX(30); 1559 node.getTransforms().add(new Translate(20, 10)); 1560 node.getTransforms().add(new Translate(10, 20)); 1561 node.getTransforms().add(new Translate(5, 5, 0)); 1562 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1563 assertSame(Translate2D.class, 1564 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1565 } 1566 1567 @Test 1568 public void mirroringShouldSyncAffine2DTransform() { 1569 final Node node = createTestRect(); 1570 node.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); 1571 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1572 assertSame(Affine2D.class, 1573 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1574 } 1575 1576 @Test 1577 public void rotatedNodeShouldSyncAffine2DTransform1() { 1578 final Node node = createTestRect(); 1579 node.setRotate(20); 1580 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1581 assertSame(Affine2D.class, 1582 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1583 } 1584 1585 @Test 1586 public void rotatedNodeShouldSyncAffine2DTransform2() { 1587 final Node node = createTestRect(); 1588 node.getTransforms().add(new Rotate(20)); 1589 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1590 assertSame(Affine2D.class, 1591 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1592 } 1593 1594 @Test 1595 public void multiRotatedNodeShouldSyncAffine2DTransform() { 1596 final Node node = createTestRect(); 1597 node.setRotate(20); 1598 node.getTransforms().add(new Rotate(20)); 1599 node.getTransforms().add(new Rotate(0, Rotate.X_AXIS)); 1600 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1601 assertSame(Affine2D.class, 1602 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1603 } 1604 1605 @Test 1606 public void scaledNodeShouldSyncAffine2DTransform1() { 1607 final Node node = createTestRect(); 1608 node.setScaleX(2); 1609 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1610 assertSame(Affine2D.class, 1611 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1612 } 1613 1614 @Test 1615 public void scaledNodeShouldSyncAffine2DTransform2() { 1616 final Node node = createTestRect(); 1617 node.getTransforms().add(new Scale(2, 1)); 1618 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1619 assertSame(Affine2D.class, 1620 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1621 } 1622 1623 @Test 1624 public void multiScaledNodeShouldSyncAffine2DTransform() { 1625 final Node node = createTestRect(); 1626 node.setScaleX(20); 1627 node.getTransforms().add(new Scale(2, 1)); 1628 node.getTransforms().add(new Scale(0.5, 2, 1)); 1629 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1630 assertSame(Affine2D.class, 1631 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1632 } 1633 1634 @Test 1635 public void shearedNodeShouldSyncAffine2DTransform() { 1636 final Node node = createTestRect(); 1637 node.getTransforms().add(new Shear(2, 1)); 1638 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1639 assertSame(Affine2D.class, 1640 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1641 } 1642 1643 @Test 1644 public void ztranslatedNodeShouldSyncAffine3DTransform1() { 1645 final Node node = createTestRect(); 1646 node.setTranslateZ(30); 1647 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1648 assertSame(Affine3D.class, 1649 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1650 } 1651 1652 @Test 1653 public void ztranslatedNodeShouldSyncAffine3DTransform2() { 1654 final Node node = createTestRect(); 1655 node.getTransforms().add(new Translate(0, 0, 10)); 1656 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1657 assertSame(Affine3D.class, 1658 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1659 } 1660 1661 @Test 1662 public void zscaledNodeShouldSyncAffine3DTransform1() { 1663 final Node node = createTestRect(); 1664 node.setScaleZ(0.5); 1665 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1666 assertSame(Affine3D.class, 1667 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1668 } 1669 1670 @Test 1671 public void zscaledNodeShouldSyncAffine3DTransform2() { 1672 final Node node = createTestRect(); 1673 node.getTransforms().add(new Scale(1, 1, 2)); 1674 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1675 assertSame(Affine3D.class, 1676 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1677 } 1678 1679 @Test 1680 public void nonZRotatedNodeShouldSyncAffine3DTransform1() { 1681 final Node node = createTestRect(); 1682 node.setRotationAxis(Rotate.Y_AXIS); 1683 node.setRotate(10); 1684 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1685 assertSame(Affine3D.class, 1686 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1687 } 1688 1689 @Test 1690 public void nonZRotatedNodeShouldSyncAffine3DTransform2() { 1691 final Node node = createTestRect(); 1692 node.getTransforms().add(new Rotate(10, Rotate.X_AXIS)); 1693 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1694 assertSame(Affine3D.class, 1695 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass()); 1696 } 1697 1698 @Test 1699 public void translateTransformShouldBeReusedWhenPossible() { 1700 final Node node = createTestRect(); 1701 node.setTranslateX(10); 1702 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1703 1704 BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t; 1705 1706 ((MockNGRect) NodeHelper.getPeer(node)).t = null; 1707 node.setTranslateX(20); 1708 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1709 1710 assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t); 1711 } 1712 1713 @Test 1714 public void affine2DTransformShouldBeReusedWhenPossible() { 1715 final Node node = createTestRect(); 1716 node.setScaleX(10); 1717 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1718 1719 BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t; 1720 1721 ((MockNGRect) NodeHelper.getPeer(node)).t = null; 1722 node.setRotate(20); 1723 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1724 1725 assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t); 1726 } 1727 1728 @Test 1729 public void affine3DTransformShouldBeReusedWhenPossible() { 1730 final Node node = createTestRect(); 1731 node.setScaleZ(10); 1732 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1733 1734 BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t; 1735 1736 ((MockNGRect) NodeHelper.getPeer(node)).t = null; 1737 node.setRotate(20); 1738 ((StubToolkit) Toolkit.getToolkit()).firePulse(); 1739 1740 assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t); 1741 } 1742 1743 @Test 1744 public void rtlSceneSizeShouldBeComputedCorrectly() { 1745 Scene scene = new Scene(new Group(new Rectangle(100, 100))); 1746 scene.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); 1747 Stage stage = new Stage(); 1748 stage.setScene(scene); 1749 stage.show(); 1750 assertEquals(100.0, scene.getWidth(), 0.00001); 1751 } 1752 1753 private Node createTestRect() { 1754 final Rectangle rect = new StubRect(); 1755 Scene scene = new Scene(new Group(rect)); 1756 Stage stage = new Stage(); 1757 stage.setScene(scene); 1758 stage.show(); 1759 return rect; 1760 } 1761 1762 private static class MockNGRect extends NGRectangle { 1763 double w = 0; 1764 BaseTransform t = null; 1765 1766 @Override public void updateRectangle(float x, float y, float width, 1767 float height, float arcWidth, float arcHeight) { 1768 w = width; 1769 } 1770 1771 @Override 1772 public void setTransformMatrix(BaseTransform tx) { 1773 t = tx; 1774 } 1775 } 1776 1777 static class StubRect extends Rectangle { 1778 static { 1779 StubRectHelper.setStubRectAccessor(new StubRectHelper.StubRectAccessor() { 1780 @Override 1781 public NGNode doCreatePeer(Node node) { 1782 return ((StubRect) node).doCreatePeer(); 1783 } 1784 }); 1785 } 1786 1787 StubRect() { 1788 super(); 1789 StubRectHelper.initHelper(this); 1790 } 1791 1792 StubRect(double width, double height) { 1793 super(width, height); 1794 StubRectHelper.initHelper(this); 1795 } 1796 1797 private NGNode doCreatePeer() { 1798 return new MockNGRect(); 1799 } 1800 } 1801 1802 public static class StubRectHelper extends RectangleHelper { 1803 1804 private static final StubRectHelper theInstance; 1805 private static StubRectAccessor stubRectAccessor; 1806 1807 static { 1808 theInstance = new StubRectHelper(); 1809 Utils.forceInit(StubRect.class); 1810 } 1811 1812 private static StubRectHelper getInstance() { 1813 return theInstance; 1814 } 1815 1816 public static void initHelper(StubRect stubRect) { 1817 setHelper(stubRect, getInstance()); 1818 } 1819 1820 public static void setStubRectAccessor(final StubRectAccessor newAccessor) { 1821 if (stubRectAccessor != null) { 1822 throw new IllegalStateException(); 1823 } 1824 1825 stubRectAccessor = newAccessor; 1826 } 1827 1828 @Override 1829 protected NGNode createPeerImpl(Node node) { 1830 return stubRectAccessor.doCreatePeer(node); 1831 } 1832 1833 public interface StubRectAccessor { 1834 NGNode doCreatePeer(Node node); 1835 } 1836 1837 } 1838 }