changeset 5055:610ea3cbc099

Fix for RT-29891: PopupWindow reports incorrect width and height
author Lubomir Nerad <lubomir.nerad@oracle.com>
date Thu, 12 Sep 2013 10:09:43 +0200
parents 7a302d988f83
children 6a2c7c339d17
files modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ComboBoxPopupControl.java modules/controls/src/main/java/javafx/scene/control/PopupControl.java modules/controls/src/main/java/javafx/scene/control/Tooltip.java modules/graphics/src/main/java/com/sun/javafx/scene/SceneHelper.java modules/graphics/src/main/java/com/sun/javafx/stage/PopupWindowPeerListener.java modules/graphics/src/main/java/com/sun/javafx/stage/WindowHelper.java modules/graphics/src/main/java/com/sun/javafx/stage/WindowPeerListener.java modules/graphics/src/main/java/javafx/scene/Scene.java modules/graphics/src/main/java/javafx/stage/PopupWindow.java modules/graphics/src/main/java/javafx/stage/Window.java modules/graphics/src/test/java/javafx/stage/PopupTest.java modules/graphics/src/test/java/javafx/stage/WindowTest.java
diffstat 12 files changed, 550 insertions(+), 370 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ComboBoxPopupControl.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ComboBoxPopupControl.java	Thu Sep 12 10:09:43 2013 +0200
@@ -155,8 +155,8 @@
         final double minWidth = popupContent.prefWidth(1);
         final double minHeight = popupContent.prefHeight(1);
 
-        if (p.getX() > -1) popupControl.setX(p.getX());
-        if (p.getY() > -1) popupControl.setY(p.getY());
+        if (p.getX() > -1) popupControl.setAnchorX(p.getX());
+        if (p.getY() > -1) popupControl.setAnchorY(p.getY());
         if (minWidth > -1) popupControl.setMinWidth(minWidth);
         if (minHeight > -1) popupControl.setMinHeight(minHeight);
 
--- a/modules/controls/src/main/java/javafx/scene/control/PopupControl.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/controls/src/main/java/javafx/scene/control/PopupControl.java	Thu Sep 12 10:09:43 2013 +0200
@@ -118,6 +118,7 @@
         bridge.idProperty().bind(idProperty());
         bridge.styleProperty().bind(styleProperty());
 
+        setAnchorLocation(AnchorLocation.CONTENT_TOP_LEFT);
         getContent().add(bridge);
 
         // TODO the fact that PopupWindow uses a group for auto-moving things
--- a/modules/controls/src/main/java/javafx/scene/control/Tooltip.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/controls/src/main/java/javafx/scene/control/Tooltip.java	Thu Sep 12 10:09:43 2013 +0200
@@ -190,8 +190,8 @@
         if (isShowing() && value != null && !value.equals(getText())) {
             //Dynamic tooltip content is location-dependant.
             //Chromium trick.
-            setX(BEHAVIOR.lastMouseX);
-            setY(BEHAVIOR.lastMouseY);
+            setAnchorX(BEHAVIOR.lastMouseX);
+            setAnchorY(BEHAVIOR.lastMouseY);
         }
         textProperty().setValue(value);
     }
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/SceneHelper.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/SceneHelper.java	Thu Sep 12 10:09:43 2013 +0200
@@ -26,6 +26,7 @@
 package com.sun.javafx.scene;
 
 import javafx.scene.Camera;
