changeset 6488:6820d900774a

RT-36185 Custom TraversalEngine introduced in RT-36065 cannot delegate subtree traversal to the default traversal algorithm Reviewed by: dgrieve
author Martin Sladecek <martin.sladecek@oracle.com>
date Mon, 17 Mar 2014 13:22:16 +0100
parents a595dd55588f
children c0d5845f82d5
files modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ToolBarSkin.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Algorithm.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/ContainerTabOrder.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Direction.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Hueristic2D.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TabOrderHelper.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TopMostTraversalEngine.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TraversalContext.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TraversalEngine.java modules/graphics/src/main/java/com/sun/javafx/scene/traversal/WeightedClosestCorner.java modules/web/src/main/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java
diffstat 11 files changed, 304 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ToolBarSkin.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ToolBarSkin.java	Mon Mar 17 13:22:16 2014 +0100
@@ -31,6 +31,7 @@
 
 import com.sun.javafx.scene.traversal.Algorithm;
 import com.sun.javafx.scene.traversal.ParentTraversalEngine;
+import com.sun.javafx.scene.traversal.TraversalContext;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.DoubleProperty;
 import javafx.beans.value.ChangeListener;
@@ -39,9 +40,7 @@
 import javafx.collections.ListChangeListener;
 import javafx.collections.ObservableList;
 import javafx.event.EventHandler;
-import javafx.geometry.Bounds;
 import javafx.geometry.HPos;
-import javafx.geometry.NodeOrientation;
 import javafx.geometry.Orientation;
 import javafx.geometry.Pos;
 import javafx.geometry.Side;
@@ -71,8 +70,6 @@
 import com.sun.javafx.css.converters.SizeConverter;
 import com.sun.javafx.scene.control.behavior.ToolBarBehavior;
 import com.sun.javafx.scene.traversal.Direction;
