1 /*
   2  * Copyright (c) 2009, 2012, 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  */
  24 package test.scenegraph.app;
  25 
  26 import java.util.Arrays;
  27 import java.util.List;
  28 import javafx.collections.FXCollections;
  29 import javafx.collections.ListChangeListener;
  30 import javafx.collections.ObservableList;
  31 import javafx.event.ActionEvent;
  32 import javafx.event.EventHandler;
  33 import javafx.scene.*;
  34 import javafx.scene.control.*;
  35 import javafx.scene.input.MouseEvent;
  36 import javafx.scene.layout.*;
  37 import javafx.scene.paint.Color;
  38 import javafx.scene.shape.Circle;
  39 import javafx.scene.shape.Rectangle;
  40 import javafx.scene.shape.Shape;
  41 
  42 /**
  43  *
  44  * @author Aleksandr Sakharuk
  45  */
  46 public class DepthTestScene extends Scene
  47 {
  48 
  49     public DepthTestScene()
  50     {
  51         super(new BorderPane(), 500, 280.25, true);
  52         BorderPane root = (BorderPane) getRoot();
  53         final Group depthTestGroup = new Group();
  54         depthTestGroup.getChildren().addListener(new NodeListener());
  55         depthTestGroup.getChildren().addAll(Nodes.RECTANGLE.getNode("green"), Nodes.CIRCLE.getNode("red"));
  56 
  57         parentDT.setOnAction(new DepthTestChangeHandler(depthTestGroup));
  58 
  59         GridPane tuneGrid = new GridPane();
  60         tuneGrid.setVgap(1);
  61         tuneGrid.setHgap(1);
  62         tuneGrid.addRow(0, oneLabel, oneDT);
  63         tuneGrid.addRow(1, anotherLabel, anotherDT);
  64         tuneGrid.addRow(2, parentLabel, parentDT);
  65 
  66         oneNodeCmb.getSelectionModel().select(Nodes.RECTANGLE);
  67         oneNodeCmb.setOnAction(new NodeChangeHandler(depthTestGroup.getChildren(), 0));
  68         anotherNodeCmb.getSelectionModel().select(Nodes.CIRCLE);
  69         anotherNodeCmb.setOnAction(new NodeChangeHandler(depthTestGroup.getChildren(), 1));
  70 
  71         HBox nodeSwitches = new HBox(1);
  72         nodeSwitches.getChildren().addAll(oneNodeCmb, anotherNodeCmb);
  73 
  74         oneToFront.setOnAction(new FrontBackChangeHandler(depthTestGroup.getChildren(), 0));
  75         anotherToFront.setOnAction(new FrontBackChangeHandler(depthTestGroup.getChildren(), 1));
  76 
  77         VBox backFrontRadio = new VBox(1);
  78         backFrontRadio.getChildren().addAll(oneToFront, anotherToFront);
  79 
  80         VBox switchesAndRadios = new VBox(1);
  81         switchesAndRadios.getChildren().addAll(tuneGrid, backFrontRadio,
  82                 new HBox(10, indicator, referenceGreen, referenceRed), details);
  83 
  84         root.setLeft(depthTestGroup);
  85         root.setRight(switchesAndRadios);
  86         root.setTop(nodeSwitches);
  87         setCamera(new PerspectiveCamera());
  88 
  89         oneDT.setValue(DepthTest.INHERIT);
  90         anotherDT.setValue(DepthTest.INHERIT);
  91         parentDT.setValue(DepthTest.DISABLE);
  92     }
  93 
  94     private Label oneLabel = new Label("First node DepthTest");
  95     private Label anotherLabel = new Label("Second node DepthTest");
  96     private Label parentLabel = new Label("Parent node DepthTest");
  97 
  98     private ObservableList<DepthTest> depthTestList = FXCollections.observableArrayList(DepthTest.values());
  99     private ComboBox<DepthTest> oneDT = new ComboBox<DepthTest>(depthTestList);
 100     private ComboBox<DepthTest> anotherDT = new ComboBox<DepthTest>(depthTestList);
 101     private ComboBox<DepthTest> parentDT = new ComboBox<DepthTest>(
 102             FXCollections.observableArrayList(DepthTest.DISABLE, DepthTest.ENABLE));
 103 
 104     private ObservableList<DepthTestScene.Nodes> nodesList = FXCollections.observableArrayList(DepthTestScene.Nodes.values());
 105     private ComboBox<DepthTestScene.Nodes> oneNodeCmb = new ComboBox<DepthTestScene.Nodes>(nodesList);
 106     private ComboBox<DepthTestScene.Nodes> anotherNodeCmb = new ComboBox<DepthTestScene.Nodes>(nodesList);
 107 
 108     private ToggleGroup backFrontSwitch = new ToggleGroup();
 109     private RadioButton oneToFront = new RadioButton("First node to front");
 110     private RadioButton anotherToFront = new RadioButton("Second node to front");
 111 
 112     private Circle indicator = new Circle(10);
 113     private Label details = new Label("");
 114     private Circle referenceGreen = new Circle(10, Color.GREEN);
 115     private Circle referenceRed = new Circle(10, Color.RED);
 116 
 117     {
 118         oneDT.setId("first_depth_test");
 119         anotherDT.setId("second_depth_test");
 120         parentDT.setId("parent_depth_test");
 121 
 122         oneNodeCmb.setId("first_node_combo");
 123         anotherNodeCmb.setId("second_node_combo");
 124 
 125         oneToFront.setId("first_node_to_front");
 126         anotherToFront.setId("second_node_to_front");
 127 
 128         oneToFront.setToggleGroup(backFrontSwitch);
 129         anotherToFront.setToggleGroup(backFrontSwitch);
 130 
 131         indicator.setId("indicator");
 132         //details.setWrapText(true);
 133         referenceGreen.setId("reference_green");
 134         referenceRed.setId("reference_red");
 135     }
 136 
 137     public enum Nodes
 138     {
 139 
 140         RECTANGLE(){
 141             public Node create()
 142             {
 143                 Node node = new Rectangle(100, 100, Color.GREEN);
 144                 return node;
 145             }
 146         },
 147         CIRCLE(){
 148             public Node create()
 149             {
 150                 Node node = new Circle(40, Color.RED);
 151                 return node;
 152             }
 153         },
 154         BUTTON(){
 155             public Node create()
 156             {
 157                 Node node = new Button("Button");
 158                 return node;
 159             }
 160         };
 161 
 162         protected abstract Node create();
 163 
 164         public Node getNode()
 165         {
 166             Node node = create();
 167             node.setId(toString());
 168             node.setTranslateX(100);
 169             node.setTranslateY(100);
 170             return node;
 171         }
 172 
 173         public Node getNode(String color)
 174         {
 175             Node node = create();
 176             node.setId(toString() + "-" + color);
 177             if(node instanceof Shape)
 178             {
 179                 ((Shape) node).setFill(Color.web(color));
 180             }
 181             else if(node instanceof Control)
 182             {
 183                 node.setStyle(String.format("-fx-background-color:%1$s; -fx-text-fill:%1$s", color));
 184             }
 185             return node;
 186         }
 187 
 188         public String getId()
 189         {
 190             return id;
 191         }
 192 
 193         private String id;
 194 
 195     }
 196 
 197     class NodeListener implements ListChangeListener<Node>
 198     {
 199 
 200         public void onChanged(final ListChangeListener.Change<? extends Node> change)
 201         {
 202             while(change.next())
 203             {
 204                 if(change.wasAdded())
 205                 {
 206                     final List<? extends Node> added = change.getAddedSubList();
 207                     for(final Node node: added)
 208                     {
 209                         final ObservableList<? extends Node> list = change.getList();
 210                         final int current = list.indexOf(node);
 211                         ComboBox<DepthTest> box = boxes.get(current);
 212                         box.setOnAction(new DepthTestScene.DepthTestChangeHandler(node));
 213                         node.setDepthTest(box.getValue());
 214                         labels.get(current).setText(node.getClass().getSimpleName() + " DepthTest");
 215                         RadioButton radio = radios.get(current);
 216                         radio.setText(node.getClass().getSimpleName() + " to front");
 217                         for(RadioButton rb: radios)
 218                         {
 219                             if(rb.selectedProperty().get())
 220                             {
 221                                 ActionEvent.fireEvent(rb, new ActionEvent(rb, rb));
 222                             }
 223                         }
 224                         node.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
 225 
 226                             public void handle(MouseEvent t)
 227                             {
 228                                 details.setText(node.getId() + " was reached");
 229                                 if(isOnTop())
 230                                 {
 231                                     indicator.setStyle("-fx-fill: green;");
 232                                 }
 233                                 else
 234                                 {
 235                                     indicator.setStyle("-fx-fill: red;");
 236                                 }
 237                             }
 238 
 239                             public boolean isOnTop()
 240                             {
 241                                 boolean meets = false;
 242                                 boolean onTop = list.indexOf(node) == list.size() - 1;
 243                                 boolean selfDTEnable = isDepthTestEnable(boxes.get(current));
 244                                 boolean otherDTEnable = isDepthTestEnable(boxes.get((current + 1) % boxes.size()));
 245                                 boolean inFront = radios.get(current).isSelected();
 246 
 247                                 if((!selfDTEnable || !otherDTEnable) && onTop)
 248                                     meets = true;
 249                                 if(selfDTEnable && otherDTEnable && inFront)
 250                                     meets = true;
 251 
 252                                 return meets;
 253                                 }
 254 
 255                             private boolean isDepthTestEnable(ComboBox<DepthTest> cb)
 256                             {
 257                                 boolean enable = cb.getValue().equals(DepthTest.ENABLE) ||
 258                                         (cb.getValue().equals(DepthTest.INHERIT) &&
 259                                         parentDT.getValue().equals(DepthTest.ENABLE));
 260                                 return enable;
 261                             }
 262                         });
 263                     }
 264                 }
 265             }
 266         }
 267 
 268         private List<ComboBox<DepthTest>> boxes = Arrays.asList(oneDT, anotherDT);
 269         private List<Label> labels = Arrays.asList(oneLabel, anotherLabel);
 270         private List<RadioButton> radios = Arrays.asList(oneToFront, anotherToFront);
 271 
 272     }
 273 
 274     class DepthTestChangeHandler implements EventHandler<ActionEvent>
 275     {
 276 
 277         DepthTestChangeHandler(Node node)
 278         {
 279             this.node = node;
 280         }
 281 
 282         public void handle(ActionEvent t)
 283         {
 284             Object source = t.getSource();
 285             if(source instanceof ComboBox)
 286             {
 287                 node.setDepthTest(((ComboBox<DepthTest>) source).getValue());
 288             }
 289         }
 290 
 291         private Node node;
 292 
 293     }
 294 
 295     class NodeChangeHandler implements EventHandler<ActionEvent>
 296     {
 297 
 298         NodeChangeHandler(ObservableList<Node> nodes, int nodeIndex)
 299         {
 300             this.nodes = nodes;
 301             this.nodeIndex = nodeIndex;
 302         }
 303 
 304         public void handle(ActionEvent t)
 305         {
 306             Object source = t.getSource();
 307             if(source instanceof ComboBox)
 308             {
 309                 DepthTestScene.Nodes nd = ((ComboBox<DepthTestScene.Nodes>) source).getSelectionModel().getSelectedItem();
 310                 nodes.remove(nodeIndex);
 311                 String color = nodeIndex == 0 ? "green" : "red";
 312                 nodes.add(nodeIndex, nd.getNode(color));
 313             }
 314         }
 315 
 316         private ObservableList<Node> nodes;
 317         private int nodeIndex;
 318 
 319     }
 320 
 321     class FrontBackChangeHandler implements EventHandler<ActionEvent>
 322     {
 323 
 324         FrontBackChangeHandler(ObservableList<Node> nodes, int nodeIndex)
 325         {
 326             this.nodes = nodes;
 327             this.nodeIndex = nodeIndex;
 328         }
 329 
 330         public void handle(ActionEvent t)
 331         {
 332             for(int i = 0; i < nodes.size(); i++)
 333             {
 334                 if(i == nodeIndex)
 335                 {
 336                     nodes.get(i).setTranslateZ(-1);
 337                 }
 338                 else
 339                 {
 340                     nodes.get(i).setTranslateZ(0);
 341                 }
 342             }
 343         }
 344 
 345         private ObservableList<Node> nodes;
 346         private int nodeIndex;
 347 
 348     }
 349 
 350 }