cssjavafxbringtofront

JavafX & CSS - Move Node to Front


As the title basically. I have a node in JavaFX which I want to be displayed in front of all other nodes according to certain CSS rules. I do not want this to change the ordering of the nodes in my VBox which .toFront() appears to do. See this question.

Is this even possible?

EDIT: To clarify. The situation is the following. I have a VBox containing a bunch of tightly packed ImageViews. When I hover over one I want it to grow slightly to give it the feel that the image is being lifted off of the screen. But since the ImageViews are so tightly packed only the top edge grows (visibly). The bottom edge grows but is below the following image and cannot be seen.

EDIT 2: Upon request here is a screenshot of what I am doing.

enter image description here

The different colour gradients are ImageViews and as I hover over one it should grow as the top edge of the top gradient has in this image (look closely at the top right corner next to the X). However as is also visible in this image the bottom edge of this ImageView has become hidden by the next gradient in this VBox and the grow is not visible.


Solution

  • This sounds like the perfect situation for using the viewOrder property of Node added in Java 9. The viewOrder controls how Nodes are drawn in relation to other Nodes of the same Parent without changing the order of the Nodes in the child list. Here's the Javadoc:

    Defines the rendering and picking order of this Node within its parent.

    This property is used to alter the rendering and picking order of a node within its parent without reordering the parent's children list. For example, this can be used as a more efficient way to implement transparency sorting. To do this, an application can assign the viewOrder value of each node to the computed distance between that node and the viewer.

    The parent will traverse its children in decreasing viewOrder order. This means that a child with a lower viewOrder will be in front of a child with a higher viewOrder. If two children have the same viewOrder, the parent will traverse them in the order they appear in the parent's children list.

    However, viewOrder does not alter the layout and focus traversal order of this Node within its parent. A parent always traverses its children list in order when doing layout or focus traversal.

    Here's an example using this property:

    import javafx.animation.ScaleTransition;
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.layout.HBox;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    import java.util.ArrayList;
    
    public class Main extends Application {
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            var box = new HBox(createRectangles(Color.DARKBLUE, Color.FIREBRICK, 25));
            box.setAlignment(Pos.CENTER);
            box.setPadding(new Insets(50, 20, 50, 20));
            primaryStage.setScene(new Scene(box));
            primaryStage.show();
        }
    
        private Rectangle[] createRectangles(Color start, Color end, int count) {
            var list = new ArrayList<Rectangle>(count);
            for (double i = 0; i < count; i++) {
                var rect = new Rectangle(30, 60, start.interpolate(end, i / count));
    
                var scaleTrans = new ScaleTransition(Duration.millis(250), rect);
                scaleTrans.setFromX(1.0);
                scaleTrans.setFromY(1.0);
                scaleTrans.setToX(1.2);
                scaleTrans.setToY(1.2);
    
                rect.setOnMouseEntered(e -> {
                    scaleTrans.stop(); // <--- doesn't seem necessary*
                    scaleTrans.setRate(1.0);
                    rect.setViewOrder(-1.0);
                    scaleTrans.play();
                });
                rect.setOnMouseExited(e -> {
                    scaleTrans.stop(); // <--- doesn't seem necessary*
                    scaleTrans.setRate(-1.0);
                    rect.setViewOrder(0.0);
                    scaleTrans.play();
                });
                // *the "stop()"'s don't seem to be necessary. When I commented
                // them out the animation still worked. In fact, the animation
                // actually seems smoother in the situation where you move the
                // mouse over and then away quickly (before the zoom-in completes).
    
                list.add(rect);
            }
            return list.toArray(new Rectangle[0]);
        }
    
    }
    

    It uses Rectangles instead of ImageViews but the concept is the same. When the mouse hovers over a Rectangle it sets the view order to be lower than the others and then plays a ScaleTransition to make it bigger. When the mouse exits it resets the view order back to 0 and then reverses the ScaleTransition.

    Note: I used the var keyword which was added in Java 10.

    And here is a GIF of the example in action:

    the GIF of the application in action

    Edit: Since you brought up CSS I went and checked if the view order could be set from a stylesheet. And it appears it can. Looking at the CSS Reference Guide there is a CSS property defined for Node named -fx-view-order.