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()