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