+import javafx.scene.Parent;
 import javafx.scene.Scene;
 
 /**
@@ -54,10 +55,8 @@
         return sceneAccessor.getEffectiveCamera(scene);
     }
 
-    public static void setSceneDelta(final Scene scene,
-                                     final double deltaX,
-                                     final double deltaY) {
-        sceneAccessor.setSceneDelta(scene, deltaX, deltaY);
+    public static Scene createPopupScene(final Parent root) {
+        return sceneAccessor.createPopupScene(root);
     }
 
     public static void setSceneAccessor(final SceneAccessor newAccessor) {
@@ -75,7 +74,7 @@
 
         Camera getEffectiveCamera(Scene scene);
 
-        void setSceneDelta(Scene scene, double deltaX, double deltaY);
+        Scene createPopupScene(Parent root);
     }
 
     private static void forceInit(final Class<?> classToInit) {
--- a/modules/graphics/src/main/java/com/sun/javafx/stage/PopupWindowPeerListener.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/com/sun/javafx/stage/PopupWindowPeerListener.java	Thu Sep 12 10:09:43 2013 +0200
@@ -51,9 +51,6 @@
     public void closing() {
     }
 
-    public void changedLocation(float x, float y) {
-    }
-
     public void changedIconified(boolean iconified) {
         // Not applicable for popups
     }
--- a/modules/graphics/src/main/java/com/sun/javafx/stage/WindowHelper.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/com/sun/javafx/stage/WindowHelper.java	Thu Sep 12 10:09:43 2013 +0200
@@ -42,16 +42,16 @@
     private WindowHelper() {
     }
 
-    public static void setLocation(final Window window,
-                                   final double x,
-                                   final double y) {
-        windowAccessor.setLocation(window, x, y);
+    public static void notifyLocationChanged(final Window window,
+                                             final double x,
+                                             final double y) {
+        windowAccessor.notifyLocationChanged(window, x, y);
     }
 
-    public static void setSize(final Window window,
-                               final double width,
-                               final double height) {
-        windowAccessor.setSize(window, width, height);
+    public static void notifySizeChanged(final Window window,
+                                         final double width,
+                                         final double height) {
+        windowAccessor.notifySizeChanged(window, width, height);
     }
 
     static AccessControlContext getAccessControlContext(Window window) {
@@ -67,9 +67,9 @@
     }
 
     public interface WindowAccessor {
-        void setLocation(Window window, double x, double y);
+        void notifyLocationChanged(Window window, double x, double y);
 
-        void setSize(Window window, double width, double height);
+        void notifySizeChanged(Window window, double width, double height);
 
         AccessControlContext getAccessControlContext(Window window);
     }
--- a/modules/graphics/src/main/java/com/sun/javafx/stage/WindowPeerListener.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/com/sun/javafx/stage/WindowPeerListener.java	Thu Sep 12 10:09:43 2013 +0200
@@ -46,12 +46,12 @@
 
     @Override
     public void changedLocation(float x, float y) {
-        WindowHelper.setLocation(window, x, y);
+        WindowHelper.notifyLocationChanged(window, x, y);
     }
 
     @Override
     public void changedSize(float width, float height) {
-        WindowHelper.setSize(window, width, height);
+        WindowHelper.notifySizeChanged(window, width, height);
     }
 
     public void changedFocused(boolean focused, FocusCause cause) {
--- a/modules/graphics/src/main/java/javafx/scene/Scene.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/scene/Scene.java	Thu Sep 12 10:09:43 2013 +0200
@@ -415,10 +415,21 @@
                         }
 
                         @Override
-                        public void setSceneDelta(Scene scene,
-                                                  double deltaX,
-                                                  double deltaY) {
-                            scene.setSceneDelta(deltaX, deltaY);
+                        public Scene createPopupScene(Parent root) {
+                            return new Scene(root) {
+                                       @Override
+                                       void doLayoutPass() {
+                                           resizeRootToPreferredSize(getRoot());
+                                           super.doLayoutPass();
+                                       }
+
+                                       @Override
+                                       void resizeRootOnSceneSizeChange(
+                                               double newWidth,
+                                               double newHeight) {
+                                           // don't resize
+                                       }
+                                   };
                         }
                     });
         }
@@ -542,7 +553,7 @@
         }
     }
 
-    private void doLayoutPass() {
+    void doLayoutPass() {
         final Parent r = getRoot();
         if (r != null) {
             r.layout();
@@ -814,7 +825,7 @@
                         _root.impl_transformsChanged();
                     }
                     if (_root.isResizable()) {
-                        _root.resize(get() - _root.getLayoutX() - _root.getTranslateX(), _root.getLayoutBounds().getHeight());
+                        resizeRootOnSceneSizeChange(get() - _root.getLayoutX() - _root.getTranslateX(), _root.getLayoutBounds().getHeight());
                     }
 
                     getEffectiveCamera().setViewWidth(get());
@@ -859,7 +870,7 @@
                 protected void invalidated() {
                     final Parent _root = getRoot();
                     if (_root.isResizable()) {
-                        _root.resize(_root.getLayoutBounds().getWidth(), get() - _root.getLayoutY() - _root.getTranslateY());
+                        resizeRootOnSceneSizeChange(_root.getLayoutBounds().getWidth(), get() - _root.getLayoutY() - _root.getTranslateY());
                     }
 
                     getEffectiveCamera().setViewHeight(get());
@@ -879,17 +890,8 @@
         return height;
     }
 
-    private double sceneDeltaX;
-    private double sceneDeltaY;
-
-    private void setSceneDelta(final double deltaX, final double deltaY) {
-        if ((sceneDeltaX != deltaX) || (sceneDeltaY != deltaY)) {
-            setX(getX() - sceneDeltaX + deltaX);
-            setY(getY() - sceneDeltaY + deltaY);
-
-            sceneDeltaX = deltaX;
-            sceneDeltaY = deltaY;
-        }
+    void resizeRootOnSceneSizeChange(double newWidth, double newHeight) {
+        getRoot().resize(newWidth, newHeight);
     }
 
     // Reusable target wrapper (to avoid creating new one for each picking)
@@ -1499,54 +1501,17 @@
         // scene
         doCSSPass();
 
-        boolean computeWidth = false;
-        boolean computeHeight = false;
-
-        double rootWidth = widthSetByUser;
-        double rootHeight = heightSetByUser;
+        resizeRootToPreferredSize(root);
+        doLayoutPass();
 
         if (widthSetByUser < 0) {
-            rootWidth = root.prefWidth(heightSetByUser >= 0 ? heightSetByUser : -1);
-            rootWidth = root.boundedSize(rootWidth,
-                    root.minWidth(heightSetByUser >= 0 ? heightSetByUser : -1),
-                    root.maxWidth(heightSetByUser >= 0 ? heightSetByUser : -1));
-            computeWidth = true;
-        }
-        if (heightSetByUser < 0) {
-            rootHeight = root.prefHeight(widthSetByUser >= 0 ? widthSetByUser : -1);
-            rootHeight = root.boundedSize(rootHeight,
-                    root.minHeight(widthSetByUser >= 0 ? widthSetByUser : -1),
-                    root.maxHeight(widthSetByUser >= 0 ? widthSetByUser : -1));
-            computeHeight = true;
-        }
-        if (root.getContentBias() == Orientation.HORIZONTAL) {
-            if (heightSetByUser < 0) {
-                rootHeight = root.boundedSize(
-                        root.prefHeight(rootWidth),
-                        root.minHeight(rootWidth),
-                        root.maxHeight(rootWidth));
-                computeHeight = true;
-            }
-        } else if (root.getContentBias() == Orientation.VERTICAL) {
-            if (widthSetByUser < 0) {
-                rootWidth = root.boundedSize(
-                        root.prefWidth(rootHeight),
-                        root.minWidth(rootHeight),
-                        root.maxWidth(rootHeight));
-                computeWidth = true;
-            }
-        }
-        root.resize(rootWidth, rootHeight);
-        doLayoutPass();
-
-        if (computeWidth) {
             setWidth(root.isResizable()? root.getLayoutX() + root.getTranslateX() + root.getLayoutBounds().getWidth() :
                             root.getBoundsInParent().getMaxX());
         } else {
             setWidth(widthSetByUser);
         }
 
-        if (computeHeight) {
+        if (heightSetByUser < 0) {
             setHeight(root.isResizable()? root.getLayoutY() + root.getTranslateY() + root.getLayoutBounds().getHeight() :
                             root.getBoundsInParent().getMaxY());
         } else {
@@ -1558,6 +1523,53 @@
         PerformanceTracker.logEvent("Scene preferred bounds computation complete");
     }
 
+    final void resizeRootToPreferredSize(Parent root) {
+        final double preferredWidth;
+        final double preferredHeight;
+
+        final Orientation contentBias = root.getContentBias();
+        if (contentBias == null) {
+            preferredWidth = getPreferredWidth(root, widthSetByUser, -1);
+            preferredHeight = getPreferredHeight(root, heightSetByUser, -1);
+        } else if (contentBias == Orientation.HORIZONTAL) {
+            // height depends on width
+            preferredWidth = getPreferredWidth(root, widthSetByUser, -1);
+            preferredHeight = getPreferredHeight(root, heightSetByUser,
+                                                       preferredWidth);
+        } else /* if (contentBias == Orientation.VERTICAL) */ {
+            // width depends on height
+            preferredHeight = getPreferredHeight(root, heightSetByUser, -1);
+            preferredWidth = getPreferredWidth(root, widthSetByUser,
+                                                     preferredHeight);
+        }
+
+        root.resize(preferredWidth, preferredHeight);
+    }
+
+    private static double getPreferredWidth(Parent root,
+                                            double forcedWidth,
+                                            double height) {
+        if (forcedWidth >= 0) {
+            return forcedWidth;
+        }
+        final double normalizedHeight = (height >= 0) ? height : -1;
+        return root.boundedSize(root.prefWidth(normalizedHeight),
+                                root.minWidth(normalizedHeight),
+                                root.maxWidth(normalizedHeight));
+    }
+
+    private static double getPreferredHeight(Parent root,
+                                             double forcedHeight,
+                                             double width) {
+        if (forcedHeight >= 0) {
+            return forcedHeight;
+        }
+        final double normalizedWidth = (width >= 0) ? width : -1;
+        return root.boundedSize(root.prefHeight(normalizedWidth),
+                                root.minHeight(normalizedWidth),
+                                root.maxHeight(normalizedWidth));
+    }
+
     /**
      * @treatAsPrivate implementation detail
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
@@ -2363,11 +2375,11 @@
     class ScenePeerListener implements TKSceneListener {
         @Override
         public void changedLocation(float x, float y) {
-            if ((x + sceneDeltaX) != Scene.this.getX()) {
-                Scene.this.setX(x + sceneDeltaX);
+            if (x != Scene.this.getX()) {
+                Scene.this.setX(x);
             }
-            if ((y + sceneDeltaY) != Scene.this.getY()) {
-                Scene.this.setY(y + sceneDeltaY);
+            if (y != Scene.this.getY()) {
+                Scene.this.setY(y);
             }
         }
 
--- a/modules/graphics/src/main/java/javafx/stage/PopupWindow.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/stage/PopupWindow.java	Thu Sep 12 10:09:43 2013 +0200
@@ -35,6 +35,8 @@
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.BooleanPropertyBase;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import javafx.beans.property.ReadOnlyDoubleWrapper;
 import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
@@ -42,6 +44,7 @@
 import javafx.collections.ObservableList;
 import javafx.event.Event;
 import javafx.event.EventHandler;
+import javafx.geometry.BoundingBox;
 import javafx.geometry.Bounds;
 import javafx.geometry.Rectangle2D;
 import javafx.scene.Group;
@@ -59,6 +62,7 @@
 import com.sun.javafx.stage.WindowCloseRequestHandler;
 import com.sun.javafx.stage.WindowEventDispatcher;
 import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.ObjectPropertyBase;
 import javafx.beans.property.ReadOnlyObjectProperty;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.event.EventTarget;
@@ -66,6 +70,8 @@
 import javafx.scene.input.KeyCombination;
 import javafx.scene.input.KeyEvent;
 import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.Pane;
 
 /**
  * PopupWindow is the parent for a variety of different types of popup
@@ -95,20 +101,22 @@
     private final List<PopupWindow> children = new ArrayList<PopupWindow>();
 
     /**
-     * Keeps track of the bounds of the group, and adjust the size of the
-     * popup window accordingly. This way as the popup content changes, the
-     * window will be changed to match.
+     * Keeps track of the bounds of the content, and adjust the position and
+     * size of the popup window accordingly. This way as the popup content
+     * changes, the window will be changed to match.
      */
