Отображение всплывающей подсказки в javafx приводит к выходу на передний план
Я пытаюсь обойти эту ошибку в jdk: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8088624
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("Click");
btn.setTooltip(new Tooltip("Blubb"));
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
}
}
Если кнопка на основной ступени зависнет, она появится перед вторым этапом. Я обнаружил, что вызов initOwner()
на сцене устраняет это поведение.
Теперь моя проблема следующая: у меня есть несколько "всплывающих окон", которые имеют общего владельца (основной этап). Наведение курсора на элементы управления на первичной стадии не вызывает неожиданного поведения после обходного пути initOwner()
. Если вы, однако, нависаете над элементами управления во всплывающем окне, а другое всплывающее окно находилось в фокусе, зависающее всплывающее окно будет красть фокус.
Есть ли способ обойти эту ошибку не только для основного этапа, но и для всплывающих окон?
UPDATE. Оказывается, у моего обходного пути есть нежелательные побочные эффекты. Javadocs для состояния Stage:
Этап всегда будет находиться над его родительским окном.
Итак, что было бы обходным путем, которое делает всплывающее окно "всегда сверху" и минимизируемым?
Ответы
Ответ 1
Есть способ обойти его, наложив StackPanes. Создайте Scene
с помощью StackPane
, чтобы добавить еще один StackPane
, когда эта сцена потеряла фокус. Наложенная панель предотвратит Tooltip
или что-то еще, что происходит при наведении мыши, пока панель не находится в фокусе. Вы также можете свести к минимуму любые ваши этапы, и они не будут всегда на высоте.
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button button_1 = new Button("Button #1");
button_1.setTooltip(new Tooltip("Blubb #1"));
StackPane primary = new StackPane(new BorderPane(button_1));
primaryStage.setScene(new Scene(primary, 320, 240));
addStageFocusListener(primaryStage, primary);
primaryStage.show();
Button button_2 = new Button("Button #2");
button_2.setTooltip(new Tooltip("Blubb #2"));
StackPane second = new StackPane(new BorderPane(button_2));
Stage secondStage = new Stage();
addStageFocusListener(secondStage, second);
secondStage.setScene(new Scene(second, 320, 240));
secondStage.show();
}
public void addStageFocusListener(Stage stage, StackPane stackPane) {
stage.focusedProperty().addListener(new ChangeListener<Boolean>(){
public final StackPane preventTooltip = new StackPane();
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(stage.isFocused()) {
if(stackPane.getChildren().contains(preventTooltip)) {
stackPane.getChildren().remove(preventTooltip);
}
} else {
stackPane.getChildren().add(preventTooltip);
}
}
});
}
}
Ответ 2
Вы можете попробовать следующее:
public static final disableMouseEventOnUnfocus(final Stage stage)
{
if (stage == null
|| stage.getScene() == null
|| stage.getScene().getRoot() == null)
return;
else
{
stage.getScene().getRoot().mouseTransparentProperty().bind(stage.focusedProperty().not());
}
}
Я не пробовал, но если это работает, это должна быть хорошая альтернатива. Нет необходимости реструктурировать макет, и вы можете оставить все свои макеты в FXML, не указав fx:id
для всплывающих подсказок.
Ответ 3
Я придумал это альтернативное решение, поскольку мне было легче в моем случае подклассифицировать подсказку и применить исправление. Я просто перегружаю метод show(), чтобы показать, только ли окно с привязкой. Он работает как прелесть для меня...
public class FixedTooltip extends Tooltip {
public FixedTooltip(String string) {
super(string);
}
@Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused())
super.show();
}
}
Ответ 4
Вы можете попытаться отключить всплывающую подсказку, когда окно node теряет фокус. Например, ниже:
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
public static void installTooltip(Node n, Tooltip tp)
{
Window w = n.getScene().getWindow();
w.focusedProperty().addListener((val, before, after) -> {
if (after)
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
});
if (w.isFocused())
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
}
@Override
public void start(Stage primaryStage) throws Exception {
Tooltip tp = new Tooltip("Blubb");
Button btn = new Button("Click");
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
//primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
primaryStage.show();
installTooltip(btn, tp);
}
}
Конечно, вам нужно будет вызвать installTooltip
после добавления node к компоненту.