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

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: use weight in orthogonal distance calculation. Created 6 years 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)
96 {
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) 90 static inline bool isHorizontalMove(FocusType type)
111 { 91 {
112 return type == FocusTypeLeft || type == FocusTypeRight; 92 return type == FocusTypeLeft || type == FocusTypeRight;
113 } 93 }
114 94
115 static inline LayoutUnit start(FocusType type, const LayoutRect& rect) 95 static inline LayoutUnit start(FocusType type, const LayoutRect& rect)
116 { 96 {
117 return isHorizontalMove(type) ? rect.y() : rect.x(); 97 return isHorizontalMove(type) ? rect.y() : rect.x();
118 } 98 }
119 99
120 static inline LayoutUnit middle(FocusType type, const LayoutRect& rect) 100 static inline LayoutUnit middle(FocusType type, const LayoutRect& rect)
121 { 101 {
122 LayoutPoint center(rect.center()); 102 LayoutPoint center(rect.center());
123 return isHorizontalMove(type) ? center.y(): center.x(); 103 return isHorizontalMove(type) ? center.y(): center.x();
124 } 104 }
125 105
126 static inline LayoutUnit end(FocusType type, const LayoutRect& rect) 106 static inline LayoutUnit end(FocusType type, const LayoutRect& rect)
127 { 107 {
128 return isHorizontalMove(type) ? rect.maxY() : rect.maxX(); 108 return isHorizontalMove(type) ? rect.maxY() : rect.maxX();
129 } 109 }
130 110
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) {
143 case FocusTypeLeft:
144 aStart = a.x();
145 bEnd = b.x();
146 break;
147 case FocusTypeRight:
148 aStart = b.x();
149 bEnd = a.x();
150 break;
151 case FocusTypeUp:
152 aStart = a.y();
153 bEnd = b.y();
154 break;
155 case FocusTypeDown:
156 aStart = b.y();
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:
240 ASSERT_NOT_REACHED();
241 return true;
242 }
243 }
244
245 // Return true if rect |a| is below |b|. False otherwise. 111 // Return true if rect |a| is below |b|. False otherwise.
246 // For overlapping rects, |a| is considered to be below |b| 112 // For overlapping rects, |a| is considered to be below |b|
247 // if both edges of |a| are below the respective ones of |b| 113 // if both edges of |a| are below the respective ones of |b|
248 static inline bool below(const LayoutRect& a, const LayoutRect& b) 114 static inline bool below(const LayoutRect& a, const LayoutRect& b)
249 { 115 {
250 return a.y() >= b.maxY() 116 return a.y() >= b.maxY()
251 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX( ) > b.x()); 117 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX( ) > b.x());
252 } 118 }
253 119
254 // Return true if rect |a| is on the right of |b|. False otherwise. 120 // Return true if rect |a| is on the right of |b|. False otherwise.
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after
637 return false; 503 return false;
638 504
639 return true; 505 return true;
640 } 506 }
641 507
642 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate) 508 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate)
643 { 509 {
644 if (areElementsOnSameLine(current, candidate)) { 510 if (areElementsOnSameLine(current, candidate)) {
645 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) { 511 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) {
646 candidate.distance = 0; 512 candidate.distance = 0;
647 candidate.alignment = Full;
648 return; 513 return;
649 } 514 }
650 } 515 }
651 516
652 LayoutRect nodeRect = candidate.rect; 517 LayoutRect nodeRect = candidate.rect;
653 LayoutRect currentRect = current.rect; 518 LayoutRect currentRect = current.rect;
654 deflateIfOverlapped(currentRect, nodeRect); 519 deflateIfOverlapped(currentRect, nodeRect);
655 520
656 if (!isRectInDirection(type, currentRect, nodeRect)) 521 if (!isRectInDirection(type, currentRect, nodeRect))
657 return; 522 return;
658 523
659 LayoutPoint exitPoint; 524 LayoutPoint exitPoint;
660 LayoutPoint entryPoint; 525 LayoutPoint entryPoint;
661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point); 526 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point);
662 527
663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); 528 LayoutUnit xAxis = exitPoint.x() - entryPoint.x();
664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); 529 LayoutUnit yAxis = exitPoint.y() - entryPoint.y();
665 530
666 LayoutUnit navigationAxisDistance; 531 LayoutUnit navigationAxisDistance;
667 LayoutUnit orthogonalAxisDistance; 532 LayoutUnit weightedOrthogonalAxisDistance;
668 533
669 switch (type) { 534 switch (type) {
670 case FocusTypeLeft: 535 case FocusTypeLeft:
671 case FocusTypeRight: 536 case FocusTypeRight:
672 navigationAxisDistance = xAxis.abs(); 537 navigationAxisDistance = xAxis.abs();
673 orthogonalAxisDistance = yAxis.abs(); 538 weightedOrthogonalAxisDistance = yAxis.abs() * 30;
fs 2014/12/17 09:51:11 Could you try to add some form of documentation to
674 break; 539 break;
675 case FocusTypeUp: 540 case FocusTypeUp:
676 case FocusTypeDown: 541 case FocusTypeDown:
677 navigationAxisDistance = yAxis.abs(); 542 navigationAxisDistance = yAxis.abs();
678 orthogonalAxisDistance = xAxis.abs(); 543 weightedOrthogonalAxisDistance = xAxis.abs() * 4;
679 break; 544 break;
680 default: 545 default:
681 ASSERT_NOT_REACHED(); 546 ASSERT_NOT_REACHED();
682 return; 547 return;
683 } 548 }
684 549
685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); 550 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble();
686 LayoutRect intersectionRect = intersection(currentRect, nodeRect); 551 LayoutRect intersectionRect = intersection(currentRect, nodeRect);
687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble(); 552 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble();
688 553
689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin g 554 // 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); 555 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 } 556 }
695 557
696 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate) 558 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate)
697 { 559 {
698 ASSERT(candidate.visibleNode && candidate.isOffscreen); 560 ASSERT(candidate.visibleNode && candidate.isOffscreen);
699 LayoutRect candidateRect = candidate.rect; 561 LayoutRect candidateRect = candidate.rect;
700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) { 562 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) {
701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); 563 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode);
702 if (!candidateRect.intersects(parentRect)) { 564 if (!candidateRect.intersects(parentRect)) {
703 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode ->renderer()->style()->overflowX() == OHIDDEN) 565 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); 609 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1);
748 return rect; 610 return rect;
749 } 611 }
750 612
751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) 613 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate)
752 { 614 {
753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr; 615 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr;
754 }; 616 };
755 617
756 } // namespace blink 618 } // 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