From 740f4f94adf83f2613161f2ff578ff37eeb89dc6 2015-06-17 08:02:08 From: Titta Heikkala Date: 2015-06-17 08:02:08 Subject: [PATCH] Added possibility to set labels position for QCategoryAxis The position of the labels in QCategyAxis can now be set to center of the category or to the high end of the category. The first and the last labels may overlap with other axes labels when positioned on value. Change-Id: Ide0f12b723ffabf6682001e03ea5080f9642da22 Task-number: QTRD-1715 Reviewed-by: Miikka Heikkinen --- diff --git a/src/charts/axis/categoryaxis/qcategoryaxis.cpp b/src/charts/axis/categoryaxis/qcategoryaxis.cpp index 70f84b6..d550bb1 100644 --- a/src/charts/axis/categoryaxis/qcategoryaxis.cpp +++ b/src/charts/axis/categoryaxis/qcategoryaxis.cpp @@ -99,6 +99,25 @@ QT_CHARTS_BEGIN_NAMESPACE Axis emits signal when the categories of the axis have changed. */ +/*! + \enum QCategoryAxis::AxisLabelsPosition + + This enum describes the position of the category labels. + + \value AxisLabelsPositionCenter Labels are centered to category. + \value AxisLabelsPositionOnValue Labels are positioned to the high end limit of the category. + */ +/*! + \property QCategoryAxis::labelsPosition + Defines the position of the category labels. The labels in the beginning and in the end of the + axes may overlap other axes labels when positioned on value. +*/ +/*! + \qmlproperty AxisLabelsPosition CategoryAxis::labelsPosition + Defines the position of the category labels. The labels in the beginning and in the end of the + axes may overlap other axes labels when positioned on value. +*/ + /*! Constructs an axis object which is a child of \a parent. @@ -284,11 +303,27 @@ QAbstractAxis::AxisType QCategoryAxis::type() const return QAbstractAxis::AxisTypeCategory; } +void QCategoryAxis::setLabelsPosition(QCategoryAxis::AxisLabelsPosition position) +{ + Q_D(QCategoryAxis); + if (d->m_labelsPosition != position) { + d->m_labelsPosition = position; + emit labelsPositionChanged(position); + } +} + +QCategoryAxis::AxisLabelsPosition QCategoryAxis::labelsPosition() const +{ + Q_D(const QCategoryAxis); + return d->m_labelsPosition; +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QCategoryAxisPrivate::QCategoryAxisPrivate(QCategoryAxis *q) : QValueAxisPrivate(q), - m_categoryMinimum(0) + m_categoryMinimum(0), + m_labelsPosition(QCategoryAxis::AxisLabelsPositionCenter) { } diff --git a/src/charts/axis/categoryaxis/qcategoryaxis.h b/src/charts/axis/categoryaxis/qcategoryaxis.h index 43743a4..d221162 100644 --- a/src/charts/axis/categoryaxis/qcategoryaxis.h +++ b/src/charts/axis/categoryaxis/qcategoryaxis.h @@ -32,8 +32,16 @@ class QT_CHARTS_EXPORT QCategoryAxis : public QValueAxis Q_PROPERTY(qreal startValue READ startValue WRITE setStartValue) Q_PROPERTY(int count READ count) Q_PROPERTY(QStringList categoriesLabels READ categoriesLabels) + Q_PROPERTY(AxisLabelsPosition labelsPosition READ labelsPosition WRITE setLabelsPosition NOTIFY labelsPositionChanged) + Q_ENUMS(AxisLabelsPosition) public: + + enum AxisLabelsPosition { + AxisLabelsPositionCenter = 0x0, + AxisLabelsPositionOnValue = 0x1 + }; + explicit QCategoryAxis(QObject *parent = 0); ~QCategoryAxis(); @@ -55,8 +63,12 @@ public: QStringList categoriesLabels(); int count() const; + QCategoryAxis::AxisLabelsPosition labelsPosition() const; + void setLabelsPosition(QCategoryAxis::AxisLabelsPosition position); + Q_SIGNALS: void categoriesChanged(); + void labelsPositionChanged(QCategoryAxis::AxisLabelsPosition position); private: Q_DECLARE_PRIVATE(QCategoryAxis) diff --git a/src/charts/axis/categoryaxis/qcategoryaxis_p.h b/src/charts/axis/categoryaxis/qcategoryaxis_p.h index 78a24e2..c852de4 100644 --- a/src/charts/axis/categoryaxis/qcategoryaxis_p.h +++ b/src/charts/axis/categoryaxis/qcategoryaxis_p.h @@ -50,6 +50,7 @@ private: QMap m_categoriesMap; QStringList m_categories; qreal m_categoryMinimum; + QCategoryAxis::AxisLabelsPosition m_labelsPosition; private: Q_DECLARE_PUBLIC(QCategoryAxis) diff --git a/src/charts/axis/horizontalaxis.cpp b/src/charts/axis/horizontalaxis.cpp index 10f6a55..f2dfdcf 100644 --- a/src/charts/axis/horizontalaxis.cpp +++ b/src/charts/axis/horizontalaxis.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -142,12 +143,28 @@ void HorizontalAxis::updateGeometry() qreal leftBound = qMax(layout[i], gridRect.left()); qreal rightBound = qMin(layout[i + 1], gridRect.right()); 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())) { - forceHide = true; + if (axis()->type() != QAbstractAxis::AxisTypeCategory) { + // 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())) { + forceHide = true; + } else { + labelItem->setPos(leftBound + (delta / 2.0) - center.x(), labelItem->pos().y()); + } } else { - labelItem->setPos(leftBound + (delta / 2.0) - center.x(), labelItem->pos().y()); + QCategoryAxis *categoryAxis = static_cast(axis()); + if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionCenter) { + if (delta < boundingRect.width() + && (leftBound == gridRect.left() || rightBound == gridRect.right())) { + forceHide = true; + } else { + labelItem->setPos(leftBound + (delta / 2.0) - center.x(), + labelItem->pos().y()); + } + } else if (categoryAxis->labelsPosition() + == QCategoryAxis::AxisLabelsPositionOnValue) { + labelItem->setPos(rightBound - center.x(), labelItem->pos().y()); + } } } diff --git a/src/charts/axis/verticalaxis.cpp b/src/charts/axis/verticalaxis.cpp index 50ce37e..89760c3 100644 --- a/src/charts/axis/verticalaxis.cpp +++ b/src/charts/axis/verticalaxis.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include QT_CHARTS_BEGIN_NAMESPACE @@ -140,23 +141,43 @@ void VerticalAxis::updateGeometry() //label in between bool forceHide = false; + bool labelOnValue = false; if (intervalAxis() && (i + 1) != layout.size()) { qreal lowerBound = qMin(layout[i], gridRect.bottom()); qreal upperBound = qMax(layout[i + 1], gridRect.top()); 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())) { - forceHide = true; + if (axis()->type() != QAbstractAxis::AxisTypeCategory) { + // 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())) { + forceHide = true; + } else { + labelItem->setPos(labelItem->pos().x(), + lowerBound - (delta / 2.0) - center.y()); + } } else { - labelItem->setPos(labelItem->pos().x() , lowerBound - (delta / 2.0) - center.y()); + QCategoryAxis *categoryAxis = static_cast(axis()); + if (categoryAxis->labelsPosition() == QCategoryAxis::AxisLabelsPositionCenter) { + if (delta < boundingRect.height() + && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) { + forceHide = true; + } else { + labelItem->setPos(labelItem->pos().x(), + lowerBound - (delta / 2.0) - center.y()); + } + } else if (categoryAxis->labelsPosition() + == QCategoryAxis::AxisLabelsPositionOnValue) { + labelOnValue = true; + labelItem->setPos(labelItem->pos().x(), upperBound - center.y()); + } } } //label overlap detection - compensate one pixel for rounding errors 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) - 1.0) > axisRect.bottom() + && !labelOnValue) || + (labelItem->pos().y() + (heightDiff / 2.0) < (axisRect.top() - 1.0) && !labelOnValue)) { labelItem->setVisible(false); } else { diff --git a/src/chartsqml2/chartsqml2_plugin.cpp b/src/chartsqml2/chartsqml2_plugin.cpp index 30f17fa..4d5c0ff 100644 --- a/src/chartsqml2/chartsqml2_plugin.cpp +++ b/src/chartsqml2/chartsqml2_plugin.cpp @@ -300,7 +300,11 @@ public: QLatin1String("Trying to create uncreatable: DeclarativeAxes.")); qmlRegisterUncreatableType(uri, 2, 0, "Margins", QLatin1String("Trying to create uncreatable: Margins.")); + + // QtCharts 2.1 + qmlRegisterType(uri, 2, 1, "CategoryAxis"); } + }; QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativecategoryaxis.cpp b/src/chartsqml2/declarativecategoryaxis.cpp index a1c30cf..36592eb 100644 --- a/src/chartsqml2/declarativecategoryaxis.cpp +++ b/src/chartsqml2/declarativecategoryaxis.cpp @@ -93,6 +93,20 @@ void DeclarativeCategoryAxis::appendAxisChildren(QQmlListProperty *list Q_UNUSED(element) } +DeclarativeCategoryAxis::AxisLabelsPosition DeclarativeCategoryAxis::labelsPosition() const +{ + return (DeclarativeCategoryAxis::AxisLabelsPosition) QCategoryAxis::labelsPosition(); +} + +void DeclarativeCategoryAxis::setLabelsPosition(AxisLabelsPosition position) +{ + QCategoryAxis::AxisLabelsPosition labelsPosition = (QCategoryAxis::AxisLabelsPosition) position; + if (labelsPosition != m_labelsPosition) { + QCategoryAxis::setLabelsPosition(labelsPosition); + emit labelsPositionChanged(position); + } +} + #include "moc_declarativecategoryaxis.cpp" QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativecategoryaxis.h b/src/chartsqml2/declarativecategoryaxis.h index c40d8b4..6de2372 100644 --- a/src/chartsqml2/declarativecategoryaxis.h +++ b/src/chartsqml2/declarativecategoryaxis.h @@ -50,15 +50,31 @@ class DeclarativeCategoryAxis : public QCategoryAxis, public QQmlParserStatus Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QQmlListProperty axisChildren READ axisChildren) Q_CLASSINFO("DefaultProperty", "axisChildren") + Q_PROPERTY(AxisLabelsPosition labelsPosition READ labelsPosition WRITE setLabelsPosition NOTIFY labelsPositionChanged REVISION 1) + Q_ENUMS(AxisLabelsPosition) public: + // duplicating enums from QChart to make the QML api namings 1-to-1 with the C++ api + enum AxisLabelsPosition { + AxisLabelsPositionCenter = 0x0, + AxisLabelsPositionOnValue = 0x1 + }; + explicit DeclarativeCategoryAxis(QObject *parent = 0); QQmlListProperty axisChildren(); + public: // from QDeclarativeParserStatus void classBegin(); void componentComplete(); +public: + AxisLabelsPosition labelsPosition() const; + void setLabelsPosition(AxisLabelsPosition position); + +Q_SIGNALS: + Q_REVISION(1) void labelsPositionChanged(AxisLabelsPosition position); + public Q_SLOTS: Q_INVOKABLE void append(const QString &label, qreal categoryEndValue); Q_INVOKABLE void remove(const QString &label); @@ -67,6 +83,9 @@ public Q_SLOTS: private: static bool endValueLessThan(const QPair &value1, const QPair &value2); + +private: + AxisLabelsPosition m_labelsPosition; }; QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/plugins.qmltypes b/src/chartsqml2/plugins.qmltypes index b7d9665..5097473 100644 --- a/src/chartsqml2/plugins.qmltypes +++ b/src/chartsqml2/plugins.qmltypes @@ -430,9 +430,26 @@ Module { name: "QtCharts::DeclarativeCategoryAxis" defaultProperty: "axisChildren" prototype: "QtCharts::QCategoryAxis" - exports: ["QtCharts/CategoryAxis 1.1", "QtCharts/CategoryAxis 2.0"] - exportMetaObjectRevisions: [0, 0] + exports: [ + "QtCharts/CategoryAxis 1.1", + "QtCharts/CategoryAxis 2.0", + "QtCharts/CategoryAxis 2.1" + ] + exportMetaObjectRevisions: [0, 0, 1] + Enum { + name: "AxisLabelsPosition" + values: { + "AxisLabelsPositionCenter": 0, + "AxisLabelsPositionOnValue": 1 + } + } Property { name: "axisChildren"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "labelsPosition"; revision: 1; type: "AxisLabelsPosition" } + Signal { + name: "labelsPositionChanged" + revision: 1 + Parameter { name: "position"; type: "AxisLabelsPosition" } + } Method { name: "append" Parameter { name: "label"; type: "string" } @@ -1911,10 +1928,22 @@ Module { Component { name: "QtCharts::QCategoryAxis" prototype: "QtCharts::QValueAxis" + Enum { + name: "AxisLabelsPosition" + values: { + "AxisLabelsPositionCenter": 0, + "AxisLabelsPositionOnValue": 1 + } + } Property { name: "startValue"; type: "double" } Property { name: "count"; type: "int"; isReadonly: true } Property { name: "categoriesLabels"; type: "QStringList"; isReadonly: true } + Property { name: "labelsPosition"; type: "AxisLabelsPosition" } Signal { name: "categoriesChanged" } + Signal { + name: "labelsPositionChanged" + Parameter { name: "position"; type: "QCategoryAxis::AxisLabelsPosition" } + } } Component { name: "QtCharts::QDateTimeAxis" diff --git a/tests/auto/qcategoryaxis/tst_qcategoryaxis.cpp b/tests/auto/qcategoryaxis/tst_qcategoryaxis.cpp index 5312f2a..023eae2 100644 --- a/tests/auto/qcategoryaxis/tst_qcategoryaxis.cpp +++ b/tests/auto/qcategoryaxis/tst_qcategoryaxis.cpp @@ -52,6 +52,7 @@ private slots: void range(); void range_animation_data(); void range_animation(); + void labels_position(); void interval_data(); void interval(); @@ -63,6 +64,7 @@ private: void tst_QCategoryAxis::initTestCase() { + qRegisterMetaType("QCategoryAxis::AxisLabelsPosition"); } void tst_QCategoryAxis::cleanupTestCase() @@ -100,6 +102,7 @@ void tst_QCategoryAxis::qcategoryaxis() QVERIFY(qFuzzyCompare(m_categoryaxis->max(), 0)); QVERIFY(qFuzzyCompare(m_categoryaxis->min(), 0)); QCOMPARE(m_categoryaxis->type(), QAbstractAxis::AxisTypeCategory); + QCOMPARE(m_categoryaxis->labelsPosition(), QCategoryAxis::AxisLabelsPositionCenter); m_chart->setAxisX(m_categoryaxis, m_series); m_view->show(); @@ -301,6 +304,15 @@ void tst_QCategoryAxis::interval() QCOMPARE(m_categoryaxis->endValue("replaced"), (qreal)75); } +void tst_QCategoryAxis::labels_position() +{ + QSignalSpy spy(m_categoryaxis, + SIGNAL(labelsPositionChanged(QCategoryAxis::AxisLabelsPosition))); + m_categoryaxis->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue); + QCOMPARE(m_categoryaxis->labelsPosition(), QCategoryAxis::AxisLabelsPositionOnValue); + QCOMPARE(spy.count(), 1); +} + QTEST_MAIN(tst_QCategoryAxis) #include "tst_qcategoryaxis.moc" diff --git a/tests/auto/qml-qtquicktest/tst_categoryaxis.qml b/tests/auto/qml-qtquicktest/tst_categoryaxis.qml index 56b5a27..55d1123 100644 --- a/tests/auto/qml-qtquicktest/tst_categoryaxis.qml +++ b/tests/auto/qml-qtquicktest/tst_categoryaxis.qml @@ -18,7 +18,7 @@ import QtQuick 2.0 import QtTest 1.0 -import QtCharts 2.0 +import QtCharts 2.1 Rectangle { width: 400 @@ -43,6 +43,16 @@ Rectangle { compare(lineSeries1.axisY.categoriesLabels[1], "label1", "AxisY categories labels"); compare(lineSeries1.axisY.categoriesLabels[2], "label2", "AxisY categories labels"); } + + function test_properties() { + compare(lineSeries1.axisY.labelsPosition, CategoryAxis.AxisLabelsPositionCenter); + } + + function test_signals() { + axisLabelsPositionSpy.clear(); + lineSeries1.axisY.labelsPosition = CategoryAxis.AxisLabelsPositionOnValue; + compare(axisLabelsPositionSpy.count, 1, "onLabelsPositionChanged") + } } ChartView { @@ -73,6 +83,11 @@ Rectangle { label: "label2" endValue: 10 } + SignalSpy { + id: axisLabelsPositionSpy + target: axisY + signalName: "labelsPositionChanged" + } } XYPoint { x: -1; y: -1 } XYPoint { x: 0; y: 0 } diff --git a/tests/auto/qml/tst_qml.cpp b/tests/auto/qml/tst_qml.cpp index 4d3d308..559d80e 100644 --- a/tests/auto/qml/tst_qml.cpp +++ b/tests/auto/qml/tst_qml.cpp @@ -38,6 +38,7 @@ private: QString imports_1_3(); QString imports_1_4(); QString imports_2_0(); + QString imports_2_1(); }; @@ -78,6 +79,12 @@ QString tst_qml::imports_2_0() "import QtCharts 2.0 \n"; } +QString tst_qml::imports_2_1() +{ + return "import QtQuick 2.1 \n" + "import QtCharts 2.1 \n"; +} + void tst_qml::initTestCase() { } @@ -168,6 +175,8 @@ void tst_qml::checkPlugin_data() QTest::newRow("LogValueAxis_2_0") << imports_2_0() + "LogValueAxis{}"; QTest::newRow("BoxPlotSeries_2_0") << imports_2_0() + "BoxPlotSeries{}"; QTest::newRow("BoxSet_2_0") << imports_2_0() + "BoxSet{}"; + + QTest::newRow("CategoryAxis") << imports_2_1() + "CategoryAxis{}"; } void tst_qml::checkPlugin()