homescreenapp/hsutils/src/hswidgetpositioningonwidgetadd.cpp
changeset 69 87476091b3f5
parent 62 341166945d65
child 81 7dd137878ff8
equal deleted inserted replaced
67:474929a40a0f 69:87476091b3f5
     1 /*
     1 /*
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     4 * This component and the accompanying materials are made available
     5 * under the terms of "Eclipse Public License v1.0"
     5 * under the terms of "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
    13 *
    13 *
    14 * Description: 
    14 * Description: 
    15 *
    15 *
    16 */
    16 */
    17 
    17 
       
    18 #include <QLineF>
    18 #include <QtGlobal>
    19 #include <QtGlobal>
       
    20 #include <QPointF>
       
    21 #include <math.h>
       
    22 
       
    23 #include <HbInstance>
       
    24 
       
    25 
    19 #include "hswidgetpositioningonwidgetadd.h"
    26 #include "hswidgetpositioningonwidgetadd.h"
    20 #include <math.h>
       
    21 #include <QLineF>
       
    22 
    27 
    23 const qreal offset = 40; //TODO: Implement this as configurable parameter
    28 const qreal offset = 40; //TODO: Implement this as configurable parameter
    24 
    29 
    25 
    30 
    26 /*!
    31 /*!
    69     Sets widget's lower right corner to follow content area's diagonal.
    74     Sets widget's lower right corner to follow content area's diagonal.
    70     Widgets are positioned to certain offset to each other.
    75     Widgets are positioned to certain offset to each other.
    71 */
    76 */
    72 QList<QRectF> HsAnchorPointInBottomRight::convert(
    77 QList<QRectF> HsAnchorPointInBottomRight::convert(
    73     const QRectF &contentArea,
    78     const QRectF &contentArea,
    74     const QList<QRectF> &rects,
    79     const QList<QRectF> &existingRects,
       
    80     const QList<QRectF> &newRects,
    75     const QPointF &startPoint)
    81     const QPointF &startPoint)
    76 {
    82 {
       
    83     Q_UNUSED(existingRects);
       
    84 
    77     QList<QRectF> toGeometries;
    85     QList<QRectF> toGeometries;
    78 
    86 
    79     //Offset for widgets' bottom right position to each other
    87     //Offset for widgets' bottom right position to each other
    80     qreal k = contentArea.height()/contentArea.width(); //slope of the diagonal
    88     qreal k = contentArea.height()/contentArea.width(); //slope of the diagonal
    81     qreal offset_x = offset/(sqrt(k + 1));
    89     qreal offset_x = offset/(sqrt(k + 1));
    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
   135 #ifdef COVERAGE_MEASUREMENT
   143 #ifdef COVERAGE_MEASUREMENT
   136 #pragma CTC SKIP
   144 #pragma CTC SKIP
   137 #endif //COVERAGE_MEASUREMENT
   145 #endif //COVERAGE_MEASUREMENT
   138 QList<QRectF> HsAnchorPointInCenter::convert(
   146 QList<QRectF> HsAnchorPointInCenter::convert(
   139     const QRectF &contentArea,
   147     const QRectF &contentArea,
   140     const QList<QRectF> &rects,
   148     const QList<QRectF> &existingRects,
       
   149     const QList<QRectF> &newRects,
   141     const QPointF &startPoint )
   150     const QPointF &startPoint )
   142 {
   151 {
       
   152     Q_UNUSED(existingRects);
   143     Q_UNUSED(startPoint)
   153     Q_UNUSED(startPoint)
   144 
   154 
   145     QList<QRectF> toGeometries;
   155     QList<QRectF> toGeometries;
   146 
   156 
   147     //Offset for widgets' centers position to each other
   157     //Offset for widgets' centers position to each other
   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