From 093775dc61578d0ed4567056c95e991ceeb8d897 Mon Sep 17 00:00:00 2001 From: Phil Bryant Date: Thu, 16 Apr 2026 07:42:04 -0500 Subject: [PATCH 1/3] BENTO-31: * Added top-level properties `sourceIsOwner` and `applyMousePosition` to `StageBuilding`. * Restore prior default values for `sourceIsOwner` and `applyMousePosition`. * Provides setters to change the `sourceIsOwner` and `applyMousePosition` properties. --- .../coley/bentofx/building/StageBuilding.java | 46 ++++--------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java index ae7129c8..f46c96d6 100644 --- a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java +++ b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java @@ -24,6 +24,8 @@ public class StageBuilding { private final Bento bento; private StageFactory stageFactory = DEFAULT_STAGE_FACTORY; private SceneFactory sceneFactory = DEFAULT_SCENE_FACTORY; + private boolean applyMousePosition = false; + private boolean sourceIsOwner = true; public StageBuilding(@NonNull Bento bento) { this.bento = bento; @@ -95,42 +97,6 @@ public DragDropStage newStageForDockable(@Nullable Scene sourceScene, @NonNull DockContainerLeaf leaf, @NonNull Dockable dockable, double width, double height) { - return newStageForDockable(sourceScene, root, leaf, dockable, width, height, false, true); - } - - /** - * Create a new stage for the given dockable., - * - * @param sourceScene - * Original scene to copy state from. - * @param root - * Newly created root branch to place into the resulting stage. - * @param leaf - * Newly created leaf container to place the dockable into. - * @param dockable - * Dockable to place into the newly created stage. - * @param width - * Preferred stage width. - * @param height - * Preferred stage height. - * @param sourceIsOwner - * {@code true} to invoke {@link Stage#initOwner(Window)}, where the - * owner is the source stage. - * @param applyMousePosition - * {@code true} to set the stage's X and Y positions to the position of - * the mouse when the new stage is created. - * - * @return Newly created stage. - */ - public DragDropStage newStageForDockable(@Nullable Scene sourceScene, - @NonNull DockContainerRootBranch root, - @NonNull DockContainerLeaf leaf, - @NonNull Dockable dockable, - double width, - double height, - boolean sourceIsOwner, - boolean applyMousePosition - ) { // Sanity check, leaf shouldn't have an existing parent. if (leaf.getParentContainer() != root && leaf.getParentContainer() != null) leaf.removeFromParent(); @@ -217,4 +183,12 @@ public void setSceneFactory(@Nullable SceneFactory factory) { factory = DEFAULT_SCENE_FACTORY; sceneFactory = factory; } + + public void setSourceIsOwner(boolean sourceIsOwner) { + this.sourceIsOwner = sourceIsOwner; + } + + public void setApplyMousePosition(boolean applyMousePosition) { + this.applyMousePosition = applyMousePosition; + } } From f2bc06ce38e6af35c40b74b976ea460cca7c2224 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 17 Apr 2026 11:45:55 -0400 Subject: [PATCH 2/3] Document stage-building options for mouse-positioning/source-window-ownership --- .../coley/bentofx/building/StageBuilding.java | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java index 42fc4dca..5d3131bc 100644 --- a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java +++ b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java @@ -1,8 +1,10 @@ package software.coley.bentofx.building; +import javafx.geometry.Point2D; import javafx.geometry.Side; import javafx.scene.Scene; import javafx.scene.layout.Region; +import javafx.scene.robot.Robot; import javafx.stage.Stage; import javafx.stage.Window; import org.jspecify.annotations.Nullable; @@ -22,8 +24,8 @@ public class StageBuilding { private final Bento bento; private StageFactory stageFactory = DEFAULT_STAGE_FACTORY; private SceneFactory sceneFactory = DEFAULT_SCENE_FACTORY; - private boolean applyMousePosition = false; - private boolean sourceIsOwner = true; + private boolean applyMousePosition = true; + private boolean applySourceAsOwner = true; public StageBuilding(Bento bento) { this.bento = bento; @@ -123,7 +125,15 @@ public DragDropStage newStageForDockable(@Nullable Scene sourceScene, // Copy properties from the source scene/stage. if (sourceScene != null) - initializeFromSource(sourceScene, scene, sourceStage, stage, true); + initializeFromSource(sourceScene, scene, sourceStage, stage, applySourceAsOwner); + + // Position the stage at the mouse position, if enabled. + if (applyMousePosition) { + final Robot robot = new Robot(); + final Point2D mousePosition = robot.getMousePosition(); + stage.setX(mousePosition.getX()); + stage.setY(mousePosition.getY()); + } return stage; } @@ -186,10 +196,23 @@ public void setSceneFactory(@Nullable SceneFactory factory) { sceneFactory = factory; } - public void setSourceIsOwner(boolean sourceIsOwner) { - this.sourceIsOwner = sourceIsOwner; + /** + * @param applySourceAsOwner + * {@code true} to make newly created stages have their owner set to the source stage the dockable is being dragged out of. + * {@code false} to not set an owner, allowing the new stage to be handled independently of the source stage. + * + * @see Stage#initOwner(Window) + */ + public void setApplySourceAsOwner(boolean applySourceAsOwner) { + this.applySourceAsOwner = applySourceAsOwner; } + /** + * + * @param applyMousePosition + * {@code true} to position newly created stages at the current mouse position. + * {@code false} to not apply any special positioning, allowing the stage to be positioned automatically (Generally centered). + */ public void setApplyMousePosition(boolean applyMousePosition) { this.applyMousePosition = applyMousePosition; } From 982015af833b68337a13cb4e21db0eb5b1f88921 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 17 Apr 2026 12:00:10 -0400 Subject: [PATCH 3/3] Prevent stage positioning resulting in inaccessible stage --- .../coley/bentofx/building/StageBuilding.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java index 5d3131bc..c13c0e23 100644 --- a/core/src/main/java/software/coley/bentofx/building/StageBuilding.java +++ b/core/src/main/java/software/coley/bentofx/building/StageBuilding.java @@ -1,10 +1,14 @@ package software.coley.bentofx.building; +import javafx.collections.ObservableList; +import javafx.geometry.Bounds; import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; import javafx.geometry.Side; import javafx.scene.Scene; import javafx.scene.layout.Region; import javafx.scene.robot.Robot; +import javafx.stage.Screen; import javafx.stage.Stage; import javafx.stage.Window; import org.jspecify.annotations.Nullable; @@ -24,7 +28,7 @@ public class StageBuilding { private final Bento bento; private StageFactory stageFactory = DEFAULT_STAGE_FACTORY; private SceneFactory sceneFactory = DEFAULT_SCENE_FACTORY; - private boolean applyMousePosition = true; + private boolean applyMousePosition = false; private boolean applySourceAsOwner = true; public StageBuilding(Bento bento) { @@ -131,8 +135,23 @@ public DragDropStage newStageForDockable(@Nullable Scene sourceScene, if (applyMousePosition) { final Robot robot = new Robot(); final Point2D mousePosition = robot.getMousePosition(); - stage.setX(mousePosition.getX()); - stage.setY(mousePosition.getY()); + + // Clamp the position to the screen bounds. + // We don't want a new stage that spawns on the bottom or right edge of the screen to be inaccessible. + double x = mousePosition.getX(); + double y = mousePosition.getY(); + ObservableList screens = Screen.getScreensForRectangle(x, y, x + 1, y + 1); + if (!screens.isEmpty()) { + final Bounds dockableContainerBounds = leaf.getBoundsInLocal(); + final Rectangle2D screenBounds = screens.getFirst().getVisualBounds(); + final double maxX = screenBounds.getMaxX() - dockableContainerBounds.getWidth(); + final double maxY = screenBounds.getMaxY() - dockableContainerBounds.getHeight(); + x = Math.min(x, maxX); + y = Math.min(y, maxY); + } + + stage.setX(x); + stage.setY(y); } return stage;