Revision 1 as of 2016-12-07 09:04:04

Clear message
Locked History Actions

JavaFX/RelocateAroundNode

それを起動したボタンなどの近くにダイアログを表示する

import javafx.beans.property.*;
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()) {
        relocateWindow();
        return;
      }
    }
    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<?> stage) {
      super(node, stage.widthProperty(), stage.heightProperty());
      this.dialog = stage;
      if (stage.isShowing()) {
        relocateWindow();
        return;
      }
    }
    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;
  
  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;
    width.addListener((o, ol, ne)-> windowSizeChanged());
    height.addListener((o, ol, ne)-> windowSizeChanged());
  }
  
  protected void windowSizeChanged() {
    if (Double.isNaN(windowWidthProperty.get())) return;
    if (Double.isNaN(windowHeightProperty.get())) return;
    relocateWindow();    
  }
  
  protected void relocateWindow() {
    getScreenSize();
    
    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);
    
    setX(windowNewX);
    setY(windowNewY); 
  }

  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;
}

以下のように使用する。

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