1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <private/chartvalueaxisy_p.h> |
5 | #include <QtCharts/QAbstractAxis> |
6 | #include <private/chartpresenter_p.h> |
7 | #include <QtCharts/QValueAxis> |
8 | #include <private/abstractchartlayout_p.h> |
9 | #include <private/valueaxislabel_p.h> |
10 | #include <QtWidgets/QGraphicsLayout> |
11 | #include <QtCore/QtMath> |
12 | #include <QtCore/QDebug> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | ChartValueAxisY::ChartValueAxisY(QValueAxis *axis, QGraphicsItem *item) |
17 | : VerticalAxis(axis, item), |
18 | m_axis(axis) |
19 | { |
20 | QObject::connect(sender: m_axis, SIGNAL(tickCountChanged(int)), receiver: this, SLOT(handleTickCountChanged(int))); |
21 | QObject::connect(sender: m_axis, SIGNAL(minorTickCountChanged(int)), |
22 | receiver: this, SLOT(handleMinorTickCountChanged(int))); |
23 | QObject::connect(sender: m_axis, SIGNAL(labelFormatChanged(QString)), receiver: this, SLOT(handleLabelFormatChanged(QString))); |
24 | QObject::connect(sender: m_axis, SIGNAL(tickIntervalChanged(qreal)), receiver: this, SLOT(handleTickIntervalChanged(qreal))); |
25 | QObject::connect(sender: m_axis, SIGNAL(tickAnchorChanged(qreal)), receiver: this, SLOT(handleTickAnchorChanged(qreal))); |
26 | QObject::connect(sender: m_axis, SIGNAL(tickTypeChanged(QValueAxis::TickType)), receiver: this, |
27 | SLOT(handleTickTypeChanged(QValueAxis::TickType))); |
28 | } |
29 | |
30 | ChartValueAxisY::~ChartValueAxisY() |
31 | { |
32 | } |
33 | |
34 | QList<qreal> ChartValueAxisY::calculateLayout() const |
35 | { |
36 | if (m_axis->tickType() == QValueAxis::TicksFixed) { |
37 | int tickCount = m_axis->tickCount(); |
38 | |
39 | Q_ASSERT(tickCount >= 2); |
40 | |
41 | QList<qreal> points; |
42 | points.resize(size: tickCount); |
43 | |
44 | const QRectF &gridRect = gridGeometry(); |
45 | const qreal deltaY = gridRect.height() / (qreal(tickCount) - 1.0); |
46 | for (int i = 0; i < tickCount; ++i) |
47 | points[i] = qreal(i) * -deltaY + gridRect.bottom(); |
48 | return points; |
49 | } else { |
50 | const qreal interval = m_axis->tickInterval(); |
51 | const qreal anchor = m_axis->tickAnchor(); |
52 | const qreal maxValue = max(); |
53 | const qreal minValue = min(); |
54 | |
55 | // Find the first major tick right after the min of the range |
56 | const qreal ticksFromAnchor = (anchor - minValue) / interval; |
57 | const qreal firstMajorTick = anchor - std::floor(x: ticksFromAnchor) * interval; |
58 | |
59 | const QRectF &gridRect = gridGeometry(); |
60 | const qreal deltaY = gridRect.height() / (maxValue - minValue); |
61 | |
62 | QList<qreal> points; |
63 | const qreal bottomPos = gridRect.bottom(); |
64 | qreal value = firstMajorTick; |
65 | while (value <= maxValue) { |
66 | points << (value - minValue) * -deltaY + bottomPos; |
67 | value += interval; |
68 | } |
69 | |
70 | return points; |
71 | } |
72 | } |
73 | |
74 | void ChartValueAxisY::updateGeometry() |
75 | { |
76 | const QList<qreal> &layout = ChartAxisElement::layout(); |
77 | const QList<qreal> &dynamicMinorTicklayout = ChartAxisElement::dynamicMinorTicklayout(); |
78 | if (layout.isEmpty() && dynamicMinorTicklayout.isEmpty()) |
79 | return; |
80 | setLabels(createValueLabels(max: min(), min: max(), ticks: layout.size(), tickInterval: m_axis->tickInterval(), |
81 | tickAnchor: m_axis->tickAnchor(), tickType: m_axis->tickType(), format: m_axis->labelFormat())); |
82 | VerticalAxis::updateGeometry(); |
83 | updateLabelsValues(axis: m_axis); |
84 | } |
85 | |
86 | void ChartValueAxisY::handleTickCountChanged(int tick) |
87 | { |
88 | Q_UNUSED(tick); |
89 | QGraphicsLayoutItem::updateGeometry(); |
90 | if (presenter()) presenter()->layout()->invalidate(); |
91 | } |
92 | |
93 | void ChartValueAxisY::handleMinorTickCountChanged(int tick) |
94 | { |
95 | Q_UNUSED(tick); |
96 | QGraphicsLayoutItem::updateGeometry(); |
97 | if (presenter()) |
98 | presenter()->layout()->invalidate(); |
99 | } |
100 | |
101 | void ChartValueAxisY::handleLabelFormatChanged(const QString &format) |
102 | { |
103 | Q_UNUSED(format); |
104 | QGraphicsLayoutItem::updateGeometry(); |
105 | if (presenter()) presenter()->layout()->invalidate(); |
106 | } |
107 | |
108 | void ChartValueAxisY::handleTickIntervalChanged(qreal interval) |
109 | { |
110 | Q_UNUSED(interval); |
111 | QGraphicsLayoutItem::updateGeometry(); |
112 | if (presenter()) presenter()->layout()->invalidate(); |
113 | } |
114 | |
115 | void ChartValueAxisY::handleTickAnchorChanged(qreal anchor) |
116 | { |
117 | Q_UNUSED(anchor); |
118 | QGraphicsLayoutItem::updateGeometry(); |
119 | if (presenter()) presenter()->layout()->invalidate(); |
120 | } |
121 | |
122 | void ChartValueAxisY::handleTickTypeChanged(QValueAxis::TickType type) |
123 | { |
124 | Q_UNUSED(type); |
125 | QGraphicsLayoutItem::updateGeometry(); |
126 | if (presenter()) presenter()->layout()->invalidate(); |
127 | } |
128 | |
129 | QSizeF ChartValueAxisY::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const |
130 | { |
131 | Q_UNUSED(constraint); |
132 | |
133 | QSizeF sh; |
134 | QSizeF base = VerticalAxis::sizeHint(which, constraint); |
135 | QStringList ticksList = createValueLabels(max: min(), min: max(), ticks: m_axis->tickCount(), |
136 | tickInterval: m_axis->tickInterval(), tickAnchor: m_axis->tickAnchor(), |
137 | tickType: m_axis->tickType(), format: m_axis->labelFormat()); |
138 | qreal width = 0; |
139 | // Height of vertical axis sizeHint indicates the maximum distance labels can extend past |
140 | // first and last ticks. Base height is irrelevant. |
141 | qreal height = 0; |
142 | |
143 | switch (which) { |
144 | case Qt::MinimumSize: { |
145 | if (labelsVisible()) { |
146 | QRectF boundingRect = ChartPresenter::textBoundingRect(font: axis()->labelsFont(), |
147 | QStringLiteral("..." ), |
148 | angle: axis()->labelsAngle()); |
149 | width = boundingRect.width() + labelPadding() + base.width() + 1.0; |
150 | height = boundingRect.height() / 2.0; |
151 | } else { |
152 | width = base.width() + 1.0; |
153 | height = 0; |
154 | } |
155 | sh = QSizeF(width, height); |
156 | break; |
157 | } |
158 | case Qt::PreferredSize: { |
159 | if (labelsVisible()) { |
160 | qreal labelWidth = 0.0; |
161 | qreal firstHeight = -1.0; |
162 | foreach (const QString& s, ticksList) { |
163 | QRectF rect = ChartPresenter::textBoundingRect(font: axis()->labelsFont(), text: s, angle: axis()->labelsAngle()); |
164 | labelWidth = qMax(a: rect.width(), b: labelWidth); |
165 | height = rect.height(); |
166 | if (firstHeight < 0.0) |
167 | firstHeight = height; |
168 | } |
169 | width = labelWidth + labelPadding() + base.width() + 2.0; //two pixels of tolerance |
170 | height = qMax(a: height, b: firstHeight) / 2.0; |
171 | } else { |
172 | width = base.width() + 2.0; //two pixels of tolerance |
173 | height = 0; |
174 | } |
175 | sh = QSizeF(width, height); |
176 | break; |
177 | } |
178 | default: |
179 | break; |
180 | } |
181 | return sh; |
182 | } |
183 | |
184 | QT_END_NAMESPACE |
185 | |
186 | #include "moc_chartvalueaxisy_p.cpp" |
187 | |