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