##// END OF EJS Templates
Fix multiline axis titles truncation...
Miikka Heikkinen -
r2540:18da5db7d538
parent child
Show More
@@ -1,149 +1,149
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30 #ifndef CHARTAXISELEMENT_H
31 31 #define CHARTAXISELEMENT_H
32 32
33 33 #include "qchartglobal.h"
34 34 #include "chartelement_p.h"
35 35 #include "axisanimation_p.h"
36 36 #include <QGraphicsItem>
37 37 #include <QGraphicsLayoutItem>
38 38 #include <QFont>
39 39
40 40 QTCOMMERCIALCHART_BEGIN_NAMESPACE
41 41
42 42 class ChartPresenter;
43 43 class QAbstractAxis;
44 44
45 45 class ChartAxisElement : public ChartElement, public QGraphicsLayoutItem
46 46 {
47 47 Q_OBJECT
48 48 public:
49 49 ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false);
50 50 ~ChartAxisElement();
51 51
52 52 virtual QRectF gridGeometry() const = 0;
53 53 virtual void setGeometry(const QRectF &axis, const QRectF &grid) = 0;
54 54 virtual bool isEmpty() = 0;
55 55
56 56 void setAnimation(AxisAnimation *animation) { m_animation = animation; }
57 57 AxisAnimation *animation() const { return m_animation; }
58 58
59 59 QAbstractAxis *axis() const { return m_axis; }
60 60 void setLayout(QVector<qreal> &layout) { m_layout = layout; }
61 61 QVector<qreal> &layout() { return m_layout; } // Modifiable reference
62 int labelPadding() const { return 5; }
63 int titlePadding() const { return 3; }
62 inline qreal labelPadding() const { return qreal(5.0); }
63 inline qreal titlePadding() const { return qreal(3.0); }
64 64 void setLabels(const QStringList &labels) { m_labelsList = labels; }
65 65 QStringList labels() const { return m_labelsList; }
66 66
67 67 qreal min() const;
68 68 qreal max() const;
69 69
70 70 QRectF axisGeometry() const { return m_axisRect; }
71 71 void setAxisGeometry(const QRectF &axisGeometry) { m_axisRect = axisGeometry; }
72 72
73 73 void axisSelected();
74 74
75 75 //this flag indicates that axis is used to show intervals it means labels are in between ticks
76 76 bool intervalAxis() const { return m_intervalAxis; }
77 77
78 78 static QStringList createValueLabels(qreal max, qreal min, int ticks, const QString &format);
79 79 static QStringList createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString &format);
80 80 static QStringList createDateTimeLabels(qreal max, qreal min, int ticks, const QString &format);
81 81
82 82 // from QGraphicsLayoutItem
83 83 QRectF boundingRect() const
84 84 {
85 85 return QRectF();
86 86 }
87 87
88 88 // from QGraphicsLayoutItem
89 89 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
90 90 {
91 91 }
92 92
93 93 protected:
94 94 virtual QVector<qreal> calculateLayout() const = 0;
95 95 virtual void updateLayout(QVector<qreal> &layout) = 0;
96 96
97 97 QList<QGraphicsItem *> gridItems() { return m_grid->childItems(); }
98 98 QList<QGraphicsItem *> labelItems() { return m_labels->childItems(); }
99 99 QList<QGraphicsItem *> shadeItems() { return m_shades->childItems(); }
100 100 QList<QGraphicsItem *> arrowItems() { return m_arrow->childItems(); }
101 101 QGraphicsTextItem *titleItem() const { return m_title.data(); }
102 102 QGraphicsItemGroup *gridGroup() { return m_grid.data(); }
103 103 QGraphicsItemGroup *labelGroup() { return m_labels.data(); }
104 104 QGraphicsItemGroup *shadeGroup() { return m_shades.data(); }
105 105 QGraphicsItemGroup *arrowGroup() { return m_arrow.data(); }
106 106
107 107 public Q_SLOTS:
108 108 void handleVisibleChanged(bool visible);
109 109 void handleArrowVisibleChanged(bool visible);
110 110 void handleGridVisibleChanged(bool visible);
111 111 void handleLabelsVisibleChanged(bool visible);
112 112 void handleShadesVisibleChanged(bool visible);
113 113 void handleLabelsAngleChanged(int angle);
114 114 virtual void handleShadesBrushChanged(const QBrush &brush) = 0;
115 115 virtual void handleShadesPenChanged(const QPen &pen) = 0;
116 116 virtual void handleArrowPenChanged(const QPen &pen) = 0;
117 117 virtual void handleGridPenChanged(const QPen &pen) = 0;
118 118 void handleLabelsPenChanged(const QPen &pen);
119 119 void handleLabelsBrushChanged(const QBrush &brush);
120 120 void handleLabelsFontChanged(const QFont &font);
121 121 void handleTitlePenChanged(const QPen &pen);
122 122 void handleTitleBrushChanged(const QBrush &brush);
123 123 void handleTitleFontChanged(const QFont &font);
124 124 void handleTitleTextChanged(const QString &title);
125 125 void handleTitleVisibleChanged(bool visible);
126 126 void handleRangeChanged(qreal min, qreal max);
127 127
128 128 Q_SIGNALS:
129 129 void clicked();
130 130
131 131 private:
132 132 void connectSlots();
133 133
134 134 QAbstractAxis *m_axis;
135 135 AxisAnimation *m_animation;
136 136 QVector<qreal> m_layout;
137 137 QStringList m_labelsList;
138 138 QRectF m_axisRect;
139 139 QScopedPointer<QGraphicsItemGroup> m_grid;
140 140 QScopedPointer<QGraphicsItemGroup> m_arrow;
141 141 QScopedPointer<QGraphicsItemGroup> m_shades;
142 142 QScopedPointer<QGraphicsItemGroup> m_labels;
143 143 QScopedPointer<QGraphicsTextItem> m_title;
144 144 bool m_intervalAxis;
145 145 };
146 146
147 147 QTCOMMERCIALCHART_END_NAMESPACE
148 148
149 149 #endif /* CHARTAXISELEMENT_H */
@@ -1,206 +1,223
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "horizontalaxis_p.h"
22 22 #include "qabstractaxis_p.h"
23 23 #include "chartpresenter_p.h"
24 24 #include <qmath.h>
25 25 #include <QDebug>
26 26
27 27 QTCOMMERCIALCHART_BEGIN_NAMESPACE
28 28
29 29 HorizontalAxis::HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
30 30 : CartesianChartAxis(axis, item, intervalAxis)
31 31 {
32 32 }
33 33
34 34 HorizontalAxis::~HorizontalAxis()
35 35 {
36 36 }
37 37
38 38 void HorizontalAxis::updateGeometry()
39 39 {
40 40 const QVector<qreal> &layout = ChartAxisElement::layout();
41 41
42 42 if (layout.isEmpty())
43 43 return;
44 44
45 45 QStringList labelList = labels();
46 46
47 47 QList<QGraphicsItem *> lines = gridItems();
48 48 QList<QGraphicsItem *> labels = labelItems();
49 49 QList<QGraphicsItem *> shades = shadeItems();
50 50 QList<QGraphicsItem *> arrow = arrowItems();
51 51 QGraphicsTextItem *title = titleItem();
52 52
53 53 Q_ASSERT(labels.size() == labelList.size());
54 54 Q_ASSERT(layout.size() == labelList.size());
55 55
56 56 const QRectF &axisRect = axisGeometry();
57 57 const QRectF &gridRect = gridGeometry();
58 58
59 59 //arrow
60 60 QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem *>(arrow.at(0));
61 61
62 62 if (axis()->alignment() == Qt::AlignTop)
63 63 arrowItem->setLine(gridRect.left(), axisRect.bottom(), gridRect.right(), axisRect.bottom());
64 64 else if (axis()->alignment() == Qt::AlignBottom)
65 65 arrowItem->setLine(gridRect.left(), axisRect.top(), gridRect.right(), axisRect.top());
66 66
67 67 qreal width = 0;
68 68
69 69 //title
70 int titlePad = 0;
71 70 QRectF titleBoundingRect;
72 71 QString titleText = axis()->titleText();
72 qreal availableSpace = axisRect.height() - labelPadding();
73 73 if (!titleText.isEmpty() && titleItem()->isVisible()) {
74 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), gridRect.width(), Qt::Horizontal, QRectF()));
74 availableSpace -= titlePadding() * 2.0;
75 qreal minimumLabelHeight = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").height();
76 QString truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0),
77 gridRect.width(), Qt::Horizontal, titleBoundingRect);
78 qreal titleSpace = availableSpace - minimumLabelHeight;
79 if (titleSpace < titleBoundingRect.height()) {
80 // Need to also truncate title vertically (multiline title)
81 bool skip = false;
82 if (truncatedTitle.endsWith("...")) {
83 if (truncatedTitle.size() == 3)
84 skip = true; // Already truncated to minimum
85 else
86 truncatedTitle.chop(3);
87 }
88 if (!skip)
89 truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), truncatedTitle, qreal(0.0),
90 titleSpace, Qt::Vertical, titleBoundingRect);
91 }
92 title->setHtml(truncatedTitle);
75 93
76 titlePad = titlePadding();
77 94 titleBoundingRect = title->boundingRect();
78 95
79 96 QPointF center = gridRect.center() - titleBoundingRect.center();
80 if (axis()->alignment() == Qt::AlignTop) {
81 title->setPos(center.x(), axisRect.top() + titlePad);
82 } else if (axis()->alignment() == Qt::AlignBottom) {
83 title->setPos(center.x(), axisRect.bottom() - titleBoundingRect.height() - titlePad);
84 }
97 if (axis()->alignment() == Qt::AlignTop)
98 title->setPos(center.x(), axisRect.top() + titlePadding());
99 else if (axis()->alignment() == Qt::AlignBottom)
100 title->setPos(center.x(), axisRect.bottom() - titleBoundingRect.height() - titlePadding());
101
102 availableSpace -= titleBoundingRect.height();
85 103 }
86 104
87 105 for (int i = 0; i < layout.size(); ++i) {
88 106 //items
89 107 QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem*>(lines.at(i));
90 108 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem*>(arrow.at(i + 1));
91 109 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labels.at(i));
92 110
93 111 //grid line
94 112 gridItem->setLine(layout[i], gridRect.top(), layout[i], gridRect.bottom());
95 113
96 114 //label text wrapping
97 115 QString text = labelList.at(i);
98 116 QRectF boundingRect;
99 qreal size = axisRect.bottom() - axisRect.top() - labelPadding() - titleBoundingRect.height() - (titlePad * 2);
100 117 labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, axis()->labelsAngle(),
101 size, Qt::Vertical, boundingRect));
118 availableSpace, Qt::Vertical, boundingRect));
102 119
103 120 //label transformation origin point
104 121 const QRectF& rect = labelItem->boundingRect();
105 122 QPointF center = rect.center();
106 123 labelItem->setTransformOriginPoint(center.x(), center.y());
107 int heightDiff = rect.height() - boundingRect.height();
124 qreal heightDiff = rect.height() - boundingRect.height();
108 125
109 126 //ticks and label position
110 127 if (axis()->alignment() == Qt::AlignTop) {
111 labelItem->setPos(layout[i] - center.x(), axisRect.bottom() - rect.height() + (heightDiff / 2) - labelPadding());
128 labelItem->setPos(layout[i] - center.x(), axisRect.bottom() - rect.height() + (heightDiff / 2.0) - labelPadding());
112 129 tickItem->setLine(layout[i], axisRect.bottom(), layout[i], axisRect.bottom() - labelPadding());
113 130 } else if (axis()->alignment() == Qt::AlignBottom) {
114 labelItem->setPos(layout[i] - center.x(), axisRect.top() - (heightDiff / 2) + labelPadding());
131 labelItem->setPos(layout[i] - center.x(), axisRect.top() - (heightDiff / 2.0) + labelPadding());
115 132 tickItem->setLine(layout[i], axisRect.top(), layout[i], axisRect.top() + labelPadding());
116 133 }
117 134
118 135 //label in between
119 136 bool forceHide = false;
120 137 if (intervalAxis() && (i + 1) != layout.size()) {
121 138 qreal leftBound = qMax(layout[i], gridRect.left());
122 139 qreal rightBound = qMin(layout[i + 1], gridRect.right());
123 140 const qreal delta = rightBound - leftBound;
124 141 // Hide label in case visible part of the category at the grid edge is too narrow
125 142 if (delta < boundingRect.width()
126 143 && (leftBound == gridRect.left() || rightBound == gridRect.right())) {
127 144 forceHide = true;
128 145 } else {
129 146 labelItem->setPos(leftBound + (delta / 2.0) - center.x(), labelItem->pos().y());
130 147 }
131 148 }
132 149
133 150 //label overlap detection - compensate one pixel for rounding errors
134 151 if (labelItem->pos().x() < width || forceHide ||
135 152 labelItem->pos().x() < (axisRect.left() - 1.0) ||
136 153 (labelItem->pos().x() + boundingRect.width() - 1.0) > axisRect.right()){
137 154 labelItem->setVisible(false);
138 155 } else {
139 156 labelItem->setVisible(true);
140 157 width = boundingRect.width() + labelItem->pos().x();
141 158 }
142 159
143 160 //shades
144 161 if ((i + 1) % 2 && i > 1) {
145 162 QGraphicsRectItem *rectItem = static_cast<QGraphicsRectItem *>(shades.at(i / 2 - 1));
146 163 qreal leftBound = qMax(layout[i - 1], gridRect.left());
147 164 qreal rightBound = qMin(layout[i], gridRect.right());
148 165 rectItem->setRect(leftBound, gridRect.top(), rightBound - leftBound, gridRect.height());
149 166 if (rectItem->rect().width() <= 0.0)
150 167 rectItem->setVisible(false);
151 168 else
152 169 rectItem->setVisible(true);
153 170 }
154 171
155 172 // check if the grid line and the axis tick should be shown
156 173 qreal x = gridItem->line().p1().x();
157 174 if (x < gridRect.left() || x > gridRect.right()) {
158 175 gridItem->setVisible(false);
159 176 tickItem->setVisible(false);
160 177 } else {
161 178 gridItem->setVisible(true);
162 179 tickItem->setVisible(true);
163 180 }
164 181
165 182 }
166 183
167 184 //begin/end grid line in case labels between
168 185 if (intervalAxis()) {
169 186 QGraphicsLineItem *gridLine;
170 187 gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
171 188 gridLine->setLine(gridRect.right(), gridRect.top(), gridRect.right(), gridRect.bottom());
172 189 gridLine->setVisible(true);
173 190 gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size()+1));
174 191 gridLine->setLine(gridRect.left(), gridRect.top(), gridRect.left(), gridRect.bottom());
175 192 gridLine->setVisible(true);
176 193 }
177 194 }
178 195
179 196 QSizeF HorizontalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
180 197 {
181 198 Q_UNUSED(constraint);
182 199 QSizeF sh(0,0);
183 200
184 201 if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
185 202 return sh;
186 203
187 204 switch (which) {
188 205 case Qt::MinimumSize: {
189 206 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), "...");
190 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2));
207 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
191 208 break;
192 209 }
193 210 case Qt::MaximumSize:
194 211 case Qt::PreferredSize: {
195 212 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
196 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2));
213 sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
197 214 break;
198 215 }
199 216 default:
200 217 break;
201 218 }
202 219
203 220 return sh;
204 221 }
205 222
206 223 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,408 +1,408
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "polarchartaxisangular_p.h"
22 22 #include "chartpresenter_p.h"
23 23 #include "abstractchartlayout_p.h"
24 24 #include "qabstractaxis.h"
25 25 #include "qabstractaxis_p.h"
26 26 #include <QDebug>
27 27
28 28 QTCOMMERCIALCHART_BEGIN_NAMESPACE
29 29
30 30 PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
31 31 : PolarChartAxis(axis, item, intervalAxis)
32 32 {
33 33 }
34 34
35 35 PolarChartAxisAngular::~PolarChartAxisAngular()
36 36 {
37 37 }
38 38
39 39 void PolarChartAxisAngular::updateGeometry()
40 40 {
41 41 QGraphicsLayoutItem::updateGeometry();
42 42
43 43 const QVector<qreal> &layout = this->layout();
44 44 if (layout.isEmpty())
45 45 return;
46 46
47 47 createAxisLabels(layout);
48 48 QStringList labelList = labels();
49 49 QPointF center = axisGeometry().center();
50 50 QList<QGraphicsItem *> arrowItemList = arrowItems();
51 51 QList<QGraphicsItem *> gridItemList = gridItems();
52 52 QList<QGraphicsItem *> labelItemList = labelItems();
53 53 QList<QGraphicsItem *> shadeItemList = shadeItems();
54 54 QGraphicsTextItem *title = titleItem();
55 55
56 56 QGraphicsEllipseItem *axisLine = static_cast<QGraphicsEllipseItem *>(arrowItemList.at(0));
57 57 axisLine->setRect(axisGeometry());
58 58
59 59 qreal radius = axisGeometry().height() / 2.0;
60 60
61 61 QRectF previousLabelRect;
62 62 QRectF firstLabelRect;
63 63
64 64 qreal labelHeight = 0;
65 65
66 66 bool firstShade = true;
67 67 bool nextTickVisible = false;
68 68 if (layout.size())
69 69 nextTickVisible = !(layout.at(0) < 0.0 || layout.at(0) > 360.0);
70 70
71 71 for (int i = 0; i < layout.size(); ++i) {
72 72 qreal angularCoordinate = layout.at(i);
73 73
74 74 QGraphicsLineItem *gridLineItem = static_cast<QGraphicsLineItem *>(gridItemList.at(i));
75 75 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i + 1));
76 76 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labelItemList.at(i));
77 77 QGraphicsPathItem *shadeItem = 0;
78 78 if (i == 0)
79 79 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
80 80 else if (i % 2)
81 81 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at((i / 2) + 1));
82 82
83 83 // Ignore ticks outside valid range
84 84 bool currentTickVisible = nextTickVisible;
85 85 if ((i == layout.size() - 1)
86 86 || layout.at(i + 1) < 0.0
87 87 || layout.at(i + 1) > 360.0) {
88 88 nextTickVisible = false;
89 89 } else {
90 90 nextTickVisible = true;
91 91 }
92 92
93 93 qreal labelCoordinate = angularCoordinate;
94 94 qreal labelVisible = currentTickVisible;
95 95 if (intervalAxis()) {
96 96 qreal farEdge;
97 97 if (i == (layout.size() - 1))
98 98 farEdge = 360.0;
99 99 else
100 100 farEdge = qMin(qreal(360.0), layout.at(i + 1));
101 101
102 102 // Adjust the labelCoordinate to show it if next tick is visible
103 103 if (nextTickVisible)
104 104 labelCoordinate = qMax(qreal(0.0), labelCoordinate);
105 105
106 106 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
107 107 // Don't display label once the category gets too small near the axis
108 108 if (labelCoordinate < 5.0 || labelCoordinate > 355.0)
109 109 labelVisible = false;
110 110 else
111 111 labelVisible = true;
112 112 }
113 113
114 114 // Need this also in label calculations, so determine it first
115 115 QLineF tickLine(QLineF::fromPolar(radius - tickWidth(), 90.0 - angularCoordinate).p2(),
116 116 QLineF::fromPolar(radius + tickWidth(), 90.0 - angularCoordinate).p2());
117 117 tickLine.translate(center);
118 118
119 119 // Angular axis label
120 120 if (axis()->labelsVisible() && labelVisible) {
121 121 labelItem->setHtml(labelList.at(i));
122 122 const QRectF &rect = labelItem->boundingRect();
123 123 QPointF labelCenter = rect.center();
124 124 labelItem->setTransformOriginPoint(labelCenter.x(), labelCenter.y());
125 125 QRectF boundingRect = ChartPresenter::textBoundingRect(axis()->labelsFont(), labelList.at(i), axis()->labelsAngle());
126 126 boundingRect.moveCenter(labelCenter);
127 127 QPointF positionDiff(rect.topLeft() - boundingRect.topLeft());
128 128
129 129 QPointF labelPoint;
130 130 if (intervalAxis()) {
131 131 QLineF labelLine = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate);
132 132 labelLine.translate(center);
133 133 labelPoint = labelLine.p2();
134 134 } else {
135 135 labelPoint = tickLine.p2();
136 136 }
137 137
138 138 QRectF labelRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect);
139 139 labelItem->setPos(labelRect.topLeft() + positionDiff);
140 140
141 141 // Store height for title calculations
142 142 qreal labelClearance = axisGeometry().top() - labelRect.top();
143 143 labelHeight = qMax(labelHeight, labelClearance);
144 144
145 145 // Label overlap detection
146 146 if (i && (previousLabelRect.intersects(labelRect) || firstLabelRect.intersects(labelRect))) {
147 147 labelVisible = false;
148 148 } else {
149 149 // Store labelRect for future comparison. Some area is deducted to make things look
150 150 // little nicer, as usually intersection happens at label corner with angular labels.
151 151 labelRect.adjust(-2.0, -4.0, -2.0, -4.0);
152 152 if (firstLabelRect.isEmpty())
153 153 firstLabelRect = labelRect;
154 154
155 155 previousLabelRect = labelRect;
156 156 labelVisible = true;
157 157 }
158 158 }
159 159
160 160 labelItem->setVisible(labelVisible);
161 161 if (!currentTickVisible) {
162 162 gridLineItem->setVisible(false);
163 163 tickItem->setVisible(false);
164 164 if (shadeItem)
165 165 shadeItem->setVisible(false);
166 166 continue;
167 167 }
168 168
169 169 // Angular grid line
170 170 QLineF gridLine = QLineF::fromPolar(radius, 90.0 - angularCoordinate);
171 171 gridLine.translate(center);
172 172 gridLineItem->setLine(gridLine);
173 173 gridLineItem->setVisible(true);
174 174
175 175 // Tick
176 176 tickItem->setLine(tickLine);
177 177 tickItem->setVisible(true);
178 178
179 179 // Shades
180 180 if (i % 2 || (i == 0 && !nextTickVisible)) {
181 181 QPainterPath path;
182 182 path.moveTo(center);
183 183 if (i == 0) {
184 184 // If first tick is also the last, we need to custom fill the first partial arc
185 185 // or it won't get filled.
186 186 path.arcTo(axisGeometry(), 90.0 - layout.at(0), layout.at(0));
187 187 path.closeSubpath();
188 188 } else {
189 189 qreal nextCoordinate;
190 190 if (!nextTickVisible) // Last visible tick
191 191 nextCoordinate = 360.0;
192 192 else
193 193 nextCoordinate = layout.at(i + 1);
194 194 qreal arcSpan = angularCoordinate - nextCoordinate;
195 195 path.arcTo(axisGeometry(), 90.0 - angularCoordinate, arcSpan);
196 196 path.closeSubpath();
197 197
198 198 // Add additional arc for first shade item if there is a partial arc to be filled
199 199 if (firstShade) {
200 200 QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
201 201 if (layout.at(i - 1) > 0.0) {
202 202 QPainterPath specialPath;
203 203 specialPath.moveTo(center);
204 204 specialPath.arcTo(axisGeometry(), 90.0 - layout.at(i - 1), layout.at(i - 1));
205 205 specialPath.closeSubpath();
206 206 specialShadeItem->setPath(specialPath);
207 207 specialShadeItem->setVisible(true);
208 208 } else {
209 209 specialShadeItem->setVisible(false);
210 210 }
211 211 }
212 212 }
213 213 shadeItem->setPath(path);
214 214 shadeItem->setVisible(true);
215 215 firstShade = false;
216 216 }
217 217 }
218 218
219 219 // Title, centered above the chart
220 220 QString titleText = axis()->titleText();
221 221 if (!titleText.isEmpty() && axis()->isTitleVisible()) {
222 222 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), axisGeometry().width(), Qt::Horizontal, QRectF()));
223 223
224 224 QRectF titleBoundingRect = title->boundingRect();
225 225 QPointF titleCenter = center - titleBoundingRect.center();
226 title->setPos(titleCenter.x(), axisGeometry().top() - qreal(titlePadding()) * 2.0 - titleBoundingRect.height() - labelHeight);
226 title->setPos(titleCenter.x(), axisGeometry().top() - titlePadding() * 2.0 - titleBoundingRect.height() - labelHeight);
227 227 }
228 228 }
229 229
230 230 Qt::Orientation PolarChartAxisAngular::orientation() const
231 231 {
232 232 return Qt::Horizontal;
233 233 }
234 234
235 235 void PolarChartAxisAngular::createItems(int count)
236 236 {
237 237 if (arrowItems().count() == 0) {
238 238 // angular axis line
239 239 // TODO: need class similar to LineArrowItem for click handling?
240 240 QGraphicsEllipseItem *arrow = new QGraphicsEllipseItem(presenter()->rootItem());
241 241 arrow->setPen(axis()->linePen());
242 242 arrowGroup()->addToGroup(arrow);
243 243 }
244 244
245 245 for (int i = 0; i < count; ++i) {
246 246 QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem());
247 247 QGraphicsLineItem *grid = new QGraphicsLineItem(presenter()->rootItem());
248 248 QGraphicsTextItem *label = new QGraphicsTextItem(presenter()->rootItem());
249 249 QGraphicsTextItem *title = titleItem();
250 250 arrow->setPen(axis()->linePen());
251 251 grid->setPen(axis()->gridLinePen());
252 252 label->setFont(axis()->labelsFont());
253 253 label->setDefaultTextColor(axis()->labelsBrush().color());
254 254 label->setRotation(axis()->labelsAngle());
255 255 title->setFont(axis()->titleFont());
256 256 title->setDefaultTextColor(axis()->titleBrush().color());
257 257 title->setHtml(axis()->titleText());
258 258 arrowGroup()->addToGroup(arrow);
259 259 gridGroup()->addToGroup(grid);
260 260 labelGroup()->addToGroup(label);
261 261 if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
262 262 QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem());
263 263 shade->setPen(axis()->shadesPen());
264 264 shade->setBrush(axis()->shadesBrush());
265 265 shadeGroup()->addToGroup(shade);
266 266 }
267 267 }
268 268 }
269 269
270 270 void PolarChartAxisAngular::handleArrowPenChanged(const QPen &pen)
271 271 {
272 272 bool first = true;
273 273 foreach (QGraphicsItem *item, arrowItems()) {
274 274 if (first) {
275 275 first = false;
276 276 // First arrow item is the outer circle of axis
277 277 static_cast<QGraphicsEllipseItem *>(item)->setPen(pen);
278 278 } else {
279 279 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
280 280 }
281 281 }
282 282 }
283 283
284 284 void PolarChartAxisAngular::handleGridPenChanged(const QPen &pen)
285 285 {
286 286 foreach (QGraphicsItem *item, gridItems())
287 287 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
288 288 }
289 289
290 290 QSizeF PolarChartAxisAngular::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
291 291 {
292 292 Q_UNUSED(which);
293 293 Q_UNUSED(constraint);
294 294 return QSizeF(-1, -1);
295 295 }
296 296
297 297 qreal PolarChartAxisAngular::preferredAxisRadius(const QSizeF &maxSize)
298 298 {
299 299 qreal radius = maxSize.height() / 2.0;
300 300 if (maxSize.width() < maxSize.height())
301 301 radius = maxSize.width() / 2.0;
302 302
303 303 if (axis()->labelsVisible()) {
304 304 QVector<qreal> layout = calculateLayout();
305 305 if (layout.isEmpty())
306 306 return radius;
307 307
308 308 createAxisLabels(layout);
309 309 QStringList labelList = labels();
310 310 QFont font = axis()->labelsFont();
311 311
312 312 QRectF maxRect;
313 313 maxRect.setSize(maxSize);
314 314 maxRect.moveCenter(QPointF(0.0, 0.0));
315 315
316 316 // This is a horrible way to find out the maximum radius for angular axis and its labels.
317 317 // It just increments the radius down until everyhing fits the constraint size.
318 318 // Proper way would be to actually calculate it but this seems to work reasonably fast as it is.
319 319 bool nextTickVisible = false;
320 320 for (int i = 0; i < layout.size(); ) {
321 321 if ((i == layout.size() - 1)
322 322 || layout.at(i + 1) < 0.0
323 323 || layout.at(i + 1) > 360.0) {
324 324 nextTickVisible = false;
325 325 } else {
326 326 nextTickVisible = true;
327 327 }
328 328
329 329 qreal labelCoordinate = layout.at(i);
330 330 qreal labelVisible;
331 331
332 332 if (intervalAxis()) {
333 333 qreal farEdge;
334 334 if (i == (layout.size() - 1))
335 335 farEdge = 360.0;
336 336 else
337 337 farEdge = qMin(qreal(360.0), layout.at(i + 1));
338 338
339 339 // Adjust the labelCoordinate to show it if next tick is visible
340 340 if (nextTickVisible)
341 341 labelCoordinate = qMax(qreal(0.0), labelCoordinate);
342 342
343 343 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
344 344 }
345 345
346 346 if (labelCoordinate < 0.0 || labelCoordinate > 360.0)
347 347 labelVisible = false;
348 348 else
349 349 labelVisible = true;
350 350
351 351 if (!labelVisible) {
352 352 i++;
353 353 continue;
354 354 }
355 355
356 356 QRectF boundingRect = ChartPresenter::textBoundingRect(axis()->labelsFont(), labelList.at(i), axis()->labelsAngle());
357 357 QPointF labelPoint = QLineF::fromPolar(radius + tickWidth(), 90.0 - labelCoordinate).p2();
358 358
359 359 boundingRect = moveLabelToPosition(labelCoordinate, labelPoint, boundingRect);
360 360 if (boundingRect.isEmpty() || maxRect.intersected(boundingRect) == boundingRect) {
361 361 i++;
362 362 } else {
363 363 radius -= 1.0;
364 364 if (radius < 1.0) // safeguard
365 365 return 1.0;
366 366 }
367 367 }
368 368 }
369 369
370 370 if (!axis()->titleText().isEmpty() && axis()->isTitleVisible()) {
371 371 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
372 372
373 373 radius -= titlePadding() + (titleRect.height() / 2.0);
374 374 if (radius < 1.0) // safeguard
375 375 return 1.0;
376 376 }
377 377
378 378 return radius;
379 379 }
380 380
381 381 QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const
382 382 {
383 383 // TODO use fuzzy compare for "==" cases?
384 384 // TODO Adjust the rect position near 0, 90, 180, and 270 angles for smoother animation?
385 385 if (angularCoordinate == 0.0)
386 386 labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0));
387 387 else if (angularCoordinate < 90.0)
388 388 labelRect.moveBottomLeft(labelPoint);
389 389 else if (angularCoordinate == 90.0)
390 390 labelRect.moveCenter(labelPoint + QPointF(labelRect.width() / 2.0 + 2.0, 0)); // +2 so that it does not hit the radial axis
391 391 else if (angularCoordinate < 180.0)
392 392 labelRect.moveTopLeft(labelPoint);
393 393 else if (angularCoordinate == 180.0)
394 394 labelRect.moveCenter(labelPoint + QPointF(0, labelRect.height() / 2.0));
395 395 else if (angularCoordinate < 270.0)
396 396 labelRect.moveTopRight(labelPoint);
397 397 else if (angularCoordinate == 270.0)
398 398 labelRect.moveCenter(labelPoint + QPointF(-labelRect.width() / 2.0 - 2.0, 0)); // -2 so that it does not hit the radial axis
399 399 else if (angularCoordinate < 360.0)
400 400 labelRect.moveBottomRight(labelPoint);
401 401 else
402 402 labelRect.moveCenter(labelPoint + QPointF(0, -labelRect.height() / 2.0));
403 403 return labelRect;
404 404 }
405 405
406 406 #include "moc_polarchartaxisangular_p.cpp"
407 407
408 408 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,288 +1,288
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "polarchartaxisradial_p.h"
22 22 #include "chartpresenter_p.h"
23 23 #include "abstractchartlayout_p.h"
24 24 #include "qabstractaxis_p.h"
25 25 #include "linearrowitem_p.h"
26 26
27 27 QTCOMMERCIALCHART_BEGIN_NAMESPACE
28 28
29 29 PolarChartAxisRadial::PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
30 30 : PolarChartAxis(axis, item, intervalAxis)
31 31 {
32 32 }
33 33
34 34 PolarChartAxisRadial::~PolarChartAxisRadial()
35 35 {
36 36 }
37 37
38 38 void PolarChartAxisRadial::updateGeometry()
39 39 {
40 40 const QVector<qreal> &layout = this->layout();
41 41 if (layout.isEmpty())
42 42 return;
43 43
44 44 createAxisLabels(layout);
45 45 QStringList labelList = labels();
46 46 QPointF center = axisGeometry().center();
47 47 QList<QGraphicsItem *> arrowItemList = arrowItems();
48 48 QList<QGraphicsItem *> gridItemList = gridItems();
49 49 QList<QGraphicsItem *> labelItemList = labelItems();
50 50 QList<QGraphicsItem *> shadeItemList = shadeItems();
51 51 QGraphicsTextItem* title = titleItem();
52 52 qreal radius = axisGeometry().height() / 2.0;
53 53
54 54 QLineF line(center, center + QPointF(0, -radius));
55 55 QGraphicsLineItem *axisLine = static_cast<QGraphicsLineItem *>(arrowItemList.at(0));
56 56 axisLine->setLine(line);
57 57
58 58 QRectF previousLabelRect;
59 59 bool firstShade = true;
60 60 bool nextTickVisible = false;
61 61 if (layout.size())
62 62 nextTickVisible = !(layout.at(0) < 0.0 || layout.at(0) > radius);
63 63
64 64 for (int i = 0; i < layout.size(); ++i) {
65 65 qreal radialCoordinate = layout.at(i);
66 66
67 67 QGraphicsEllipseItem *gridItem = static_cast<QGraphicsEllipseItem *>(gridItemList.at(i));
68 68 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrowItemList.at(i + 1));
69 69 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labelItemList.at(i));
70 70 QGraphicsPathItem *shadeItem = 0;
71 71 if (i == 0)
72 72 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
73 73 else if (i % 2)
74 74 shadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at((i / 2) + 1));
75 75
76 76 // Ignore ticks outside valid range
77 77 bool currentTickVisible = nextTickVisible;
78 78 if ((i == layout.size() - 1)
79 79 || layout.at(i + 1) < 0.0
80 80 || layout.at(i + 1) > radius) {
81 81 nextTickVisible = false;
82 82 } else {
83 83 nextTickVisible = true;
84 84 }
85 85
86 86 qreal labelCoordinate = radialCoordinate;
87 87 qreal labelVisible = currentTickVisible;
88 88 qreal labelPad = labelPadding() / 2.0;
89 89 if (intervalAxis()) {
90 90 qreal farEdge;
91 91 if (i == (layout.size() - 1))
92 92 farEdge = radius;
93 93 else
94 94 farEdge = qMin(radius, layout.at(i + 1));
95 95
96 96 // Adjust the labelCoordinate to show it if next tick is visible
97 97 if (nextTickVisible)
98 98 labelCoordinate = qMax(qreal(0.0), labelCoordinate);
99 99
100 100 labelCoordinate = (labelCoordinate + farEdge) / 2.0;
101 101 if (labelCoordinate > 0.0 && labelCoordinate < radius)
102 102 labelVisible = true;
103 103 else
104 104 labelVisible = false;
105 105 }
106 106
107 107 // Radial axis label
108 108 if (axis()->labelsVisible() && labelVisible) {
109 109 labelItem->setHtml(labelList.at(i));
110 110 QRectF labelRect = labelItem->boundingRect();
111 111 QPointF labelCenter = labelRect.center();
112 112 labelItem->setTransformOriginPoint(labelCenter.x(), labelCenter.y());
113 113 QRectF boundingRect = ChartPresenter::textBoundingRect(axis()->labelsFont(), labelList.at(i), axis()->labelsAngle());
114 114 boundingRect.moveCenter(labelCenter);
115 115 QPointF positionDiff(labelRect.topLeft() - boundingRect.topLeft());
116 116 QPointF labelPoint = center;
117 117 if (intervalAxis())
118 118 labelPoint += QPointF(labelPad, -labelCoordinate - (boundingRect.height() / 2.0));
119 119 else
120 120 labelPoint += QPointF(labelPad, labelPad - labelCoordinate);
121 121 labelRect.moveTopLeft(labelPoint);
122 122 labelItem->setPos(labelRect.topLeft() + positionDiff);
123 123
124 124 // Label overlap detection
125 125 labelRect.setSize(boundingRect.size());
126 126 if ((i && previousLabelRect.intersects(labelRect))
127 127 || !axisGeometry().contains(labelRect)) {
128 128 labelVisible = false;
129 129 } else {
130 130 previousLabelRect = labelRect;
131 131 labelVisible = true;
132 132 }
133 133 }
134 134
135 135 labelItem->setVisible(labelVisible);
136 136 if (!currentTickVisible) {
137 137 gridItem->setVisible(false);
138 138 tickItem->setVisible(false);
139 139 if (shadeItem)
140 140 shadeItem->setVisible(false);
141 141 continue;
142 142 }
143 143
144 144 // Radial grid line
145 145 QRectF gridRect;
146 146 gridRect.setWidth(radialCoordinate * 2.0);
147 147 gridRect.setHeight(radialCoordinate * 2.0);
148 148 gridRect.moveCenter(center);
149 149
150 150 gridItem->setRect(gridRect);
151 151 gridItem->setVisible(true);
152 152
153 153 // Tick
154 154 QLineF tickLine(-tickWidth(), 0.0, tickWidth(), 0.0);
155 155 tickLine.translate(center.rx(), gridRect.top());
156 156 tickItem->setLine(tickLine);
157 157 tickItem->setVisible(true);
158 158
159 159 // Shades
160 160 if (i % 2 || (i == 0 && !nextTickVisible)) {
161 161 QPainterPath path;
162 162 if (i == 0) {
163 163 // If first tick is also the last, we need to custom fill the inner circle
164 164 // or it won't get filled.
165 165 QRectF innerCircle(0.0, 0.0, layout.at(0) * 2.0, layout.at(0) * 2.0);
166 166 innerCircle.moveCenter(center);
167 167 path.addEllipse(innerCircle);
168 168 } else {
169 169 QRectF otherGridRect;
170 170 if (!nextTickVisible) { // Last visible tick
171 171 otherGridRect = axisGeometry();
172 172 } else {
173 173 qreal otherGridRectDimension = layout.at(i + 1) * 2.0;
174 174 otherGridRect.setWidth(otherGridRectDimension);
175 175 otherGridRect.setHeight(otherGridRectDimension);
176 176 otherGridRect.moveCenter(center);
177 177 }
178 178 path.addEllipse(gridRect);
179 179 path.addEllipse(otherGridRect);
180 180
181 181 // Add additional shading in first visible shade item if there is a partial tick
182 182 // to be filled at the center (log & category axes)
183 183 if (firstShade) {
184 184 QGraphicsPathItem *specialShadeItem = static_cast<QGraphicsPathItem *>(shadeItemList.at(0));
185 185 if (layout.at(i - 1) > 0.0) {
186 186 QRectF innerCircle(0.0, 0.0, layout.at(i - 1) * 2.0, layout.at(i - 1) * 2.0);
187 187 innerCircle.moveCenter(center);
188 188 QPainterPath specialPath;
189 189 specialPath.addEllipse(innerCircle);
190 190 specialShadeItem->setPath(specialPath);
191 191 specialShadeItem->setVisible(true);
192 192 } else {
193 193 specialShadeItem->setVisible(false);
194 194 }
195 195 }
196 196 }
197 197 shadeItem->setPath(path);
198 198 shadeItem->setVisible(true);
199 199 firstShade = false;
200 200 }
201 201 }
202 202
203 203 // Title, along the 0 axis
204 204 QString titleText = axis()->titleText();
205 205 if (!titleText.isEmpty() && axis()->isTitleVisible()) {
206 206 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), radius, Qt::Horizontal, QRectF()));
207 207
208 208 QRectF titleBoundingRect = title->boundingRect();
209 209 QPointF titleCenter = titleBoundingRect.center();
210 210 QPointF arrowCenter = axisLine->boundingRect().center();
211 211 QPointF titleCenterDiff = arrowCenter - titleCenter;
212 title->setPos(titleCenterDiff.x() - qreal(titlePadding()) - (titleBoundingRect.height() / 2.0), titleCenterDiff.y());
212 title->setPos(titleCenterDiff.x() - titlePadding() - (titleBoundingRect.height() / 2.0), titleCenterDiff.y());
213 213 title->setTransformOriginPoint(titleCenter);
214 214 title->setRotation(270.0);
215 215 }
216 216
217 217 QGraphicsLayoutItem::updateGeometry();
218 218 }
219 219
220 220 Qt::Orientation PolarChartAxisRadial::orientation() const
221 221 {
222 222 return Qt::Vertical;
223 223 }
224 224
225 225 void PolarChartAxisRadial::createItems(int count)
226 226 {
227 227 if (arrowItems().count() == 0) {
228 228 // radial axis center line
229 229 QGraphicsLineItem *arrow = new LineArrowItem(this, presenter()->rootItem());
230 230 arrow->setPen(axis()->linePen());
231 231 arrowGroup()->addToGroup(arrow);
232 232 }
233 233
234 234 for (int i = 0; i < count; ++i) {
235 235 QGraphicsLineItem *arrow = new QGraphicsLineItem(presenter()->rootItem());
236 236 QGraphicsEllipseItem *grid = new QGraphicsEllipseItem(presenter()->rootItem());
237 237 QGraphicsTextItem *label = new QGraphicsTextItem(presenter()->rootItem());
238 238 QGraphicsTextItem *title = titleItem();
239 239 arrow->setPen(axis()->linePen());
240 240 grid->setPen(axis()->gridLinePen());
241 241 label->setFont(axis()->labelsFont());
242 242 label->setDefaultTextColor(axis()->labelsBrush().color());
243 243 label->setRotation(axis()->labelsAngle());
244 244 title->setFont(axis()->titleFont());
245 245 title->setDefaultTextColor(axis()->titleBrush().color());
246 246 title->setHtml(axis()->titleText());
247 247 arrowGroup()->addToGroup(arrow);
248 248 gridGroup()->addToGroup(grid);
249 249 labelGroup()->addToGroup(label);
250 250 if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
251 251 QGraphicsPathItem *shade = new QGraphicsPathItem(presenter()->rootItem());
252 252 shade->setPen(axis()->shadesPen());
253 253 shade->setBrush(axis()->shadesBrush());
254 254 shadeGroup()->addToGroup(shade);
255 255 }
256 256 }
257 257 }
258 258
259 259 void PolarChartAxisRadial::handleArrowPenChanged(const QPen &pen)
260 260 {
261 261 foreach (QGraphicsItem *item, arrowItems())
262 262 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
263 263 }
264 264
265 265 void PolarChartAxisRadial::handleGridPenChanged(const QPen &pen)
266 266 {
267 267 foreach (QGraphicsItem *item, gridItems())
268 268 static_cast<QGraphicsEllipseItem *>(item)->setPen(pen);
269 269 }
270 270
271 271 QSizeF PolarChartAxisRadial::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
272 272 {
273 273 Q_UNUSED(which);
274 274 Q_UNUSED(constraint);
275 275 return QSizeF(-1.0, -1.0);
276 276 }
277 277
278 278 qreal PolarChartAxisRadial::preferredAxisRadius(const QSizeF &maxSize)
279 279 {
280 280 qreal radius = maxSize.height() / 2.0;
281 281 if (maxSize.width() < maxSize.height())
282 282 radius = maxSize.width() / 2.0;
283 283 return radius;
284 284 }
285 285
286 286 #include "moc_polarchartaxisradial_p.cpp"
287 287
288 288 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,211 +1,228
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "verticalaxis_p.h"
22 22 #include "qabstractaxis.h"
23 23 #include "chartpresenter_p.h"
24 24 #include <QDebug>
25 25
26 26 QTCOMMERCIALCHART_BEGIN_NAMESPACE
27 27
28 28 VerticalAxis::VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
29 29 : CartesianChartAxis(axis, item, intervalAxis)
30 30 {
31 31 }
32 32
33 33 VerticalAxis::~VerticalAxis()
34 34 {
35 35 }
36 36
37 37 void VerticalAxis::updateGeometry()
38 38 {
39 39 const QVector<qreal> &layout = ChartAxisElement::layout();
40 40
41 41 if (layout.isEmpty())
42 42 return;
43 43
44 44 QStringList labelList = labels();
45 45
46 46 QList<QGraphicsItem *> lines = gridItems();
47 47 QList<QGraphicsItem *> labels = labelItems();
48 48 QList<QGraphicsItem *> shades = shadeItems();
49 49 QList<QGraphicsItem *> arrow = arrowItems();
50 50 QGraphicsTextItem *title = titleItem();
51 51
52 52 Q_ASSERT(labels.size() == labelList.size());
53 53 Q_ASSERT(layout.size() == labelList.size());
54 54
55 55 const QRectF &axisRect = axisGeometry();
56 56 const QRectF &gridRect = gridGeometry();
57 57
58 58 qreal height = axisRect.bottom();
59 59
60 60
61 61 //arrow
62 62 QGraphicsLineItem *arrowItem = static_cast<QGraphicsLineItem*>(arrow.at(0));
63 63
64 64 //arrow position
65 65 if (axis()->alignment() == Qt::AlignLeft)
66 66 arrowItem->setLine(axisRect.right(), gridRect.top(), axisRect.right(), gridRect.bottom());
67 67 else if (axis()->alignment() == Qt::AlignRight)
68 68 arrowItem->setLine(axisRect.left(), gridRect.top(), axisRect.left(), gridRect.bottom());
69 69
70 70 //title
71 int titlePad = 0;
72 71 QRectF titleBoundingRect;
73 72 QString titleText = axis()->titleText();
73 qreal availableSpace = axisRect.width() - labelPadding();
74 74 if (!titleText.isEmpty() && titleItem()->isVisible()) {
75 title->setHtml(ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0), gridRect.height(), Qt::Horizontal, QRectF()));
75 availableSpace -= titlePadding() * 2.0;
76 qreal minimumLabelWidth = ChartPresenter::textBoundingRect(axis()->labelsFont(), "...").width();
77 QString truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), titleText, qreal(0.0),
78 gridRect.height(), Qt::Horizontal, titleBoundingRect);
79 qreal titleSpace = availableSpace - minimumLabelWidth;
80 if (titleSpace < titleBoundingRect.width()) {
81 // Need to also truncate title vertically (multiline title)
82 bool skip = false;
83 if (truncatedTitle.endsWith("...")) {
84 if (truncatedTitle.size() == 3)
85 skip = true; // Already truncated to minimum
86 else
87 truncatedTitle.chop(3);
88 }
89 if (!skip)
90 truncatedTitle = ChartPresenter::truncatedText(axis()->titleFont(), truncatedTitle, qreal(0.0),
91 titleSpace, Qt::Vertical, titleBoundingRect);
92 }
93 title->setHtml(truncatedTitle);
76 94
77 titlePad = titlePadding();
78 95 titleBoundingRect = title->boundingRect();
79 96
80 97 QPointF center = gridRect.center() - titleBoundingRect.center();
81 if (axis()->alignment() == Qt::AlignLeft) {
82 title->setPos(axisRect.left() - titleBoundingRect.width() / 2 + titleBoundingRect.height() / 2 + titlePad, center.y());
83 }
84 else if (axis()->alignment() == Qt::AlignRight) {
85 title->setPos(axisRect.right() - titleBoundingRect.width() / 2 - titleBoundingRect.height() / 2 - titlePad, center.y());
86 }
98 if (axis()->alignment() == Qt::AlignLeft)
99 title->setPos(axisRect.left() - titleBoundingRect.width() / 2.0 + titleBoundingRect.height() / 2.0 + titlePadding(), center.y());
100 else if (axis()->alignment() == Qt::AlignRight)
101 title->setPos(axisRect.right() - titleBoundingRect.width() / 2.0 - titleBoundingRect.height() / 2.0 - titlePadding(), center.y());
102
87 103 title->setTransformOriginPoint(titleBoundingRect.center());
88 104 title->setRotation(270);
105
106 availableSpace -= titleBoundingRect.height();
89 107 }
90 108
91 109 for (int i = 0; i < layout.size(); ++i) {
92 110 //items
93 111 QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem *>(lines.at(i));
94 112 QGraphicsLineItem *tickItem = static_cast<QGraphicsLineItem *>(arrow.at(i + 1));
95 113 QGraphicsTextItem *labelItem = static_cast<QGraphicsTextItem *>(labels.at(i));
96 114
97 115 //grid line
98 116 gridItem->setLine(gridRect.left(), layout[i], gridRect.right(), layout[i]);
99 117
100 118 //label text wrapping
101 119 QString text = labelList.at(i);
102 120 QRectF boundingRect;
103 qreal size = axisRect.right() - axisRect.left() - labelPadding() - titleBoundingRect.height() - (titlePad * 2);
104 121 labelItem->setHtml(ChartPresenter::truncatedText(axis()->labelsFont(), text, axis()->labelsAngle(),
105 size, Qt::Horizontal, boundingRect));
122 availableSpace, Qt::Horizontal, boundingRect));
106 123
107 124 //label transformation origin point
108 125 const QRectF &rect = labelItem->boundingRect();
109 126 QPointF center = rect.center();
110 127 labelItem->setTransformOriginPoint(center.x(), center.y());
111 int widthDiff = rect.width() - boundingRect.width();
128 qreal widthDiff = rect.width() - boundingRect.width();
112 129
113 130 //ticks and label position
114 131 if (axis()->alignment() == Qt::AlignLeft) {
115 labelItem->setPos(axisRect.right() - rect.width() + (widthDiff / 2) - labelPadding(), layout[i] - center.y());
132 labelItem->setPos(axisRect.right() - rect.width() + (widthDiff / 2.0) - labelPadding(), layout[i] - center.y());
116 133 tickItem->setLine(axisRect.right() - labelPadding(), layout[i], axisRect.right(), layout[i]);
117 134 } else if (axis()->alignment() == Qt::AlignRight) {
118 labelItem->setPos(axisRect.left() + labelPadding() - (widthDiff / 2), layout[i] - center.y());
135 labelItem->setPos(axisRect.left() + labelPadding() - (widthDiff / 2.0), layout[i] - center.y());
119 136 tickItem->setLine(axisRect.left(), layout[i], axisRect.left() + labelPadding(), layout[i]);
120 137 }
121 138
122 139 //label in between
123 140 bool forceHide = false;
124 141 if (intervalAxis() && (i + 1) != layout.size()) {
125 142 qreal lowerBound = qMin(layout[i], gridRect.bottom());
126 143 qreal upperBound = qMax(layout[i + 1], gridRect.top());
127 144 const qreal delta = lowerBound - upperBound;
128 145 // Hide label in case visible part of the category at the grid edge is too narrow
129 146 if (delta < boundingRect.height()
130 147 && (lowerBound == gridRect.bottom() || upperBound == gridRect.top())) {
131 148 forceHide = true;
132 149 } else {
133 150 labelItem->setPos(labelItem->pos().x() , lowerBound - (delta / 2.0) - center.y());
134 151 }
135 152 }
136 153
137 154 //label overlap detection - compensate one pixel for rounding errors
138 155 if (labelItem->pos().y() + boundingRect.height() > height || forceHide ||
139 156 (labelItem->pos().y() + (boundingRect.height() / 2.0) - 1.0) > axisRect.bottom() ||
140 157 labelItem->pos().y() + (boundingRect.height() / 2.0) < (axisRect.top() - 1.0)) {
141 158 labelItem->setVisible(false);
142 159 }
143 160 else {
144 161 labelItem->setVisible(true);
145 162 height=labelItem->pos().y();
146 163 }
147 164
148 165 //shades
149 166 if ((i + 1) % 2 && i > 1) {
150 167 QGraphicsRectItem *rectItem = static_cast<QGraphicsRectItem *>(shades.at(i / 2 - 1));
151 168 qreal lowerBound = qMin(layout[i - 1], gridRect.bottom());
152 169 qreal upperBound = qMax(layout[i], gridRect.top());
153 170 rectItem->setRect(gridRect.left(), upperBound, gridRect.width(), lowerBound - upperBound);
154 171 if (rectItem->rect().height() <= 0.0)
155 172 rectItem->setVisible(false);
156 173 else
157 174 rectItem->setVisible(true);
158 175 }
159 176
160 177 // check if the grid line and the axis tick should be shown
161 178 qreal y = gridItem->line().p1().y();
162 179 if ((y < gridRect.top() || y > gridRect.bottom()))
163 180 {
164 181 gridItem->setVisible(false);
165 182 tickItem->setVisible(false);
166 183 }else{
167 184 gridItem->setVisible(true);
168 185 tickItem->setVisible(true);
169 186 }
170 187
171 188 }
172 189 //begin/end grid line in case labels between
173 190 if (intervalAxis()) {
174 191 QGraphicsLineItem *gridLine;
175 192 gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
176 193 gridLine->setLine(gridRect.left(), gridRect.top(), gridRect.right(), gridRect.top());
177 194 gridLine->setVisible(true);
178 195 gridLine = static_cast<QGraphicsLineItem*>(lines.at(layout.size() + 1));
179 196 gridLine->setLine(gridRect.left(), gridRect.bottom(), gridRect.right(), gridRect.bottom());
180 197 gridLine->setVisible(true);
181 198 }
182 199 }
183 200
184 201 QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
185 202 {
186 203 Q_UNUSED(constraint);
187 204 QSizeF sh(0, 0);
188 205
189 206 if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
190 207 return sh;
191 208
192 209 switch (which) {
193 210 case Qt::MinimumSize: {
194 211 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), "...");
195 sh = QSizeF(titleRect.height() + (titlePadding() * 2), titleRect.width());
212 sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
196 213 break;
197 214 }
198 215 case Qt::MaximumSize:
199 216 case Qt::PreferredSize: {
200 217 QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
201 sh = QSizeF(titleRect.height() + (titlePadding() * 2), titleRect.width());
218 sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
202 219 break;
203 220 }
204 221 default:
205 222 break;
206 223 }
207 224
208 225 return sh;
209 226 }
210 227
211 228 QTCOMMERCIALCHART_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now