1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <private/cartesianchartlayout_p.h> |
5 | #include <private/chartpresenter_p.h> |
6 | #include <private/chartaxiselement_p.h> |
7 | #include <QtCore/QDebug> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | static const qreal maxAxisPortion = 0.4; |
12 | |
13 | CartesianChartLayout::CartesianChartLayout(ChartPresenter *presenter) |
14 | : AbstractChartLayout(presenter) |
15 | { |
16 | } |
17 | |
18 | CartesianChartLayout::~CartesianChartLayout() |
19 | { |
20 | } |
21 | |
22 | QRectF CartesianChartLayout::calculateAxisGeometry(const QRectF &geometry, |
23 | const QList<ChartAxisElement *> &axes, |
24 | bool update) const |
25 | { |
26 | Q_UNUSED(update); |
27 | QSizeF left(0,0); |
28 | QSizeF minLeft(0,0); |
29 | QSizeF right(0,0); |
30 | QSizeF minRight(0,0); |
31 | QSizeF bottom(0,0); |
32 | QSizeF minBottom(0,0); |
33 | QSizeF top(0,0); |
34 | QSizeF minTop(0,0); |
35 | QSizeF labelExtents(0,0); |
36 | int leftCount = 0; |
37 | int rightCount = 0; |
38 | int topCount = 0; |
39 | int bottomCount = 0; |
40 | |
41 | for (ChartAxisElement *axis : axes) { |
42 | if (!axis->isVisible()) |
43 | continue; |
44 | |
45 | |
46 | QSizeF size = axis->effectiveSizeHint(which: Qt::PreferredSize); |
47 | //this is used to get single thick font size |
48 | QSizeF minSize = axis->effectiveSizeHint(which: Qt::MinimumSize); |
49 | |
50 | switch (axis->axis()->alignment()) { |
51 | case Qt::AlignLeft: |
52 | left.setWidth(left.width()+size.width()); |
53 | left.setHeight(qMax(a: left.height(),b: size.height())); |
54 | minLeft.setWidth(minLeft.width()+minSize.width()); |
55 | minLeft.setHeight(qMax(a: minLeft.height(),b: minSize.height())); |
56 | labelExtents.setHeight(qMax(a: size.height(), b: labelExtents.height())); |
57 | leftCount++; |
58 | break; |
59 | case Qt::AlignRight: |
60 | right.setWidth(right.width()+size.width()); |
61 | right.setHeight(qMax(a: right.height(),b: size.height())); |
62 | minRight.setWidth(minRight.width()+minSize.width()); |
63 | minRight.setHeight(qMax(a: minRight.height(),b: minSize.height())); |
64 | labelExtents.setHeight(qMax(a: size.height(), b: labelExtents.height())); |
65 | rightCount++; |
66 | break; |
67 | case Qt::AlignTop: |
68 | top.setWidth(qMax(a: top.width(),b: size.width())); |
69 | top.setHeight(top.height()+size.height()); |
70 | minTop.setWidth(qMax(a: minTop.width(),b: minSize.width())); |
71 | minTop.setHeight(minTop.height()+minSize.height()); |
72 | labelExtents.setWidth(qMax(a: size.width(), b: labelExtents.width())); |
73 | topCount++; |
74 | break; |
75 | case Qt::AlignBottom: |
76 | bottom.setWidth(qMax(a: bottom.width(), b: size.width())); |
77 | bottom.setHeight(bottom.height() + size.height()); |
78 | minBottom.setWidth(qMax(a: minBottom.width(),b: minSize.width())); |
79 | minBottom.setHeight(minBottom.height() + minSize.height()); |
80 | labelExtents.setWidth(qMax(a: size.width(), b: labelExtents.width())); |
81 | bottomCount++; |
82 | break; |
83 | default: |
84 | qWarning(msg: "Axis is without alignment !" ); |
85 | break; |
86 | } |
87 | } |
88 | |
89 | qreal totalVerticalAxes = leftCount + rightCount; |
90 | qreal leftSqueezeRatio = 1.0; |
91 | qreal rightSqueezeRatio = 1.0; |
92 | qreal vratio = 0; |
93 | |
94 | if (totalVerticalAxes > 0) |
95 | vratio = (maxAxisPortion * geometry.width()) / totalVerticalAxes; |
96 | |
97 | if (leftCount > 0) { |
98 | int maxWidth = vratio * leftCount; |
99 | if (left.width() > maxWidth) { |
100 | leftSqueezeRatio = maxWidth / left.width(); |
101 | left.setWidth(maxWidth); |
102 | } |
103 | } |
104 | if (rightCount > 0) { |
105 | int maxWidth = vratio * rightCount; |
106 | if (right.width() > maxWidth) { |
107 | rightSqueezeRatio = maxWidth / right.width(); |
108 | right.setWidth(maxWidth); |
109 | } |
110 | } |
111 | |
112 | qreal totalHorizontalAxes = topCount + bottomCount; |
113 | qreal topSqueezeRatio = 1.0; |
114 | qreal bottomSqueezeRatio = 1.0; |
115 | qreal hratio = 0; |
116 | |
117 | if (totalHorizontalAxes > 0) |
118 | hratio = (maxAxisPortion * geometry.height()) / totalHorizontalAxes; |
119 | |
120 | if (topCount > 0) { |
121 | int maxHeight = hratio * topCount; |
122 | if (top.height() > maxHeight) { |
123 | topSqueezeRatio = maxHeight / top.height(); |
124 | top.setHeight(maxHeight); |
125 | } |
126 | } |
127 | if (bottomCount > 0) { |
128 | int maxHeight = hratio * bottomCount; |
129 | if (bottom.height() > maxHeight) { |
130 | bottomSqueezeRatio = maxHeight / bottom.height(); |
131 | bottom.setHeight(maxHeight); |
132 | } |
133 | } |
134 | |
135 | qreal minHeight = qMax(a: minLeft.height(),b: minRight.height()) + 1; |
136 | qreal minWidth = qMax(a: minTop.width(),b: minBottom.width()) + 1; |
137 | |
138 | // Ensure that there is enough space for first and last tick labels. |
139 | left.setWidth(qMax(a: labelExtents.width(), b: left.width())); |
140 | right.setWidth(qMax(a: labelExtents.width(), b: right.width())); |
141 | top.setHeight(qMax(a: labelExtents.height(), b: top.height())); |
142 | bottom.setHeight(qMax(a: labelExtents.height(), b: bottom.height())); |
143 | |
144 | QRectF chartRect = geometry.adjusted( |
145 | xp1: qMax(a: left.width(), b: minWidth / 2), yp1: qMax(a: top.height(), b: minHeight / 2), |
146 | xp2: -qMax(a: right.width(), b: minWidth / 2), yp2: -qMax(a: bottom.height(), b: minHeight / 2)); |
147 | |
148 | qreal leftOffset = 0; |
149 | qreal rightOffset = 0; |
150 | qreal topOffset = 0; |
151 | qreal bottomOffset = 0; |
152 | |
153 | // The axes are positioned here for the first time, so we need to catch any possible resizing |
154 | // of the chart when in fixed geometry to prevent them being moved out of place. |
155 | if (m_presenter->isFixedGeometry()) |
156 | chartRect = m_presenter->geometry(); |
157 | |
158 | for (ChartAxisElement *axis : axes) { |
159 | |
160 | if (!axis->isVisible()) |
161 | continue; |
162 | |
163 | QSizeF size = axis->effectiveSizeHint(which: Qt::PreferredSize); |
164 | |
165 | switch (axis->axis()->alignment()){ |
166 | case Qt::AlignLeft:{ |
167 | qreal width = size.width(); |
168 | if (leftSqueezeRatio < 1.0) |
169 | width *= leftSqueezeRatio; |
170 | leftOffset+=width; |
171 | axis->setGeometry( |
172 | axis: QRect(chartRect.left() - leftOffset, geometry.top(), width, geometry.bottom()), |
173 | grid: chartRect); |
174 | break; |
175 | } |
176 | case Qt::AlignRight:{ |
177 | qreal width = size.width(); |
178 | if (rightSqueezeRatio < 1.0) |
179 | width *= rightSqueezeRatio; |
180 | axis->setGeometry(axis: QRect(chartRect.right() + rightOffset, geometry.top(), |
181 | width, geometry.bottom()), |
182 | grid: chartRect); |
183 | rightOffset+=width; |
184 | break; |
185 | } |
186 | case Qt::AlignTop: { |
187 | qreal height = size.height(); |
188 | if (topSqueezeRatio < 1.0) |
189 | height *= topSqueezeRatio; |
190 | axis->setGeometry(axis: QRect(geometry.left(), chartRect.top() - topOffset - height, |
191 | geometry.width(), height), |
192 | grid: chartRect); |
193 | topOffset += height; |
194 | break; |
195 | } |
196 | case Qt::AlignBottom: |
197 | qreal height = size.height(); |
198 | if (bottomSqueezeRatio < 1.0) |
199 | height *= bottomSqueezeRatio; |
200 | axis->setGeometry(axis: QRect(geometry.left(), chartRect.bottom() + bottomOffset, |
201 | geometry.width(), height), |
202 | grid: chartRect); |
203 | bottomOffset += height; |
204 | break; |
205 | } |
206 | } |
207 | |
208 | return chartRect; |
209 | } |
210 | |
211 | QRectF CartesianChartLayout::calculateAxisMinimum(const QRectF &minimum, const QList<ChartAxisElement *> &axes) const |
212 | { |
213 | QSizeF left; |
214 | QSizeF right; |
215 | QSizeF bottom; |
216 | QSizeF top; |
217 | |
218 | for (ChartAxisElement *axis : axes) { |
219 | QSizeF size = axis->effectiveSizeHint(which: Qt::MinimumSize); |
220 | |
221 | if (!axis->isVisible()) |
222 | continue; |
223 | |
224 | switch (axis->axis()->alignment()) { |
225 | case Qt::AlignLeft: |
226 | left.setWidth(left.width() + size.width()); |
227 | left.setHeight(qMax(a: left.height(), b: size.height())); |
228 | break; |
229 | case Qt::AlignRight: |
230 | right.setWidth(right.width() + size.width()); |
231 | right.setHeight(qMax(a: right.height(), b: size.height())); |
232 | break; |
233 | case Qt::AlignTop: |
234 | top.setWidth(qMax(a: top.width(), b: size.width())); |
235 | top.setHeight(top.height() + size.height()); |
236 | break; |
237 | case Qt::AlignBottom: |
238 | bottom.setWidth(qMax(a: bottom.width(), b: size.width())); |
239 | bottom.setHeight(bottom.height() + size.height()); |
240 | break; |
241 | } |
242 | } |
243 | return minimum.adjusted(xp1: 0, yp1: 0, xp2: left.width() + right.width() + qMax(a: top.width(), b: bottom.width()), |
244 | yp2: top.height() + bottom.height() + qMax(a: left.height(), b: right.height())); |
245 | } |
246 | |
247 | QT_END_NAMESPACE |
248 | |