-    private final InvalidationListener rootBoundsListener =
+    private final InvalidationListener popupWindowUpdater =
             new InvalidationListener() {
                 @Override
                 public void invalidated(final Observable observable) {
-                    syncWithRootBounds();
+                    cachedExtendedBounds = null;
+                    cachedAnchorBounds = null;
+                    updateWindow(getAnchorX(), getAnchorY());
                 }
             };
 
     /**
-     * RT-28454: When a parent node or parent window we are associated with is not 
+     * RT-28454: When a parent node or parent window we are associated with is not
      * visible anymore, possibly because the scene was not valid anymore, we should hide.
      */
     private final ChangeListener<Boolean> ownerNodeListener = new ChangeListener<Boolean>() {
@@ -120,13 +128,17 @@
             }
         }
     };
-    
+
     public PopupWindow() {
-        final Scene scene = new Scene(new Group());
+        final Pane popupRoot = new Pane();
+        popupRoot.setBackground(Background.EMPTY);
+
+        final Scene scene = SceneHelper.createPopupScene(popupRoot);
         scene.setFill(null);
         super.setScene(scene);
 
-        scene.getRoot().layoutBoundsProperty().addListener(rootBoundsListener);
+        popupRoot.layoutBoundsProperty().addListener(popupWindowUpdater);
+        popupRoot.boundsInLocalProperty().addListener(popupWindowUpdater);
         scene.rootProperty().addListener(
                 new InvalidationListener() {
                     private Node oldRoot = scene.getRoot();
@@ -137,20 +149,26 @@
                         if (oldRoot != newRoot) {
                             if (oldRoot != null) {
                                 oldRoot.layoutBoundsProperty()
-                                       .removeListener(rootBoundsListener);
+                                       .removeListener(popupWindowUpdater);
+                                oldRoot.boundsInLocalProperty()
+                                       .removeListener(popupWindowUpdater);
                             }
 
                             if (newRoot != null) {
                                 newRoot.layoutBoundsProperty()
-                                       .addListener(rootBoundsListener);
+                                       .addListener(popupWindowUpdater);
+                                newRoot.boundsInLocalProperty()
+                                       .addListener(popupWindowUpdater);
                             }
 
                             oldRoot = newRoot;
-                            syncWithRootBounds();
+
+                            cachedExtendedBounds = null;
+                            cachedAnchorBounds = null;
+                            updateWindow(getAnchorX(), getAnchorY());
                         }
                     }
                 });
-        syncWithRootBounds();
     }
 
     /**
@@ -164,12 +182,16 @@
     @Deprecated
     protected ObservableList<Node> getContent() {
         final Parent rootNode = getScene().getRoot();
-        if (!(rootNode instanceof Group)) {
-            throw new IllegalStateException(
-                    "The content of the Popup can't be accessed");
+        if (rootNode instanceof Group) {
+            return ((Group) rootNode).getChildren();
         }
 
-        return ((Group) rootNode).getChildren();
+        if (rootNode instanceof Pane) {
+            return ((Pane) rootNode).getChildren();
+        }
+
+        throw new IllegalStateException(
+                "The content of the Popup can't be accessed");
     }
 
     /**
@@ -320,23 +342,26 @@
     }
 
     /**
-     * Shows the popup at the specified x,y location relative to the screen.
+     * Shows the popup at the specified location on the screen. The popup window
+     * is positioned in such way that its anchor point ({@see #anchorLocation})
+     * is displayed at the specified {@code anchorX} and {@code anchorY}
+     * coordinates.
+     * <p>
      * The popup is associated with the specified owner node. The {@code Window}
      * which contains the owner node at the time of the call becomes an owner
      * window of the displayed popup.
-     * 
+     * </p>
+     *
      * @param ownerNode The owner Node of the popup. It must not be null
      *        and must be associated with a Window.
-     * @param screenX the x location in screen coordinates at which to
-     *        show this PopupWindow.
-     * @param screenY the y location in screen coordiates at which to
-     *        show this PopupWindow.
+     * @param anchorX the x position of the popup anchor in screen coordinates
+     * @param anchorY the y position of the popup anchor in screen coordinates
      * @throws NullPointerException if ownerNode is null
      * @throws IllegalArgumentException if the specified owner node is not
      *      associated with a Window or when the window would create cycle
      *      in the window hierarchy
      */
