Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(762)

Side by Side Diff: Source/core/page/SpatialNavigation.cpp

Issue 797463003: spatnav: Allow focus move to a close-by not-fully-aligned node over a distant but fully-aligned nod… (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebaseline Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/core/page/SpatialNavigation.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) 2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2009 Antonio Gomes <tonikitoo@webkit.org> 3 * Copyright (C) 2009 Antonio Gomes <tonikitoo@webkit.org>
4 * 4 *
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
(...skipping 27 matching lines...) Expand all
38 #include "core/html/HTMLImageElement.h" 38 #include "core/html/HTMLImageElement.h"
39 #include "core/page/FrameTree.h" 39 #include "core/page/FrameTree.h"
40 #include "core/page/Page.h" 40 #include "core/page/Page.h"
41 #include "core/rendering/RenderLayer.h" 41 #include "core/rendering/RenderLayer.h"
42 #include "platform/geometry/IntRect.h" 42 #include "platform/geometry/IntRect.h"
43 43
44 namespace blink { 44 namespace blink {
45 45
46 using namespace HTMLNames; 46 using namespace HTMLNames;
47 47
48 static RectsAlignment alignmentForRects(WebFocusType, const LayoutRect&, const L ayoutRect&, const LayoutSize& viewSize);
49 static bool areRectsFullyAligned(WebFocusType, const LayoutRect&, const LayoutRe ct&);
50 static bool areRectsPartiallyAligned(WebFocusType, const LayoutRect&, const Layo utRect&);
51 static bool areRectsMoreThanFullScreenApart(WebFocusType, const LayoutRect& curR ect, const LayoutRect& targetRect, const LayoutSize& viewSize);
52 static bool isRectInDirection(WebFocusType, const LayoutRect&, const LayoutRect& );
53 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); 48 static void deflateIfOverlapped(LayoutRect&, LayoutRect&);
54 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&); 49 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&);
55 static bool isScrollableNode(const Node*); 50 static bool isScrollableNode(const Node*);
56 51
57 FocusCandidate::FocusCandidate(Node* node, WebFocusType type) 52 FocusCandidate::FocusCandidate(Node* node, WebFocusType type)
58 : visibleNode(nullptr) 53 : visibleNode(nullptr)
59 , focusableNode(nullptr) 54 , focusableNode(nullptr)
60 , enclosingScrollableBox(nullptr) 55 , enclosingScrollableBox(nullptr)
61 , distance(maxDistance()) 56 , distance(maxDistance())
62 , alignment(None)
63 , isOffscreen(true) 57 , isOffscreen(true)
64 , isOffscreenAfterScrolling(true) 58 , isOffscreenAfterScrolling(true)
65 { 59 {
66 ASSERT(node); 60 ASSERT(node);
67 ASSERT(node->isElementNode()); 61 ASSERT(node->isElementNode());
68 62
69 if (isHTMLAreaElement(*node)) { 63 if (isHTMLAreaElement(*node)) {
70 HTMLAreaElement& area = toHTMLAreaElement(*node); 64 HTMLAreaElement& area = toHTMLAreaElement(*node);
71 HTMLImageElement* image = area.imageElement(); 65 HTMLImageElement* image = area.imageElement();
72 if (!image || !image->renderer()) 66 if (!image || !image->renderer())
(...skipping 12 matching lines...) Expand all
85 focusableNode = node; 79 focusableNode = node;
86 isOffscreen = hasOffscreenRect(visibleNode); 80 isOffscreen = hasOffscreenRect(visibleNode);
87 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); 81 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type);
88 } 82 }
89 83
90 bool isSpatialNavigationEnabled(const LocalFrame* frame) 84 bool isSpatialNavigationEnabled(const LocalFrame* frame)
91 { 85 {
92 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled()); 86 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled());
93 } 87 }
94 88
95 static RectsAlignment alignmentForRects(WebFocusType type, const LayoutRect& cur Rect, const LayoutRect& targetRect, const LayoutSize& viewSize) 89 static bool rectsIntersectOnOrthogonalAxis(WebFocusType type, const LayoutRect& a, const LayoutRect& b)
96 { 90 {
97 // If we found a node in full alignment, but it is too far away, ignore it.
98 if (areRectsMoreThanFullScreenApart(type, curRect, targetRect, viewSize))
99 return None;
100
101 if (areRectsFullyAligned(type, curRect, targetRect))
102 return Full;
103
104 if (areRectsPartiallyAligned(type, curRect, targetRect))
105 return Partial;
106
107 return None;
108 }
109
110 static inline bool isHorizontalMove(WebFocusType type)
111 {
112 return type == WebFocusTypeLeft || type == WebFocusTypeRight;
113 }
114
115 static inline LayoutUnit start(WebFocusType type, const LayoutRect& rect)
116 {
117 return isHorizontalMove(type) ? rect.y() : rect.x();
118 }
119
120 static inline LayoutUnit middle(WebFocusType type, const LayoutRect& rect)
121 {
122 LayoutPoint center(rect.center());
123 return isHorizontalMove(type) ? center.y(): center.x();
124 }
125
126 static inline LayoutUnit end(WebFocusType type, const LayoutRect& rect)
127 {
128 return isHorizontalMove(type) ? rect.maxY() : rect.maxX();
129 }
130
131 // This method checks if rects |a| and |b| are fully aligned either vertically o r
132 // horizontally. In general, rects whose central point falls between the top or
133 // bottom of each other are considered fully aligned.
134 // Rects that match this criteria are preferable target nodes in move focus chan ging
135 // operations.
136 // * a = Current focused node's rect.
137 // * b = Focus candidate node's rect.
138 static bool areRectsFullyAligned(WebFocusType type, const LayoutRect& a, const L ayoutRect& b)
139 {
140 LayoutUnit aStart, bStart, aEnd, bEnd;
141
142 switch (type) { 91 switch (type) {
143 case WebFocusTypeLeft: 92 case WebFocusTypeLeft:
144 aStart = a.x();
145 bEnd = b.x();
146 break;
147 case WebFocusTypeRight: 93 case WebFocusTypeRight:
148 aStart = b.x(); 94 return a.maxY() > b.y() && a.y() < b.maxY();
149 bEnd = a.x();
150 break;
151 case WebFocusTypeUp: 95 case WebFocusTypeUp:
152 aStart = a.y();
153 bEnd = b.y();
154 break;
155 case WebFocusTypeDown: 96 case WebFocusTypeDown:
156 aStart = b.y(); 97 return a.maxX() > b.x() && a.x() < b.maxX();
157 bEnd = a.y();
158 break;
159 default: 98 default:
160 ASSERT_NOT_REACHED(); 99 ASSERT_NOT_REACHED();
161 return false; 100 return false;
162 } 101 }
163
164 if (aStart < bEnd)
165 return false;
166
167 aStart = start(type, a);
168 bStart = start(type, b);
169
170 LayoutUnit aMiddle = middle(type, a);
171 LayoutUnit bMiddle = middle(type, b);
172
173 aEnd = end(type, a);
174 bEnd = end(type, b);
175
176 // Picture of the totally aligned logic:
177 //
178 // Horizontal Vertical Horizontal Vertical
179 // **************************** *****************************
180 // * _ * _ _ _ _ * * _ * _ _ *
181 // * |_| _ * |_|_|_|_| * * _ |_| * |_|_| *
182 // * |_|....|_| * . * * |_|....|_| * . *
183 // * |_| |_| (1) . * * |_| |_| (2) . *
184 // * |_| * _._ * * |_| * _ _._ _ *
185 // * * |_|_| * * * |_|_|_|_| *
186 // * * * * * *
187 // **************************** *****************************
188
189 return (bMiddle >= aStart && bMiddle <= aEnd) // (1)
190 || (aMiddle >= bStart && aMiddle <= bEnd); // (2)
191 }
192
193 // This method checks if rects |a| and |b| are partially aligned either vertical ly or
194 // horizontally. In general, rects whose either of edges falls between the top o r
195 // bottom of each other are considered partially-aligned.
196 // This is a separate set of conditions from "fully-aligned" and do not include cases
197 // that satisfy the former.
198 // * a = Current focused node's rect.
199 // * b = Focus candidate node's rect.
200 static bool areRectsPartiallyAligned(WebFocusType type, const LayoutRect& a, con st LayoutRect& b)
201 {
202 LayoutUnit aStart = start(type, a);
203 LayoutUnit bStart = start(type, b);
204 LayoutUnit aEnd = end(type, a);
205 LayoutUnit bEnd = end(type, b);
206
207 // Picture of the partially aligned logic:
208 //
209 // Horizontal Vertical
210 // ********************************
211 // * _ * _ _ _ *
212 // * |_| * |_|_|_| *
213 // * |_|.... _ * . . *
214 // * |_| |_| * . . *
215 // * |_|....|_| * ._._ _ *
216 // * |_| * |_|_|_| *
217 // * |_| * *
218 // * * *
219 // ********************************
220 //
221 // ... and variants of the above cases.
222 return (bStart >= aStart && bStart <= aEnd)
223 || (bEnd >= aStart && bEnd <= aEnd);
224 }
225
226 static bool areRectsMoreThanFullScreenApart(WebFocusType type, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize)
227 {
228 ASSERT(isRectInDirection(type, curRect, targetRect));
229
230 switch (type) {
231 case WebFocusTypeLeft:
232 return curRect.x() - targetRect.maxX() > viewSize.width();
233 case WebFocusTypeRight:
234 return targetRect.x() - curRect.maxX() > viewSize.width();
235 case WebFocusTypeUp:
236 return curRect.y() - targetRect.maxY() > viewSize.height();
237 case WebFocusTypeDown:
238 return targetRect.y() - curRect.maxY() > viewSize.height();
239 default:
240 ASSERT_NOT_REACHED();
241 return true;
242 }
243 } 102 }
244 103
245 // Return true if rect |a| is below |b|. False otherwise. 104 // Return true if rect |a| is below |b|. False otherwise.
246 // For overlapping rects, |a| is considered to be below |b| 105 // For overlapping rects, |a| is considered to be below |b|
247 // if both edges of |a| are below the respective ones of |b| 106 // if both edges of |a| are below the respective ones of |b|
248 static inline bool below(const LayoutRect& a, const LayoutRect& b) 107 static inline bool below(const LayoutRect& a, const LayoutRect& b)
249 { 108 {
250 return a.y() >= b.maxY() 109 return a.y() >= b.maxY()
251 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX( ) > b.x()); 110 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX( ) > b.x());
252 } 111 }
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after
637 return false; 496 return false;
638 497
639 return true; 498 return true;
640 } 499 }
641 500
642 void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus Candidate& candidate) 501 void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus Candidate& candidate)
643 { 502 {
644 if (areElementsOnSameLine(current, candidate)) { 503 if (areElementsOnSameLine(current, candidate)) {
645 if ((type == WebFocusTypeUp && current.rect.y() > candidate.rect.y()) || (type == WebFocusTypeDown && candidate.rect.y() > current.rect.y())) { 504 if ((type == WebFocusTypeUp && current.rect.y() > candidate.rect.y()) || (type == WebFocusTypeDown && candidate.rect.y() > current.rect.y())) {
646 candidate.distance = 0; 505 candidate.distance = 0;
647 candidate.alignment = Full;
648 return; 506 return;
649 } 507 }
650 } 508 }
651 509
652 LayoutRect nodeRect = candidate.rect; 510 LayoutRect nodeRect = candidate.rect;
653 LayoutRect currentRect = current.rect; 511 LayoutRect currentRect = current.rect;
654 deflateIfOverlapped(currentRect, nodeRect); 512 deflateIfOverlapped(currentRect, nodeRect);
655 513
656 if (!isRectInDirection(type, currentRect, nodeRect)) 514 if (!isRectInDirection(type, currentRect, nodeRect))
657 return; 515 return;
658 516
659 LayoutPoint exitPoint; 517 LayoutPoint exitPoint;
660 LayoutPoint entryPoint; 518 LayoutPoint entryPoint;
661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point); 519 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point);
662 520
663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); 521 LayoutUnit xAxis = (exitPoint.x() - entryPoint.x()).abs();
664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); 522 LayoutUnit yAxis = (exitPoint.y() - entryPoint.y()).abs();
665 523
666 LayoutUnit navigationAxisDistance; 524 LayoutUnit navigationAxisDistance;
667 LayoutUnit orthogonalAxisDistance; 525 LayoutUnit weightedOrthogonalAxisDistance;
526
527 // Bias and weights are put to the orthogonal axis distance calculation
528 // so aligned candidates would have advantage over partially-aligned ones
529 // and then over not-aligned candidates. The bias is given to not-aligned
530 // candidates with respect to size of the current rect. The weight for
531 // left/right direction is given a higher value to allow navigation on
532 // common horizonally-aligned elements. The hardcoded values are based on
533 // tests and experiments.
534 const int orthogonalWeightForLeftRight = 30;
535 const int orthogonalWeightForUpDown = 2;
536 int orthogonalBias = 0;
668 537
669 switch (type) { 538 switch (type) {
670 case WebFocusTypeLeft: 539 case WebFocusTypeLeft:
671 case WebFocusTypeRight: 540 case WebFocusTypeRight:
672 navigationAxisDistance = xAxis.abs(); 541 navigationAxisDistance = xAxis;
673 orthogonalAxisDistance = yAxis.abs(); 542 if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect))
543 orthogonalBias = currentRect.height() / 2;
544 weightedOrthogonalAxisDistance = (yAxis + orthogonalBias) * orthogonalWe ightForLeftRight;
674 break; 545 break;
675 case WebFocusTypeUp: 546 case WebFocusTypeUp:
676 case WebFocusTypeDown: 547 case WebFocusTypeDown:
677 navigationAxisDistance = yAxis.abs(); 548 navigationAxisDistance = yAxis;
678 orthogonalAxisDistance = xAxis.abs(); 549 if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect))
550 orthogonalBias = currentRect.width() / 2;
551 weightedOrthogonalAxisDistance = (xAxis + orthogonalBias) * orthogonalWe ightForUpDown;
679 break; 552 break;
680 default: 553 default:
681 ASSERT_NOT_REACHED(); 554 ASSERT_NOT_REACHED();
682 return; 555 return;
683 } 556 }
684 557
685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); 558 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble();
686 LayoutRect intersectionRect = intersection(currentRect, nodeRect); 559 LayoutRect intersectionRect = intersection(currentRect, nodeRect);
687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble(); 560 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble();
688 561
689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin g 562 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin g
690 candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance+ o rthogonalAxisDistance * 2 - sqrt(overlap); 563 candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance + weightedOrthogonalAxisDistance - sqrt(overlap);
691
692 LayoutSize viewSize = LayoutSize(candidate.visibleNode->document().page()->d eprecatedLocalMainFrame()->view()->visibleContentRect().size());
693 candidate.alignment = alignmentForRects(type, currentRect, nodeRect, viewSiz e);
694 } 564 }
695 565
696 bool canBeScrolledIntoView(WebFocusType type, const FocusCandidate& candidate) 566 bool canBeScrolledIntoView(WebFocusType type, const FocusCandidate& candidate)
697 { 567 {
698 ASSERT(candidate.visibleNode && candidate.isOffscreen); 568 ASSERT(candidate.visibleNode && candidate.isOffscreen);
699 LayoutRect candidateRect = candidate.rect; 569 LayoutRect candidateRect = candidate.rect;
700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) { 570 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) {
701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); 571 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode);
702 if (!candidateRect.intersects(parentRect)) { 572 if (!candidateRect.intersects(parentRect)) {
703 if (((type == WebFocusTypeLeft || type == WebFocusTypeRight) && pare ntNode->renderer()->style()->overflowX() == OHIDDEN) 573 if (((type == WebFocusTypeLeft || type == WebFocusTypeRight) && pare ntNode->renderer()->style()->overflowX() == OHIDDEN)
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
747 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1); 617 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1);
748 return rect; 618 return rect;
749 } 619 }
750 620
751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) 621 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate)
752 { 622 {
753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr; 623 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr;
754 }; 624 };
755 625
756 } // namespace blink 626 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/page/SpatialNavigation.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698