homescreenapp/hsutils/src/hswidgetpositioningonwidgetadd.cpp
changeset 69 87476091b3f5
parent 62 341166945d65
child 81 7dd137878ff8
--- 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