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