Index: Source/core/page/SpatialNavigation.cpp |
diff --git a/Source/core/page/SpatialNavigation.cpp b/Source/core/page/SpatialNavigation.cpp |
index b18a7ec1d7569ce2478d0052aecb3f1130b59bb2..31a70ec6557fade88ced2e765db5eb9675eff3a6 100644 |
--- a/Source/core/page/SpatialNavigation.cpp |
+++ b/Source/core/page/SpatialNavigation.cpp |
@@ -45,11 +45,6 @@ namespace blink { |
using namespace HTMLNames; |
-static RectsAlignment alignmentForRects(WebFocusType, const LayoutRect&, const LayoutRect&, const LayoutSize& viewSize); |
-static bool areRectsFullyAligned(WebFocusType, const LayoutRect&, const LayoutRect&); |
-static bool areRectsPartiallyAligned(WebFocusType, const LayoutRect&, const LayoutRect&); |
-static bool areRectsMoreThanFullScreenApart(WebFocusType, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize); |
-static bool isRectInDirection(WebFocusType, const LayoutRect&, const LayoutRect&); |
static void deflateIfOverlapped(LayoutRect&, LayoutRect&); |
static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const LayoutRect&); |
static bool isScrollableNode(const Node*); |
@@ -59,7 +54,6 @@ FocusCandidate::FocusCandidate(Node* node, WebFocusType type) |
, focusableNode(nullptr) |
, enclosingScrollableBox(nullptr) |
, distance(maxDistance()) |
- , alignment(None) |
, isOffscreen(true) |
, isOffscreenAfterScrolling(true) |
{ |
@@ -92,154 +86,19 @@ bool isSpatialNavigationEnabled(const LocalFrame* frame) |
return (frame && frame->settings() && frame->settings()->spatialNavigationEnabled()); |
} |
-static RectsAlignment alignmentForRects(WebFocusType type, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize) |
+static bool rectsIntersectOnOrthogonalAxis(WebFocusType type, const LayoutRect& a, const LayoutRect& b) |
{ |
- // If we found a node in full alignment, but it is too far away, ignore it. |
- if (areRectsMoreThanFullScreenApart(type, curRect, targetRect, viewSize)) |
- return None; |
- |
- if (areRectsFullyAligned(type, curRect, targetRect)) |
- return Full; |
- |
- if (areRectsPartiallyAligned(type, curRect, targetRect)) |
- return Partial; |
- |
- return None; |
-} |
- |
-static inline bool isHorizontalMove(WebFocusType type) |
-{ |
- return type == WebFocusTypeLeft || type == WebFocusTypeRight; |
-} |
- |
-static inline LayoutUnit start(WebFocusType type, const LayoutRect& rect) |
-{ |
- return isHorizontalMove(type) ? rect.y() : rect.x(); |
-} |
- |
-static inline LayoutUnit middle(WebFocusType type, const LayoutRect& rect) |
-{ |
- LayoutPoint center(rect.center()); |
- return isHorizontalMove(type) ? center.y(): center.x(); |
-} |
- |
-static inline LayoutUnit end(WebFocusType type, const LayoutRect& rect) |
-{ |
- return isHorizontalMove(type) ? rect.maxY() : rect.maxX(); |
-} |
- |
-// This method checks if rects |a| and |b| are fully aligned either vertically or |
-// horizontally. In general, rects whose central point falls between the top or |
-// bottom of each other are considered fully aligned. |
-// Rects that match this criteria are preferable target nodes in move focus changing |
-// operations. |
-// * a = Current focused node's rect. |
-// * b = Focus candidate node's rect. |
-static bool areRectsFullyAligned(WebFocusType type, const LayoutRect& a, const LayoutRect& b) |
-{ |
- LayoutUnit aStart, bStart, aEnd, bEnd; |
- |
switch (type) { |
case WebFocusTypeLeft: |
- aStart = a.x(); |
- bEnd = b.x(); |
- break; |
case WebFocusTypeRight: |
- aStart = b.x(); |
- bEnd = a.x(); |
- break; |
+ return a.maxY() > b.y() && a.y() < b.maxY(); |
case WebFocusTypeUp: |
- aStart = a.y(); |
- bEnd = b.y(); |
- break; |
case WebFocusTypeDown: |
- aStart = b.y(); |
- bEnd = a.y(); |
- break; |
+ return a.maxX() > b.x() && a.x() < b.maxX(); |
default: |
ASSERT_NOT_REACHED(); |
return false; |
} |
- |
- if (aStart < bEnd) |
- return false; |
- |
- aStart = start(type, a); |
- bStart = start(type, b); |
- |
- LayoutUnit aMiddle = middle(type, a); |
- LayoutUnit bMiddle = middle(type, b); |
- |
- aEnd = end(type, a); |
- bEnd = end(type, b); |
- |
- // Picture of the totally aligned logic: |
- // |
- // Horizontal Vertical Horizontal Vertical |
- // **************************** ***************************** |
- // * _ * _ _ _ _ * * _ * _ _ * |
- // * |_| _ * |_|_|_|_| * * _ |_| * |_|_| * |
- // * |_|....|_| * . * * |_|....|_| * . * |
- // * |_| |_| (1) . * * |_| |_| (2) . * |
- // * |_| * _._ * * |_| * _ _._ _ * |
- // * * |_|_| * * * |_|_|_|_| * |
- // * * * * * * |
- // **************************** ***************************** |
- |
- return (bMiddle >= aStart && bMiddle <= aEnd) // (1) |
- || (aMiddle >= bStart && aMiddle <= bEnd); // (2) |
-} |
- |
-// This method checks if rects |a| and |b| are partially aligned either vertically or |
-// horizontally. In general, rects whose either of edges falls between the top or |
-// bottom of each other are considered partially-aligned. |
-// This is a separate set of conditions from "fully-aligned" and do not include cases |
-// that satisfy the former. |
-// * a = Current focused node's rect. |
-// * b = Focus candidate node's rect. |
-static bool areRectsPartiallyAligned(WebFocusType type, const LayoutRect& a, const LayoutRect& b) |
-{ |
- LayoutUnit aStart = start(type, a); |
- LayoutUnit bStart = start(type, b); |
- LayoutUnit aEnd = end(type, a); |
- LayoutUnit bEnd = end(type, b); |
- |
- // Picture of the partially aligned logic: |
- // |
- // Horizontal Vertical |
- // ******************************** |
- // * _ * _ _ _ * |
- // * |_| * |_|_|_| * |
- // * |_|.... _ * . . * |
- // * |_| |_| * . . * |
- // * |_|....|_| * ._._ _ * |
- // * |_| * |_|_|_| * |
- // * |_| * * |
- // * * * |
- // ******************************** |
- // |
- // ... and variants of the above cases. |
- return (bStart >= aStart && bStart <= aEnd) |
- || (bEnd >= aStart && bEnd <= aEnd); |
-} |
- |
-static bool areRectsMoreThanFullScreenApart(WebFocusType type, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize) |
-{ |
- ASSERT(isRectInDirection(type, curRect, targetRect)); |
- |
- switch (type) { |
- case WebFocusTypeLeft: |
- return curRect.x() - targetRect.maxX() > viewSize.width(); |
- case WebFocusTypeRight: |
- return targetRect.x() - curRect.maxX() > viewSize.width(); |
- case WebFocusTypeUp: |
- return curRect.y() - targetRect.maxY() > viewSize.height(); |
- case WebFocusTypeDown: |
- return targetRect.y() - curRect.maxY() > viewSize.height(); |
- default: |
- ASSERT_NOT_REACHED(); |
- return true; |
- } |
} |
// Return true if rect |a| is below |b|. False otherwise. |
@@ -644,7 +503,6 @@ void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus |
if (areElementsOnSameLine(current, candidate)) { |
if ((type == WebFocusTypeUp && current.rect.y() > candidate.rect.y()) || (type == WebFocusTypeDown && candidate.rect.y() > current.rect.y())) { |
candidate.distance = 0; |
- candidate.alignment = Full; |
return; |
} |
} |
@@ -660,22 +518,37 @@ void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus |
LayoutPoint entryPoint; |
entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entryPoint); |
- LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); |
- LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); |
+ LayoutUnit xAxis = (exitPoint.x() - entryPoint.x()).abs(); |
+ LayoutUnit yAxis = (exitPoint.y() - entryPoint.y()).abs(); |
LayoutUnit navigationAxisDistance; |
- LayoutUnit orthogonalAxisDistance; |
+ LayoutUnit weightedOrthogonalAxisDistance; |
+ |
+ // Bias and weights are put to the orthogonal axis distance calculation |
+ // so aligned candidates would have advantage over partially-aligned ones |
+ // and then over not-aligned candidates. The bias is given to not-aligned |
+ // candidates with respect to size of the current rect. The weight for |
+ // left/right direction is given a higher value to allow navigation on |
+ // common horizonally-aligned elements. The hardcoded values are based on |
+ // tests and experiments. |
+ const int orthogonalWeightForLeftRight = 30; |
+ const int orthogonalWeightForUpDown = 2; |
+ int orthogonalBias = 0; |
switch (type) { |
case WebFocusTypeLeft: |
case WebFocusTypeRight: |
- navigationAxisDistance = xAxis.abs(); |
- orthogonalAxisDistance = yAxis.abs(); |
+ navigationAxisDistance = xAxis; |
+ if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect)) |
+ orthogonalBias = currentRect.height() / 2; |
+ weightedOrthogonalAxisDistance = (yAxis + orthogonalBias) * orthogonalWeightForLeftRight; |
break; |
case WebFocusTypeUp: |
case WebFocusTypeDown: |
- navigationAxisDistance = yAxis.abs(); |
- orthogonalAxisDistance = xAxis.abs(); |
+ navigationAxisDistance = yAxis; |
+ if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect)) |
+ orthogonalBias = currentRect.width() / 2; |
+ weightedOrthogonalAxisDistance = (xAxis + orthogonalBias) * orthogonalWeightForUpDown; |
break; |
default: |
ASSERT_NOT_REACHED(); |
@@ -687,10 +560,7 @@ void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus |
double overlap = (intersectionRect.width() * intersectionRect.height()).toDouble(); |
// Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handling |
- candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance+ orthogonalAxisDistance * 2 - sqrt(overlap); |
- |
- LayoutSize viewSize = LayoutSize(candidate.visibleNode->document().page()->deprecatedLocalMainFrame()->view()->visibleContentRect().size()); |
- candidate.alignment = alignmentForRects(type, currentRect, nodeRect, viewSize); |
+ candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance + weightedOrthogonalAxisDistance - sqrt(overlap); |
} |
bool canBeScrolledIntoView(WebFocusType type, const FocusCandidate& candidate) |