JavaFX: как закрыть подокно, не фокусируясь на главном окне - PullRequest
6 голосов
/ 02 июля 2019

Я пытаюсь закрыть подокно программно через определенное время.Это подокно initOwner устанавливается с главной сценой.Но при закрытии этого подокна главное окно становится сфокусированным.Есть ли способ закрыть подокно (программно), не фокусируясь на главном окне?

Ниже приведена краткая демонстрация моей проблемы.Я перепробовал все возможные способы закрыть окно.Шаги для воспроизведения:

  1. После запуска приложения нажмите кнопку, чтобы открыть подокно.Это подокно автоматически закроется через 10 секунд.

  2. В то же время откройте любое другое приложение (блокнот, Outlook, браузер и т. Д.).Пока вы работаете над этим приложением, когда вспомогательное окно закрыто, основной этап становится фокусом и появляется перед моим текущим приложением.Это довольно раздражает моего клиента.

Примечание: я не могу удалить initOwner (), так как я всегда хочу держать свое подокно поверх главного окна.

Обновление: На основании комментариев я попытался запустить демонстрацию с различными версиями jdk (u91, u121 и u211) и в Windows 10. Во всех трехВ случаях, когда подокно закрыто, основная сцена выходит на передний план.Я даже пытался в другой системе, но результаты те же: (

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;

public class OwnerStage_Demo extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Button button = new Button("Open Window");
        button.setOnAction(e -> {
            Stage stg = new Stage();
            stg.setScene(new Scene(new StackPane(), 300, 300));
            stg.initOwner(stage);
            stg.show();
            // Window will close automatically after 10secs.
            Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
                //stg.close();
                //stg.hide();
                stg.fireEvent(new WindowEvent(stg, WindowEvent.WINDOW_CLOSE_REQUEST));
            }));
            timeline.setCycleCount(1);
            timeline.play();
        });
        VBox root = new VBox(button);
        root.setSpacing(10);
        Scene sc = new Scene(root, 600, 600);
        stage.setScene(sc);
        stage.show();
    }

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

Обновление: прикрепленный gif, демонстрирующий проблему. enter image description here

1 Ответ

1 голос
/ 05 июля 2019

Как окно получает фокус, зависит от платформы (OS + JRE). Платформа обрабатывает сфокусированное окно, поэтому после вызова фокуса окно может работать по-разному в разных ОС.

Невозможно добиться требуемого поведения с помощью чистого JFX из-за установленного вами ограничения:

Примечание: я не могу удалить initOwner (), так как я всегда хочу держать свое подокно поверх главного окна.

com.sun.javafx.tk.quantum.WindowStage

if (!isPopupStage && owner != null && owner instanceof WindowStage) {
    WindowStage ownerStage = (WindowStage)owner;
    ownerStage.requestToFront();
}

То, что вы можете сделать, это подражать owner window <- child window отношениям без инициализации реального владельца.

* Источник: 1017 *

public class PlainZStage extends Stage {

    public PlainZStage(final Window owner) {
        init(owner, this::focusedChanged);
    }

    private void init(final Window owner, final ChangeListener<Boolean> listener) {
        showingProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue,
                    final Boolean newValue) {
                owner.getScene().getRoot().setDisable(newValue);
                if (newValue) {
                    owner.focusedProperty().addListener(listener);
                } else {
                    owner.focusedProperty().removeListener(listener);
                    showingProperty().removeListener(this);
                }
            }
        });
    }

    private void focusedChanged(final ObservableValue<? extends Boolean> source, final Boolean oldValue,
            final Boolean newValue) {
        if (newValue && isShowing()) {
            toFront();
        }
    }
}

Использование:

button.setOnAction(e -> {
    final Stage stg = new PlainZStage(stage);
    stg.setScene(new Scene(new StackPane(), 300, 300));
    stg.show();
    // Window will close automatically after 10secs.
    final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
        stg.close();
    }));

Кроме того, вы можете комбинировать JFX и SWING для фильтрации событий фокуса, но вы столкнетесь с чистым архитектурным злом:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...