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
9QT_BEGIN_NAMESPACE
10
11static const qreal maxAxisPortion = 0.4;
12
13CartesianChartLayout::CartesianChartLayout(ChartPresenter *presenter)
14 : AbstractChartLayout(presenter)
15{
16}
17
18CartesianChartLayout::~CartesianChartLayout()
19{
20}
21
22QRectF 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
211QRectF 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
247QT_END_NAMESPACE
248

source code of qtcharts/src/charts/layout/cartesianchartlayout.cpp