--- a/homescreenapp/hsutils/src/hswidgetpositioningonwidgetadd.cpp Wed Jul 14 15:53:30 2010 +0300
+++ b/homescreenapp/hsutils/src/hswidgetpositioningonwidgetadd.cpp Fri Jul 23 13:47:57 2010 +0300
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
@@ -15,10 +15,15 @@
*
*/
+#include <QLineF>
#include <QtGlobal>
+#include <QPointF>
+#include <math.h>
+
+#include <HbInstance>
+
+
#include "hswidgetpositioningonwidgetadd.h"
-#include <math.h>
-#include <QLineF>
const qreal offset = 40; //TODO: Implement this as configurable parameter
@@ -71,9 +76,12 @@
*/
QList<QRectF> HsAnchorPointInBottomRight::convert(
const QRectF &contentArea,
- const QList<QRectF> &rects,
+ const QList<QRectF> &existingRects,
+ const QList<QRectF> &newRects,
const QPointF &startPoint)
{
+ Q_UNUSED(existingRects);
+
QList<QRectF> toGeometries;
//Offset for widgets' bottom right position to each other
@@ -87,16 +95,16 @@
if(startPoint.isNull()){
QLineF diagonal(contentArea.topLeft(), contentArea.bottomRight());
- QLineF widgetRightSide(contentArea.center().x()+ rects.at(0).width()/2,
+ QLineF widgetRightSide(contentArea.center().x()+ newRects.at(0).width()/2,
contentArea.top(),
- contentArea.center().x()+ rects.at(0).width()/2,
+ contentArea.center().x()+ newRects.at(0).width()/2,
contentArea.bottom());
// right side line intersection with diagonal will be bottom right position
// for the first rect
if(QLineF::BoundedIntersection !=
diagonal.intersect(widgetRightSide, &anchorPoint)) {
- return rects; //Return original since undefined error.
+ return newRects; //Return original since undefined error.
//In this case widget's must be wider than the content area.
}
}else{
@@ -104,8 +112,8 @@
}
QRectF widgetRect;
- for(int i=0;i<rects.count();++i) {
- widgetRect = rects.at(i);
+ for(int i=0;i<newRects.count();++i) {
+ widgetRect = newRects.at(i);
widgetRect.moveBottomRight(anchorPoint);
//if widget rect doesn't fit, try to move it
if(!contentArea.contains(widgetRect)) {
@@ -137,9 +145,11 @@
#endif //COVERAGE_MEASUREMENT
QList<QRectF> HsAnchorPointInCenter::convert(
const QRectF &contentArea,
- const QList<QRectF> &rects,
+ const QList<QRectF> &existingRects,
+ const QList<QRectF> &newRects,
const QPointF &startPoint )
{
+ Q_UNUSED(existingRects);
Q_UNUSED(startPoint)
QList<QRectF> toGeometries;
@@ -152,7 +162,7 @@
//First widget to the center of the content area
QPointF anchorPoint = contentArea.center();
- foreach (QRectF g, rects) {
+ foreach (QRectF g, newRects) {
g.moveCenter(anchorPoint);
toGeometries << g;
anchorPoint -= offsetPoint;
@@ -163,6 +173,279 @@
return toGeometries;
}
+/*!
+ \class HsWidgetOrganizer
+ \brief Advanced widget positioning algorithm.
+
+ Organizes widget's starting from upper left corner towards right,
+ and then continues the on the next line.
+*/
+QList<QRectF> HsWidgetOrganizer::convert(
+ const QRectF &contentArea,
+ const QList<QRectF> &existingRects,
+ const QList<QRectF> &newRects,
+ const QPointF &startPoint)
+{
+ Q_UNUSED(startPoint)
+
+ // TODO: maybe we can utilize start point in some use cases / optimizations?
+
+ QList<QRectF> toGeometries;
+
+ // TODO: anchor distance to configuration?
+ // TODO: optimize anchor distance based on new content amount
+ // TODO: snap value to same as anchor distance?
+ mAnchorDistance = 5;
+ QList<bool> temp;
+ mAnchors = temp;
+
+ // test flag
+ int test = 0;
+
+ // initialize anchor network for widget positions
+ if (test == 0) {
+ initAnchors(contentArea.size());
+ } else {
+ mAnchorDistance = 2;
+ initAnchors(QSizeF(6,6));
+ }
+
+ // mark existing rects (widgets) reserved
+ foreach (QRectF rect, existingRects) {
+ // TODO: could mStartWidthAnchorPoint, mEndWidthAnchorPoint, mEndHeightAnchorPoint be somehow refactored better way?
+ mStartWidthAnchorPoint.setX(lenghtInAnchorPoints(rect.x() - contentArea.x()));
+ mEndWidthAnchorPoint.setX(lenghtInAnchorPoints(rect.x() + rect.width() - contentArea.x()));
+ mStartWidthAnchorPoint.setY(lenghtInAnchorPoints(rect.y() - contentArea.y()));
+ mEndHeightAnchorPoint.setY(lenghtInAnchorPoints(rect.y() + rect.height() - contentArea.y()));
+ // mark reserved anchor points
+ markReservedAnchors();
+ mStartWidthAnchorPoint = QPointF(0,0);
+ mEndWidthAnchorPoint = QPointF(0,0);
+ mEndHeightAnchorPoint = QPointF(0,0);
+ }
+
+ QList<QRectF> notOrganizedRects;
+
+ // get positions for all new rects (widgets)
+ for ( int i = 0; i < newRects.count(); i++) {
+ bool found = false;
+ if (test == 0) {
+ // find first free anchor point for rect
+ found = getAnchorPoint(newRects.at(i).size());
+ } else {
+ found = getAnchorPoint(QSizeF(2,2));
+ }
+
+ if (found) {
+ // save to geometry list
+ toGeometries << QRectF(mStartWidthAnchorPoint.x() * mAnchorDistance + contentArea.x(),
+ mStartWidthAnchorPoint.y() * mAnchorDistance + contentArea.y(),
+ newRects.at(i).width(), newRects.at(i).height());
+ // mark new widgets rect reserved
+ markReservedAnchors();
+ // TODO: these optimizations could be used for empty page
+ //mStartWidthAnchorPoint.setX(mEndWidthAnchorPoint.x() + 1);
+ //mStartWidthAnchorPoint.setY(mEndWidthAnchorPoint.y());
+ } else {
+ // collect widgets that do not fit
+ notOrganizedRects << newRects.at(i);
+ }
+ // TODO: remove these to optimize for empty page
+ mStartWidthAnchorPoint = QPointF(0,0);
+ mEndWidthAnchorPoint = QPointF(0,0);
+ }
+
+ // use center algorithm with offset for the rest widget that did not fit to screen
+ if (notOrganizedRects.count() > 0) {
+ QList<QRectF> tmpExistingRects;
+ tmpExistingRects += newRects;
+ tmpExistingRects += existingRects;
+ HsAnchorPointInCenter *centerAlgorithm = new HsAnchorPointInCenter();
+ QList<QRectF> calculatedRects =
+ centerAlgorithm->convert(contentArea, tmpExistingRects, notOrganizedRects, QPointF());
+ toGeometries += calculatedRects;
+ }
+
+ return toGeometries;
+}
+
+
+/*!
+ Initializes anchor points for context area
+*/
+bool HsWidgetOrganizer::initAnchors(const QSizeF &areaSize)
+{
+ // mandatory check ups
+ // TODO: these mAnchorDistance checks to earlier phase
+ if (areaSize == QSizeF(0,0) || areaSize.width() < mAnchorDistance ||
+ areaSize.height() < mAnchorDistance || mAnchorDistance == 0 || mAnchorDistance == 1) {
+ return false;
+ }
+ mAnchorColumns = 0;
+ mAnchorRows = 0;
+
+ // TODO: can we optimize anchor amount utilizing minimum widget size
+ mAnchorColumns = lenghtInAnchorPoints(areaSize.width());
+ mAnchorRows = lenghtInAnchorPoints(areaSize.height());
+
+ // create anchor network
+ for (int i = 0; i < (mAnchorRows * mAnchorColumns); i = i++) {
+ mAnchors << false;
+ }
+ // zero start points
+ mStartWidthAnchorPoint = QPointF(0,0);
+ mEndWidthAnchorPoint = QPointF(0,0);
+
+ return true;
+}
+
+/*!
+ Finds anchor points for content size
+*/
+bool HsWidgetOrganizer::getAnchorPoint(const QSizeF &contentSize)
+{
+ bool anchorFound = false;
+
+ while (anchorFound == false) {
+ // if no width found for content
+ if (!searchWidthSpace(contentSize)) {
+ // when content organized in height order remove this line for optimization
+ mStartWidthAnchorPoint = QPointF(0,0);
+ mEndWidthAnchorPoint = QPointF(0,0);
+ return false;
+ }
+ // search height for content
+ int height = lenghtInAnchorPoints(contentSize.height());
+ anchorFound = searchHeightSpace(height);
+ }
+ return true;
+}
+
+/*!
+ Searches anchor point width for content size
+*/
+bool HsWidgetOrganizer::searchWidthSpace(const QSizeF &contentSize)
+{
+ int availableWidth = 0;
+ int contentWidth = lenghtInAnchorPoints(contentSize.width());
+ // TODO: use this optimizations for empty page
+ //int contentHeight = lenghtInAnchorPoints(contentSize.height());
+ bool newRow = true;
+
+ for (int i = getIndexForCoordinate(mStartWidthAnchorPoint); i <= mAnchors.count(); i++) {
+ // no width left on the page
+ if ((newRow == false) && ((i % (mAnchorColumns)) == 0)) {
+ availableWidth = 0;
+ // jump to new row
+ mStartWidthAnchorPoint.setX(0);
+ // TODO: use this optimizations for empty page
+ //mStartWidthAnchorPoint.setY(mStartWidthAnchorPoint.y() + contentHeight + 1);
+ mStartWidthAnchorPoint.setY(mStartWidthAnchorPoint.y() + 1);
+ i = getIndexForCoordinate(mStartWidthAnchorPoint) - 1;
+ // if no height found
+ if (i < 0) {
+ return false;
+ }
+ newRow = true;
+ } else {
+ // if enough width found
+ if (availableWidth == contentWidth) {
+ mEndWidthAnchorPoint = getAnchorCoordinates(i);
+ if (mEndWidthAnchorPoint == QPointF()) {
+ return false;
+ }
+ return true;
+ }
+ // if anchor reserved
+ if (mAnchors[i] == true) {
+ availableWidth = 0;
+ } else {
+ // update available width
+ availableWidth = availableWidth + 1;
+ }
+ newRow = false;
+ }
+ }
+ return false;
+}
+
+/*!
+ Searches anchor point area for content size
+*/
+bool HsWidgetOrganizer::searchHeightSpace(int contentHeight)
+{
+ mEndHeightAnchorPoint = QPointF(0,0);
+
+ for (int i = mStartWidthAnchorPoint.x(); i <= mEndWidthAnchorPoint.x(); i = i++) {
+ for (int j = mStartWidthAnchorPoint.y(); j <= (mStartWidthAnchorPoint.y() + contentHeight); j = j++) {
+ int index = getIndexForCoordinate(QPointF(i,j));
+ // check that index is not out of bounds
+ if (index == -1) {
+ // update start width point one step
+ mStartWidthAnchorPoint.setX(mStartWidthAnchorPoint.x() + 1);
+ return false;
+ }
+ // if anchor reserved
+ if (mAnchors[index] == true) {
+ // update start width point one step
+ mStartWidthAnchorPoint.setX(mStartWidthAnchorPoint.x() + 1);
+ return false;
+ }
+ }
+ }
+ mEndHeightAnchorPoint = QPointF(mEndWidthAnchorPoint.x(), mEndWidthAnchorPoint.y() + contentHeight);
+ return true;
+}
+
+/*!
+ Marks reserved anchor points based on pre-defined starting and ending points
+*/
+bool HsWidgetOrganizer::markReservedAnchors()
+{
+ for (int i = mStartWidthAnchorPoint.x(); i <= mEndWidthAnchorPoint.x(); i++) {
+ for (int j = mStartWidthAnchorPoint.y(); j <= mEndHeightAnchorPoint.y(); j++) {
+ mAnchors[getIndexForCoordinate(QPointF(i,j))] = true;
+ }
+ }
+ return true;
+}
+
+/*!
+ Returns pixel coordinate based on anchor coordinate
+*/
+QPointF HsWidgetOrganizer::getAnchorCoordinates(int index)
+{
+ if (index < mAnchors.count()) {
+ int x = index % mAnchorColumns;
+ int y = (index - x) / mAnchorColumns;
+ return QPointF(x,y);
+ } else {
+ return QPointF();
+ }
+}
+
+/*!
+ Returns anchor coordinate based on pixel coordinate
+*/
+int HsWidgetOrganizer::getIndexForCoordinate(QPointF position)
+{
+ int index = (position.y() * mAnchorColumns) + position.x();
+ if (index < mAnchors.count()) {
+ return index;
+ } else {
+ return -1;
+ }
+}
+
+/*!
+ Calculates pixel length as anchor points
+*/
+int HsWidgetOrganizer::lenghtInAnchorPoints(QVariant length)
+{
+ // check remainder
+ int remainder = length.toInt() % mAnchorDistance;
+ return ((length.toInt() - remainder) / mAnchorDistance);
+}
#ifdef COVERAGE_MEASUREMENT
#pragma CTC ENDSKIP