From 776457a845dd114b185daeb756ef5bed099d842e 2013-09-03 04:52:45 From: Titta Heikkala Date: 2013-09-03 04:52:45 Subject: [PATCH] Fix long labels visibility for QBarChart Long labels are truncated vertically and horizontally so that they fit into the space available. There's no need to hide the labels explicitly as they are already truncated to the space available. The truncatedText() method now takes into account also the height of the space available. Task-number: QTRD-1929 Change-Id: Ibe2b996e514ddb44c96ce040fc729a605d24c8ab Reviewed-by: Miikka Heikkinen --- diff --git a/src/axis/horizontalaxis.cpp b/src/axis/horizontalaxis.cpp index ac8ccc8..4aef626 100644 --- a/src/axis/horizontalaxis.cpp +++ b/src/axis/horizontalaxis.cpp @@ -73,23 +73,10 @@ void HorizontalAxis::updateGeometry() if (!titleText.isEmpty() && titleItem()->isVisible()) { availableSpace -= titlePadding() * 2.0; qreal minimumLabelHeight = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").height(); - QString truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), - gridRect.width(), Qt::Horizontal, titleBoundingRect); qreal titleSpace = availableSpace - minimumLabelHeight; - if (titleSpace < titleBoundingRect.height()) { - // Need to also truncate title vertically (multiline title) - bool skip = false; - if (truncatedTitle.endsWith("...")) { - if (truncatedTitle.size() == 3) - skip = true; // Already truncated to minimum - else - truncatedTitle.chop(3); - } - if (!skip) - truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), truncatedTitle, qreal(0.0), - titleSpace, Qt::Vertical, titleBoundingRect); - } - title->setHtml(truncatedTitle); + title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), + gridRect.width(), titleSpace, + titleBoundingRect)); titleBoundingRect = title->boundingRect(); @@ -114,8 +101,15 @@ void HorizontalAxis::updateGeometry() //label text wrapping QString text = labelList.at(i); QRectF boundingRect; - labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, axis()->labelsAngle(), - availableSpace, Qt::Vertical, boundingRect)); + // don't truncate empty labels + if (text.isEmpty()) { + labelItem->setHtml(text); + } else { + qreal labelWidth = axisRect.width() / layout.count() - (2 * labelPadding()); + labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, + axis()->labelsAngle(), labelWidth, + availableSpace, boundingRect)); + } //label transformation origin point const QRectF& rect = labelItem->boundingRect(); @@ -141,16 +135,18 @@ void HorizontalAxis::updateGeometry() const qreal delta = rightBound - leftBound; // Hide label in case visible part of the category at the grid edge is too narrow if (delta < boundingRect.width() - && (leftBound == gridRect.left() || rightBound == gridRect.right())) { + && (leftBound == gridRect.left() || rightBound == gridRect.right()) + && !intervalAxis()) { forceHide = true; } else { labelItem->setPos(leftBound + (delta / 2.0) - center.x(), labelItem->pos().y()); } } //label overlap detection - compensate one pixel for rounding errors - if (labelItem->pos().x() < width || forceHide || + if ((labelItem->pos().x() < width || forceHide || (labelItem->pos().x() + (widthDiff / 2.0)) < (axisRect.left() - 1.0) || - (labelItem->pos().x() + (widthDiff / 2.0) - 1.0) > axisRect.right()){ + (labelItem->pos().x() + (widthDiff / 2.0) - 1.0) > axisRect.right()) + && !intervalAxis()) { labelItem->setVisible(false); } else { labelItem->setVisible(true); diff --git a/src/axis/polarchartaxisangular.cpp b/src/axis/polarchartaxisangular.cpp index f82b3f2..69af03c 100644 --- a/src/axis/polarchartaxisangular.cpp +++ b/src/axis/polarchartaxisangular.cpp @@ -222,7 +222,12 @@ void PolarChartAxisAngular::updateGeometry() QString titleText = axis()->titleText(); if (!titleText.isEmpty() && axis()->isTitleVisible()) { QRectF dummyRect; - title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), axisGeometry().width(), Qt::Horizontal, dummyRect)); + qreal availableTitleHeight = axisGeometry().height() - labelPadding() - titlePadding() * 2.0; + qreal minimumLabelHeight = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").height(); + availableTitleHeight -= minimumLabelHeight; + title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), + axisGeometry().width(), availableTitleHeight, + dummyRect)); QRectF titleBoundingRect = title->boundingRect(); QPointF titleCenter = center - titleBoundingRect.center(); diff --git a/src/axis/polarchartaxisradial.cpp b/src/axis/polarchartaxisradial.cpp index bfb2732..2587b91 100644 --- a/src/axis/polarchartaxisradial.cpp +++ b/src/axis/polarchartaxisradial.cpp @@ -205,7 +205,8 @@ void PolarChartAxisRadial::updateGeometry() QString titleText = axis()->titleText(); if (!titleText.isEmpty() && axis()->isTitleVisible()) { QRectF dummyRect; - title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), radius, Qt::Horizontal, dummyRect)); + title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), + radius, radius, dummyRect)); QRectF titleBoundingRect = title->boundingRect(); QPointF titleCenter = titleBoundingRect.center(); diff --git a/src/axis/verticalaxis.cpp b/src/axis/verticalaxis.cpp index e0d3451..2986b51 100644 --- a/src/axis/verticalaxis.cpp +++ b/src/axis/verticalaxis.cpp @@ -74,23 +74,10 @@ void VerticalAxis::updateGeometry() if (!titleText.isEmpty() && titleItem()->isVisible()) { availableSpace -= titlePadding() * 2.0; qreal minimumLabelWidth = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").width(); - QString truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), - gridRect.height(), Qt::Horizontal, titleBoundingRect); qreal titleSpace = availableSpace - minimumLabelWidth; - if (titleSpace < titleBoundingRect.height()) { - // Need to also truncate title vertically (multiline title) - bool skip = false; - if (truncatedTitle.endsWith("...")) { - if (truncatedTitle.size() == 3) - skip = true; // Already truncated to minimum - else - truncatedTitle.chop(3); - } - if (!skip) - truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), truncatedTitle, qreal(0.0), - titleSpace, Qt::Vertical, titleBoundingRect); - } - title->setHtml(truncatedTitle); + title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(90.0), + titleSpace, gridRect.height(), + titleBoundingRect)); titleBoundingRect = title->boundingRect(); @@ -118,8 +105,15 @@ void VerticalAxis::updateGeometry() //label text wrapping QString text = labelList.at(i); QRectF boundingRect; - labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, axis()->labelsAngle(), - availableSpace, Qt::Horizontal, boundingRect)); + // don't truncate empty labels + if (text.isEmpty()) { + labelItem->setHtml(text); + } else { + qreal labelHeight = (axisRect.height() / layout.count()) - (2 * labelPadding()); + labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, + axis()->labelsAngle(), availableSpace, + labelHeight, boundingRect)); + } //label transformation origin point const QRectF &rect = labelItem->boundingRect(); @@ -145,7 +139,8 @@ void VerticalAxis::updateGeometry() const qreal delta = lowerBound - upperBound; // Hide label in case visible part of the category at the grid edge is too narrow if (delta < boundingRect.height() - && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) { + && (lowerBound == gridRect.bottom() || upperBound == gridRect.top()) + && !intervalAxis()) { forceHide = true; } else { labelItem->setPos(labelItem->pos().x() , lowerBound - (delta / 2.0) - center.y()); @@ -153,9 +148,10 @@ void VerticalAxis::updateGeometry() } //label overlap detection - compensate one pixel for rounding errors - if (labelItem->pos().y() + boundingRect.height() > height || forceHide || + if ((labelItem->pos().y() + boundingRect.height() > height || forceHide || (labelItem->pos().y() + (heightDiff / 2.0) - 1.0) > axisRect.bottom() || - labelItem->pos().y() + (heightDiff / 2.0) < (axisRect.top() - 1.0)) { + labelItem->pos().y() + (heightDiff / 2.0) < (axisRect.top() - 1.0)) + && !intervalAxis()) { labelItem->setVisible(false); } else { diff --git a/src/chartpresenter.cpp b/src/chartpresenter.cpp index 765a228..9bf9d2a 100644 --- a/src/chartpresenter.cpp +++ b/src/chartpresenter.cpp @@ -410,14 +410,11 @@ QRectF ChartPresenter::textBoundingRect(const QFont &font, const QString &text, // boundingRect parameter returns the rotated bounding rect of the text QString ChartPresenter::truncatedText(const QFont &font, const QString &text, qreal angle, - qreal maxSize, Qt::Orientation constraintOrientation, - QRectF &boundingRect) + qreal maxWidth, qreal maxHeight, QRectF &boundingRect) { QString truncatedString(text); boundingRect = textBoundingRect(font, truncatedString, angle); - qreal checkDimension = ((constraintOrientation == Qt::Horizontal) - ? boundingRect.width() : boundingRect.height()); - if (checkDimension > maxSize) { + if (boundingRect.width() > maxWidth || boundingRect.height() > maxHeight) { // It can be assumed that almost any amount of string manipulation is faster // than calculating one bounding rectangle, so first prepare a list of truncated strings // to try. @@ -452,12 +449,11 @@ QString ChartPresenter::truncatedText(const QFont &font, const QString &text, qr int maxIndex(count - 1); int bestIndex(count); QRectF checkRect; + while (maxIndex >= minIndex) { int mid = (maxIndex + minIndex) / 2; checkRect = textBoundingRect(font, testStrings.at(mid), angle); - checkDimension = ((constraintOrientation == Qt::Horizontal) - ? checkRect.width() : checkRect.height()); - if (checkDimension > maxSize) { + if (checkRect.width() > maxWidth || checkRect.height() > maxHeight) { // Checked index too large, all under this are also too large minIndex = mid + 1; } else { diff --git a/src/chartpresenter_p.h b/src/chartpresenter_p.h index 93a616f..116aa6a 100644 --- a/src/chartpresenter_p.h +++ b/src/chartpresenter_p.h @@ -145,8 +145,8 @@ public: QChart *chart() { return m_chart; } static QRectF textBoundingRect(const QFont &font, const QString &text, qreal angle = 0.0); - static QString truncatedText(const QFont &font, const QString &text, qreal angle, qreal maxSize, - Qt::Orientation constraintOrientation, QRectF &boundingRect); + static QString truncatedText(const QFont &font, const QString &text, qreal angle, + qreal maxWidth, qreal maxHeight, QRectF &boundingRect); inline static qreal textMargin() { return qreal(0.5); } private: void createBackgroundItem(); diff --git a/src/charttitle.cpp b/src/charttitle.cpp index d5306cf..d6fd0e0 100644 --- a/src/charttitle.cpp +++ b/src/charttitle.cpp @@ -51,7 +51,9 @@ QString ChartTitle::text() const void ChartTitle::setGeometry(const QRectF &rect) { QRectF dummyRect; - QGraphicsTextItem::setHtml(ChartPresenter::truncatedText(font(), m_text, qreal(0.0), rect.width(), Qt::Horizontal, dummyRect)); + QGraphicsTextItem::setHtml(ChartPresenter::truncatedText(font(), m_text, qreal(0.0), + rect.width(), rect.height(), + dummyRect)); setPos(rect.topLeft()); } diff --git a/src/legend/legendmarkeritem.cpp b/src/legend/legendmarkeritem.cpp index fbd809f..0fe48a9 100644 --- a/src/legend/legendmarkeritem.cpp +++ b/src/legend/legendmarkeritem.cpp @@ -116,7 +116,8 @@ void LegendMarkerItem::setGeometry(const QRectF &rect) qreal x = m_margin + m_markerRect.width() + m_space + m_margin; QRectF truncatedRect; - m_textItem->setHtml(ChartPresenter::truncatedText(m_textItem->font(), m_label, qreal(0.0), width - x, Qt::Horizontal, truncatedRect)); + m_textItem->setHtml(ChartPresenter::truncatedText(m_textItem->font(), m_label, qreal(0.0), + width - x, rect.height(), truncatedRect)); qreal y = qMax(m_markerRect.height() + 2 * m_margin, truncatedRect.height() + 2 * m_margin); diff --git a/src/piechart/piesliceitem.cpp b/src/piechart/piesliceitem.cpp index b0de3f7..aaca4a8 100644 --- a/src/piechart/piesliceitem.cpp +++ b/src/piechart/piesliceitem.cpp @@ -94,9 +94,11 @@ void PieSliceItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*o painter->setClipRect(parentItem()->boundingRect()); painter->strokePath(m_labelArmPath, m_data.m_labelBrush.color()); if (fm.width(m_data.m_labelText) > m_labelTextRect.width()) { + // Only one line label text is supported currently. + // The height for the label is set one pixel over the font metrics. label = ChartPresenter::truncatedText(m_data.m_labelFont, m_data.m_labelText, qreal(0.0), m_labelTextRect.width(), - Qt::Horizontal, labelBoundingRect); + fm.height() + 1.0, labelBoundingRect); } painter->drawText(m_labelTextRect, Qt::AlignCenter, label); break;