package application.displaybehaviour; import application.constants.ApplicationConstants; import application.model.ModelConfigObject; import application.utils.GuiUtils; import javafx.beans.binding.StringExpression; import javafx.beans.property.BooleanProperty; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.stage.Stage; public class DetachableDisplayBehaviour implements DisplayBehaviour { private static final int DEFAULT_WIDHT = 1024; private static final int DEFAULT_HEIGHT = 768; private static final int MIN_WIDHT = 600; private static final int MIN_HEIGHT = 400; private static final double POSITION_NOT_SET = -1; private final AnchorPane parentPane; private final Pane childNode; private final BooleanProperty isDetachedProperty; private final ModelConfigObject trackedModeObject; private final DetachedPaneContent detachedPaneContent; private StringExpression windowTitleProperty; private ModelConfigObjectsGuiInformer guiInformer; private final Stage stage = new Stage(); private double sceneWidth = DEFAULT_WIDHT; private double sceneHeight = DEFAULT_HEIGHT; private double positionX = POSITION_NOT_SET; private double positionY = POSITION_NOT_SET; private Scene scene; public DetachableDisplayBehaviour(AnchorPane parentPane, StringExpression windowTitleProperty, Pane childNode, BooleanProperty isDetachedProperty, ModelConfigObject trackedModeObject, ModelConfigObjectsGuiInformer guiInformer) { this.parentPane = parentPane; this.windowTitleProperty = windowTitleProperty; this.childNode = childNode; this.isDetachedProperty = isDetachedProperty; this.trackedModeObject = trackedModeObject; this.guiInformer = guiInformer; setStageAppearance(); setDetachPropertyListener(); setStageOnCloseEvent(); setSizeChangedListeners(); setPositionChangedListeners(); setLastElementRemovedListener(); detachedPaneContent = new DetachedPaneContent(this::reattachPaneCallback); } @Override public void display() { if (isDetachedProperty.getValue()) { detachFromParentPane(); displayOnOwnStage(); } else { displayAttachedToParentPane(); } } private void reattachPaneCallback() { isDetachedProperty.setValue(false); } private void setStageAppearance() { GuiUtils.addApplicationIcon(stage); stage.titleProperty().bind(windowTitleProperty); stage.setMinHeight(MIN_HEIGHT); stage.setMinWidth(MIN_WIDHT); } private void setLastElementRemovedListener() { guiInformer.lastRemovedObjectProperty().addListener((observable, oldValue, newValue) -> { if (newValue == trackedModeObject) { // element (modelObject) for this view is just removed // if we are in detached state then closeOldDependencies the window detachFromOwnStage(); } }); } private void setPositionChangedListeners() { stage.xProperty().addListener((observable, oldValue, newValue) -> positionX = newValue.doubleValue()); stage.yProperty().addListener((observable, oldValue, newValue) -> positionY = newValue.doubleValue()); } private void setSizeChangedListeners() { stage.heightProperty().addListener((observable, oldValue, newValue) -> sceneHeight = newValue.doubleValue()); stage.widthProperty().addListener((observable, oldValue, newValue) -> sceneWidth = newValue.doubleValue()); } private void setStageOnCloseEvent() { stage.setOnCloseRequest(windowEvent -> isDetachedProperty.setValue(false)); } private void setDetachPropertyListener() { isDetachedProperty.addListener((observable, oldValue, newValue) -> { display(); }); } private void detachFromParentPane() { GuiUtils.setOnParentPane(parentPane, detachedPaneContent); } private void displayOnOwnStage() { initSceneIfNeeded(); stage.show(); } private void initSceneIfNeeded() { if (scene == null) { scene = new Scene(childNode, sceneWidth, sceneHeight); GuiUtils.loadCssIfPossible(scene, ApplicationConstants.GLOBAL_CSS_FILE_NAME); } if (positionX != POSITION_NOT_SET && positionY != POSITION_NOT_SET) { stage.setX(positionX); stage.setY(positionY); } stage.setScene(scene); } private void displayAttachedToParentPane() { detachFromOwnStage(); if (isMyTrackedModelObjectSelectedAndFocused()) { attachChildToParent(); } } private void detachFromOwnStage() { scene = null; stage.setScene(null); stage.close(); } private boolean isMyTrackedModelObjectSelectedAndFocused() { return guiInformer.selectedObject() == trackedModeObject; } private void attachChildToParent() { GuiUtils.setOnParentPane(parentPane, childNode); } }