1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <private/abstractchartlayout_p.h>
31#include <private/chartpresenter_p.h>
32#include <private/qlegend_p.h>
33#include <private/chartaxiselement_p.h>
34#include <private/charttitle_p.h>
35#include <private/chartbackground_p.h>
36#include <QtCore/QDebug>
37
38QT_CHARTS_BEGIN_NAMESPACE
39
40static const qreal golden_ratio = 0.4;
41
42AbstractChartLayout::AbstractChartLayout(ChartPresenter *presenter)
43 : m_presenter(presenter),
44 m_margins(20, 20, 20, 20)
45{
46}
47
48AbstractChartLayout::~AbstractChartLayout()
49{
50}
51
52void AbstractChartLayout::setGeometry(const QRectF &rect)
53{
54 if (!rect.isValid())
55 return;
56
57 // If the chart has a fixed geometry then don't update visually
58 const bool updateLayout = (!m_presenter->isFixedGeometry() || m_presenter->geometry() == rect);
59 if (m_presenter->chart()->isVisible()) {
60 QList<ChartAxisElement *> axes = m_presenter->axisItems();
61 ChartTitle *title = m_presenter->titleElement();
62 QLegend *legend = m_presenter->legend();
63 ChartBackground *background = m_presenter->backgroundElement();
64
65 QRectF contentGeometry = calculateBackgroundGeometry(geometry: rect, background, update: updateLayout);
66
67 contentGeometry = calculateContentGeometry(geometry: contentGeometry);
68
69 if (title && title->isVisible())
70 contentGeometry = calculateTitleGeometry(geometry: contentGeometry, title, update: updateLayout);
71
72 if (legend->isAttachedToChart() && legend->isVisible())
73 contentGeometry = calculateLegendGeometry(geometry: contentGeometry, legend, update: updateLayout);
74
75 contentGeometry = calculateAxisGeometry(geometry: contentGeometry, axes, update: updateLayout);
76
77 if (contentGeometry.isValid()) {
78 m_presenter->setGeometry(contentGeometry);
79 if (updateLayout) {
80 if (m_presenter->chart()->chartType() == QChart::ChartTypeCartesian)
81 static_cast<QGraphicsRectItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
82 else
83 static_cast<QGraphicsEllipseItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
84 }
85 }
86 }
87
88 QGraphicsLayout::setGeometry(rect);
89}
90
91QRectF AbstractChartLayout::calculateContentGeometry(const QRectF &geometry) const
92{
93 return geometry.adjusted(xp1: m_margins.left(), yp1: m_margins.top(), xp2: -m_margins.right(), yp2: -m_margins.bottom());
94}
95
96QRectF AbstractChartLayout::calculateContentMinimum(const QRectF &minimum) const
97{
98 return minimum.adjusted(xp1: 0, yp1: 0, xp2: m_margins.left() + m_margins.right(), yp2: m_margins.top() + m_margins.bottom());
99}
100
101
102QRectF AbstractChartLayout::calculateBackgroundGeometry(const QRectF &geometry,
103 ChartBackground *background,
104 bool update) const
105{
106 qreal left;
107 qreal top;
108 qreal right;
109 qreal bottom;
110 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
111 QRectF backgroundGeometry = geometry.adjusted(xp1: left, yp1: top, xp2: -right, yp2: -bottom);
112 if (background && update)
113 background->setRect(backgroundGeometry);
114 return backgroundGeometry;
115}
116
117QRectF AbstractChartLayout::calculateBackgroundMinimum(const QRectF &minimum) const
118{
119 qreal left;
120 qreal top;
121 qreal right;
122 qreal bottom;
123 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
124 return minimum.adjusted(xp1: 0, yp1: 0, xp2: left + right, yp2: top + bottom);
125}
126
127QRectF AbstractChartLayout::calculateLegendGeometry(const QRectF &geometry, QLegend *legend,
128 bool update) const
129{
130 QSizeF size = legend->effectiveSizeHint(which: Qt::PreferredSize, constraint: QSizeF(-1, -1));
131 QRectF legendRect;
132 QRectF result;
133
134 switch (legend->alignment()) {
135 case Qt::AlignTop: {
136 legendRect = QRectF(geometry.topLeft(), QSizeF(geometry.width(), size.height()));
137 result = geometry.adjusted(xp1: 0, yp1: legendRect.height(), xp2: 0, yp2: 0);
138 break;
139 }
140 case Qt::AlignBottom: {
141 legendRect = QRectF(QPointF(geometry.left(), geometry.bottom() - size.height()), QSizeF(geometry.width(), size.height()));
142 result = geometry.adjusted(xp1: 0, yp1: 0, xp2: 0, yp2: -legendRect.height());
143 break;
144 }
145 case Qt::AlignLeft: {
146 qreal width = qMin(a: size.width(), b: geometry.width() * golden_ratio);
147 legendRect = QRectF(geometry.topLeft(), QSizeF(width, geometry.height()));
148 result = geometry.adjusted(xp1: width, yp1: 0, xp2: 0, yp2: 0);
149 break;
150 }
151 case Qt::AlignRight: {
152 qreal width = qMin(a: size.width(), b: geometry.width() * golden_ratio);
153 legendRect = QRectF(QPointF(geometry.right() - width, geometry.top()), QSizeF(width, geometry.height()));
154 result = geometry.adjusted(xp1: 0, yp1: 0, xp2: -width, yp2: 0);
155 break;
156 }
157 default: {
158 legendRect = QRectF(0, 0, 0, 0);
159 result = geometry;
160 break;
161 }
162 }
163 if (update)
164 legend->setGeometry(legendRect);
165
166 return result;
167}
168
169QRectF AbstractChartLayout::calculateLegendMinimum(const QRectF &geometry, QLegend *legend) const
170{
171 if (!legend->isAttachedToChart() || !legend->isVisible()) {
172 return geometry;
173 } else {
174 QSizeF minSize = legend->effectiveSizeHint(which: Qt::MinimumSize, constraint: QSizeF(-1, -1));
175 return geometry.adjusted(xp1: 0, yp1: 0, xp2: minSize.width(), yp2: minSize.height());
176 }
177}
178
179QRectF AbstractChartLayout::calculateTitleGeometry(const QRectF &geometry, ChartTitle *title,
180 bool update) const
181{
182 if (update)
183 title->setGeometry(geometry);
184 if (title->text().isEmpty()) {
185 return geometry;
186 } else {
187 // Round to full pixel via QPoint to avoid one pixel clipping on the edge in some cases
188 QPointF center((geometry.center() - title->boundingRect().center()).toPoint());
189 if (update)
190 title->setPos(ax: center.x(), ay: title->pos().y());
191 return geometry.adjusted(xp1: 0, yp1: title->boundingRect().height() + 1, xp2: 0, yp2: 0);
192 }
193}
194
195QRectF AbstractChartLayout::calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const
196{
197 if (!title->isVisible() || title->text().isEmpty()) {
198 return minimum;
199 } else {
200 QSizeF min = title->sizeHint(which: Qt::MinimumSize);
201 return minimum.adjusted(xp1: 0, yp1: 0, xp2: min.width(), yp2: min.height());
202 }
203}
204
205QSizeF AbstractChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
206{
207 Q_UNUSED(constraint);
208 if (which == Qt::MinimumSize) {
209 QList<ChartAxisElement *> axes = m_presenter->axisItems();
210 ChartTitle *title = m_presenter->titleElement();
211 QLegend *legend = m_presenter->legend();
212 QRectF minimumRect(0, 0, 0, 0);
213 minimumRect = calculateBackgroundMinimum(minimum: minimumRect);
214 minimumRect = calculateContentMinimum(minimum: minimumRect);
215 minimumRect = calculateTitleMinimum(minimum: minimumRect, title);
216 minimumRect = calculateLegendMinimum(geometry: minimumRect, legend);
217 minimumRect = calculateAxisMinimum(minimum: minimumRect, axes);
218 return minimumRect.size().toSize();
219 }
220 return QSize(-1, -1);
221}
222
223void AbstractChartLayout::setMargins(const QMargins &margins)
224{
225 if (m_margins != margins) {
226 m_margins = margins;
227 updateGeometry();
228 }
229}
230
231QMargins AbstractChartLayout::margins() const
232{
233 return m_margins;
234}
235
236QT_CHARTS_END_NAMESPACE
237

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