181 } |
182 } |
182 } |
183 } |
183 return i; |
184 return i; |
184 } |
185 } |
185 |
186 |
186 QSizeF HbTextItemPrivate::respectSizeLimits(QSizeF size) const |
187 qreal HbTextItemPrivate::respectHeightLimits(qreal height) const |
187 { |
188 { |
188 QFontMetricsF metrics(mTextLayout.font()); |
189 QFontMetricsF metrics(mTextLayout.font()); |
189 |
190 |
190 Q_ASSERT(mMinLines>0); |
191 Q_ASSERT(mMinLines>0); |
191 qreal minHeight = metrics.lineSpacing()*mMinLines - metrics.leading(); |
192 qreal minHeight = metrics.lineSpacing()*mMinLines - metrics.leading(); |
192 if (size.height()<minHeight) { |
193 if (height<minHeight) { |
193 size.setHeight(minHeight); |
194 height=minHeight; |
194 } |
195 } |
195 |
196 |
196 if (mMaxLines>0) { |
197 if (mMaxLines>0) { |
197 qreal maxHeight = metrics.lineSpacing()*mMaxLines - metrics.leading(); |
198 qreal maxHeight = metrics.lineSpacing()*mMaxLines - metrics.leading(); |
198 if (size.height()>maxHeight) { |
199 if (height>maxHeight) { |
199 size.setHeight(maxHeight); |
200 height=maxHeight; |
200 } |
201 } |
201 } |
202 } |
202 return size; |
203 return height; |
203 } |
204 } |
204 |
205 |
205 QString HbTextItemPrivate::elideLayoutedText(const QSizeF& size, const QFontMetricsF& metrics) const |
206 QString HbTextItemPrivate::elideLayoutedText(const QSizeF& size, const QFontMetricsF& metrics) const |
206 { |
207 { |
207 int lastVisibleLine =findIndexOfLastLineBeforeY(size.height()); |
208 int lastVisibleLine =findIndexOfLastLineBeforeY(size.height()); |
286 } |
287 } |
287 |
288 |
288 return flags; |
289 return flags; |
289 } |
290 } |
290 |
291 |
291 /*! |
292 /* |
292 This method check spetial case of calculating sizeHint. |
293 This method check spatial case of calculating sizeHint. |
293 By default prefferedSize returns size of text which has lots of free space. |
294 By default preferredSize (sizeHint(Qt::PreferredSize)) returns size of text |
294 But there can be situaltion that text has less avaible free width ther requred. |
295 which has lots of free space and it is not wrapped. |
295 In such cases prefered hight of HbTextItem should be recaculated because wrapping |
296 After size of HbTextItem is set (width is know) there can be situation |
296 of the line may requre more space in vertical direction. |
297 that text has less available width then required. |
297 |
298 In such cases when text wrapping is available preferred height of HbTextItem |
298 In such case bigger hight should be cached for sizeHint calculations. |
299 should be recalculated to take wrapped text into account. |
299 */ |
300 */ |
300 bool HbTextItemPrivate::isAdjustHightNeeded(const QSizeF& newSize, |
301 bool HbTextItemPrivate::isAdjustHightNeeded(qreal newWidth, |
301 const QSizeF& prefSize) |
302 qreal prefHeight, |
|
303 qreal minHeight, |
|
304 qreal maxHeight) |
302 { |
305 { |
303 #ifdef HB_TEXT_ITEM_LOGS |
306 #ifdef HB_TEXT_ITEM_LOGS |
304 qDebug() << "isAdjustHightNeeded for: " << q_ptr->objectName() |
307 qDebug() << "isAdjustHightNeeded for: " << q_ptr->objectName() |
305 << " text=" << mText.left(20) |
308 << " text=" << mText.left(KMaxLogedTextLength) |
306 << " adjusted=" << mAdjustedSize |
309 << " adjusted=" << mAdjustedSize |
307 << " pref=" << prefSize |
310 << " prefHeight=" << prefHeight |
308 << mTextLayout.font(); |
311 << " minHeight=" << minHeight |
|
312 << " lastConstraint=" << mLastConstraint |
|
313 << " " << mTextLayout.font(); |
309 #endif // HB_TEXT_ITEM_LOGS |
314 #endif // HB_TEXT_ITEM_LOGS |
310 |
315 |
311 // first check if wrapping of text is not active |
316 // first check if wrapping of text is not active |
312 QTextOption::WrapMode wrapMode = mTextLayout.textOption().wrapMode(); |
317 QTextOption::WrapMode wrapMode = mTextLayout.textOption().wrapMode(); |
313 if (wrapMode==QTextOption::NoWrap |
318 if (wrapMode==QTextOption::NoWrap |
314 || wrapMode==QTextOption::ManualWrap) { |
319 || wrapMode==QTextOption::ManualWrap) { |
315 return false; |
320 return false; |
316 } |
321 } |
317 |
322 |
|
323 // preferred height was set from outside of this class so there is mo reson to adjust it |
|
324 if (mLastConstraint.height()>0) { |
|
325 return false; |
|
326 } |
|
327 |
318 // check if line count is fixed |
328 // check if line count is fixed |
319 if (mMaxLines == mMinLines) { |
329 if (mMaxLines == mMinLines) { |
320 return false; |
330 return false; |
321 } |
331 } |
322 |
332 |
323 // check if preffered height is defined by user |
|
324 // so check if preff height is same as value returned by sizeHint |
|
325 if (!qFuzzyCompare(prefSize.height(), mAdjustedSize.height())) { |
|
326 return false; |
|
327 } |
|
328 |
|
329 // check if adjusted size has been already calculated |
333 // check if adjusted size has been already calculated |
330 // new width is bigger then last estimated range of same height |
334 // new width is bigger then last estimated range of same height |
331 if ((mMaxWidthForAdjust>=newSize.width() |
335 if ((mMaxWidthForAdjust>=newWidth |
332 // new width is smaller then last estimated range of same height |
336 // new width is smaller then last estimated range of same height |
333 && newSize.width()>=mMinWidthForAdjust)) { |
337 && newWidth>=mMinWidthForAdjust)) { |
334 return false; |
338 return false; |
335 } |
339 } |
336 |
340 |
337 if (!mAdjustedSize.isValid()) { |
341 if (!mAdjustedSize.isValid()) { |
338 // this means that preferred size is set outside of class by setPreferredSize |
342 // this means that preferred size is set outside of class by setPreferredSize |
342 |
346 |
343 // if preconditions are met test if current hight is enough |
347 // if preconditions are met test if current hight is enough |
344 const QFontMetricsF metrics(mTextLayout.font()); |
348 const QFontMetricsF metrics(mTextLayout.font()); |
345 |
349 |
346 // heavy calculation: check if text fits in current size and cache result |
350 // heavy calculation: check if text fits in current size and cache result |
347 QSizeF newAdjust = metrics.boundingRect(QRectF(0, 0, newSize.width(), QWIDGETSIZE_MAX), |
351 QSizeF newAdjust = metrics.boundingRect(QRectF(0, 0, newWidth, QWIDGETSIZE_MAX), |
348 textFlagsFromTextOption(), |
352 textFlagsFromTextOption(), |
349 mText).size(); |
353 mText).size(); |
350 |
354 |
351 if (qFuzzyCompare(newAdjust.height(), mAdjustedSize.height())) { |
355 if (qFuzzyCompare(newAdjust.height(), mAdjustedSize.height())) { |
352 // height is same as last time update range of same height |
356 // height is same as last time update range of same height |
353 mMaxWidthForAdjust = qMax(mMaxWidthForAdjust, newSize.width()); |
357 mMaxWidthForAdjust = qMax(mMaxWidthForAdjust, newWidth); |
354 mMinWidthForAdjust = qMin(mMinWidthForAdjust, newSize.width()); |
358 mMinWidthForAdjust = qMin(mMinWidthForAdjust, newWidth); |
355 |
359 |
356 // and don't update geometry |
360 // and don't update geometry |
357 return false; |
361 return false; |
358 } |
362 } |
359 |
363 |
360 // new height was calculated create new range for which |
364 // new height was calculated create new range for which |
361 // current mAdjustedSize.height is valid |
365 // current mAdjustedSize.height is valid |
362 mMaxWidthForAdjust = newSize.width(); |
366 mMaxWidthForAdjust = newWidth; |
363 mMinWidthForAdjust = newAdjust.width(); |
367 mMinWidthForAdjust = newAdjust.width(); |
364 |
368 |
365 // store new hieght use don't change width |
369 // store new hieght, don't change width |
366 mAdjustedSize.setHeight(newAdjust.height()); |
370 mAdjustedSize.setHeight(newAdjust.height()); |
367 Q_ASSERT_X(mAdjustedSize.width()>=newAdjust.width(), |
371 |
|
372 Q_ASSERT_X(mLastConstraint.width()>0 || mAdjustedSize.width()>=newAdjust.width(), |
368 "HbTextItemPrivate::isAdjustHightNeeded", |
373 "HbTextItemPrivate::isAdjustHightNeeded", |
369 QString("Fail for string: \"%1\"").arg(mText).toAscii().data()); |
374 QString("Fail for string: \"%1\"").arg(mText).toAscii().data()); |
370 |
375 |
371 if (respectSizeLimits(mAdjustedSize)==prefSize) { |
376 if (qFuzzyCompare(qBound(minHeight, |
|
377 respectHeightLimits(mAdjustedSize.height()), |
|
378 maxHeight), |
|
379 prefHeight)) { |
372 // updateGeometry has no effect |
380 // updateGeometry has no effect |
373 return false; |
381 return false; |
374 } |
382 } |
375 |
383 |
376 // all conditions for calling updateGeometry are meet |
384 // all conditions for calling updateGeometry are meet |
387 |
395 |
388 QSizeF HbTextItemPrivate::calculatePrefferedSize(const QSizeF& constraint) const |
396 QSizeF HbTextItemPrivate::calculatePrefferedSize(const QSizeF& constraint) const |
389 { |
397 { |
390 const QFontMetricsF metrics(mTextLayout.font()); |
398 const QFontMetricsF metrics(mTextLayout.font()); |
391 |
399 |
392 if (mAdjustedSize.isValid() && |
400 if (mAdjustedSize.isValid() && constraint == mLastConstraint) { |
393 (constraint.height()<0 || qFuzzyCompare(constraint.height(), mAdjustedSize.height())) && |
|
394 (constraint.width()<0 || qFuzzyCompare(constraint.width(), mAdjustedSize.width()))) { |
|
395 // return cached value, see more in: |
401 // return cached value, see more in: |
396 // - HbTextItemPrivate::isAdjustHightNeeded |
402 // - HbTextItemPrivate::isAdjustHightNeeded |
397 // - HbTextItem::resizeEvent |
403 // - HbTextItem::resizeEvent |
|
404 |
|
405 #ifdef HB_TEXT_ITEM_LOGS |
|
406 qDebug() << "HbTextItemPrivate::calculatePrefferedSize: returning cached value: " << mAdjustedSize |
|
407 << " font:" << mTextLayout.font() |
|
408 << " text:" << mText.left(KMaxLogedTextLength); |
|
409 #endif |
398 return mAdjustedSize; |
410 return mAdjustedSize; |
399 } |
411 } |
|
412 mLastConstraint = constraint; |
400 |
413 |
401 QSizeF maxSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); |
414 QSizeF maxSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); |
402 if(constraint.width()>0) { |
415 if(constraint.width()>0) { |
403 maxSize.setWidth(constraint.width()); |
416 maxSize.setWidth(constraint.width()); |
404 } |
417 } |
411 mText).size(); |
424 mText).size(); |
412 |
425 |
413 mAdjustedSize = size; |
426 mAdjustedSize = size; |
414 mDefaultHeight = size.height(); |
427 mDefaultHeight = size.height(); |
415 mMinWidthForAdjust = size.width(); |
428 mMinWidthForAdjust = size.width(); |
416 mMaxWidthForAdjust = QWIDGETSIZE_MAX; |
429 mMaxWidthForAdjust = maxSize.width(); |
417 |
430 |
|
431 #ifdef HB_TEXT_ITEM_LOGS |
|
432 qDebug() << "HbTextItemPrivate::calculatePrefferedSize:" |
|
433 << " text: " << mText.left(KMaxLogedTextLength) |
|
434 << " returnSize: " << mAdjustedSize |
|
435 << " constraint: " << constraint |
|
436 << " font: " << mTextLayout.font(); |
|
437 #endif |
418 return size; |
438 return size; |
419 } |
439 } |
420 |
440 |
421 bool HbTextItemPrivate::fadeNeeded(const QRectF& contentRect) const |
441 bool HbTextItemPrivate::fadeNeeded(const QRectF& contentRect) const |
422 { |
442 { |
826 } |
846 } |
827 |
847 |
828 QRectF HbTextItemPrivate::boundingRect (const QRectF& contentsRect) const |
848 QRectF HbTextItemPrivate::boundingRect (const QRectF& contentsRect) const |
829 { |
849 { |
830 QRectF result(layoutBoundingRect()); |
850 QRectF result(layoutBoundingRect()); |
|
851 |
|
852 if (mPaintFaded) { |
|
853 result = result.intersected(mFadeToRect); |
|
854 } |
|
855 |
831 if(q_ptr->flags().testFlag(QGraphicsItem::ItemClipsToShape)) { |
856 if(q_ptr->flags().testFlag(QGraphicsItem::ItemClipsToShape)) { |
832 // clip |
857 result = result.intersected(contentsRect); |
833 QRectF clippedTo = contentsRect; |
|
834 |
|
835 qreal dx = qMin(mFadeLengthX, (qreal)0.0); |
|
836 qreal dy = qMin(mFadeLengthY, (qreal)0.0); |
|
837 clippedTo.adjust(dx, dy, -dx, -dy); |
|
838 |
|
839 result = result.intersected(clippedTo); |
|
840 } |
858 } |
841 |
859 |
842 if (HbTextItemPrivate::outlinesEnabled) { |
860 if (HbTextItemPrivate::outlinesEnabled) { |
843 result = result.united(contentsRect); |
861 result = result.united(contentsRect); |
844 } |
862 } |
988 |
1006 |
989 // check if call of updateGeometry can be ignored |
1007 // check if call of updateGeometry can be ignored |
990 // don't call it when minimum and maximum lines are equal (height is fixed) or ... |
1008 // don't call it when minimum and maximum lines are equal (height is fixed) or ... |
991 if ((d->mMinLines == d->mMaxLines) |
1009 if ((d->mMinLines == d->mMaxLines) |
992 // or when preferred height is ignored |
1010 // or when preferred height is ignored |
993 || (sizePolicy().verticalPolicy()&QSizePolicy::IgnoreFlag)) { |
1011 || (sizePolicy().verticalPolicy()&QSizePolicy::IgnoreFlag) |
994 |
1012 // or was preferred height set from outside of this class? |
995 // and when preferred width is ignored or ... |
1013 || d->mLastConstraint.height()>0) { |
|
1014 |
|
1015 // and when preferred width is ignored |
996 if (sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag |
1016 if (sizePolicy().horizontalPolicy()&QSizePolicy::IgnoreFlag |
997 // or when preferred width is defined from outside |
1017 // or was preferred width set from outside of this class? |
998 || !qFuzzyCompare(preferredWidth(), d->mAdjustedSize.width())) { |
1018 || d->mLastConstraint.width()>0) { |
999 // TODO: looking for better solution since preferredWidth() can cause call of sizeHint |
1019 #ifdef HB_TEXT_ITEM_LOGS |
1000 |
1020 qDebug() << "HbTextItem::setText: skiping updateGeometry for: " |
|
1021 << objectName() << " text:" << d->mText.left(KMaxLogedTextLength); |
|
1022 #endif |
1001 // in those cases skip updateGeometry |
1023 // in those cases skip updateGeometry |
1002 return; |
1024 return; |
1003 } |
1025 } |
1004 } |
1026 } |
1005 updateGeometry(); |
1027 updateGeometry(); |
1110 |
1127 |
1111 painter->setPen(textColor()); |
1128 painter->setPen(textColor()); |
1112 |
1129 |
1113 Q_ASSERT(d->mPaintFaded == d->fadeNeeded(contentsRect())); |
1130 Q_ASSERT(d->mPaintFaded == d->fadeNeeded(contentsRect())); |
1114 if(d->mPaintFaded ) { |
1131 if(d->mPaintFaded ) { |
|
1132 // there is issue with restoring state of painter when |
|
1133 // setClipPath is used. It is impossible to restore |
|
1134 // original clipPath without save and restore whole painter |
|
1135 painter->save(); // see comment above |
|
1136 |
1115 d->paintWithFadeEffect(painter); |
1137 d->paintWithFadeEffect(painter); |
|
1138 |
|
1139 painter->restore(); // see comment above |
1116 } else { |
1140 } else { |
1117 d->mTextLayout.draw(painter, |
1141 d->mTextLayout.draw(painter, |
1118 d->mOffsetPos, |
1142 d->mOffsetPos, |
1119 QVector<QTextLayout::FormatRange>(), |
1143 QVector<QTextLayout::FormatRange>(), |
1120 flags().testFlag(ItemClipsToShape)?contentsRect():QRectF()); |
1144 flags().testFlag(ItemClipsToShape)?contentsRect():QRectF()); |
1137 if (parentLayoutItem() && parentLayoutItem()->isLayout()) { |
1161 if (parentLayoutItem() && parentLayoutItem()->isLayout()) { |
1138 // rect.size can't be used here since size can be limited inside of |
1162 // rect.size can't be used here since size can be limited inside of |
1139 // called method HbWidgetBase::setGeometry(rect) so size is used which |
1163 // called method HbWidgetBase::setGeometry(rect) so size is used which |
1140 // holds current size |
1164 // holds current size |
1141 Q_D(HbTextItem); |
1165 Q_D(HbTextItem); |
1142 if (d->isAdjustHightNeeded(size(), preferredSize())) { |
1166 if (d->isAdjustHightNeeded(size().width(), |
1143 updateGeometry(); |
1167 preferredHeight(), |
|
1168 minimumHeight(), |
|
1169 maximumHeight())) { |
|
1170 updateGeometry(); |
|
1171 #ifdef HB_TEXT_ITEM_LOGS |
|
1172 qDebug() << "isAdjustHightNeeded returned true - updateGeometry was called"; |
|
1173 #endif // HB_TEXT_ITEM_LOGS |
1144 } |
1174 } |
1145 } |
1175 } |
1146 } |
1176 } |
1147 |
1177 |
1148 /*! |
1178 /*! |
1173 // so updating font here (this is needed because of sizeHint adjustments). |
1203 // so updating font here (this is needed because of sizeHint adjustments). |
1174 if (d->mTextLayout.font()!=font()) { |
1204 if (d->mTextLayout.font()!=font()) { |
1175 #ifdef HB_TEXT_ITEM_LOGS |
1205 #ifdef HB_TEXT_ITEM_LOGS |
1176 qWarning() << "Font change was not recieved on time: work-around is active" |
1206 qWarning() << "Font change was not recieved on time: work-around is active" |
1177 << objectName() |
1207 << objectName() |
1178 << " test: " << d->mText.left(20) |
1208 << " test: " << d->mText.left(KMaxLogedTextLength) |
1179 << " oldFont:" << d->mTextLayout.font() |
1209 << " oldFont:" << d->mTextLayout.font() |
1180 << " newFont:" << font(); |
1210 << " newFont:" << font(); |
1181 #endif // HB_TEXT_ITEM_LOGS |
1211 #endif // HB_TEXT_ITEM_LOGS |
1182 |
1212 |
1183 const_cast<HbTextItemPrivate *>(d)->mTextLayout.setFont(font()); |
1213 const_cast<HbTextItemPrivate *>(d)->mTextLayout.setFont(font()); |
1201 switch(which) { |
1231 switch(which) { |
1202 case Qt::MinimumSize: |
1232 case Qt::MinimumSize: |
1203 { |
1233 { |
1204 if ( !d->mText.isEmpty() ) { |
1234 if ( !d->mText.isEmpty() ) { |
1205 size.setWidth(MinimumWidth); // just to show something -- should not matter in read use-case |
1235 size.setWidth(MinimumWidth); // just to show something -- should not matter in read use-case |
1206 size = d->respectSizeLimits(size); |
1236 size.setHeight(d->respectHeightLimits(size.height())); |
1207 } |
1237 } |
1208 break; |
1238 break; |
1209 } |
1239 } |
1210 |
1240 |
1211 case Qt::PreferredSize: |
1241 case Qt::PreferredSize: |
1212 { |
1242 { |
1213 if ( !effectiveOrientations.testFlag(Qt::Horizontal) |
1243 if ( !effectiveOrientations.testFlag(Qt::Horizontal) |
1214 && (d->mMinLines == d->mMaxLines) ) { |
1244 && (d->mMinLines == d->mMaxLines) ) { |
1215 //optimize single line if the horizontal sizeHint is ignored |
1245 //optimize single line if the horizontal sizeHint is ignored |
1216 size = d->respectSizeLimits(size); |
1246 size.setHeight(d->respectHeightLimits(size.height())); |
1217 break; |
1247 break; |
1218 } |
1248 } |
1219 |
1249 |
1220 size = d->calculatePrefferedSize(constraint); |
1250 size = d->calculatePrefferedSize(constraint); |
1221 size = d->respectSizeLimits(size); |
1251 size.setHeight(d->respectHeightLimits(size.height())); |
1222 break; |
1252 break; |
1223 } |
1253 } |
1224 |
1254 |
1225 default: |
1255 default: |
1226 size = HbWidgetBase::sizeHint(which, constraint); |
1256 size = HbWidgetBase::sizeHint(which, constraint); |
1250 |
1280 |
1251 if (!d->mTextLayout.font().isCopyOf(font())) { |
1281 if (!d->mTextLayout.font().isCopyOf(font())) { |
1252 |
1282 |
1253 #ifdef HB_TEXT_ITEM_LOGS |
1283 #ifdef HB_TEXT_ITEM_LOGS |
1254 qDebug() << "fontChangeEvent: " << objectName() |
1284 qDebug() << "fontChangeEvent: " << objectName() |
1255 << " text: " << text().left(20) |
1285 << " text: " << text().left(KMaxLogedTextLength) |
1256 << " font: " << font(); |
1286 << " font: " << font(); |
1257 #endif // HB_TEXT_ITEM_LOGS |
1287 #endif // HB_TEXT_ITEM_LOGS |
1258 |
1288 |
1259 d->mTextLayout.setFont(font()); |
1289 d->mTextLayout.setFont(font()); |
1260 d->clearAdjustedSizeCache(); |
1290 d->clearAdjustedSizeCache(); |
1321 // is size hint adjustable? |
1351 // is size hint adjustable? |
1322 if (parentLayoutItem() && parentLayoutItem()->isLayout()) { |
1352 if (parentLayoutItem() && parentLayoutItem()->isLayout()) { |
1323 if (d->mAdjustedSize.isValid() && |
1353 if (d->mAdjustedSize.isValid() && |
1324 d->mAdjustedSize.width() > size().width()) { |
1354 d->mAdjustedSize.width() > size().width()) { |
1325 // restore default size hint |
1355 // restore default size hint |
1326 d->mAdjustedSize = d->respectSizeLimits( |
1356 d->mAdjustedSize.setHeight( |
1327 QSizeF(d->mAdjustedSize.width(), d->mDefaultHeight)); |
1357 d->respectHeightLimits(d->mDefaultHeight)); |
1328 d->mMinWidthForAdjust = d->mAdjustedSize.width(); |
1358 d->mMinWidthForAdjust = d->mAdjustedSize.width(); |
1329 d->mMaxWidthForAdjust = QWIDGETSIZE_MAX; |
1359 d->mMaxWidthForAdjust = QWIDGETSIZE_MAX; |
1330 |
1360 |
1331 updateGeometry(); |
1361 updateGeometry(); |
1332 } |
1362 } |