6

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);
    }
}
Sai Dandem
  • 8,229
  • 11
  • 26
  • 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. – Jai Jul 23 '19 at 01:43
  • 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. – Zephyr Jul 23 '19 at 02:59
  • 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](https://stackoverflow.com/questions/17673292/internal-frames-in-javafx) ? – c0der Jul 23 '19 at 06:08
  • 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. – Sai Dandem Jul 24 '19 at 01:39

1 Answers1

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

c0der
  • 18,467
  • 6
  • 33
  • 65
  • Thanks @c0der 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. :) – Sai Dandem Jul 24 '19 at 01:43