OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |