diff -r 474929a40a0f -r 87476091b3f5 homescreenapp/hsutils/src/hswidgetpositioningonwidgetadd.cpp --- 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 #include +#include +#include + +#include + + #include "hswidgetpositioningonwidgetadd.h" -#include -#include const qreal offset = 40; //TODO: Implement this as configurable parameter @@ -71,9 +76,12 @@ */ QList HsAnchorPointInBottomRight::convert( const QRectF &contentArea, - const QList &rects, + const QList &existingRects, + const QList &newRects, const QPointF &startPoint) { + Q_UNUSED(existingRects); + QList 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 HsAnchorPointInCenter::convert( const QRectF &contentArea, - const QList &rects, + const QList &existingRects, + const QList &newRects, const QPointF &startPoint ) { + Q_UNUSED(existingRects); Q_UNUSED(startPoint) QList 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 HsWidgetOrganizer::convert( + const QRectF &contentArea, + const QList &existingRects, + const QList &newRects, + const QPointF &startPoint) +{ + Q_UNUSED(startPoint) + + // TODO: maybe we can utilize start point in some use cases / optimizations? + + QList 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 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 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 tmpExistingRects; + tmpExistingRects += newRects; + tmpExistingRects += existingRects; + HsAnchorPointInCenter *centerAlgorithm = new HsAnchorPointInCenter(); + QList 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