-    public void show(Node ownerNode, double screenX, double screenY) {
+    public void show(Node ownerNode, double anchorX, double anchorY) {
         if (ownerNode == null) {
             throw new NullPointerException("The owner node must not be null");
         }
@@ -352,44 +377,37 @@
         validateOwnerWindow(newOwnerWindow);
 
         this.ownerNode.set(ownerNode);
-        
+
         // RT-28454 PopupWindow should disappear when owner node is not visible
-        if (ownerNode != null) { 
+        if (ownerNode != null) {
             ownerNode.visibleProperty().addListener(ownerNodeListener);
         }
-       
-        setX(screenX);
-        setY(screenY);
+
+        updateWindow(anchorX, anchorY);
         showImpl(newOwnerWindow);
     }
 
     /**
-     * Show the Popup at the specified x,y location relative to the screen
+     * Shows the popup at the specified location on the screen. The popup window
+     * is positioned in such way that its anchor point ({@see #anchorLocation})
+     * is displayed at the specified {@code anchorX} and {@code anchorY}
+     * coordinates.
+     *
      * @param ownerWindow The owner of the popup. This must not be null.
-     * @param screenX the x location in screen coordinates at which to
-     *        show this PopupWindow.
-     * @param screenY the y location in screen coordiates at which to
-     *        show this PopupWindow.
+     * @param anchorX the x position of the popup anchor in screen coordinates
+     * @param anchorY the y position of the popup anchor in screen coordinates
      * @throws NullPointerException if ownerWindow is null
      * @throws IllegalArgumentException if the specified owner window would
      *      create cycle in the window hierarchy
      */
-    public void show(Window ownerWindow, double screenX, double screenY) {
+    public void show(Window ownerWindow, double anchorX, double anchorY) {
         validateOwnerWindow(ownerWindow);
 
-        setX(screenX);
-        setY(screenY);
+        updateWindow(anchorX, anchorY);
         showImpl(ownerWindow);
     }
 
     private void showImpl(final Window owner) {
-        if (isShowing()) {
-            if (autofixHandler != null) {
-                autofixHandler.adjustPosition();
-            }
-            return;
-        }
-
         // Update the owner field
         this.ownerWindow.set(owner);
         if (owner instanceof PopupWindow) {
@@ -488,73 +506,68 @@
         PerformanceTracker.logEvent("PopupWindow.storeVisible for [PopupWindow] finished");
     }
 
-    private void syncWithRootBounds() {
-        final Parent rootNode = getScene().getRoot();
-        final Bounds layoutBounds = rootNode.getLayoutBounds();
+    /**
+     * Specifies the x coordinate of the popup anchor point on the screen. If
+     * the {@code anchorLocation} is set to {@code WINDOW_TOP_LEFT} or
+     * {@code WINDOW_BOTTOM_LEFT} the {@code x} and {@code anchorX} values will
+     * be identical.
+     *
+     * @since JavaFX 8.0
+     */
+    private final ReadOnlyDoubleWrapper anchorX =
+            new ReadOnlyDoubleWrapper(this, "anchorX", Double.NaN);
 
-        final double layoutX = layoutBounds.getMinX();
-        final double layoutY = layoutBounds.getMinY();
-
-        // update popup dimensions
-        setWidth(layoutBounds.getMaxX() - layoutX);
-        setHeight(layoutBounds.getMaxY() - layoutY);
-        // update transform
-        rootNode.setTranslateX(-layoutX);
-        rootNode.setTranslateY(-layoutY);
-
-        if (isAlignWithContentOrigin()) {
-            // update window position
-            setWindowTranslate(layoutX, layoutY);
-            // compensate with scene's delta, so the manual Node.localToScene
-            // + sceenXY + windowXY calculation still works for local to screen
-            // conversions
-            SceneHelper.setSceneDelta(getScene(), layoutX, layoutY);
-
-            if (autofixActive) {
-                autofixHandler.adjustPosition();
-            }
-        }
+    public final void setAnchorX(final double value) {
+        updateWindow(value, getAnchorY());
+    }
+    public final double getAnchorX() {
+        return anchorX.get();
+    }
+    public final ReadOnlyDoubleProperty anchorXProperty() {
+        return anchorX.getReadOnlyProperty();
     }
 
     /**
-     * Specifies the reference point associated with the x, y location of the
-     * window on the screen. If set to {@code false} this point corresponds to
-     * the window's upper left corner. If set to {@code true} the reference
-     * point is moved to the origin of the popup content coordinate space. This
-     * simplifies placement of popup windows which content have some additional
-     * borders extending past their origins. Setting the property to {code true}
-     * for such windows makes their position independent of their borders.
+     * Specifies the y coordinate of the popup anchor point on the screen. If
+     * the {@code anchorLocation} is set to {@code WINDOW_TOP_LEFT} or
+     * {@code WINDOW_TOP_RIGHT} the {@code y} and {@code anchorY} values will
+     * be identical.
      *
-     * @defaultValue {@code true}
      * @since JavaFX 8.0
      */
-    private BooleanProperty alignWithContentOrigin =
-            new BooleanPropertyBase(true) {
-                private boolean oldValue = true;
+    private final ReadOnlyDoubleWrapper anchorY =
+            new ReadOnlyDoubleWrapper(this, "anchorY", Double.NaN);
 
+    public final void setAnchorY(final double value) {
+        updateWindow(getAnchorX(), value);
+    }
+    public final double getAnchorY() {
+        return anchorY.get();
+    }
+    public final ReadOnlyDoubleProperty anchorYProperty() {
+        return anchorY.getReadOnlyProperty();
+    }
+
+    /**
+     * Specifies the popup anchor point which is used in popup positioning. The
+     * point can be set to a corner of the popup window or a corner of its
+     * content. In this context the content corners are derived from the popup
+     * root node's layout bounds.
+     * <p>
+     * In general changing of the anchor location won't change the current
+     * window position. Instead of that, the {@code anchorX} and {@code anchorY}
+     * values are recalculated to correspond to the new anchor point.
+     * </p>
+     * @since JavaFX 8.0
+     */
+    private final ObjectProperty<AnchorLocation> anchorLocation =
+            new ObjectPropertyBase<AnchorLocation>(
+                    AnchorLocation.WINDOW_TOP_LEFT) {
                 @Override
                 protected void invalidated() {
-                    final boolean newValue = get();
-                    if (oldValue != newValue) {
-                        if (newValue) {
-                            final Bounds layoutBounds =
-                                    getScene().getRoot().getLayoutBounds();
-                            setWindowTranslate(layoutBounds.getMinX(),
-                                               layoutBounds.getMinY());
-                            SceneHelper.setSceneDelta(getScene(),
-                                                      layoutBounds.getMinX(),
-                                                      layoutBounds.getMinY());
-                        } else {
-                            setWindowTranslate(0, 0);
-                            SceneHelper.setSceneDelta(getScene(), 0, 0);
-                        }
-
-                        if (autofixActive) {
-                            autofixHandler.adjustPosition();
-                        }
-
-                        oldValue = newValue;
-                    }
+                    cachedAnchorBounds = null;
+                    updateWindow(windowToAnchorX(getX()),
+                                 windowToAnchorY(getY()));
                 }
 
                 @Override
@@ -564,20 +577,211 @@
 
                 @Override
                 public String getName() {
-                    return "alignWithContentOrigin";
+                    return "anchorLocation";
                 }
             };
-
-    public final void setAlignWithContentOrigin(boolean value) {
-        alignWithContentOrigin.set(value);
+    public final void setAnchorLocation(final AnchorLocation value) {
+        anchorLocation.set(value);
+    }
+    public final AnchorLocation getAnchorLocation() {
+        return anchorLocation.get();
+    }
+    public final ObjectProperty<AnchorLocation> anchorLocationProperty() {
+        return anchorLocation;
     }
 
-    public final boolean isAlignWithContentOrigin() {
-        return alignWithContentOrigin.get();
+    /**
+     * Anchor location constants for popup anchor point selection.
+     *
+     * @since JavaFX 8.0
+     */
+    public enum AnchorLocation {
+        /** Represents top left window corner. */
+        WINDOW_TOP_LEFT(0, 0, false),
+        /** Represents top right window corner. */
+        WINDOW_TOP_RIGHT(1, 0, false),
+        /** Represents bottom left window corner. */
+        WINDOW_BOTTOM_LEFT(0, 1, false),
+        /** Represents bottom right window corner. */
+        WINDOW_BOTTOM_RIGHT(1, 1, false),
+        /** Represents top left content corner. */
+        CONTENT_TOP_LEFT(0, 0, true),
+        /** Represents top right content corner. */
+        CONTENT_TOP_RIGHT(1, 0, true),
+        /** Represents bottom left content corner. */
+        CONTENT_BOTTOM_LEFT(0, 1, true),
+        /** Represents bottom right content corner. */
+        CONTENT_BOTTOM_RIGHT(1, 1, true);
+
+        private final double xCoef;
+        private final double yCoef;
+        private final boolean contentLocation;
+
+        private AnchorLocation(final double xCoef, final double yCoef,
+                               final boolean contentLocation) {
+            this.xCoef = xCoef;
+            this.yCoef = yCoef;
+            this.contentLocation = contentLocation;
+        }
+
+        double getXCoef() {
+            return xCoef;
+        }
+
+        double getYCoef() {
+            return yCoef;
+        }
+
+        boolean isContentLocation() {
+            return contentLocation;
+        }
+    };
+
+    @Override
+    void setXInternal(final double value) {
+        updateWindow(windowToAnchorX(value), getAnchorY());
     }
 
-    public final BooleanProperty alignWithContentOriginProperty() {
-        return alignWithContentOrigin;
+    @Override
+    void setYInternal(final double value) {
+        updateWindow(getAnchorX(), windowToAnchorY(value));
+    }
+
+    @Override
+    void notifyLocationChanged(final double newX, final double newY) {
+        super.notifyLocationChanged(newX, newY);
+        anchorX.set(windowToAnchorX(newX));
+        anchorY.set(windowToAnchorY(newY));
+    }
+
+    private Bounds cachedExtendedBounds;
+    private Bounds cachedAnchorBounds;
+
+    private Bounds getExtendedBounds() {
+        if (cachedExtendedBounds == null) {
+            final Parent rootNode = getScene().getRoot();
+            cachedExtendedBounds = union(rootNode.getLayoutBounds(),
+                                         rootNode.getBoundsInLocal());
+        }
+
+        return cachedExtendedBounds;
+    }
+
+    private Bounds getAnchorBounds() {
+        if (cachedAnchorBounds == null) {
+            cachedAnchorBounds = getAnchorLocation().isContentLocation()
+                                         ? getScene().getRoot()
+                                                     .getLayoutBounds()
+                                         : getExtendedBounds();
+        }
+
+        return cachedAnchorBounds;
+    }
+
+    private void updateWindow(final double newAnchorX,
+                              final double newAnchorY) {
+        final AnchorLocation anchorLocationValue = getAnchorLocation();
+        final Parent rootNode = getScene().getRoot();
+        final Bounds extendedBounds = getExtendedBounds();
+        final Bounds anchorBounds = getAnchorBounds();
+
+        final double anchorXCoef = anchorLocationValue.getXCoef();
+        final double anchorYCoef = anchorLocationValue.getYCoef();
+        final double anchorDeltaX = anchorXCoef * anchorBounds.getWidth();
+        final double anchorDeltaY = anchorYCoef * anchorBounds.getHeight();
+        double anchorScrMinX = newAnchorX - anchorDeltaX;
+        double anchorScrMinY = newAnchorY - anchorDeltaY;
+
+        if (autofixActive) {
+            final Screen currentScreen =
+                    Utils.getScreenForPoint(newAnchorX, newAnchorY);
+            final Rectangle2D screenBounds =
+                    Utils.hasFullScreenStage(currentScreen)
+                            ? currentScreen.getBounds()
+                            : currentScreen.getVisualBounds();
+
+            if (anchorXCoef <= 0.5) {
+                // left side of the popup is more important, try to keep it
+                // visible if the popup width is larger than screen width
+                anchorScrMinX = Math.min(anchorScrMinX,
+                                         screenBounds.getMaxX()
+                                             - anchorBounds.getWidth());
+                anchorScrMinX = Math.max(anchorScrMinX, screenBounds.getMinX());
+            } else {
+                // right side of the popup is more important
+                anchorScrMinX = Math.max(anchorScrMinX, screenBounds.getMinX());
+                anchorScrMinX = Math.min(anchorScrMinX,
+                                         screenBounds.getMaxX()
+                                             - anchorBounds.getWidth());
+            }
+
+            if (anchorYCoef <= 0.5) {
+                // top side of the popup is more important
+                anchorScrMinY = Math.min(anchorScrMinY,
+                                         screenBounds.getMaxY()
+                                             - anchorBounds.getHeight());
+                anchorScrMinY = Math.max(anchorScrMinY, screenBounds.getMinY());
+            } else {
+                // bottom side of the popup is more important
+                anchorScrMinY = Math.max(anchorScrMinY, screenBounds.getMinY());
+                anchorScrMinY = Math.min(anchorScrMinY,
+                                         screenBounds.getMaxY()
+                                             - anchorBounds.getHeight());
+            }
+        }
+
+        final double windowScrMinX =
+                anchorScrMinX - anchorBounds.getMinX()
+                              + extendedBounds.getMinX();
+        final double windowScrMinY =
+                anchorScrMinY - anchorBounds.getMinY()
+                              + extendedBounds.getMinY();
+
+        // update popup dimensions
+        setWidth(extendedBounds.getWidth());
+        setHeight(extendedBounds.getHeight());
+        // update transform
+        rootNode.setTranslateX(-extendedBounds.getMinX());
+        rootNode.setTranslateY(-extendedBounds.getMinY());
+
+        // update popup position
+        // don't set Window.xExplicit unnecessarily
+        if (!Double.isNaN(windowScrMinX) || !Double.isNaN(getX())) {
+            super.setXInternal(windowScrMinX);
+        }
+        // don't set Window.yExplicit unnecessarily
+        if (!Double.isNaN(windowScrMinY) || !Double.isNaN(getY())) {
+            super.setYInternal(windowScrMinY);
+        }
+
+        // set anchor x, anchor y
+        anchorX.set(anchorScrMinX + anchorDeltaX);
+        anchorY.set(anchorScrMinY + anchorDeltaY);
+    }
+
+    private Bounds union(final Bounds bounds1, final Bounds bounds2) {
+        final double minX = Math.min(bounds1.getMinX(), bounds2.getMinX());
+        final double minY = Math.min(bounds1.getMinY(), bounds2.getMinY());
+        final double maxX = Math.max(bounds1.getMaxX(), bounds2.getMaxX());
+        final double maxY = Math.max(bounds1.getMaxY(), bounds2.getMaxY());
+
+        return new BoundingBox(minX, minY, maxX - minX, maxY - minY);
+    }
+
+    private double windowToAnchorX(final double windowX) {
+        final Bounds anchorBounds = getAnchorBounds();
+        return windowX - getExtendedBounds().getMinX()
+                       + anchorBounds.getMinX()
+                       + getAnchorLocation().getXCoef()
+                             * anchorBounds.getWidth();
+    }
+
+    private double windowToAnchorY(final double windowY) {
+        final Bounds anchorBounds = getAnchorBounds();
+        return windowY - getExtendedBounds().getMinY()
+                       + anchorBounds.getMinY()
+                       + getAnchorLocation().getYCoef()
+                             * anchorBounds.getHeight();
     }
 
     /**
@@ -657,23 +861,16 @@
     }
 
     private boolean autofixActive;
-    private AutofixHandler autofixHandler;
     private void handleAutofixActivation(final boolean visible,
                                          final boolean autofix) {
         final boolean newAutofixActive = visible && autofix;
         if (autofixActive != newAutofixActive) {
             autofixActive = newAutofixActive;
             if (newAutofixActive) {
-                autofixHandler = new AutofixHandler();
-                widthProperty().addListener(autofixHandler);
-                heightProperty().addListener(autofixHandler);
-                Screen.getScreens().addListener(autofixHandler);
-                autofixHandler.adjustPosition();
+                Screen.getScreens().addListener(popupWindowUpdater);
+                updateWindow(getAnchorX(), getAnchorY());
             } else {
-                widthProperty().removeListener(autofixHandler);
-                heightProperty().removeListener(autofixHandler);
-                Screen.getScreens().removeListener(autofixHandler);
-                autofixHandler = null;
+                Screen.getScreens().removeListener(popupWindowUpdater);
             }
         }
     }
@@ -722,38 +919,6 @@
        return false;
     }
 
-    private final class AutofixHandler implements InvalidationListener {
-        @Override
-        public void invalidated(final Observable observable) {
-            adjustPosition();
-        }
-
-        public void adjustPosition() {
-            final Screen currentScreen =
-                    Utils.getScreenForPoint(getX(), getY());
-            final Rectangle2D screenBounds =
-                    Utils.hasFullScreenStage(currentScreen)
-                            ? currentScreen.getBounds()
-                            : currentScreen.getVisualBounds();
-            double wtX = getWindowTranslateX();
-            double wtY = getWindowTranslateY();
-            double oldWindowX = getX() + wtX;
-            double oldWindowY = getY() + wtY;
-            double _x = Math.min(oldWindowX,
-                                 screenBounds.getMaxX() - getWidth());
-            double _y = Math.min(oldWindowY,
-                                 screenBounds.getMaxY() - getHeight());
-            _x = Math.max(_x, screenBounds.getMinX());
-            _y = Math.max(_y, screenBounds.getMinY());
-            if (_x != oldWindowX) {
-                setX(_x - wtX);
-            }
-            if (_y != oldWindowY) {
-                setY(_y - wtY);
-            }
-        }
-    }
-
     static class PopupEventRedirector extends EventRedirector {
 
         private static final KeyCombination ESCAPE_KEY_COMBINATION =
--- a/modules/graphics/src/main/java/javafx/stage/Window.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/main/java/javafx/stage/Window.java	Thu Sep 12 10:09:43 2013 +0200
@@ -82,22 +82,21 @@
         WindowHelper.setWindowAccessor(
                 new WindowHelper.WindowAccessor() {
                     /**
-                     * Allow window peer listeners to directly change window
-                     * location and size without changing the xExplicit,
+                     * Allow window peer listeners to directly change reported
+                     * window location and size without changing the xExplicit,
                      * yExplicit, widthExplicit and heightExplicit values.
                      */
                     @Override
-                    public void setLocation(Window window, double x, double y) {
-                        window.x.set(x - window.winTranslateX);
-                        window.y.set(y - window.winTranslateY);
+                    public void notifyLocationChanged(
+                            Window window, double x, double y) {
+                        window.notifyLocationChanged(x, y);
                     }
 
                     @Override
-                    public void setSize(Window window,
-                                        double width,
-                                        double height) {
-                        window.width.set(width);
-                        window.height.set(height);
+                    public void notifySizeChanged(Window window,
+                                                  double width,
+                                                  double height) {
+                        window.notifySizeChanged(width, height);
                     }
 
                     @Override
@@ -246,8 +245,8 @@
                     bounds.getMinY() + (bounds.getHeight() - getHeight())
                                            * CENTER_ON_SCREEN_Y_FRACTION;
 
-            x.set(centerX - winTranslateX);
-            y.set(centerY - winTranslateY);
+            x.set(centerX);
+            y.set(centerY);
             peerBoundsConfigurator.setLocation(centerX, centerY,
                                                CENTER_ON_SCREEN_X_FRACTION,
                                                CENTER_ON_SCREEN_Y_FRACTION);
@@ -255,29 +254,6 @@
         }
     }
 
-    private double winTranslateX;
-    private double winTranslateY;
-
-    final void setWindowTranslate(final double translateX,
-                                  final double translateY) {
-        if (translateX != winTranslateX) {
-            winTranslateX = translateX;
-            peerBoundsConfigurator.setX(getX() + translateX, 0);
-        }
-        if (translateY != winTranslateY) {
-            winTranslateY = translateY;
-            peerBoundsConfigurator.setY(getY() + translateY, 0);
-        }
-    }
-
-    final double getWindowTranslateX() {
-        return winTranslateX;
-    }
-
-    final double getWindowTranslateY() {
-        return winTranslateY;
-    }
-
     private boolean xExplicit = false;
     /**
      * The horizontal location of this {@code Stage} on the screen. Changing
@@ -290,13 +266,17 @@
             new ReadOnlyDoubleWrapper(this, "x", Double.NaN);
 
     public final void setX(double value) {
-        x.set(value);
-        peerBoundsConfigurator.setX(value + winTranslateX, 0);
-        xExplicit = true;
+        setXInternal(value);
     }
     public final double getX() { return x.get(); }
     public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); }
 
+    void setXInternal(double value) {
+        x.set(value);
+        peerBoundsConfigurator.setX(value, 0);
+        xExplicit = true;
+    }
+
     private boolean yExplicit = false;
     /**
      * The vertical location of this {@code Stage} on the screen. Changing this
@@ -309,14 +289,31 @@
             new ReadOnlyDoubleWrapper(this, "y", Double.NaN);
 
     public final void setY(double value) {
-        y.set(value);
-        peerBoundsConfigurator.setY(value + winTranslateY, 0);
-        yExplicit = true;
+        setYInternal(value);
     }
     public final double getY() { return y.get(); }
     public final ReadOnlyDoubleProperty yProperty() { return y.getReadOnlyProperty(); }
 
+    void setYInternal(double value) {
+        y.set(value);
+        peerBoundsConfigurator.setY(value, 0);
+        yExplicit = true;
+    }
+
+    /**
+     * Notification from the windowing system that the window's position has
+     * changed.
+     *
+     * @param newX the new window x position
+     * @param newY the new window y position
+     */
+    void notifyLocationChanged(double newX, double newY) {
+        x.set(newX);
+        y.set(newY);
+    }
+
     private boolean widthExplicit = false;
+
     /**
      * The width of this {@code Stage}. Changing this attribute will narrow or
      * widen the width of the {@code Stage}. Changing this
@@ -368,6 +365,18 @@
     public final ReadOnlyDoubleProperty heightProperty() { return height.getReadOnlyProperty(); }
 
     /**
+     * Notification from the windowing system that the window's size has
+     * changed.
+     *
+     * @param newWidth the new window width
+     * @param newHeight the new window height
+     */
+    void notifySizeChanged(double newWidth, double newHeight) {
+        width.set(newWidth);
+        height.set(newHeight);
+    }
+
+    /**
      * Whether or not this {@code Window} has the keyboard or input focus.
      * <p>
      * The property is read only because it can be changed externally
@@ -752,10 +761,8 @@
                     if (!xExplicit && !yExplicit) {
                         centerOnScreen();
                     } else {
-                        peerBoundsConfigurator.setLocation(
-                                getX() + winTranslateX,
-                                getY() + winTranslateY,
-                                0, 0);
+                        peerBoundsConfigurator.setLocation(getX(), getY(),
+                                                           0, 0);
                     }
 
                     // set peer bounds before the window is shown
--- a/modules/graphics/src/test/java/javafx/stage/PopupTest.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/test/java/javafx/stage/PopupTest.java	Thu Sep 12 10:09:43 2013 +0200
@@ -50,17 +50,16 @@
 import com.sun.javafx.pgstub.StubToolkit;
 import com.sun.javafx.tk.Toolkit;
 import javafx.geometry.Bounds;
-import javafx.geometry.Point3D;
-import javafx.scene.shape.Box;
+import javafx.scene.Parent;
 import static org.junit.Assert.assertEquals;
 
 public class PopupTest {
-    
+
     private StubToolkit toolkit;
     private Stage stage;
     private Scene scene;
     private boolean done = false;
-    
+
     @Before
     public void setUp() {
         stage = new Stage();
@@ -122,83 +121,111 @@
         assertEquals(20, peer.y, 1e-100);
     }
 
-    @Test
-    public void testContentAlignment() {
-        final Popup popup = new Popup();
-        final Rectangle contentRect = new Rectangle(10, 20, 100, 100);
+    private static final class PopupRoot extends Parent {
+        private final Rectangle geomBoundsRect;
 
-        popup.setAlignWithContentOrigin(true);
-        popup.getContent().add(contentRect);
-        popup.show(stage, 50, 50);
-        pulse();
-        final StubPopupStage peer = (StubPopupStage) popup.impl_getPeer();
+        private double layoutBoundsX;
+        private double layoutBoundsY;
+        private double layoutBoundsWidth;
+        private double layoutBoundsHeight;
 
-        assertEquals(60.0, peer.x, 1e-100);
-        assertEquals(70.0, peer.y, 1e-100);
+        public PopupRoot() {
+            geomBoundsRect = new Rectangle(0, 0, 100, 100);
+            layoutBoundsWidth = 100;
+            layoutBoundsHeight = 100;
 
-        contentRect.setX(-20);
-        contentRect.setY(-10);
-        pulse();
+            getChildren().add(geomBoundsRect);
+        }
 
-        assertEquals(30.0, peer.x, 1e-100);
-        assertEquals(40.0, peer.y, 1e-100);
+        public void setGeomBounds(final double x, final double y,
+                                  final double width,
+                                  final double height) {
+            geomBoundsRect.setX(x);
+            geomBoundsRect.setY(y);
+            geomBoundsRect.setWidth(width);
+            geomBoundsRect.setHeight(height);
+        }
+
+        public void setLayoutBounds(final double x, final double y,
+                                    final double width,
+                                    final double height) {
+            layoutBoundsX = x;
+            layoutBoundsY = y;
+            layoutBoundsWidth = width;
+            layoutBoundsHeight = height;
+
+            impl_layoutBoundsChanged();
+        }
+
+        @Override
+        protected Bounds impl_computeLayoutBounds() {
+            return new BoundingBox(layoutBoundsX, layoutBoundsY,
+                                   layoutBoundsWidth, layoutBoundsHeight);
+        }
     }
 
     @Test
-    public void testContentAlignmentChange() {
+    public void testAnchorPositioning() {
         final Popup popup = new Popup();
-        final Rectangle contentRect = new Rectangle(-20, -10, 100, 100);
+        final PopupRoot root = new PopupRoot();
 
-        popup.setAlignWithContentOrigin(false);
-        popup.getContent().add(contentRect);
-        popup.show(stage, 50, 50);
-        pulse();
+        root.setGeomBounds(-10, 20, 120, 100);
+        root.setLayoutBounds(0, 0, 100, 140);
+
+        popup.getScene().setRoot(root);
+
+        popup.setAnchorLocation(PopupWindow.AnchorLocation.WINDOW_BOTTOM_RIGHT);
+        popup.show(stage, 400, 400);
+
         final StubPopupStage peer = (StubPopupStage) popup.impl_getPeer();
 
-        assertEquals(50.0, peer.x, 1e-100);
-        assertEquals(50.0, peer.y, 1e-100);
+        pulse();
+        assertEquals(280.0, peer.x, 1e-100);
+        assertEquals(260.0, peer.y, 1e-100);
 
-        popup.setAlignWithContentOrigin(true);
+        popup.setAnchorLocation(PopupWindow.AnchorLocation.CONTENT_TOP_LEFT);
+        assertEquals(290.0, popup.getAnchorX(), 1e-100);
+        assertEquals(260.0, popup.getAnchorY(), 1e-100);
+
         pulse();
+        assertEquals(280.0, peer.x, 1e-100);
+        assertEquals(260.0, peer.y, 1e-100);
 
-        assertEquals(30.0, peer.x, 1e-100);
-        assertEquals(40.0, peer.y, 1e-100);
-    }
+        popup.setAnchorX(200);
+        popup.setAnchorY(100);
 
-
-    @Test
-    public void testLocalToScreenWithContentAlignment() {
-        final Popup popup = new Popup();
-        final Rectangle contentRect = new Rectangle(-30, -20, 100, 100);
-
-        popup.setAlignWithContentOrigin(true);
-        popup.getContent().add(contentRect);
-        popup.show(stage, 100, 200);
-
-        Point2D p = contentRect.localToScreen(new Point2D(0, 0));
-        assertEquals(100.0, p.getX(), 0.0001);
-        assertEquals(200.0, p.getY(), 0.0001);
-        Bounds b = contentRect.localToScreen(new BoundingBox(10, 10, 50, 50));
-        assertEquals(110.0, b.getMinX(), 0.0001);
-        assertEquals(210.0, b.getMinY(), 0.0001);
-        assertEquals(50.0, b.getWidth(), 0.0001);
-        assertEquals(50.0, b.getHeight(), 0.0001);
+        pulse();
+        assertEquals(190.0, peer.x, 1e-100);
+        assertEquals(100.0, peer.y, 1e-100);
     }
 
     @Test
-    public void testScreenToLocalWithContentAlignment() {
+    public void testAnchorKeepsPositionOnContentChange() {
         final Popup popup = new Popup();
-        final Rectangle contentRect = new Rectangle(10, 20, 100, 100);
+        final PopupRoot root = new PopupRoot();
 
-        popup.setAlignWithContentOrigin(true);
-        popup.getContent().add(contentRect);
-        popup.show(stage, 100, 200);
+        root.setGeomBounds(0, 0, 100, 140);
+        root.setLayoutBounds(-10, 20, 120, 100);
 
-        assertEquals(new Point2D(60, 70),
-                     contentRect.screenToLocal(new Point2D(160, 270)));
-        assertEquals(new BoundingBox(0, 0, 50, 50),
-                     contentRect.screenToLocal(
-                                     new BoundingBox(100, 200, 50, 50)));
+        popup.getScene().setRoot(root);
+
+        popup.setAnchorLocation(
+                PopupWindow.AnchorLocation.CONTENT_BOTTOM_RIGHT);
+        popup.show(stage, 400, 300);
+
+        final StubPopupStage peer = (StubPopupStage) popup.impl_getPeer();
+
+        assertEquals(280.0, peer.x, 1e-100);
+        assertEquals(180.0, peer.y, 1e-100);
+
+        root.setLayoutBounds(10, -10, 80, 160);
+
+        pulse();
+
+        assertEquals(400.0, popup.getAnchorX(), 1e-100);
+        assertEquals(300.0, popup.getAnchorY(), 1e-100);
+        assertEquals(310.0, peer.x, 1e-100);
+        assertEquals(140.0, peer.y, 1e-100);
     }
 
     @Test
--- a/modules/graphics/src/test/java/javafx/stage/WindowTest.java	Thu Sep 12 09:20:00 2013 +0200
+++ b/modules/graphics/src/test/java/javafx/stage/WindowTest.java	Thu Sep 12 10:09:43 2013 +0200
@@ -66,34 +66,6 @@
         assertEquals(1.0f, peer.opacity);
     }
 
-    @Test
-    public void testWindowTranslate() {
-        testWindow.show();
-        final StubStage peer = getPeer(testWindow);
-
-        testWindow.setWindowTranslate(20, 10);
-        testWindow.setX(30);
-        testWindow.setY(30);
-        toolkit.fireTestPulse();
-
-        assertEquals(30.0, testWindow.getX());
-        assertEquals(30.0, testWindow.getY());
-        assertEquals(50f, peer.x);
-        assertEquals(40f, peer.y);
-
-        testWindow.setWindowTranslate(10, 20);
-        toolkit.fireTestPulse();
-
-        assertEquals(30.0, testWindow.getX());
-        assertEquals(30.0, testWindow.getY());
-        assertEquals(40f, peer.x);
-        assertEquals(50f, peer.y);
-
-        peer.setLocation(60, 60);
-        assertEquals(50.0, testWindow.getX());
-        assertEquals(40.0, testWindow.getY());
-    }
-
     private static StubStage getPeer(final Window window) {
         final TKStage unkPeer = window.impl_getPeer();
         assertTrue(unkPeer instanceof StubStage);