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

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: fix not-aligned node issue 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(FocusType, const LayoutRect&, const Layo utRect&, const LayoutSize& viewSize);
49 static bool areRectsFullyAligned(FocusType, const LayoutRect&, const LayoutRect& );
50 static bool areRectsPartiallyAligned(FocusType, const LayoutRect&, const LayoutR ect&);
51 static bool areRectsMoreThanFullScreenApart(FocusType, const LayoutRect& curRect , const LayoutRect& targetRect, const LayoutSize& viewSize);
52 static bool isRectInDirection(FocusType, const LayoutRect&, const LayoutRect&); 48 static bool isRectInDirection(FocusType, const LayoutRect&, const LayoutRect&);
53 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); 49 static void deflateIfOverlapped(LayoutRect&, LayoutRect&);
54 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&); 50 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&);
55 static bool isScrollableNode(const Node*); 51 static bool isScrollableNode(const Node*);
56 52
57 FocusCandidate::FocusCandidate(Node* node, FocusType type) 53 FocusCandidate::FocusCandidate(Node* node, FocusType type)
58 : visibleNode(nullptr) 54 : visibleNode(nullptr)
59 , focusableNode(nullptr) 55 , focusableNode(nullptr)
60 , enclosingScrollableBox(nullptr) 56 , enclosingScrollableBox(nullptr)
61 , distance(maxDistance()) 57 , distance(maxDistance())
62 , alignment(None)
63 , isOffscreen(true) 58 , isOffscreen(true)
64 , isOffscreenAfterScrolling(true) 59 , isOffscreenAfterScrolling(true)
65 { 60 {
66 ASSERT(node); 61 ASSERT(node);
67 ASSERT(node->isElementNode()); 62 ASSERT(node->isElementNode());
68 63
69 if (isHTMLAreaElement(*node)) { 64 if (isHTMLAreaElement(*node)) {
70 HTMLAreaElement& area = toHTMLAreaElement(*node); 65 HTMLAreaElement& area = toHTMLAreaElement(*node);
71 HTMLImageElement* image = area.imageElement(); 66 HTMLImageElement* image = area.imageElement();
72 if (!image || !image->renderer()) 67 if (!image || !image->renderer())
(...skipping 12 matching lines...) Expand all
85 focusableNode = node; 80 focusableNode = node;
86 isOffscreen = hasOffscreenRect(visibleNode); 81 isOffscreen = hasOffscreenRect(visibleNode);
87 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); 82 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type);
88 } 83 }
89 84
90 bool isSpatialNavigationEnabled(const LocalFrame* frame) 85 bool isSpatialNavigationEnabled(const LocalFrame* frame)
91 { 86 {
92 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled()); 87 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled());
93 } 88 }
94 89
95 static RectsAlignment alignmentForRects(FocusType type, const LayoutRect& curRec t, const LayoutRect& targetRect, const LayoutSize& viewSize) 90 static bool areRectsNotAligned(FocusType type, const LayoutRect& a, const Layout Rect& b)
fs 2015/01/12 12:07:23 Not the most obvious name IMHO. Maybe rectsInterse
c.shu 2015/01/12 18:03:38 will do.
96 { 91 {
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(FocusType type)
111 {
112 return type == FocusTypeLeft || type == FocusTypeRight;
113 }
114
115 static inline LayoutUnit start(FocusType type, const LayoutRect& rect)
116 {
117 return isHorizontalMove(type) ? rect.y() : rect.x();
118 }
119
120 static inline LayoutUnit middle(FocusType 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(FocusType 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(FocusType type, const LayoutRect& a, const Layo utRect& b)
139 {
140 LayoutUnit aStart, bStart, aEnd, bEnd;
141
142 switch (type) { 92 switch (type) {
143 case FocusTypeLeft: 93 case FocusTypeLeft:
144 aStart = a.x();
145 bEnd = b.x();
146 break;
147 case FocusTypeRight: 94 case FocusTypeRight:
148 aStart = b.x(); 95 return a.maxY() <= b.y() || a.y() >= b.maxY();
149 bEnd = a.x();
150 break;
151 case FocusTypeUp: 96 case FocusTypeUp:
152 aStart = a.y();
153 bEnd = b.y();
154 break;
155 case FocusTypeDown: 97 case FocusTypeDown:
156 aStart = b.y(); 98 return a.maxX() <= b.x() || a.x() >= b.maxX();
157 bEnd = a.y();
158 break;
159 default:
160 ASSERT_NOT_REACHED();
161 return false;
162 }
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(FocusType type, const LayoutRect& a, const 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(FocusType type, const LayoutRect& cu rRect, const LayoutRect& targetRect, const LayoutSize& viewSize)
227 {
228 ASSERT(isRectInDirection(type, curRect, targetRect));
229
230 switch (type) {
231 case FocusTypeLeft:
232 return curRect.x() - targetRect.maxX() > viewSize.width();
233 case FocusTypeRight:
234 return targetRect.x() - curRect.maxX() > viewSize.width();
235 case FocusTypeUp:
236 return curRect.y() - targetRect.maxY() > viewSize.height();
237 case FocusTypeDown:
238 return targetRect.y() - curRect.maxY() > viewSize.height();
239 default: 99 default:
240 ASSERT_NOT_REACHED(); 100 ASSERT_NOT_REACHED();
241 return true; 101 return true;
242 } 102 }
243 } 103 }
244 104
245 // Return true if rect |a| is below |b|. False otherwise. 105 // Return true if rect |a| is below |b|. False otherwise.
246 // For overlapping rects, |a| is considered to be below |b| 106 // For overlapping rects, |a| is considered to be below |b|
247 // if both edges of |a| are below the respective ones of |b| 107 // if both edges of |a| are below the respective ones of |b|
248 static inline bool below(const LayoutRect& a, const LayoutRect& b) 108 static inline bool below(const LayoutRect& a, const LayoutRect& b)
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after
637 return false; 497 return false;
638 498
639 return true; 499 return true;
640 } 500 }
641 501
642 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate) 502 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate)
643 { 503 {
644 if (areElementsOnSameLine(current, candidate)) { 504 if (areElementsOnSameLine(current, candidate)) {
645 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) { 505 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) {
646 candidate.distance = 0; 506 candidate.distance = 0;
647 candidate.alignment = Full;
648 return; 507 return;
649 } 508 }
650 } 509 }
651 510
652 LayoutRect nodeRect = candidate.rect; 511 LayoutRect nodeRect = candidate.rect;
653 LayoutRect currentRect = current.rect; 512 LayoutRect currentRect = current.rect;
654 deflateIfOverlapped(currentRect, nodeRect); 513 deflateIfOverlapped(currentRect, nodeRect);
655 514
656 if (!isRectInDirection(type, currentRect, nodeRect)) 515 if (!isRectInDirection(type, currentRect, nodeRect))
657 return; 516 return;
658 517
659 LayoutPoint exitPoint; 518 LayoutPoint exitPoint;
660 LayoutPoint entryPoint; 519 LayoutPoint entryPoint;
661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point); 520 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point);
662 521
663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); 522 LayoutUnit xAxis = (exitPoint.x() - entryPoint.x()).abs();
664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); 523 LayoutUnit yAxis = (exitPoint.y() - entryPoint.y()).abs();
665 524
666 LayoutUnit navigationAxisDistance; 525 LayoutUnit navigationAxisDistance;
667 LayoutUnit orthogonalAxisDistance; 526 LayoutUnit weightedOrthogonalAxisDistance;
527
528 // Weights are put to the orthogonal axis distance calculation so more align ed candidate
529 // would have advantage over partially- or not-aligned elements. The weight for left/right
530 // direction is given a higher value to allow navigation on common horizonal ly-aligned
531 // elements. Note the hardcoded values are based on tests and experiments.
532 // The not-aligned node is also given a penalty as the orthogonal distance c ould be 0.
533 const int orthogonalWeightForLeftRight = 30;
534 const int orthogonalWeightForUpDown = 2;
668 535
669 switch (type) { 536 switch (type) {
670 case FocusTypeLeft: 537 case FocusTypeLeft:
671 case FocusTypeRight: 538 case FocusTypeRight:
672 navigationAxisDistance = xAxis.abs(); 539 navigationAxisDistance = xAxis;
673 orthogonalAxisDistance = yAxis.abs(); 540 if (areRectsNotAligned(type, currentRect, nodeRect))
541 yAxis += currentRect.height() / 2;
fs 2015/01/12 12:07:23 Why mutate the yAxis value here? Shouldn't you jus
c.shu 2015/01/12 18:03:38 The weight has no impact on 0. That's why I have t
fs 2015/01/13 09:26:04 I get the part about 0, but why not for instance (
542 weightedOrthogonalAxisDistance = yAxis * orthogonalWeightForLeftRight;
674 break; 543 break;
675 case FocusTypeUp: 544 case FocusTypeUp:
676 case FocusTypeDown: 545 case FocusTypeDown:
677 navigationAxisDistance = yAxis.abs(); 546 navigationAxisDistance = yAxis;
678 orthogonalAxisDistance = xAxis.abs(); 547 if (areRectsNotAligned(type, currentRect, nodeRect))
548 xAxis += currentRect.width() / 2;
549 weightedOrthogonalAxisDistance = xAxis * orthogonalWeightForUpDown;
679 break; 550 break;
680 default: 551 default:
681 ASSERT_NOT_REACHED(); 552 ASSERT_NOT_REACHED();
682 return; 553 return;
683 } 554 }
684 555
685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); 556 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble();
686 LayoutRect intersectionRect = intersection(currentRect, nodeRect); 557 LayoutRect intersectionRect = intersection(currentRect, nodeRect);
687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble(); 558 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble();
688 559
689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin g 560 // 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); 561 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 } 562 }
695 563
696 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate) 564 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate)
697 { 565 {
698 ASSERT(candidate.visibleNode && candidate.isOffscreen); 566 ASSERT(candidate.visibleNode && candidate.isOffscreen);
699 LayoutRect candidateRect = candidate.rect; 567 LayoutRect candidateRect = candidate.rect;
700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) { 568 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) {
701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); 569 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode);
702 if (!candidateRect.intersects(parentRect)) { 570 if (!candidateRect.intersects(parentRect)) {
703 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode ->renderer()->style()->overflowX() == OHIDDEN) 571 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode ->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); 615 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1);
748 return rect; 616 return rect;
749 } 617 }
750 618
751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) 619 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate)
752 { 620 {
753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr; 621 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr;
754 }; 622 };
755 623
756 } // namespace blink 624 } // 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