Как вывести JavaFX Popup на передний план? - PullRequest
0 голосов
/ 14 января 2019

У меня в приложении есть JavaFX Popup. И когда любое из этих всплывающих окон находится в фокусе, мне нужно, чтобы оно появлялось поверх всех остальных всплывающих окон, независимо от их индекса в Window.getWindows().

Я пытался вызвать метод как toFront, но он не в Popup классе. Я также пытался изменить индекс сфокусированного Popup в Window.getWindows(), но это также не сработало, потому что я не знаю, как поменять индекс двух элементов в ObservableList.

например,

Допустим, у меня есть два Popup, называемые p1 и p2, и в каждом из них есть узлы n1 и n2 соответственно, которые используются для перемещения этих всплывающих окон, поэтому при перетаскивании n1 p1 должен прийти сверху, а когда n2 перетащен p2 должен прийти сверху.

Вот мой минимальный пример:

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Popup;
import javafx.stage.Stage;

public class Example extends Application{

    public static void main(String... arguments){

        launch(arguments);
    }

    public void applyTo(Pane node, Popup parent){

        final double[] dragDelta = new double[2];

        node.setOnMousePressed(e -> {
            dragDelta[0] = parent.getX() - e.getScreenX();
            dragDelta[1] = parent.getY() - e.getScreenY();
            //code to bring parent Popup to front
        });

        node.setOnMouseDragged(e -> {
            parent.setX(e.getScreenX() + dragDelta[0]);
            parent.setY(e.getScreenY() + dragDelta[1]);
        });
    }

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

        Button b1 = new Button("Open p1");
        Button b2 = new Button("Open p2");

        HBox n1 = new HBox(new Label("This is p1"));
        HBox n2 = new HBox(new Label("This is p2"));
        n1.setMinSize(200, 120);
        n2.setMinSize(200, 120);
        n1.setStyle("-fx-background-color: blue; -fx-background-radius: 4px;");
        n2.setStyle("-fx-background-color: red; -fx-background-radius: 4px;");
        n1.setAlignment(Pos.CENTER);
        n2.setAlignment(Pos.CENTER);

        Popup p1 = new Popup();
        Popup p2 = new Popup();
        p1.getContent().add(n1);
        p2.getContent().add(n2);

        applyTo(n1, p1);
        applyTo(n2, p2);

        b1.setOnAction(event -> {
            if(!p1.isShowing()) p1.show(primaryStage);
            else p1.hide();
        });
        b2.setOnAction(event -> {
            if(!p2.isShowing()) p2.show(primaryStage);
            else p2.hide();
        });

        HBox root = new HBox(10, b1, b2);
        root.setAlignment(Pos.CENTER);

        primaryStage.setScene(new Scene(root, 500, 200));
        primaryStage.show();
    }

}

Так, каково решение этой проблемы?

Ответы [ 2 ]

0 голосов
/ 14 января 2019

По какой-то причине я не понимаю, toFront / back реализован только в Stage, а не в его родительских классах, хотя фактический соавтор, который управляет укладкой, уже доступен в Window:

Реализация в стадии:

/**
 * Bring the {@code Window} to the foreground.  If the {@code Window} is
 * already in the foreground there is no visible difference.
 */
public void toFront() {
    if (getPeer() != null) {
        getPeer().toFront();
    }
}

getPeer() это закрытый для пакета метод в Window, который возвращает внутренний класс TKStage. Так что, если вам позволят испортиться (потому что доступ к внутреннему классу и необходимость доступа через отражение - все с обычным громким «Осторожно»!) Будет:

protected void toFront(Popup popup) {
    // use your favorite utility method to invoke a method  
    TKStage peer = (TKStage) FXUtils.invokeGetMethodValue(Window.class, popup, "getPeer");
    if (peer != null) {
        peer.toFront();
    }
}

Требуется экспортировать / открывать неэкспортированные пакеты в javafx.graphics - ошибки компилятора и среды выполнения помогут вам (в любом случае мой контекст сильно подправлен, поэтому не знаю точно, какие из них добавлены)

0 голосов
/ 14 января 2019

Вот решение с этапами, это единственная работа, которую я когда-либо нашел, даже если вы ненавидите идею иметь несколько этапов, если вам нужна функциональность, вот она. Если вы решили оставить их на заднем плане, это тоже круто. Идея решения вашей дилеммы с этапами также заключается в использовании очереди этапов удаления при использовании и, если все показано, добавьте новый, когда этап скрыт, отправьте в конец очереди

public class Example extends Application {


    public void applyTo(Pane node, Stage parent, Stage primaryStage){

        final double[] dragDelta = new double[2];

        node.setOnMousePressed(e -> {
            dragDelta[0] = parent.getX() - e.getScreenX();
            dragDelta[1] = parent.getY() - e.getScreenY();
            //code to bring parent Popup to front
        });

        node.setOnMouseDragged(e -> {
            parent.setX(e.getScreenX() + dragDelta[0]);
            parent.setY(e.getScreenY() + dragDelta[1]);
            primaryStage.requestFocus();
        });

        node.setOnMouseReleased(event -> {
            primaryStage.requestFocus();
        });
    }


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

        Button b1 = new Button("Open p1");
        Button b2 = new Button("Open p2");

        HBox n1 = new HBox(new Label("This is p1"));
        HBox n2 = new HBox(new Label("This is p2"));
        n1.setMinSize(200, 120);
        n2.setMinSize(200, 120);
        n1.setStyle("-fx-background-color: blue; -fx-background-radius: 4px;");
        n2.setStyle("-fx-background-color: red; -fx-background-radius: 4px;");
        n1.setAlignment(Pos.CENTER);
        n2.setAlignment(Pos.CENTER);

        Stage p1 = new Stage(StageStyle.UNDECORATED);
        Stage p2 = new Stage(StageStyle.UNDECORATED);
        p1.setAlwaysOnTop(true);
        p2.setAlwaysOnTop(true);
        p1.setScene(new Scene(n1));
        p2.setScene(new Scene(n2));

        applyTo(n1, p1, primaryStage);
        applyTo(n2, p2, primaryStage);

        b1.setOnAction(event -> {
            if(!p1.isShowing()) {
                p1.show();
                primaryStage.requestFocus();
            }
            else
                p1.hide();
        });

        b2.setOnAction(event -> {
            if(!p2.isShowing()) {
                p2.show();
                primaryStage.requestFocus();
            }
            else
                p2.hide();
        });



        HBox root = new HBox(10, b1, b2);
        root.setAlignment(Pos.CENTER);

        primaryStage.setScene(new Scene(root, 500, 200));
        primaryStage.show();
    }

    public static void main(String[] args) { launch(args); }

}
...