##// END OF EJS Templates
Don't reserve space for chart title if it is empty....
Don't reserve space for chart title if it is empty. If title.isEmpty() returns true, chart title is no longer reserved any space. If you want to reserve space for "empty" title in the future, you can do so by specifying e.g. a single space for title. Task-number: QTRD-3480 Change-Id: I5b2b44acf715b3bd9b4243049c4985ca63a4a16d Reviewed-by: Titta Heikkala <titta.heikkala@theqtcompany.com>

File last commit:

r2781:7c9f8e5a27d8
r2794:05fa3caa7879
Show More
chartaxiselement.cpp
400 lines | 14.8 KiB | text/x-c | CppLexer
/ src / charts / axis / chartaxiselement.cpp
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd
** All rights reserved.
** For any questions to The Qt Company, please use contact form at http://qt.io
**
** This file is part of the Qt Charts module.
**
** Licensees holding valid commercial license for Qt may use this file in
** accordance with the Qt License Agreement provided with the Software
** or, alternatively, in accordance with the terms contained in a written
** agreement between you and The Qt Company.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.io
**
****************************************************************************/
#include <private/chartaxiselement_p.h>
#include <private/qabstractaxis_p.h>
#include <private/chartpresenter_p.h>
#include <private/abstractchartlayout_p.h>
#include <QtCore/QtMath>
#include <QtCore/QDateTime>
#include <QtGui/QTextDocument>
#include <cmath>
QT_CHARTS_BEGIN_NAMESPACE
static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.\\'lhjztL]*([dicuoxfegXFEG])";
static const char *labelFormatMatchLocalizedString = "^([^%]*)%\\.(\\d+)([defgiEG])(.*)$";
static QRegExp *labelFormatMatcher = 0;
static QRegExp *labelFormatMatcherLocalized = 0;
class StaticLabelFormatMatcherDeleter
{
public:
StaticLabelFormatMatcherDeleter() {}
~StaticLabelFormatMatcherDeleter() {
delete labelFormatMatcher;
delete labelFormatMatcherLocalized;
}
};
static StaticLabelFormatMatcherDeleter staticLabelFormatMatcherDeleter;
ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
: ChartElement(item),
m_axis(axis),
m_animation(0),
m_grid(new QGraphicsItemGroup(item)),
m_arrow(new QGraphicsItemGroup(item)),
m_shades(new QGraphicsItemGroup(item)),
m_labels(new QGraphicsItemGroup(item)),
m_title(new QGraphicsTextItem(item)),
m_intervalAxis(intervalAxis)
{
//initial initialization
m_arrow->setHandlesChildEvents(false);
m_arrow->setZValue(ChartPresenter::AxisZValue);
m_labels->setZValue(ChartPresenter::AxisZValue);
m_shades->setZValue(ChartPresenter::ShadesZValue);
m_grid->setZValue(ChartPresenter::GridZValue);
m_title->setZValue(ChartPresenter::GridZValue);
m_title->document()->setDocumentMargin(ChartPresenter::textMargin());
handleVisibleChanged(axis->isVisible());
connectSlots();
setFlag(QGraphicsItem::ItemHasNoContents, true);
}
ChartAxisElement::~ChartAxisElement()
{
}
void ChartAxisElement::connectSlots()
{
QObject::connect(axis(), SIGNAL(visibleChanged(bool)), this, SLOT(handleVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), this, SLOT(handleArrowVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(gridVisibleChanged(bool)), this, SLOT(handleGridVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(shadesVisibleChanged(bool)), this, SLOT(handleShadesVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(labelsAngleChanged(int)), this, SLOT(handleLabelsAngleChanged(int)));
QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, SLOT(handleArrowPenChanged(const QPen&)));
QObject::connect(axis(), SIGNAL(labelsBrushChanged(const QBrush&)), this, SLOT(handleLabelsBrushChanged(const QBrush&)));
QObject::connect(axis(), SIGNAL(labelsFontChanged(const QFont&)), this, SLOT(handleLabelsFontChanged(const QFont&)));
QObject::connect(axis(), SIGNAL(gridLinePenChanged(const QPen&)), this, SLOT(handleGridPenChanged(const QPen&)));
QObject::connect(axis(), SIGNAL(shadesPenChanged(const QPen&)), this, SLOT(handleShadesPenChanged(const QPen&)));
QObject::connect(axis(), SIGNAL(shadesBrushChanged(const QBrush&)), this, SLOT(handleShadesBrushChanged(const QBrush&)));
QObject::connect(axis(), SIGNAL(titleTextChanged(const QString&)), this, SLOT(handleTitleTextChanged(const QString&)));
QObject::connect(axis(), SIGNAL(titleFontChanged(const QFont&)), this, SLOT(handleTitleFontChanged(const QFont&)));
QObject::connect(axis(), SIGNAL(titleBrushChanged(const QBrush&)), this, SLOT(handleTitleBrushChanged(const QBrush&)));
QObject::connect(axis(), SIGNAL(titleVisibleChanged(bool)), this, SLOT(handleTitleVisibleChanged(bool)));
QObject::connect(axis()->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(handleRangeChanged(qreal, qreal)));
QObject::connect(axis(), SIGNAL(reverseChanged(bool)), this, SLOT(handleReverseChanged(bool)));
}
void ChartAxisElement::handleArrowVisibleChanged(bool visible)
{
m_arrow->setVisible(visible);
}
void ChartAxisElement::handleGridVisibleChanged(bool visible)
{
m_grid->setVisible(visible);
}
void ChartAxisElement::handleLabelsVisibleChanged(bool visible)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
m_labels->setVisible(visible);
}
void ChartAxisElement::handleShadesVisibleChanged(bool visible)
{
m_shades->setVisible(visible);
}
void ChartAxisElement::handleTitleVisibleChanged(bool visible)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
m_title->setVisible(visible);
}
void ChartAxisElement::handleLabelsAngleChanged(int angle)
{
foreach (QGraphicsItem *item, m_labels->childItems())
item->setRotation(angle);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush)
{
foreach (QGraphicsItem *item, m_labels->childItems())
static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color());
}
void ChartAxisElement::handleLabelsFontChanged(const QFont &font)
{
foreach (QGraphicsItem *item, m_labels->childItems())
static_cast<QGraphicsTextItem *>(item)->setFont(font);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
void ChartAxisElement::handleTitleTextChanged(const QString &title)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
if (title.isEmpty() || !m_title->isVisible())
m_title->setHtml(title);
}
void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush)
{
m_title->setDefaultTextColor(brush.color());
}
void ChartAxisElement::handleTitleFontChanged(const QFont &font)
{
if (m_title->font() != font) {
m_title->setFont(font);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
}
void ChartAxisElement::handleVisibleChanged(bool visible)
{
setVisible(visible);
if (!visible) {
m_grid->setVisible(visible);
m_arrow->setVisible(visible);
m_shades->setVisible(visible);
m_labels->setVisible(visible);
m_title->setVisible(visible);
} else {
m_grid->setVisible(axis()->isGridLineVisible());
m_arrow->setVisible(axis()->isLineVisible());
m_shades->setVisible(axis()->shadesVisible());
m_labels->setVisible(axis()->labelsVisible());
m_title->setVisible(axis()->isTitleVisible());
}
if (presenter()) presenter()->layout()->invalidate();
}
void ChartAxisElement::handleRangeChanged(qreal min, qreal max)
{
Q_UNUSED(min);
Q_UNUSED(max);
if (!isEmpty()) {
QVector<qreal> layout = calculateLayout();
updateLayout(layout);
QSizeF before = effectiveSizeHint(Qt::PreferredSize);
QSizeF after = sizeHint(Qt::PreferredSize);
if (before != after) {
QGraphicsLayoutItem::updateGeometry();
// We don't want to call invalidate on layout, since it will change minimum size of
// component, which we would like to avoid since it causes nasty flips when scrolling
// or zooming, instead recalculate layout and use plotArea for extra space.
presenter()->layout()->setGeometry(presenter()->layout()->geometry());
}
}
}
void ChartAxisElement::handleReverseChanged(bool reverse)
{
Q_UNUSED(reverse);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
bool ChartAxisElement::isEmpty()
{
return axisGeometry().isEmpty()
|| gridGeometry().isEmpty()
|| qFuzzyCompare(min(), max());
}
qreal ChartAxisElement::min() const
{
return m_axis->d_ptr->min();
}
qreal ChartAxisElement::max() const
{
return m_axis->d_ptr->max();
}
QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array,
qreal value, int precision, const QString &preStr,
const QString &postStr) const
{
QString retVal;
if (!formatSpec.isEmpty()) {
if (formatSpec.at(0) == QLatin1Char('d')
|| formatSpec.at(0) == QLatin1Char('i')
|| formatSpec.at(0) == QLatin1Char('c')) {
if (presenter()->localizeNumbers())
retVal = preStr + presenter()->locale().toString(qint64(value)) + postStr;
else
retVal = QString().sprintf(array, qint64(value));
} else if (formatSpec.at(0) == QLatin1Char('u')
|| formatSpec.at(0) == QLatin1Char('o')
|| formatSpec.at(0) == QLatin1Char('x')
|| formatSpec.at(0) == QLatin1Char('X')) {
// These formats are not supported by localized numbers
retVal = QString().sprintf(array, quint64(value));
} else if (formatSpec.at(0) == QLatin1Char('f')
|| formatSpec.at(0) == QLatin1Char('F')
|| formatSpec.at(0) == QLatin1Char('e')
|| formatSpec.at(0) == QLatin1Char('E')
|| formatSpec.at(0) == QLatin1Char('g')
|| formatSpec.at(0) == QLatin1Char('G')) {
if (presenter()->localizeNumbers()) {
retVal = preStr
+ presenter()->locale().toString(value, formatSpec.at(0).toLatin1(),
precision)
+ postStr;
} else {
retVal = QString().sprintf(array, value);
}
}
}
return retVal;
}
QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
if (format.isNull()) {
int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0) + 1;
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << presenter()->numberToString(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
QString formatSpec;
QString preStr;
QString postStr;
int precision = 6; // Six is the default precision in Qt API
if (presenter()->localizeNumbers()) {
if (!labelFormatMatcherLocalized)
labelFormatMatcherLocalized
= new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
preStr = labelFormatMatcherLocalized->cap(1);
if (!labelFormatMatcherLocalized->cap(2).isEmpty())
precision = labelFormatMatcherLocalized->cap(2).toInt();
formatSpec = labelFormatMatcherLocalized->cap(3);
postStr = labelFormatMatcherLocalized->cap(4);
}
} else {
if (!labelFormatMatcher)
labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
if (labelFormatMatcher->indexIn(format, 0) != -1)
formatSpec = labelFormatMatcher->cap(1);
}
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
}
}
return labels;
}
QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
int firstTick;
if (base > 1)
firstTick = qCeil(std::log10(min) / std::log10(base));
else
firstTick = qCeil(std::log10(max) / std::log10(base));
if (format.isNull()) {
int n = 0;
if (ticks > 1)
n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0);
n++;
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
labels << presenter()->numberToString(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
QString formatSpec;
QString preStr;
QString postStr;
int precision = 6; // Six is the default precision in Qt API
if (presenter()->localizeNumbers()) {
if (!labelFormatMatcherLocalized)
labelFormatMatcherLocalized =
new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
preStr = labelFormatMatcherLocalized->cap(1);
if (!labelFormatMatcherLocalized->cap(2).isEmpty())
precision = labelFormatMatcherLocalized->cap(2).toInt();
formatSpec = labelFormatMatcherLocalized->cap(3);
postStr = labelFormatMatcherLocalized->cap(4);
}
} else {
if (!labelFormatMatcher)
labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
if (labelFormatMatcher->indexIn(format, 0) != -1)
formatSpec = labelFormatMatcher->cap(1);
}
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
}
}
return labels;
}
QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0);
n++;
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << presenter()->locale().toString(QDateTime::fromMSecsSinceEpoch(value), format);
}
return labels;
}
void ChartAxisElement::axisSelected()
{
emit clicked();
}
#include "moc_chartaxiselement_p.cpp"
QT_CHARTS_END_NAMESPACE