85 QPointF anchorPoint; |
93 QPointF anchorPoint; |
86 |
94 |
87 if(startPoint.isNull()){ |
95 if(startPoint.isNull()){ |
88 |
96 |
89 QLineF diagonal(contentArea.topLeft(), contentArea.bottomRight()); |
97 QLineF diagonal(contentArea.topLeft(), contentArea.bottomRight()); |
90 QLineF widgetRightSide(contentArea.center().x()+ rects.at(0).width()/2, |
98 QLineF widgetRightSide(contentArea.center().x()+ newRects.at(0).width()/2, |
91 contentArea.top(), |
99 contentArea.top(), |
92 contentArea.center().x()+ rects.at(0).width()/2, |
100 contentArea.center().x()+ newRects.at(0).width()/2, |
93 contentArea.bottom()); |
101 contentArea.bottom()); |
94 |
102 |
95 // right side line intersection with diagonal will be bottom right position |
103 // right side line intersection with diagonal will be bottom right position |
96 // for the first rect |
104 // for the first rect |
97 if(QLineF::BoundedIntersection != |
105 if(QLineF::BoundedIntersection != |
98 diagonal.intersect(widgetRightSide, &anchorPoint)) { |
106 diagonal.intersect(widgetRightSide, &anchorPoint)) { |
99 return rects; //Return original since undefined error. |
107 return newRects; //Return original since undefined error. |
100 //In this case widget's must be wider than the content area. |
108 //In this case widget's must be wider than the content area. |
101 } |
109 } |
102 }else{ |
110 }else{ |
103 anchorPoint = startPoint - offsetPoint; |
111 anchorPoint = startPoint - offsetPoint; |
104 } |
112 } |
105 |
113 |
106 QRectF widgetRect; |
114 QRectF widgetRect; |
107 for(int i=0;i<rects.count();++i) { |
115 for(int i=0;i<newRects.count();++i) { |
108 widgetRect = rects.at(i); |
116 widgetRect = newRects.at(i); |
109 widgetRect.moveBottomRight(anchorPoint); |
117 widgetRect.moveBottomRight(anchorPoint); |
110 //if widget rect doesn't fit, try to move it |
118 //if widget rect doesn't fit, try to move it |
111 if(!contentArea.contains(widgetRect)) { |
119 if(!contentArea.contains(widgetRect)) { |
112 /*! precondition is that |
120 /*! precondition is that |
113 widget's max height < content area height |
121 widget's max height < content area height |
150 qreal offset_y = k*offset_x; |
160 qreal offset_y = k*offset_x; |
151 QPointF offsetPoint(offset_x, offset_y); |
161 QPointF offsetPoint(offset_x, offset_y); |
152 |
162 |
153 //First widget to the center of the content area |
163 //First widget to the center of the content area |
154 QPointF anchorPoint = contentArea.center(); |
164 QPointF anchorPoint = contentArea.center(); |
155 foreach (QRectF g, rects) { |
165 foreach (QRectF g, newRects) { |
156 g.moveCenter(anchorPoint); |
166 g.moveCenter(anchorPoint); |
157 toGeometries << g; |
167 toGeometries << g; |
158 anchorPoint -= offsetPoint; |
168 anchorPoint -= offsetPoint; |
159 if(!contentArea.contains(anchorPoint)) { |
169 if(!contentArea.contains(anchorPoint)) { |
160 anchorPoint = contentArea.bottomRight(); |
170 anchorPoint = contentArea.bottomRight(); |
161 } |
171 } |
162 } |
172 } |
163 return toGeometries; |
173 return toGeometries; |
164 } |
174 } |
165 |
175 |
|
176 /*! |
|
177 \class HsWidgetOrganizer |
|
178 \brief Advanced widget positioning algorithm. |
|
179 |
|
180 Organizes widget's starting from upper left corner towards right, |
|
181 and then continues the on the next line. |
|
182 */ |
|
183 QList<QRectF> HsWidgetOrganizer::convert( |
|
184 const QRectF &contentArea, |
|
185 const QList<QRectF> &existingRects, |
|
186 const QList<QRectF> &newRects, |
|
187 const QPointF &startPoint) |
|
188 { |
|
189 Q_UNUSED(startPoint) |
|
190 |
|
191 // TODO: maybe we can utilize start point in some use cases / optimizations? |
|
192 |
|
193 QList<QRectF> toGeometries; |
|
194 |
|
195 // TODO: anchor distance to configuration? |
|
196 // TODO: optimize anchor distance based on new content amount |
|
197 // TODO: snap value to same as anchor distance? |
|
198 mAnchorDistance = 5; |
|
199 QList<bool> temp; |
|
200 mAnchors = temp; |
|
201 |
|
202 // test flag |
|
203 int test = 0; |
|
204 |
|
205 // initialize anchor network for widget positions |
|
206 if (test == 0) { |
|
207 initAnchors(contentArea.size()); |
|
208 } else { |
|
209 mAnchorDistance = 2; |
|
210 initAnchors(QSizeF(6,6)); |
|
211 } |
|
212 |
|
213 // mark existing rects (widgets) reserved |
|
214 foreach (QRectF rect, existingRects) { |
|
215 // TODO: could mStartWidthAnchorPoint, mEndWidthAnchorPoint, mEndHeightAnchorPoint be somehow refactored better way? |
|
216 mStartWidthAnchorPoint.setX(lenghtInAnchorPoints(rect.x() - contentArea.x())); |
|
217 mEndWidthAnchorPoint.setX(lenghtInAnchorPoints(rect.x() + rect.width() - contentArea.x())); |
|
218 mStartWidthAnchorPoint.setY(lenghtInAnchorPoints(rect.y() - contentArea.y())); |
|
219 mEndHeightAnchorPoint.setY(lenghtInAnchorPoints(rect.y() + rect.height() - contentArea.y())); |
|
220 // mark reserved anchor points |
|
221 markReservedAnchors(); |
|
222 mStartWidthAnchorPoint = QPointF(0,0); |
|
223 mEndWidthAnchorPoint = QPointF(0,0); |
|
224 mEndHeightAnchorPoint = QPointF(0,0); |
|
225 } |
|
226 |
|
227 QList<QRectF> notOrganizedRects; |
|
228 |
|
229 // get positions for all new rects (widgets) |
|
230 for ( int i = 0; i < newRects.count(); i++) { |
|
231 bool found = false; |
|
232 if (test == 0) { |
|
233 // find first free anchor point for rect |
|
234 found = getAnchorPoint(newRects.at(i).size()); |
|
235 } else { |
|
236 found = getAnchorPoint(QSizeF(2,2)); |
|
237 } |
|
238 |
|
239 if (found) { |
|
240 // save to geometry list |
|
241 toGeometries << QRectF(mStartWidthAnchorPoint.x() * mAnchorDistance + contentArea.x(), |
|
242 mStartWidthAnchorPoint.y() * mAnchorDistance + contentArea.y(), |
|
243 newRects.at(i).width(), newRects.at(i).height()); |
|
244 // mark new widgets rect reserved |
|
245 markReservedAnchors(); |
|
246 // TODO: these optimizations could be used for empty page |
|
247 //mStartWidthAnchorPoint.setX(mEndWidthAnchorPoint.x() + 1); |
|
248 //mStartWidthAnchorPoint.setY(mEndWidthAnchorPoint.y()); |
|
249 } else { |
|
250 // collect widgets that do not fit |
|
251 notOrganizedRects << newRects.at(i); |
|
252 } |
|
253 // TODO: remove these to optimize for empty page |
|
254 mStartWidthAnchorPoint = QPointF(0,0); |
|
255 mEndWidthAnchorPoint = QPointF(0,0); |
|
256 } |
|
257 |
|
258 // use center algorithm with offset for the rest widget that did not fit to screen |
|
259 if (notOrganizedRects.count() > 0) { |
|
260 QList<QRectF> tmpExistingRects; |
|
261 tmpExistingRects += newRects; |
|
262 tmpExistingRects += existingRects; |
|
263 HsAnchorPointInCenter *centerAlgorithm = new HsAnchorPointInCenter(); |
|
264 QList<QRectF> calculatedRects = |
|
265 centerAlgorithm->convert(contentArea, tmpExistingRects, notOrganizedRects, QPointF()); |
|
266 toGeometries += calculatedRects; |
|
267 } |
|
268 |
|
269 return toGeometries; |
|
270 } |
|
271 |
|
272 |
|
273 /*! |
|
274 Initializes anchor points for context area |
|
275 */ |
|
276 bool HsWidgetOrganizer::initAnchors(const QSizeF &areaSize) |
|
277 { |
|
278 // mandatory check ups |
|
279 // TODO: these mAnchorDistance checks to earlier phase |
|
280 if (areaSize == QSizeF(0,0) || areaSize.width() < mAnchorDistance || |
|
281 areaSize.height() < mAnchorDistance || mAnchorDistance == 0 || mAnchorDistance == 1) { |
|
282 return false; |
|
283 } |
|
284 mAnchorColumns = 0; |
|
285 mAnchorRows = 0; |
|
286 |
|
287 // TODO: can we optimize anchor amount utilizing minimum widget size |
|
288 mAnchorColumns = lenghtInAnchorPoints(areaSize.width()); |
|
289 mAnchorRows = lenghtInAnchorPoints(areaSize.height()); |
|
290 |
|
291 // create anchor network |
|
292 for (int i = 0; i < (mAnchorRows * mAnchorColumns); i = i++) { |
|
293 mAnchors << false; |
|
294 } |
|
295 // zero start points |
|
296 mStartWidthAnchorPoint = QPointF(0,0); |
|
297 mEndWidthAnchorPoint = QPointF(0,0); |
|
298 |
|
299 return true; |
|
300 } |
|
301 |
|
302 /*! |
|
303 Finds anchor points for content size |
|
304 */ |
|
305 bool HsWidgetOrganizer::getAnchorPoint(const QSizeF &contentSize) |
|
306 { |
|
307 bool anchorFound = false; |
|
308 |
|
309 while (anchorFound == false) { |
|
310 // if no width found for content |
|
311 if (!searchWidthSpace(contentSize)) { |
|
312 // when content organized in height order remove this line for optimization |
|
313 mStartWidthAnchorPoint = QPointF(0,0); |
|
314 mEndWidthAnchorPoint = QPointF(0,0); |
|
315 return false; |
|
316 } |
|
317 // search height for content |
|
318 int height = lenghtInAnchorPoints(contentSize.height()); |
|
319 anchorFound = searchHeightSpace(height); |
|
320 } |
|
321 return true; |
|
322 } |
|
323 |
|
324 /*! |
|
325 Searches anchor point width for content size |
|
326 */ |
|
327 bool HsWidgetOrganizer::searchWidthSpace(const QSizeF &contentSize) |
|
328 { |
|
329 int availableWidth = 0; |
|
330 int contentWidth = lenghtInAnchorPoints(contentSize.width()); |
|
331 // TODO: use this optimizations for empty page |
|
332 //int contentHeight = lenghtInAnchorPoints(contentSize.height()); |
|
333 bool newRow = true; |
|
334 |
|
335 for (int i = getIndexForCoordinate(mStartWidthAnchorPoint); i <= mAnchors.count(); i++) { |
|
336 // no width left on the page |
|
337 if ((newRow == false) && ((i % (mAnchorColumns)) == 0)) { |
|
338 availableWidth = 0; |
|
339 // jump to new row |
|
340 mStartWidthAnchorPoint.setX(0); |
|
341 // TODO: use this optimizations for empty page |
|
342 //mStartWidthAnchorPoint.setY(mStartWidthAnchorPoint.y() + contentHeight + 1); |
|
343 mStartWidthAnchorPoint.setY(mStartWidthAnchorPoint.y() + 1); |
|
344 i = getIndexForCoordinate(mStartWidthAnchorPoint) - 1; |
|
345 // if no height found |
|
346 if (i < 0) { |
|
347 return false; |
|
348 } |
|
349 newRow = true; |
|
350 } else { |
|
351 // if enough width found |
|
352 if (availableWidth == contentWidth) { |
|
353 mEndWidthAnchorPoint = getAnchorCoordinates(i); |
|
354 if (mEndWidthAnchorPoint == QPointF()) { |
|
355 return false; |
|
356 } |
|
357 return true; |
|
358 } |
|
359 // if anchor reserved |
|
360 if (mAnchors[i] == true) { |
|
361 availableWidth = 0; |
|
362 } else { |
|
363 // update available width |
|
364 availableWidth = availableWidth + 1; |
|
365 } |
|
366 newRow = false; |
|
367 } |
|
368 } |
|
369 return false; |
|
370 } |
|
371 |
|
372 /*! |
|
373 Searches anchor point area for content size |
|
374 */ |
|
375 bool HsWidgetOrganizer::searchHeightSpace(int contentHeight) |
|
376 { |
|
377 mEndHeightAnchorPoint = QPointF(0,0); |
|
378 |
|
379 for (int i = mStartWidthAnchorPoint.x(); i <= mEndWidthAnchorPoint.x(); i = i++) { |
|
380 for (int j = mStartWidthAnchorPoint.y(); j <= (mStartWidthAnchorPoint.y() + contentHeight); j = j++) { |
|
381 int index = getIndexForCoordinate(QPointF(i,j)); |
|
382 // check that index is not out of bounds |
|
383 if (index == -1) { |
|
384 // update start width point one step |
|
385 mStartWidthAnchorPoint.setX(mStartWidthAnchorPoint.x() + 1); |
|
386 return false; |
|
387 } |
|
388 // if anchor reserved |
|
389 if (mAnchors[index] == true) { |
|
390 // update start width point one step |
|
391 mStartWidthAnchorPoint.setX(mStartWidthAnchorPoint.x() + 1); |
|
392 return false; |
|
393 } |
|
394 } |
|
395 } |
|
396 mEndHeightAnchorPoint = QPointF(mEndWidthAnchorPoint.x(), mEndWidthAnchorPoint.y() + contentHeight); |
|
397 return true; |
|
398 } |
|
399 |
|
400 /*! |
|
401 Marks reserved anchor points based on pre-defined starting and ending points |
|
402 */ |
|
403 bool HsWidgetOrganizer::markReservedAnchors() |
|
404 { |
|
405 for (int i = mStartWidthAnchorPoint.x(); i <= mEndWidthAnchorPoint.x(); i++) { |
|
406 for (int j = mStartWidthAnchorPoint.y(); j <= mEndHeightAnchorPoint.y(); j++) { |
|
407 mAnchors[getIndexForCoordinate(QPointF(i,j))] = true; |
|
408 } |
|
409 } |
|
410 return true; |
|
411 } |
|
412 |
|
413 /*! |
|
414 Returns pixel coordinate based on anchor coordinate |
|
415 */ |
|
416 QPointF HsWidgetOrganizer::getAnchorCoordinates(int index) |
|
417 { |
|
418 if (index < mAnchors.count()) { |
|
419 int x = index % mAnchorColumns; |
|
420 int y = (index - x) / mAnchorColumns; |
|
421 return QPointF(x,y); |
|
422 } else { |
|
423 return QPointF(); |
|
424 } |
|
425 } |
|
426 |
|
427 /*! |
|
428 Returns anchor coordinate based on pixel coordinate |
|
429 */ |
|
430 int HsWidgetOrganizer::getIndexForCoordinate(QPointF position) |
|
431 { |
|
432 int index = (position.y() * mAnchorColumns) + position.x(); |
|
433 if (index < mAnchors.count()) { |
|
434 return index; |
|
435 } else { |
|
436 return -1; |
|
437 } |
|
438 } |
|
439 |
|
440 /*! |
|
441 Calculates pixel length as anchor points |
|
442 */ |
|
443 int HsWidgetOrganizer::lenghtInAnchorPoints(QVariant length) |
|
444 { |
|
445 // check remainder |
|
446 int remainder = length.toInt() % mAnchorDistance; |
|
447 return ((length.toInt() - remainder) / mAnchorDistance); |
|
448 } |
166 |
449 |
167 #ifdef COVERAGE_MEASUREMENT |
450 #ifdef COVERAGE_MEASUREMENT |
168 #pragma CTC ENDSKIP |
451 #pragma CTC ENDSKIP |
169 #endif //COVERAGE_MEASUREMENT |
452 #endif //COVERAGE_MEASUREMENT |
170 |
453 |