JavaFX : How to manage the z-index of stages
Asked Answered
M

1

7

Is there any way to manage the z-index ordering of multiple stages (independent to each other). Something like, say there are three Stages A, B & C. StageA should stay always at back. StageB should be in middle and StageC should be always on top. Just a special note that these three stages have no relation to each other (like owner)

Below is a quick demo of what I am expecting. I need to access any stage (for dragging or modifying) but z-order need to be maintained. Any ideas or help is highly appreciated.

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.HashMap;
import java.util.Map;

public class StagesZOrdering_Demo extends Application {
    private Map<String, Stage> stages = new HashMap<>();

    @Override
    public void start(Stage stage) throws Exception {
        Button button1 = new Button("Back");
        button1.setOnAction(e -> openStage("Back"));

        Button button2 = new Button("Middle");
        button2.setOnAction(e -> openStage("Middle"));

        Button button3 = new Button("Front");
        button3.setOnAction(e -> openStage("Front"));

        VBox root = new VBox(button1, button2, button3);
        root.setAlignment(Pos.CENTER);
        root.setSpacing(10);
        Scene sc = new Scene(root, 200, 200);
        stage.setScene(sc);
        stage.show();
    }

    private void openStage(String title) {
        if (stages.get(title) != null) {
            stages.get(title).requestFocus();
        } else {
            Stage stg = new Stage();
            stg.setTitle(title);
            stg.setScene(new Scene(new StackPane(), 300, 300, Color.GRAY));
            stg.show();
            stg.setOnHidden(e -> stages.remove(title));
            stages.put(title, stg);
        }
    }

    public static void main(String... a) {
        Application.launch(a);
    }
}
Mover answered 22/7, 2019 at 23:50 Comment(4)
Pretty sure the API doesn't allow that out-of-the-box. The only chance is to go down to native JNA. As far as I can remember, Windows (not sure of other OS) doesn't seem to have that function directly out from the box via DLL either, the nearest thing would be the global "always on top" function.Lathy
I think the closest you could get with Java would be to maintain the desired order of the stages in a List and listen for when a stage is dragged; when it's released, refocus all the stages in the proper order to reset their z-order. That's just a quick thought off the top of my head, though.Hillari
Do you want to prevent say "middle" stage from gaining focus or want it go back to middle when you are done with it ? Also can you use an internal frame ?Ent
Thank you Jai & Zephyr for your inputs. I tried working with toFront & toBack but couldnt get the exact behavior. Will keep trying with other approaches.Mover
E
2

The following mcve demonstrates re-ordering of back-to-front stages, once a ROOT MOUSE_EXITED_TARGET event is fired from one of them.
It is a simple yet limited solution:

import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventType;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class StagesZOrdering_Demo extends Application {

    public enum STAGES {BACK, MIDDLE, FRONT;}
    private final EnumMap<STAGES, Stage> stages = new EnumMap<>(STAGES.class);

    @Override
    public void start(Stage stage) throws Exception {

        VBox root = new VBox();
        for(STAGES s : STAGES.values()){
            Button button = new Button(s.name());
            button.setOnAction(e -> openStage(s));
            root.getChildren().add(button);
        }

        root.setAlignment(Pos.CENTER);
        root.setSpacing(10);
        Scene sc = new Scene(root, 200, 200);
        stage.setScene(sc);
        stage.show();
    }

    private void openStage(STAGES s) {
        if (stages.get(s) == null) {
            Stage stg = new Stage();
            stg.setTitle(s.name());
            stg.addEventHandler(EventType.ROOT, e->reOrder(e));
            stg.setScene(new Scene(new StackPane(), 300, 100, Color.GRAY));
            stg.show();
            stg.setOnHidden(e -> stages.remove(s));
            stages.put(s, stg);
        }
    }

    private void reOrder(Event e){
        if(! e.getEventType().getName().equals("MOUSE_EXITED_TARGET"))
            return;

        for(STAGES s : STAGES.values()){
            Stage stage = stages.get(s);
            if(stage != null) {
                stage.requestFocus();
            }
        }
    }

    public static void main(String... a) {
        Application.launch(a);
    }
}

enter image description here

Ent answered 23/7, 2019 at 7:26 Comment(1)
Thanks @Ent for suggesting an alternate approach. This looks promising than the approaches I tried so far. Will wait a week if I can get more suggestions,.otherwise will mark this as answered. :)Mover

© 2022 - 2024 — McMap. All rights reserved.