diff -r 7516d6d86cf5 -r ed14f46c0e55 src/hbcore/primitives/hbrichtextitem.cpp --- a/src/hbcore/primitives/hbrichtextitem.cpp Mon Oct 04 17:49:30 2010 +0300 +++ b/src/hbcore/primitives/hbrichtextitem.cpp Mon Oct 18 18:23:13 2010 +0300 @@ -37,17 +37,16 @@ #include #include #include +#include +#include -const int KMinimumLetersToShow = 4; +const qreal KMinimumRichTextWidth = 6.0; static const QString KDefaultColorThemeName = "qtc_view_normal"; HbRichTextItemPrivate::HbRichTextItemPrivate() : - mAlignment(Qt::AlignLeft|Qt::AlignVCenter), - mTextOption(mAlignment), - mDontPrint(false), - mDontClip(false), - mRtf(0) + mTextOption(Qt::AlignLeft|Qt::AlignVCenter), + mRtf(0) { } @@ -62,7 +61,7 @@ { Q_Q(HbRichTextItem); - q->setFlag(QGraphicsItem::ItemClipsToShape, !mDontClip); + q->setFlag(QGraphicsItem::ItemClipsToShape, true); q->setFlag(QGraphicsItem::ItemIsSelectable, false); q->setFlag(QGraphicsItem::ItemIsFocusable, false); @@ -77,54 +76,13 @@ void HbRichTextItemPrivate::clear() { - delete mRtf; } -int HbRichTextItemPrivate::textFlagsFromTextOption() const +void HbRichTextItemPrivate::setDocumentWidth(qreal newWidth) { - int flags = (int)mAlignment; - - switch(mTextOption.wrapMode()) { - case QTextOption::NoWrap: - flags |= Qt::TextSingleLine; - break; - case QTextOption::WordWrap: - flags |=Qt::TextWordWrap; - break; - case QTextOption::ManualWrap: - break; - case QTextOption::WrapAnywhere: - flags |=Qt::TextWrapAnywhere; - break; - case QTextOption::WrapAtWordBoundaryOrAnywhere: - flags |=Qt::TextWordWrap | Qt::TextWrapAnywhere; - break; - } - - if(mDontClip) flags |= Qt::TextDontClip; - if(mDontPrint) flags |= Qt::TextDontPrint; - - return flags; -} - -bool HbRichTextItemPrivate::setLayoutDirection(Qt::LayoutDirection newDirection) -{ - Qt::Alignment oldAlign = mTextOption.alignment(); - Qt::Alignment alignment = QStyle::visualAlignment(newDirection, mAlignment); - mTextOption.setAlignment(alignment); - mTextOption.setTextDirection(newDirection); - mRtf->setDefaultTextOption(mTextOption); - - return alignment!=oldAlign; -} - -void HbRichTextItemPrivate::setSize(const QSizeF &newSize) -{ - if(mRtf->size()!=newSize) { - Q_Q(HbRichTextItem); - mRtf->setTextWidth(newSize.width()); + if (!qFuzzyCompare(mRtf->textWidth(), newWidth)) { + mRtf->setTextWidth(newWidth); calculateOffset(); - q->update(); } } @@ -134,11 +92,12 @@ Q_Q(HbRichTextItem); qreal diff; - if(mAlignment.testFlag(Qt::AlignTop)) { + Qt::Alignment align = mTextOption.alignment(); + if (align.testFlag(Qt::AlignTop)) { diff = 0.0; } else { diff = q->geometry().height() - mRtf->size().height(); - if(!mAlignment.testFlag(Qt::AlignBottom)) { + if (!align.testFlag(Qt::AlignBottom)) { // default align Qt::AlignVCenter if no flags are set diff*=0.5; } @@ -147,12 +106,166 @@ diff = qMax(diff, (qreal)0.0); #endif - if(diff!=mOffset.y()) { + if (diff!=mOffset.y()) { mOffset.setY(diff); q->prepareGeometryChange(); } } +QSizeF HbRichTextItemPrivate::minimumSizeHint(const QSizeF &/*constraint*/) const +{ + QSizeF result(KMinimumRichTextWidth, 0); + + QTextBlock textBlock = mRtf->begin(); + if (textBlock.isValid() && textBlock.layout()->lineCount() > 0) { + QTextLine line = textBlock.layout()->lineAt(0); + result.setHeight(line.height()); + } else { + QFontMetricsF metrics(mRtf->defaultFont()); + result.setHeight(metrics.height()); + } + + qreal doubleDocMargin = mRtf->documentMargin() * (qreal)2.0; + result.rheight() += doubleDocMargin; + result.rwidth() += doubleDocMargin; + + return result; +} + +void HbRichTextItemPrivate::clearPrefSizeCache() +{ + mPrefSize.setWidth(-1); + mMinWidthForAdjust = QWIDGETSIZE_MAX; + mMaxWidthForAdjust = -1; +} + +QSizeF HbRichTextItemPrivate::preferredSizeHint(const QSizeF &constraint) const +{ + if (mPrefSizeConstraint==constraint && mPrefSize.isValid()) { + return mPrefSize; + } + mPrefSizeConstraint=constraint; + + QSizeF result; + + if (constraint.width()<=0) { + mRtf->setTextWidth(QWIDGETSIZE_MAX); + } else { + QTextOption::WrapMode wrapMode = mTextOption.wrapMode(); + // optimization when there is no automatic wrap there is no reason + // to setTextWidth with width constraint (width measure is not needed) + if (wrapMode!=QTextOption::NoWrap + && wrapMode!=QTextOption::ManualWrap) { + mRtf->setTextWidth(constraint.width()); + } + } + result = mRtf->size(); + mMaxWidthForAdjust = result.width(); + result.setWidth(mRtf->idealWidth()); + mMinWidthForAdjust = result.width(); + mDefaultPrefHeight = result.height(); + mPrefSize = result; + + return result; +} + +bool HbRichTextItemPrivate::isAdjustHeightNeeded(qreal newWidth, + qreal prefHeight, + qreal minHeight, + qreal maxHeight) +{ + // first check if wrapping of text is not active + QTextOption::WrapMode wrapMode = mTextOption.wrapMode(); + if (wrapMode==QTextOption::NoWrap + || wrapMode==QTextOption::ManualWrap) { + return false; + } + + // preferred height was set from outside of this class so there is mo reason to adjust it + if (mPrefSizeConstraint.height()>0) { + return false; + } + + // check if adjusted size has been already calculated + // new width is bigger than last estimated range of same height + if ((mMaxWidthForAdjust>=newWidth + // new width is smaller than last estimated range of same height + && newWidth>=mMinWidthForAdjust)) { + return false; + } + + if (!mPrefSize.isValid()) { + // this means that preferred size is set outside of class by setPreferredSize + // so sizeHint(Qt::Preferredsize) was not called and size adjustment is useless + return false; + } + + // new text width was set in setGeometry here it is not needed + Q_ASSERT_X(qFuzzyCompare(mRtf->textWidth(), newWidth), + "HbRichTextItemPrivate::isAdjustHeightNeeded", + QString("mRtf->textWidth()=%1, newWidth=%2") + .arg(mRtf->textWidth()) + .arg(newWidth).toAscii().data()); + + // if preconditions are met test if current height is enough + QSizeF newAdjust = mRtf->size(); + + if (qFuzzyCompare(newAdjust.height(), mPrefSize.height())) { + // height is same as last time update range of same height + mMaxWidthForAdjust = qMax(mMaxWidthForAdjust, newWidth); + mMinWidthForAdjust = qMin(mMinWidthForAdjust, newWidth); + return false; + } + + // new height was calculated create new range for which + // current mPrefSize.height is valid + mMaxWidthForAdjust = newWidth; + mMinWidthForAdjust = mRtf->idealWidth(); + + // store new height, don't change width + mPrefSize.setHeight(newAdjust.height()); + + Q_ASSERT_X(mPrefSizeConstraint.width()>0 || mPrefSize.width()>=mMinWidthForAdjust, + "HbRichTextItemPrivate::isAdjustHeightNeeded", + QString("Fail for (%1<%2) string: \"%3\"") + .arg(mPrefSize.width()) + .arg(mMinWidthForAdjust) + .arg(mText).toAscii().data()); + + if (qFuzzyCompare(qBound(minHeight, mPrefSize.height(), maxHeight), + prefHeight)) { + // updateGeometry has no effect + return false; + } + + // all conditions for calling updateGeometry are meet + return true; +} + +/* + returns true if updateGeometry is needed + */ +bool HbRichTextItemPrivate::restoreDefaultHeightHint() +{ + if (mPrefSizeConstraint.height()>0) { + return false; + } + + if (mPrefSizeConstraint.width()>0) { + clearPrefSizeCache(); + return true; + } + + if (mPrefSize.height()==mDefaultPrefHeight) { + return true; + } + + mPrefSize.setHeight(mDefaultPrefHeight); + mMinWidthForAdjust = mPrefSize.width(); + mMaxWidthForAdjust = QWIDGETSIZE_MAX; + return true; +} + /*! @proto @hbcore @@ -170,7 +283,7 @@ */ HbRichTextItem::HbRichTextItem(QGraphicsItem *parent) : - HbWidgetBase(*new HbRichTextItemPrivate, parent) + HbWidgetBase(*new HbRichTextItemPrivate, parent) { Q_D(HbRichTextItem); d->init(); @@ -180,7 +293,7 @@ Constructor which set content using \a html format. */ HbRichTextItem::HbRichTextItem(const QString &html, QGraphicsItem *parent) : - HbWidgetBase(*new HbRichTextItemPrivate, parent) + HbWidgetBase(*new HbRichTextItemPrivate, parent) { Q_D(HbRichTextItem); d->init(); @@ -191,7 +304,7 @@ Constructor for internal use only */ HbRichTextItem::HbRichTextItem(HbRichTextItemPrivate &dd, QGraphicsItem *parent) : - HbWidgetBase(dd, parent) + HbWidgetBase(dd, parent) { Q_D(HbRichTextItem); d->init(); @@ -217,6 +330,8 @@ if (d->mText != text) { d->mText = text; d->mRtf->setHtml(text); + d->clearPrefSizeCache(); + update(); updateGeometry(); } } @@ -229,7 +344,7 @@ QString HbRichTextItem::text() const { - Q_D( const HbRichTextItem ); + Q_D(const HbRichTextItem); return d->mText; } @@ -240,16 +355,15 @@ */ void HbRichTextItem::setAlignment(Qt::Alignment alignment) { - Q_D( HbRichTextItem ); - d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true); - alignment &= Qt::AlignVertical_Mask | Qt::AlignHorizontal_Mask; - if( d->mAlignment!=alignment ) { + Q_D(HbRichTextItem); + d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextAlign, true); + alignment = d->combineAlignment(alignment, d->mTextOption.alignment()); + if (d->mTextOption.alignment()!=alignment) { prepareGeometryChange(); - d->mAlignment = alignment; + d->mTextOption.setAlignment(alignment); + d->mRtf->setDefaultTextOption(d->mTextOption); d->calculateOffset(); - if(d->setLayoutDirection(layoutDirection())) { - update(); - } + update(); } } @@ -260,39 +374,39 @@ */ Qt::Alignment HbRichTextItem::alignment() const { - Q_D( const HbRichTextItem ); - return d->mAlignment; + Q_D(const HbRichTextItem); + return d->mTextOption.alignment(); } /*! \reimp */ void HbRichTextItem::paint(QPainter *painter, - const QStyleOptionGraphicsItem *option, - QWidget *widget) + const QStyleOptionGraphicsItem *option, + QWidget *widget) { - Q_UNUSED(option); Q_UNUSED(widget); Q_D(HbRichTextItem); - // Save painter's state - QRegion oldClipRegion = painter->clipRegion(); - QTransform oldTransform = painter->transform(); - - if(!d->mDontPrint) { - if(!d->mDontClip) { - painter->setClipRect(contentsRect(), Qt::IntersectClip); - } - painter->translate(d->mOffset); - QAbstractTextDocumentLayout::PaintContext context; - context.palette.setColor(QPalette::Text, textDefaultColor()); - d->mRtf->documentLayout()->draw(painter, context); + if (option->exposedRect.isEmpty()) { + // nothing to paint + return; } - // Restore painter's state - painter->setClipRegion(oldClipRegion); - painter->setTransform(oldTransform); + painter->translate(d->mOffset); + + QAbstractTextDocumentLayout::PaintContext context; + context.clip = option->exposedRect; + // painter was translated so it should be compensated + context.clip.translate(-d->mOffset); + + context.palette.setColor(QPalette::Text, textDefaultColor()); + + d->mRtf->documentLayout()->draw(painter, context); + + // restore painter + painter->translate(-d->mOffset); } /*! @@ -306,8 +420,21 @@ HbWidgetBase::setGeometry(rect); - if(rect.isValid()) { - d->setSize(rect.size()); + // this call is needed since there is possible scenario + // when size was not changed after updateGeometry and sizeHint calls + d->setDocumentWidth(size().width()); + + if (parentLayoutItem() && parentLayoutItem()->isLayout()) { + // rect.size can't be used here since size can be limited inside of + // called method HbWidgetBase::setGeometry(rect) so size is used which + // holds current size + if (d->isAdjustHeightNeeded(size().width(), + preferredHeight(), + minimumHeight(), + maximumHeight())) { + updateGeometry(); + return; + } } } @@ -318,12 +445,12 @@ { Q_D(const HbRichTextItem); - QRectF result(d->mOffset, d->mRtf->size()); + QRectF result(d->mRtf->documentLayout()->frameBoundingRect(d->mRtf->rootFrame())); + result.translate(d->mOffset); - if(!d->mDontClip) { + if (flags().testFlag(QGraphicsItem::ItemClipsToShape)) { // clip - result = result.intersect(QRectF(QPointF(), - size())); + result = result.intersect(contentsRect()); } return result; } @@ -332,16 +459,17 @@ \reimp Relayouts text according to new size. */ -void HbRichTextItem::resizeEvent(QGraphicsSceneResizeEvent *event) +void HbRichTextItem::resizeEvent(QGraphicsSceneResizeEvent * /*event*/) { Q_D(HbRichTextItem); - d->setSize(event->newSize()); + d->setDocumentWidth(size().width()); + d->calculateOffset(); } /*! \reimp - This impelementation detects layout direction changes, font changes and theme changes. + This implementation detects layout direction changes, font changes and theme changes. */ void HbRichTextItem::changeEvent(QEvent *event) { @@ -350,13 +478,15 @@ switch(event->type()) { case QEvent::LayoutDirectionChange: { prepareGeometryChange(); - d->setLayoutDirection(layoutDirection()); + d->mTextOption.setTextDirection(layoutDirection()); + d->mRtf->setDefaultTextOption(d->mTextOption); update(); } break; case QEvent::FontChange: { d->mRtf->setDefaultFont(font()); + d->clearPrefSizeCache(); updateGeometry(); } break; @@ -367,7 +497,7 @@ if (event->type() == HbEvent::ThemeChanged) { Q_D(HbRichTextItem); d->mDefaultColor = QColor(); - if(!d->mColor.isValid()) { + if (!d->mColor.isValid()) { update(); } } @@ -387,29 +517,12 @@ QSizeF result; switch(which) { case Qt::MinimumSize: { - QTextBlock textBlock = d->mRtf->begin(); - if(textBlock.isValid() && textBlock.layout()->lineCount() > 0) { - QTextLine line = textBlock.layout()->lineAt(0); - result.setHeight(line.height()); - int cursorPos(KMinimumLetersToShow); - result.setWidth( line.cursorToX(&cursorPos) ); - - qreal doubleDocMargin = d->mRtf->documentMargin() * (qreal)2.0; - result.rheight() += doubleDocMargin; - result.rwidth() += doubleDocMargin; - } else { - result = HbWidgetBase::sizeHint(which, constraint); - } + result = d->minimumSizeHint(constraint); } break; case Qt::PreferredSize: { - if(constraint.width()<=0) { - d->mRtf->adjustSize(); - } else { - d->mRtf->setTextWidth(constraint.width()); - } - result = d->mRtf->size(); + result = d->preferredSizeHint(constraint); } break; @@ -429,14 +542,17 @@ void HbRichTextItem::setTextWrapping(Hb::TextWrapping mode) { Q_D(HbRichTextItem); - d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextWrapMode, true); + d->setApiProtectionFlag(HbWidgetBasePrivate::AC_TextWrapMode, true); QTextOption::WrapMode textWrapMode = static_cast(mode); - if(d->mTextOption.wrapMode()!=textWrapMode) { + if (d->mTextOption.wrapMode()!=textWrapMode) { prepareGeometryChange(); d->mTextOption.setWrapMode(textWrapMode); d->mRtf->setDefaultTextOption(d->mTextOption); - updateGeometry(); + d->calculateOffset(); + if (d->restoreDefaultHeightHint()) { + updateGeometry(); + } } } @@ -464,7 +580,7 @@ */ QColor HbRichTextItem::textDefaultColor() const { - Q_D( const HbRichTextItem ); + Q_D(const HbRichTextItem); if (d->mColor.isValid()) { // Means user has set text color return d->mColor; @@ -513,11 +629,7 @@ */ void HbRichTextItem::setTextVisible(bool isVisible) { - Q_D(HbRichTextItem); - if( d->mDontPrint == isVisible ) { - d->mDontPrint = !isVisible; - update(); - } + setVisible(isVisible); } /*! @@ -525,22 +637,15 @@ */ bool HbRichTextItem::isTextVisible() const { - Q_D(const HbRichTextItem); - return !d->mDontPrint; + return isVisible(); } /*! * Enables (default) or disables text clipping when item geometry is too small. */ -void HbRichTextItem::setTextClip(bool cliping) +void HbRichTextItem::setTextClip(bool clipping) { - Q_D(HbRichTextItem); - if( d->mDontClip == cliping ) { - prepareGeometryChange(); - d->mDontClip = !cliping; - setFlag(QGraphicsItem::ItemClipsToShape, cliping); - update(); - } + setFlag(QGraphicsItem::ItemClipsToShape, clipping); } /*! @@ -548,8 +653,7 @@ */ bool HbRichTextItem::isTextClip() const { - Q_D(const HbRichTextItem); - return !d->mDontClip; + return flags().testFlag(ItemClipsToShape); } // end of file