-import com.sun.javafx.scene.traversal.TraversalEngine;
-import com.sun.javafx.scene.traversal.TraverseListener;
 import javafx.css.Styleable;
 
 public class ToolBarSkin extends BehaviorSkinBase<ToolBar, ToolBarBehavior> {
@@ -96,70 +93,94 @@
 
         engine = new ParentTraversalEngine(getSkinnable(), new Algorithm() {
 
-
-            @Override
-            public Node select(Node owner, Direction dir, TraversalEngine engine) {
-                final ObservableList<Node> boxChildren = box.getChildren();
-                if (owner == overflowMenu) {
-                    if (dir.isForward(overflowMenu.getEffectiveNodeOrientation())) {
-                        return null;
-                    } else {
-                        for (int i = boxChildren.size() - 1; i >= 0; --i) {
-                            Node n = box.getChildren().get(i);
-                            if (n.isFocusTraversable() && !n.isDisabled() && n.impl_isTreeVisible()) {
-                                return n;
-                            }
-                        }
+            private Node selectPrev(int from, TraversalContext context) {
+                for (int i = from; i >= 0; --i) {
+                    Node n = box.getChildren().get(i);
+                    if (n.isDisabled() || !n.impl_isTreeVisible()) continue;
+                    if (n instanceof Parent) {
+                        Node selected = context.selectLastInParent((Parent)n);
+                        if (selected != null) return selected;
+                    }
+                    if (n.isFocusTraversable() ) {
+                        return n;
                     }
                 }
-                int idx = boxChildren.indexOf(owner);
-                if (dir.isForward(overflowMenu.getEffectiveNodeOrientation())) {
-                    for (int i = idx + 1; i < boxChildren.size(); ++i) {
-                        Node n = box.getChildren().get(i);
-                        if (n.isFocusTraversable() && !n.isDisabled() && n.impl_isTreeVisible()) {
-                            return n;
-                        }
+                return null;
+            }
+
+            private Node selectNext(int from, TraversalContext context) {
+                for (int i = from, max = box.getChildren().size(); i < max; ++i) {
+                    Node n = box.getChildren().get(i);
+                    if (n.isDisabled() || !n.impl_isTreeVisible()) continue;
+                    if (n.isFocusTraversable()) {
+                        return n;
                     }
-                    if (overflow) {
-                        overflowMenu.requestFocus();
-                        return overflowMenu;
-                    }
-                } else {
-                    for (int i = idx - 1; i >= 0; --i) {
-                        Node n = box.getChildren().get(i);
-                        if (n.isFocusTraversable() && !n.isDisabled() && n.impl_isTreeVisible()) {
-                            return n;
-                        }
+                    if (n instanceof Parent) {
+                        Node selected = context.selectFirstInParent((Parent)n);
+                        if (selected != null) return selected;
                     }
                 }
                 return null;
             }
 
             @Override
-            public Node selectFirst(TraversalEngine engine) {
+            public Node select(Node owner, Direction dir, TraversalContext context) {
                 final ObservableList<Node> boxChildren = box.getChildren();
-                for (int i = 0; i < boxChildren.size(); ++i) {
-                    Node n = box.getChildren().get(i);
-                    if (n.isFocusTraversable() && !n.isDisabled() && n.impl_isTreeVisible()) {
-                        return n;
+                if (owner == overflowMenu) {
+                    if (dir.isForward()) {
+                        return null;
+                    } else {
+                        Node selected = selectPrev(boxChildren.size() - 1, context);
+                        if (selected != null) return selected;
+                    }
+                }
+
+                int idx = boxChildren.indexOf(owner);
+
+                if (idx < 0) {
+                    // The current focus owner is a child of some Toolbar's item
+                    Parent item = owner.getParent();
+                    while (!boxChildren.contains(item)) {
+                        item = item.getParent();
+                    }
+                    Node selected = context.selectInSubtree(item, owner, dir);
+                    if (selected != null) return selected;
+                    idx = boxChildren.indexOf(owner);
+                    if (dir == Direction.NEXT) dir = Direction.NEXT_IN_LINE;
+                }
+
+                if (idx >= 0) {
+                    if (dir.isForward()) {
+                        Node selected = selectNext(idx + 1, context);
+                        if (selected != null) return selected;
+                        if (overflow) {
+                            overflowMenu.requestFocus();
+                            return overflowMenu;
+                        }
+                    } else {
+                        Node selected = selectPrev(idx - 1, context);
+                        if (selected != null) return selected;
                     }
                 }
                 return null;
             }
 
             @Override
-            public Node selectLast(TraversalEngine engine) {
+            public Node selectFirst(TraversalContext context) {
+                Node selected = selectNext(0, context);
+                if (selected != null) return selected;
                 if (overflow) {
                     return overflowMenu;
                 }
-                final ObservableList<Node> boxChildren = box.getChildren();
-                for (int i = boxChildren.size() - 1; i >= 0; --i) {
-                    Node n = box.getChildren().get(i);
-                    if (n.isFocusTraversable() && !n.isDisabled() && n.impl_isTreeVisible()) {
-                        return n;
-                    }
+                return null;
+            }
+
+            @Override
+            public Node selectLast(TraversalContext context) {
+                if (overflow) {
+                    return overflowMenu;
                 }
-                return null;
+                return selectPrev(box.getChildren().size() - 1, context);
             }
         });
         getSkinnable().setImpl_traversalEngine(engine);
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Algorithm.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Algorithm.java	Mon Mar 17 13:22:16 2014 +0100
@@ -34,10 +34,10 @@
      * Traverse from origin, in direction dir.
      * Return a the new target Node or null if no suitable target is found.
      */
-    public Node select(Node owner, Direction dir, TraversalEngine engine);
+    public Node select(Node owner, Direction dir, TraversalContext context);
 
-    public Node selectFirst(TraversalEngine engine);
+    public Node selectFirst(TraversalContext context);
 
-    public Node selectLast(TraversalEngine engine);
+    public Node selectLast(TraversalContext context);
     
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/ContainerTabOrder.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/ContainerTabOrder.java	Mon Mar 17 13:22:16 2014 +0100
@@ -27,12 +27,7 @@
 
 import java.util.List;
 import javafx.geometry.Bounds;
-import javafx.scene.Group;
 import javafx.scene.Node;
-import javafx.scene.Parent;
-import com.sun.javafx.Logging;
-import sun.util.logging.PlatformLogger;
-import sun.util.logging.PlatformLogger.Level;
 
 import static com.sun.javafx.scene.traversal.Direction.*;
 
@@ -41,44 +36,45 @@
     ContainerTabOrder() {
     }
 
-    public Node select(Node node, Direction dir, TraversalEngine engine) {
-        Node newNode = null;
-        if (NEXT.equals(dir)) {
-            newNode = TabOrderHelper.findNextFocusablePeer(node);
+    public Node select(Node node, Direction dir, TraversalContext context) {
+        switch (dir) {
+            case NEXT:
+            case NEXT_IN_LINE:
+                return TabOrderHelper.findNextFocusablePeer(node, context.getRoot(), dir == NEXT);
+            case PREVIOUS:
+                return TabOrderHelper.findPreviousFocusablePeer(node, context.getRoot());
+            case UP:
+            case DOWN:
+            case LEFT:
+            case RIGHT:
+                List<Node> nodes = context.getAllTargetNodes();
+
+                int target = trav2D(context.getSceneLayoutBounds(node), dir, nodes, context);
+                if (target != -1) {
+                    return nodes.get(target);
+                }
         }
-        else if (PREVIOUS.equals(dir)) {
-            newNode = TabOrderHelper.findPreviousFocusablePeer(node);
-        }
-        else if (UP.equals(dir) || DOWN.equals(dir) || LEFT.equals(dir) || RIGHT.equals(dir) ) {
-            List<Node> nodes = engine.getAllTargetNodes();
-
-            int target = trav2D(engine.getSceneLayoutBounds(node), dir, nodes, engine);
-            if (target != -1) {
-                newNode = nodes.get(target);
-            }
-        }
-
-        return newNode;
+        return null;
     }
 
     @Override
-    public Node selectFirst(TraversalEngine engine) {
-        return TabOrderHelper.getFirstTargetNode(engine.getRoot());
+    public Node selectFirst(TraversalContext context) {
+        return TabOrderHelper.getFirstTargetNode(context.getRoot());
     }
 
     @Override
-    public Node selectLast(TraversalEngine engine) {
-        return TabOrderHelper.getLastTargetNode(engine.getRoot());
+    public Node selectLast(TraversalContext context) {
+        return TabOrderHelper.getLastTargetNode(context.getRoot());
     }
 
-    private int trav2D(Bounds origin, Direction dir, List<Node> peers, TraversalEngine engine) {
+    private int trav2D(Bounds origin, Direction dir, List<Node> peers, TraversalContext context) {
 
         Bounds bestBounds = null;
         double bestMetric = 0.0;
         int bestIndex = -1;
 
         for (int i = 0; i < peers.size(); i++) {
-            final Bounds targetBounds = engine.getSceneLayoutBounds(peers.get(i));
+            final Bounds targetBounds = context.getSceneLayoutBounds(peers.get(i));
             final double outd = outDistance(dir, origin, targetBounds);
             final double metric;
 
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Direction.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Direction.java	Mon Mar 17 13:22:16 2014 +0100
@@ -34,6 +34,7 @@
     LEFT(false),
     RIGHT(true),
     NEXT(true),
+    NEXT_IN_LINE(true), // Like NEXT, but does not traverse into the current parent
     PREVIOUS(false);
     private final boolean forward;
 
@@ -44,17 +45,4 @@
     public boolean isForward() {
         return forward;
     }
-
-    public boolean isForward(NodeOrientation orientation) {
-        switch(this) {
-            case LEFT:
-            case RIGHT:
-            case NEXT:
-            case PREVIOUS:
-                return forward ^ (orientation == NodeOrientation.RIGHT_TO_LEFT);
-            default:
-                return forward;
-        }
-
-    }
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Hueristic2D.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/Hueristic2D.java	Mon Mar 17 13:22:16 2014 +0100
@@ -30,16 +30,10 @@
 import javafx.geometry.BoundingBox;
 import javafx.geometry.Bounds;
 import javafx.geometry.Point2D;
-import javafx.scene.Group;
 import javafx.scene.Node;
-import javafx.scene.Parent;
-import com.sun.javafx.Logging;
-import sun.util.logging.PlatformLogger;
-import sun.util.logging.PlatformLogger.Level;
 
 import static com.sun.javafx.scene.traversal.Direction.*;
 import java.util.function.Function;
-import javafx.geometry.Bounds;
 
 
 public class Hueristic2D implements Algorithm {
@@ -48,16 +42,16 @@
     }
 
     @Override
-    public Node select(Node node, Direction dir, TraversalEngine engine) {
+    public Node select(Node node, Direction dir, TraversalContext context) {
         Node newNode = null;
 
-        cacheTraversal(node, dir, engine);
+        cacheTraversal(node, dir);
 
-        if (NEXT.equals(dir)) {
-            newNode = TabOrderHelper.findNextFocusablePeer(node);
+        if (NEXT.equals(dir) || NEXT_IN_LINE.equals(dir)) {
+            newNode = TabOrderHelper.findNextFocusablePeer(node, context.getRoot(), dir == NEXT);
         }
         else if (PREVIOUS.equals(dir)) {
-            newNode = TabOrderHelper.findPreviousFocusablePeer(node);
+            newNode = TabOrderHelper.findPreviousFocusablePeer(node, context.getRoot());
         }
         else if (UP.equals(dir) || DOWN.equals(dir) || LEFT.equals(dir) || RIGHT.equals(dir) ) {
             /*
@@ -79,11 +73,11 @@
                     switch (dir) {
                         case UP:
                         case DOWN:
-                            newNode = getNearestNodeUpOrDown(currentB, cachedB, engine, node, dir);
+                            newNode = getNearestNodeUpOrDown(currentB, cachedB, context, dir);
                             break;
                         case LEFT:
                         case RIGHT:
-                            newNode = getNearestNodeLeftOrRight(currentB, cachedB, engine, node, dir);
+                            newNode = getNearestNodeLeftOrRight(currentB, cachedB, context, dir);
                             break;
                         default:
                             break;
@@ -107,13 +101,13 @@
     }
 
     @Override
-    public Node selectFirst(TraversalEngine engine) {
-        return TabOrderHelper.getFirstTargetNode(engine.getRoot());
+    public Node selectFirst(TraversalContext context) {
+        return TabOrderHelper.getFirstTargetNode(context.getRoot());
     }
 
     @Override
-    public Node selectLast(TraversalEngine engine) {
-        return TabOrderHelper.getLastTargetNode(engine.getRoot());
+    public Node selectLast(TraversalContext context) {
+        return TabOrderHelper.getLastTargetNode(context.getRoot());
     }
 
     private boolean isOnAxis(Direction dir, Bounds cur, Bounds tgt) {
@@ -215,7 +209,7 @@
     protected Node cacheLastTraversalNode = null;
     protected Stack<Node> traversalNodeStack = new Stack();
 
-    private void cacheTraversal(Node node, Direction dir, TraversalEngine engine) {
+    private void cacheTraversal(Node node, Direction dir) {
         if (!traversalNodeStack.empty() && node != cacheLastTraversalNode) {
             /*
             ** we didn't get here by arrow key,
@@ -274,9 +268,9 @@
         }
     };
 
-    protected Node getNearestNodeUpOrDown(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Direction dir) {
+    protected Node getNearestNodeUpOrDown(Bounds currentB, Bounds originB, TraversalContext context, Direction dir) {
 
-        List<Node> nodes = engine.getAllTargetNodes();
+        List<Node> nodes = context.getAllTargetNodes();
         
         Function<Bounds, Double> ySideInDirection = dir == DOWN ? BOUNDS_BOTTOM_SIDE : BOUNDS_TOP_SIDE;
         Function<Bounds, Double> ySideInOpositeDirection = dir == DOWN ? BOUNDS_TOP_SIDE : BOUNDS_BOTTOM_SIDE;
@@ -580,9 +574,9 @@
         }
     };
 
-    protected Node getNearestNodeLeftOrRight(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Direction dir) {
+    protected Node getNearestNodeLeftOrRight(Bounds currentB, Bounds originB, TraversalContext context, Direction dir) {
 
-        List<Node> nodes = engine.getAllTargetNodes();
+        List<Node> nodes = context.getAllTargetNodes();
         
         Function<Bounds, Double> xSideInDirection = dir == LEFT ? BOUNDS_LEFT_SIDE : BOUNDS_RIGHT_SIDE;
         Function<Bounds, Double> xSideInOpositeDirection = dir == LEFT ? BOUNDS_RIGHT_SIDE : BOUNDS_LEFT_SIDE;
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TabOrderHelper.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TabOrderHelper.java	Mon Mar 17 13:22:16 2014 +0100
@@ -65,7 +65,7 @@
         return prevNode.isDisabled() || !prevNode.impl_isTreeVisible();
     }
 
-    public static Node findPreviousFocusablePeer(Node node) {
+    public static Node findPreviousFocusablePeer(Node node, Parent root) {
         Node startNode = node;
         Node newNode = null;
         List<Node> parentNodes = findPeers(startNode);
@@ -85,7 +85,7 @@
         ** we've reached the end of the peer nodes, and none have been selected,
         ** time to look at our parents peers.....
         */
-        while (newNode == null && startNode != null) {
+        while (newNode == null && startNode != root) {
             List<Node> peerNodes;
             int parentIndex;
 
@@ -105,15 +105,6 @@
             startNode = parent;
         }
 
-        // None of the ancestor siblings is traversable, so start from the last traversable item
-        if (newNode == null) {
-            Parent p1 = node.getParent();
-            while (p1 != null && p1.getParent() != null) {
-                p1 = p1.getParent();
-            }
-            newNode =  getLastTargetNode(p1);
-        }
-
         return newNode;
     }
 
@@ -159,12 +150,12 @@
         return null;
     }
 
-    public static Node findNextFocusablePeer(Node node) {
+    public static Node findNextFocusablePeer(Node node, Parent root, boolean traverseIntoCurrent) {
         Node startNode = node;
         Node newNode = null;
 
         // First, try to find next peer among the node children
-        if (node instanceof Parent) {
+        if (traverseIntoCurrent && node instanceof Parent) {
             newNode = findNextFocusableInList(((Parent)node).getChildrenUnmodifiable(), 0);
         }
 
@@ -184,7 +175,7 @@
         ** we've reached the end of the peer nodes, and none have been selected,
         ** time to look at our parents peers.....
         */
-        while (newNode == null && startNode != null) {
+        while (newNode == null && startNode != root) {
             List<Node> peerNodes;
             int parentIndex;
 
@@ -199,15 +190,6 @@
             startNode = parent;
         }
 
-        // None of the ancestors siblings is traversable, so find the first traversable Node from the root
-        if (newNode == null) {
-            Parent p1 = node.getParent();
-            while (p1 != null && p1.getParent() != null) {
-                p1 = p1.getParent();
-            }
-            newNode = getFirstTargetNode(p1);
-        }
-
         return newNode;
     }
 
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TopMostTraversalEngine.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TopMostTraversalEngine.java	Mon Mar 17 13:22:16 2014 +0100
@@ -28,7 +28,6 @@
 import com.sun.javafx.application.PlatformImpl;
 import javafx.scene.Node;
 import javafx.scene.Parent;
-import javafx.scene.Scene;
 
 public abstract class TopMostTraversalEngine extends TraversalEngine{
 
@@ -41,7 +40,7 @@
          * for 2D arrow behaviour with a target bias and a stack use :
          *    algorithm = new Biased2DWithStack();
          */
-        super(PlatformImpl.isContextual2DNavigation() ? new Hueristic2D() : new ContainerTabOrder());
+        super(DEFAULT_ALGORITHM);
     }
 
     public final Node trav(Node node, Direction dir) {
@@ -58,13 +57,23 @@
                     // The inner traversal engine wasn't able to select anything in the specified direction.
                     // So now we try to traverse from the whole parent (associated with that traversal engine)
                     // by a traversal engine that's higher in the hierarchy
-                    traverseNode = dir.isForward() ? engine.selectLast() : p;
+                    traverseNode = p;
+                    if (dir == Direction.NEXT) {
+                        dir = Direction.NEXT_IN_LINE;
+                    }
                 }
             }
             p = p.getParent();
         }
         if (newNode == null) {
-            newNode = algorithm.select(traverseNode, dir, this);
+            newNode = select(traverseNode, dir);
+        }
+        if (newNode == null) {
+            if (dir == Direction.NEXT || dir == Direction.NEXT_IN_LINE) {
+                newNode = selectFirst();
+            } else if (dir == Direction.PREVIOUS) {
+                newNode = selectLast();
+            }
         }
         if (newNode != null) {
             focusAndNotify(newNode);
@@ -90,14 +99,14 @@
     }
 
     public final Node traverseToFirst() {
-        Node n = algorithm.selectFirst(this);
+        Node n = selectFirst();
         if (n != null) focusAndNotify(n);
         return n;
     }
 
 
     public final Node traverseToLast() {
-        Node n = algorithm.selectLast(this);
+        Node n = selectLast();
         if (n != null) focusAndNotify(n);
         return n;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TraversalContext.java	Mon Mar 17 13:22:16 2014 +0100
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.scene.traversal;
+
+import javafx.geometry.Bounds;
+import javafx.scene.Node;
+import javafx.scene.Parent;
+
+import java.util.List;
+
+public interface TraversalContext {
+
+    /**
+     * Returns all possible targets within the context
+     */
+    List<Node> getAllTargetNodes();
+
+    /**
+     * Returns layout bounds of the Node in the relevant (Sub)Scene
+     */
+    Bounds getSceneLayoutBounds(Node node);
+
+    /**
+     * The root for this context, Traversal should be done only within the root
+     */
+    Parent getRoot();
+
+    /**
+     * If the TraversalEngine does not want to handle traversal inside some inner child (Parent), it can use this method to apply
+     * default algorithm inside that Parent and return the first Node
+     */
+    Node selectFirstInParent(Parent parent);
+
+    /**
+     * If the TraversalEngine does not want to handle traversal inside some inner child (Parent), it can use this method to apply
+     * default algorithm inside that Parent and return the last Node
+     */
+    Node selectLastInParent(Parent parent);
+
+    /**
+     * If the TraversalEngine does not want to handle traversal inside some inner child (Parent), it can use this method to apply
+     * default algorithm inside that Parent and return the next Node within the Parent or null if there's no successor.
+     * @param  subTreeRoot this will be used as a root of the traversal. Should be a Node that is still handled by the current TraversalEngine,
+     *                     but it's content is not.
+     */
+    Node selectInSubtree(Parent subTreeRoot, Node from, Direction dir);
+}
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TraversalEngine.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/TraversalEngine.java	Mon Mar 17 13:22:16 2014 +0100
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.scene.traversal;
 
+import com.sun.javafx.application.PlatformImpl;
 import javafx.geometry.BoundingBox;
 import javafx.geometry.Bounds;
 import javafx.scene.Node;
@@ -33,12 +34,16 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public abstract class TraversalEngine {
+public abstract class TraversalEngine{
 
+    static final Algorithm DEFAULT_ALGORITHM = PlatformImpl.isContextual2DNavigation() ? new Hueristic2D() : new ContainerTabOrder();
+
+    private final TraversalContext context = new EngineContext();
+    private final TempEngineContext tempEngineContext = new TempEngineContext();
     protected final Algorithm algorithm;
 
     private final Bounds initialBounds =  new BoundingBox(0, 0, 1, 1);
-    final ArrayList<TraverseListener> listeners = new ArrayList<>();
+    private final ArrayList<TraverseListener> listeners = new ArrayList<>();
 
     protected TraversalEngine(Algorithm algorithm) {
         this.algorithm = algorithm;
@@ -52,49 +57,26 @@
         listeners.add(listener);
     }
 
-    void notifyTraversedTo(Node newNode) {
+    final void notifyTraversedTo(Node newNode) {
         for (TraverseListener l : listeners) {
             l.onTraverse(newNode, getLayoutBounds(newNode, getRoot()));
         }
     }
 
     public final Node select(Node from, Direction dir) {
-        return algorithm.select(from, dir, this);
+        return algorithm.select(from, dir, context);
     }
 
     public final Node selectFirst() {
-        return algorithm.selectFirst(this);
+        return algorithm.selectFirst(context);
     }
 
     public final Node selectLast() {
-        return algorithm.selectLast(this);
-    }
-
-    // get all focusable nodes in tree...
-    public final List<Node> getAllTargetNodes() {
-        final List<Node> targetNodes = new ArrayList<>();
-        addFocusableChildrenToList(targetNodes, getRoot());
-        return targetNodes;
-    }
-
-    private void addFocusableChildrenToList(List<Node> list, Parent parent) {
-        List<Node> parentsNodes = parent.getChildrenUnmodifiable();
-        for (Node n : parentsNodes) {
-            if (n.isFocusTraversable() && !n.isFocused() && n.impl_isTreeVisible() && !n.isDisabled()) {
-                list.add(n);
-            }
-            if (n instanceof Parent) {
-                addFocusableChildrenToList(list, (Parent)n);
-            }
-        }
+        return algorithm.selectLast(context);
     }
 
     protected abstract Parent getRoot();
 
-    public final Bounds getSceneLayoutBounds(Node n) {
-        return getLayoutBounds(n, null);
-    }
-
     public final boolean canTraverse() {
         return algorithm != null;
     }
@@ -116,4 +98,68 @@
         }
         return bounds;
     }
+
+    private final class EngineContext extends BaseEngineContext {
+        @Override
+        public Parent getRoot() {
+            return TraversalEngine.this.getRoot();
+        }
+    }
+
+    private final class TempEngineContext extends BaseEngineContext {
+        private Parent root;
+
+        @Override
+        public Parent getRoot() {
+            return root;
+        }
+
+        public void setRoot(Parent root) {
+            this.root = root;
+        }
+    }
+
+    private abstract class BaseEngineContext implements TraversalContext {
+
+        @Override
+        public List<Node> getAllTargetNodes() {
+            final List<Node> targetNodes = new ArrayList<>();
+            addFocusableChildrenToList(targetNodes, getRoot());
+            return targetNodes;
+        }
+
+        public Bounds getSceneLayoutBounds(Node n) {
+            return getLayoutBounds(n, null);
+        }
+
+        private void addFocusableChildrenToList(List<Node> list, Parent parent) {
+            List<Node> parentsNodes = parent.getChildrenUnmodifiable();
+            for (Node n : parentsNodes) {
+                if (n.isFocusTraversable() && !n.isFocused() && n.impl_isTreeVisible() && !n.isDisabled()) {
+                    list.add(n);
+                }
+                if (n instanceof Parent) {
+                    addFocusableChildrenToList(list, (Parent)n);
+                }
+            }
+        }
+
+        @Override
+        public Node selectFirstInParent(Parent parent) {
+            tempEngineContext.setRoot(parent);
+            return DEFAULT_ALGORITHM.selectFirst(tempEngineContext);
+        }
+
+        @Override
+        public Node selectLastInParent(Parent parent) {
+            tempEngineContext.setRoot(parent);
+            return DEFAULT_ALGORITHM.selectLast(tempEngineContext);
+        }
+
+        @Override
+        public Node selectInSubtree(Parent subTreeRoot, Node from, Direction dir) {
+            tempEngineContext.setRoot(subTreeRoot);
+            return DEFAULT_ALGORITHM.select(from, dir, tempEngineContext);
+        }
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/WeightedClosestCorner.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/traversal/WeightedClosestCorner.java	Mon Mar 17 13:22:16 2014 +0100
@@ -29,9 +29,6 @@
 import javafx.geometry.Bounds;
 import javafx.geometry.Point2D;
 import javafx.scene.Node;
-import com.sun.javafx.Logging;
-import sun.util.logging.PlatformLogger;
-import sun.util.logging.PlatformLogger.Level;
 
 import static com.sun.javafx.scene.traversal.Direction.*;
 
@@ -163,11 +160,11 @@
         return distance;
     }
 
-    public Node select(Node node, Direction dir, TraversalEngine engine) {
+    public Node select(Node node, Direction dir, TraversalContext context) {
         Node newNode = null;
-        List<Node> nodes = engine.getAllTargetNodes();
+        List<Node> nodes = context.getAllTargetNodes();
 
-        int target = traverse(engine.getSceneLayoutBounds(node), dir, nodes, engine);
+        int target = traverse(context.getSceneLayoutBounds(node), dir, nodes, context);
         if (target != -1) {
             newNode = nodes.get(target);
         }
@@ -176,20 +173,20 @@
     }
 
     @Override
-    public Node selectFirst(TraversalEngine engine) {
-        List<Node> nodes = engine.getAllTargetNodes();
+    public Node selectFirst(TraversalContext context) {
+        List<Node> nodes = context.getAllTargetNodes();
         Point2D zeroZero = new Point2D(0,0);
 
         if (nodes.size() > 0) {
             int nodeIndex;
             Node nearestNode = nodes.get(0);
-            double nearestDistance = zeroZero.distance(engine.getSceneLayoutBounds(nodes.get(0)).getMinX(),
-                    engine.getSceneLayoutBounds(nodes.get(0)).getMinY());
+            double nearestDistance = zeroZero.distance(context.getSceneLayoutBounds(nodes.get(0)).getMinX(),
+                    context.getSceneLayoutBounds(nodes.get(0)).getMinY());
             double distance;
 
             for (nodeIndex = 1; nodeIndex < nodes.size(); nodeIndex++) {
-                distance = zeroZero.distance(engine.getSceneLayoutBounds(nodes.get(nodeIndex)).getMinX(),
-                        engine.getSceneLayoutBounds(nodes.get(nodeIndex)).getMinY());
+                distance = zeroZero.distance(context.getSceneLayoutBounds(nodes.get(nodeIndex)).getMinX(),
+                        context.getSceneLayoutBounds(nodes.get(nodeIndex)).getMinY());
                 if (nearestDistance > distance) {
                     nearestDistance = distance;
                     nearestNode = nodes.get(nodeIndex);
@@ -201,31 +198,31 @@
     }
 
     @Override
-    public Node selectLast(TraversalEngine engine) {
+    public Node selectLast(TraversalContext context) {
         return null;
     }
 
-    public int traverse(Bounds origin, Direction dir, List<Node> targets, TraversalEngine engine) {
+    public int traverse(Bounds origin, Direction dir, List<Node> targets, TraversalContext context) {
 
         final int target;
 
-        if (dir == NEXT || dir == PREVIOUS) {
-            target = trav1D(origin, dir, targets, engine);
+        if (dir == NEXT || dir == NEXT_IN_LINE || dir == PREVIOUS) {
+            target = trav1D(origin, dir, targets, context);
         } else {
-            target = trav2D(origin, dir, targets, engine);
+            target = trav2D(origin, dir, targets, context);
         }
 
         return target;
     }
 
-    private int trav2D(Bounds origin, Direction dir, List<Node> targets, TraversalEngine engine) {
+    private int trav2D(Bounds origin, Direction dir, List<Node> targets, TraversalContext context) {
 
         Bounds bestBounds = null;
         double bestMetric = 0.0;
         int bestIndex = -1;
 
         for (int i = 0; i < targets.size(); i++) {
-            final Bounds targetBounds = engine.getSceneLayoutBounds(targets.get(i));
+            final Bounds targetBounds = context.getSceneLayoutBounds(targets.get(i));
             final double outd = outDistance(dir, origin, targetBounds);
             final double metric;
 
@@ -291,26 +288,26 @@
 
 
     private int compare1D(Bounds a, Bounds b, Direction dir) {
-        return (dir == NEXT) ? compare1D(a, b) : -compare1D(a, b);
+        return (dir != PREVIOUS) ? -compare1D(a, b) : compare1D(a, b);
     }
 
-    private int trav1D(Bounds origin, Direction dir, List<Node> targets, TraversalEngine engine) {
+    private int trav1D(Bounds origin, Direction dir, List<Node> targets, TraversalContext context) {
         int bestSoFar = -1;
         int leastSoFar = -1;
 
         for (int i = 0; i < targets.size(); i++) {
             if (leastSoFar == -1 ||
-                    compare1D(engine.getSceneLayoutBounds(targets.get(i)),
-                            engine.getSceneLayoutBounds(targets.get(leastSoFar)), dir) < 0) {
+                    compare1D(context.getSceneLayoutBounds(targets.get(i)),
+                            context.getSceneLayoutBounds(targets.get(leastSoFar)), dir) < 0) {
                 leastSoFar = i;
             }
 
-            if (compare1D(engine.getSceneLayoutBounds(targets.get(i)), origin, dir) < 0) {
+            if (compare1D(context.getSceneLayoutBounds(targets.get(i)), origin, dir) < 0) {
                 continue;
             }
 
             if (bestSoFar == -1 ||
-                    compare1D(engine.getSceneLayoutBounds(targets.get(i)), engine.getSceneLayoutBounds(targets.get(bestSoFar)), dir) < 0) {
+                    compare1D(context.getSceneLayoutBounds(targets.get(i)), context.getSceneLayoutBounds(targets.get(bestSoFar)), dir) < 0) {
                 bestSoFar = i;
             }
         }
--- a/modules/web/src/main/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java	Mon Mar 17 14:14:38 2014 +0400
+++ b/modules/web/src/main/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java	Mon Mar 17 13:22:16 2014 +0100
@@ -31,6 +31,7 @@
 import com.sun.javafx.scene.traversal.Algorithm;
 import com.sun.javafx.scene.traversal.Direction;
 import com.sun.javafx.scene.traversal.ParentTraversalEngine;
+import com.sun.javafx.scene.traversal.TraversalContext;
 import org.w3c.dom.html.HTMLDocument;
 import org.w3c.dom.html.HTMLElement;
 
@@ -43,7 +44,6 @@
 import javafx.css.StyleableProperty;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
-import javafx.geometry.Bounds;
 import javafx.geometry.NodeOrientation;
 import javafx.scene.Node;
 import javafx.scene.control.Button;
@@ -73,8 +73,6 @@
 import com.sun.javafx.scene.control.skin.ColorPickerSkin;
 import com.sun.javafx.scene.control.skin.FXVK;
 import com.sun.javafx.scene.web.behavior.HTMLEditorBehavior;
-import com.sun.javafx.scene.traversal.TraversalEngine;
-import com.sun.javafx.scene.traversal.TraverseListener;
 import com.sun.webkit.WebPage;
 import com.sun.javafx.webkit.Accessor;
 
@@ -450,17 +448,17 @@
 
         engine = new ParentTraversalEngine(getSkinnable(), new Algorithm() {
             @Override
-            public Node select(Node owner, Direction dir, TraversalEngine engine) {
+            public Node select(Node owner, Direction dir, TraversalContext context) {
                 return cutButton;
             }
 
             @Override
-            public Node selectFirst(TraversalEngine engine) {
+            public Node selectFirst(TraversalContext context) {
                 return cutButton;
             }
 
             @Override
-            public Node selectLast(TraversalEngine engine) {
+            public Node selectLast(TraversalContext context) {
                 return cutButton;
             }
         });