Locked History Actions

Diff for "JavaFX/RelocateAroundNode"

Differences between revisions 5 and 6
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= それを起動したボタンなどの近くにダイアログを表示する = この内容は以下に移動しました。
Line 3: Line 3:

Sometimes you want Window(Dialog or Stage) should be shown around a button which opened that Window.
So users can easily click the buttons in the Window without moving pointer long distance on the screen.
But you can't get size of the window before showAndWait() and you can't do anything after showAndWait().
Even if you use Platform.runLater(), it seems that the window will be shown at center of the screen in a short time,
and then it will be moved to the desired position.
Very tricky but the following code is the solution.

import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.stage.*;

public abstract class RelocateAroundNode {
  public static class ForStage extends RelocateAroundNode {
    private Stage stage;
    public ForStage(Node node, Stage stage) {
      super(node, stage.widthProperty(), stage.heightProperty());
      this.stage = stage;
      if (stage.isShowing()) {
    protected void setX(double x) { stage.setX(x); }
    protected void setY(double y) { stage.setY(y); }
  public static class ForDialog extends RelocateAroundNode {
    private Dialog<?> dialog;
    public ForDialog(Node node, Dialog<?> dialog) {
      super(node, dialog.widthProperty(), dialog.heightProperty());
      this.dialog = dialog;
      if (dialog.isShowing()) {
    protected void setX(double x) { dialog.setX(x); }
    protected void setY(double y) { dialog.setY(y); }
  private Node node;
  private ReadOnlyDoubleProperty windowWidthProperty;
  private ReadOnlyDoubleProperty windowHeightProperty;
  private ChangeListener<Number>changeListener = null;
  protected abstract void setX(double x);
  protected abstract void setY(double y);
  protected RelocateAroundNode(Node node, ReadOnlyDoubleProperty width, ReadOnlyDoubleProperty height) {
    this.node = node;
    this.windowWidthProperty = width;
    this.windowHeightProperty = height;
  protected void listenWindowSizeChange() {
    changeListener = new ChangeListener<Number>() {
      public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
  protected void windowSizeChanged() {
    if (Double.isNaN(windowWidthProperty.get())) return;
    if (Double.isNaN(windowHeightProperty.get())) return;
    changeListener = null;
  protected void relocateWindow() {
    double windowWidth = windowWidthProperty.get();
    double windowHeight = windowHeightProperty.get();
    Bounds nodeB = node.localToScreen(node.getBoundsInLocal());
    double nodeCenterX = (nodeB.getMinX() + nodeB.getMaxX()) / 2;
    double nodeCenterY = (nodeB.getMinY() + nodeB.getMaxY()) / 2;
    double windowNewX = nodeCenterX - windowWidth / 2;
    double windowNewY = nodeCenterY - windowHeight / 2;

    windowNewX = Math.max(0, windowNewX);
    windowNewX = Math.min(windowNewX, screenWidth - windowWidth);
    windowNewY = Math.max(0, windowNewY);
    windowNewY = Math.min(windowNewY, screenHeight - windowHeight);

  private static void getScreenSize() {
    if (vb == null) {
      vb = Screen.getPrimary().getVisualBounds();
      screenWidth = vb.getWidth();
      screenHeight = vb.getHeight();
  private static Rectangle2D vb;
  private static double screenWidth;
  private static double screenHeight;



Usage is the following.

      Alert alert = new Alert(AlertType.WARNING, "ok?", ButtonType.OK, ButtonType.CANCEL);
      new RelocateAroundNode.ForDialog(button, alert);