|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "qmath.h" |
|
20 #include "qpainter.h" |
|
21 #include "qwebpage.h" |
|
22 #include "qwebview.h" |
|
23 #include "qwebframe.h" |
|
24 #include "qwebelement.h" |
|
25 #include "webdirectionalnavigation.h" |
|
26 |
|
27 namespace WRT { |
|
28 |
|
29 const int KInitialSize = 20; |
|
30 const int KNormalScrollRange = 40; |
|
31 const int KFullStep = 14; |
|
32 const double KMaxDistance = 1000000; |
|
33 |
|
34 /*! |
|
35 \class WebDirectionalNavigation |
|
36 \since cwrt 1.0 |
|
37 \brief cwrt navigation. |
|
38 |
|
39 \sa WebNavigation, WebTouchNavigation, WebCursorNavigation, WebHtmlTabIndexedNavigation |
|
40 */ |
|
41 WebDirectionalNavigation::WebDirectionalNavigation(QWebPage* webPage,QObject* view) : |
|
42 m_webPage(webPage) |
|
43 , m_view(view) |
|
44 { |
|
45 install(); |
|
46 } |
|
47 |
|
48 WebDirectionalNavigation::~WebDirectionalNavigation() |
|
49 { |
|
50 uninstall(); |
|
51 } |
|
52 |
|
53 void WebDirectionalNavigation::install() |
|
54 { |
|
55 m_view->installEventFilter(this); |
|
56 //initiallayoutCompleted(); |
|
57 } |
|
58 |
|
59 void WebDirectionalNavigation::uninstall() |
|
60 { |
|
61 if (m_view) |
|
62 m_view->removeEventFilter(this); |
|
63 } |
|
64 |
|
65 void WebDirectionalNavigation::initiallayoutCompleted() |
|
66 { |
|
67 m_webElement = m_webPage->currentFrame()->findFirstElement(QString("a,input,select,textarea,object")); |
|
68 if (!m_webElement.isNull()) { |
|
69 m_webElement.setFocus(); |
|
70 m_focusPoint = m_webElement.geometry().topLeft(); |
|
71 setCurrentFrameScrollPosition(m_focusPoint); |
|
72 } |
|
73 } |
|
74 |
|
75 double WebDirectionalNavigation::calculateElementDistance(int direction, const QRect& possibleRect) |
|
76 { |
|
77 // Roughly based on this algorithm http://www.w3.org/TR/WICD/#nav-distance-fkt |
|
78 // Deviates in that the overlap is not calculated. Instead, if there is any overlap |
|
79 // selection is restricted to x or y direction only. This helps for differnt size elments |
|
80 // all lying on the same plane, but might mess up backtracking the naviation path. |
|
81 |
|
82 QRect focusedRect(0,0,1,1); |
|
83 if (!m_webElement.isNull()) |
|
84 focusedRect = QRect(m_webElement.geometry()); |
|
85 |
|
86 //calculate the next focuspoint |
|
87 switch (direction) { |
|
88 case Qt::Key_Up: |
|
89 if (m_focusPoint.x() < focusedRect.x()) |
|
90 m_focusPoint.setX(focusedRect.x()); |
|
91 else if (m_focusPoint.x() > focusedRect.right()) |
|
92 m_focusPoint.setX(focusedRect.right()); |
|
93 m_focusPoint.setY(focusedRect.y()); |
|
94 break; |
|
95 case Qt::Key_Down: |
|
96 if (m_focusPoint.x() < focusedRect.x()) |
|
97 m_focusPoint.setX(focusedRect.x()); |
|
98 else if (m_focusPoint.x() > focusedRect.right()) |
|
99 m_focusPoint.setX(focusedRect.right()); |
|
100 m_focusPoint.setY(focusedRect.bottom()); |
|
101 break; |
|
102 case Qt::Key_Right: |
|
103 m_focusPoint.setX(focusedRect.right()); |
|
104 if (m_focusPoint.y() < focusedRect.y()) |
|
105 m_focusPoint.setY(focusedRect.y()); |
|
106 else if (m_focusPoint.y() > focusedRect.bottom()) |
|
107 m_focusPoint.setY(focusedRect.bottom()); |
|
108 break; |
|
109 case Qt::Key_Left: |
|
110 m_focusPoint.setX(focusedRect.x()); |
|
111 if (m_focusPoint.y() < focusedRect.y()) |
|
112 m_focusPoint.setY(focusedRect.y()); |
|
113 else if (m_focusPoint.y() > focusedRect.bottom()) |
|
114 m_focusPoint.setY(focusedRect.bottom()); |
|
115 break; |
|
116 } |
|
117 |
|
118 // Make sure the rectangle falls within the search area |
|
119 if (direction == Qt::Key_Up && possibleRect.bottom() > m_focusPoint.y() |
|
120 || direction == Qt::Key_Down && possibleRect.y() < m_focusPoint.y() |
|
121 || direction == Qt::Key_Right && possibleRect.x() < m_focusPoint.x() |
|
122 || direction == Qt::Key_Left && possibleRect.right() > m_focusPoint.x()) |
|
123 return KMaxDistance; |
|
124 |
|
125 // The absolute distance (dx or dy) on the navigation axis between the opposing edges of the currently focused |
|
126 // element and each of the candidates. |
|
127 double distanceX = 0; |
|
128 double distanceY = 0; |
|
129 |
|
130 if (direction == Qt::Key_Up) { |
|
131 // adjust the x distance based on the closest edge |
|
132 if (m_focusPoint.x() < possibleRect.x()) |
|
133 distanceX = possibleRect.x() - m_focusPoint.x(); |
|
134 else if (m_focusPoint.x() > possibleRect.right()) |
|
135 distanceX = m_focusPoint.x() - possibleRect.right(); |
|
136 distanceY = m_focusPoint.y() - possibleRect.bottom(); |
|
137 } else if (direction == Qt::Key_Down) { |
|
138 // adjust the x distance based on the closest edge |
|
139 if (m_focusPoint.x() < possibleRect.x()) |
|
140 distanceX = possibleRect.x() - m_focusPoint.x(); |
|
141 else if (m_focusPoint.x() > possibleRect.right()) |
|
142 distanceX = m_focusPoint.x() - possibleRect.right(); |
|
143 distanceY = possibleRect.y() - m_focusPoint.y(); |
|
144 } else if (direction == Qt::Key_Right) { |
|
145 distanceX = possibleRect.x() - m_focusPoint.x(); |
|
146 // adjust the y distance based on the closest edge |
|
147 if (m_focusPoint.y() < possibleRect.y()) |
|
148 distanceY = possibleRect.y() - m_focusPoint.y(); |
|
149 else if (m_focusPoint.y() > possibleRect.bottom()) |
|
150 distanceY = m_focusPoint.y() - possibleRect.bottom(); |
|
151 } else if (direction == Qt::Key_Left) { |
|
152 distanceX = m_focusPoint.x() - possibleRect.right(); |
|
153 // adjust the y distance based on the closest edge |
|
154 if (m_focusPoint.y() < possibleRect.y()) |
|
155 distanceY = possibleRect.y() - m_focusPoint.y(); |
|
156 else if (m_focusPoint.y() > possibleRect.bottom()) |
|
157 distanceY = m_focusPoint.y() - possibleRect.bottom(); |
|
158 } |
|
159 |
|
160 // The absolute distance on the axis orthogonal to the navigation axis between |
|
161 // the opposing edges of currently focused element and each of candidates |
|
162 double displacement = 0; |
|
163 |
|
164 // The euclidean distance |
|
165 double euclideanDist = sqrt(distanceX * distanceX + distanceY * distanceY); |
|
166 |
|
167 // Area of the document to search |
|
168 QRect searchRect(m_webPage->currentFrame()->scrollPosition(),m_webPage->viewportSize()); |
|
169 |
|
170 if (direction == Qt::Key_Up || direction == Qt::Key_Down) { |
|
171 // if the rectangles are on the same plane set the euclideanDist to zero to favor this navigation |
|
172 if ((possibleRect.x() == focusedRect.x()) |
|
173 || (possibleRect.right() > focusedRect.x() && possibleRect.right() < focusedRect.right()) |
|
174 || (possibleRect.x() > focusedRect.x() && possibleRect.x() < focusedRect.right()) |
|
175 || (possibleRect.x() > focusedRect.x() && possibleRect.right() < focusedRect.right()) |
|
176 || (possibleRect.x() < focusedRect.x() && possibleRect.right() > focusedRect.right())) { |
|
177 euclideanDist = 0; |
|
178 distanceX = 0; |
|
179 } else { |
|
180 // displacement is added for being orthogonally far from the current rectangle. |
|
181 if (possibleRect.x() > focusedRect.right()) |
|
182 displacement = possibleRect.x() - focusedRect.right(); |
|
183 if (possibleRect.right() < focusedRect.x()) |
|
184 displacement = focusedRect.x() - possibleRect.right(); |
|
185 |
|
186 // This is a little sketchy, but if the rectangles are all |
|
187 // touching set the displacment to 1 so rects on the same plane win |
|
188 if (displacement == 0) |
|
189 displacement = 1; |
|
190 |
|
191 // if the focus is within the view limit the diagonal search by half the search rectangle |
|
192 if (searchRect.intersects(focusedRect) && (displacement > (searchRect.width()/2))) |
|
193 return KMaxDistance; |
|
194 |
|
195 } |
|
196 |
|
197 } else if (direction == Qt::Key_Left || direction == Qt::Key_Right) { |
|
198 |
|
199 // if the rectangles are on the same plane set the euclideanDist to zero to favor this navigation |
|
200 if ((possibleRect.y() == focusedRect.y()) |
|
201 || (possibleRect.bottom() > focusedRect.y() && possibleRect.bottom() < focusedRect.bottom()) |
|
202 || (possibleRect.y() > focusedRect.y() && possibleRect.y() < focusedRect.bottom()) |
|
203 || (possibleRect.y() > focusedRect.y() && possibleRect.bottom() < focusedRect.bottom()) |
|
204 || (possibleRect.y() < focusedRect.y() && possibleRect.bottom() > focusedRect.bottom())) { |
|
205 euclideanDist = 0; |
|
206 distanceY = 0; |
|
207 } else { |
|
208 // displacement is added for being orthogonally far from the current rectangle. |
|
209 if (possibleRect.y() > focusedRect.bottom()) |
|
210 displacement = possibleRect.y() - focusedRect.bottom(); |
|
211 if (possibleRect.bottom() < focusedRect.y()) |
|
212 displacement = focusedRect.y() - possibleRect.bottom(); |
|
213 |
|
214 // This is a little sketchy, but if the rectangles are all |
|
215 // touching set the displacment to 1 so rects on the same plane win |
|
216 if (displacement == 0) |
|
217 displacement = 1; |
|
218 |
|
219 // if the focus is within the view limit the diagonal search by half the search rectangle |
|
220 if (searchRect.intersects(focusedRect) && (displacement > (searchRect.height()/2))) |
|
221 return KMaxDistance; |
|
222 } |
|
223 } |
|
224 |
|
225 return euclideanDist + distanceX + distanceY + 2 * (displacement); |
|
226 } |
|
227 |
|
228 |
|
229 |
|
230 bool WebDirectionalNavigation::nextElementInDirection(int direction) |
|
231 { |
|
232 double bestDistance = KMaxDistance; |
|
233 QWebElement bestElement; |
|
234 #if QT_VERSION < 0x040600 |
|
235 QList<QWebElement> elementList = m_webPage->currentFrame()->findAllElements(QString("a,input,select,textarea,object")); |
|
236 #else |
|
237 QList<QWebElement> elementList = m_webPage->currentFrame()->findAllElements(QString("a,input,select,textarea,object")).toList(); |
|
238 #endif |
|
239 QList<QWebElement>::iterator it; |
|
240 for (it = elementList.begin(); it != elementList.end(); it++) { |
|
241 QWebElement el(*it); |
|
242 QRect nRect(el.geometry()); |
|
243 if (nRect.isValid()) { |
|
244 if (QRect(m_webPage->currentFrame()->scrollPosition(),m_webPage->viewportSize()).intersects(nRect)) { |
|
245 double distance = calculateElementDistance (direction, nRect); |
|
246 if (bestDistance > distance) { |
|
247 bestDistance = distance; |
|
248 bestElement = el; |
|
249 } |
|
250 } |
|
251 } |
|
252 } |
|
253 |
|
254 if (!bestElement.isNull() && bestElement != m_webElement) { |
|
255 m_webElement.setStyleProperty("outline", m_webElementStyle); |
|
256 m_webElement = bestElement; |
|
257 m_webElement.setFocus(); |
|
258 m_webElementStyle = m_webElement.styleProperty("outline", QWebElement::ComputedStyle); |
|
259 m_webElement.setStyleProperty("outline", "3px ridge rgb(140,140,255)"); |
|
260 scrollFrame(direction); |
|
261 return true; |
|
262 } |
|
263 |
|
264 scrollFrameOneDirection (direction, KNormalScrollRange); |
|
265 return false; |
|
266 } |
|
267 |
|
268 |
|
269 bool WebDirectionalNavigation::eventFilter(QObject *object, QEvent *event) |
|
270 { |
|
271 if (object == m_view) { |
|
272 switch (event->type()) { |
|
273 case QEvent::KeyPress: { |
|
274 QKeyEvent* ev = static_cast<QKeyEvent*>(event); |
|
275 if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down |
|
276 || ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right ) |
|
277 { |
|
278 return true; |
|
279 } |
|
280 if (ev->key() == Qt::Key_Select ) |
|
281 { |
|
282 QKeyEvent rockerEnterEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); |
|
283 m_webPage->event(&rockerEnterEvent); |
|
284 return true; |
|
285 } |
|
286 } |
|
287 break; |
|
288 case QEvent::KeyRelease: { |
|
289 QKeyEvent* ev = static_cast<QKeyEvent*>(event); |
|
290 if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down |
|
291 || ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right ) |
|
292 { |
|
293 nextElementInDirection(ev->key()); |
|
294 return true; |
|
295 } |
|
296 if (ev->key() == Qt::Key_Select ) |
|
297 { |
|
298 QKeyEvent rockerEnterEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); |
|
299 m_webPage->event(&rockerEnterEvent); |
|
300 return true; |
|
301 } |
|
302 } |
|
303 break; |
|
304 default: |
|
305 return false; |
|
306 break; |
|
307 } // end of switch statement |
|
308 } // end of if statement |
|
309 return false; |
|
310 } |
|
311 |
|
312 |
|
313 void WebDirectionalNavigation::scrollFrame(int direction) |
|
314 { |
|
315 |
|
316 int xadjust = m_webPage->viewportSize().width()/5; |
|
317 int yadjust = m_webPage->viewportSize().height()/5; |
|
318 |
|
319 switch (direction) { |
|
320 case Qt::Key_Left: { |
|
321 int distanceX = xadjust - m_webElement.geometry().x(); |
|
322 if (distanceX > 0) |
|
323 scrollCurrentFrame(-distanceX,0); |
|
324 } |
|
325 break; |
|
326 case Qt::Key_Right: { |
|
327 int distanceX = m_webElement.geometry().right() - m_webPage->viewportSize().width()-xadjust; |
|
328 if (distanceX > 0) |
|
329 scrollCurrentFrame(distanceX,0); |
|
330 } |
|
331 break; |
|
332 case Qt::Key_Up: { |
|
333 int distanceY = yadjust - m_webElement.geometry().y(); |
|
334 if (distanceY > 0) |
|
335 scrollCurrentFrame(0,-distanceY); |
|
336 } |
|
337 break; |
|
338 case Qt::Key_Down: { |
|
339 int distanceY = m_webElement.geometry().bottom() - m_webPage->viewportSize().height()-yadjust; |
|
340 if (distanceY > 0) |
|
341 scrollCurrentFrame(0,distanceY); |
|
342 } |
|
343 break; |
|
344 } |
|
345 } |
|
346 |
|
347 void WebDirectionalNavigation::scrollFrameOneDirection (int direction, int distance) |
|
348 { |
|
349 int dx = 0; |
|
350 int dy = 0; |
|
351 |
|
352 switch (direction) { |
|
353 case Qt::Key_Up: { |
|
354 dy = -distance; |
|
355 } |
|
356 break; |
|
357 case Qt::Key_Down: { |
|
358 dy = +distance; |
|
359 } |
|
360 break; |
|
361 case Qt::Key_Left: { |
|
362 dx = -distance; |
|
363 } |
|
364 break; |
|
365 case Qt::Key_Right: { |
|
366 dx = distance; |
|
367 } |
|
368 break; |
|
369 default: |
|
370 break; |
|
371 } |
|
372 |
|
373 scrollCurrentFrame(dx, dy); |
|
374 } |
|
375 |
|
376 void WebDirectionalNavigation::scrollCurrentFrame (int dx, int dy) |
|
377 { |
|
378 QPoint scrollPosition = m_webPage->currentFrame()->scrollPosition(); |
|
379 m_webPage->currentFrame()->scroll(dx, dy); |
|
380 |
|
381 /* emit pageScrollPositionZero singal if it's mainFrame scrolling or scroll to top*/ |
|
382 if (m_webPage->currentFrame() == m_webPage->mainFrame()) { |
|
383 if (scrollPosition.y() == 0 || m_webPage->currentFrame()->scrollPosition().y() == 0) { |
|
384 emit pageScrollPositionZero(); |
|
385 } |
|
386 } |
|
387 } |
|
388 |
|
389 void WebDirectionalNavigation::setCurrentFrameScrollPosition (QPoint& pos) |
|
390 { |
|
391 QPoint scrollPosition = m_webPage->currentFrame()->scrollPosition(); |
|
392 m_webPage->currentFrame()->setScrollPosition(pos); |
|
393 |
|
394 /* emit pageScrollPositionZero singal if it's mainFrame scrolling or scroll to top*/ |
|
395 if (m_webPage->currentFrame() == m_webPage->mainFrame()) { |
|
396 if (scrollPosition.y() == 0 || m_webPage->currentFrame()->scrollPosition().y() == 0) { |
|
397 emit pageScrollPositionZero(); |
|
398 } |
|
399 } |
|
400 } |
|
401 |
|
402 } |
|
403 |