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 <QtGui/QPainter>
31#include <QtWidgets/QGraphicsSceneEvent>
32#include <QtWidgets/QGraphicsTextItem>
33#include <QtWidgets/QGraphicsEllipseItem>
34#include <QtWidgets/QGraphicsRectItem>
35#include <QtWidgets/QGraphicsLineItem>
36#include <QtGui/QTextDocument>
37#include <QtCore/QtMath>
38
39#include <QtCharts/QLegend>
40#include <QtCharts/QScatterSeries>
41#include <QtCharts/QLineSeries>
42#include <QtCharts/QSplineSeries>
43#include <private/qlegend_p.h>
44#include <QtCharts/QLegendMarker>
45#include <private/qlegendmarker_p.h>
46#include <private/legendmarkeritem_p.h>
47#include <private/chartpresenter_p.h>
48
49QT_CHARTS_BEGIN_NAMESPACE
50
51LegendMarkerItem::LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent) :
52 QGraphicsObject(parent),
53 m_marker(marker),
54 m_defaultMarkerRect(0.0, 0.0, 10.0, 10.0),
55 m_markerRect(0.0, 0.0, -1.0, -1.0),
56 m_boundingRect(0,0,0,0),
57 m_textItem(new QGraphicsTextItem(this)),
58 m_markerItem(nullptr),
59 m_margin(3),
60 m_space(4),
61 m_markerShape(QLegend::MarkerShapeDefault),
62 m_hovering(false),
63 m_itemType(TypeRect)
64{
65 m_textItem->document()->setDocumentMargin(ChartPresenter::textMargin());
66 setAcceptHoverEvents(true);
67}
68
69LegendMarkerItem::~LegendMarkerItem()
70{
71 if (m_hovering) {
72 emit m_marker->q_ptr->hovered(status: false);
73 }
74}
75
76void LegendMarkerItem::setPen(const QPen &pen)
77{
78 m_pen = pen;
79 setItemBrushAndPen();
80}
81
82QPen LegendMarkerItem::pen() const
83{
84 return m_pen;
85}
86
87void LegendMarkerItem::setBrush(const QBrush &brush)
88{
89 m_brush = brush;
90 setItemBrushAndPen();
91}
92
93QBrush LegendMarkerItem::brush() const
94{
95 return m_brush;
96}
97
98void LegendMarkerItem::setSeriesPen(const QPen &pen)
99{
100 m_seriesPen = pen;
101 setItemBrushAndPen();
102}
103
104void LegendMarkerItem::setSeriesBrush(const QBrush &brush)
105{
106 m_seriesBrush = brush;
107 setItemBrushAndPen();
108}
109
110void LegendMarkerItem::setFont(const QFont &font)
111{
112 QFontMetrics fn(font);
113 m_font = font;
114
115 m_defaultMarkerRect = QRectF(0, 0, fn.height() / 2, fn.height() / 2);
116 if (effectiveMarkerShape() != QLegend::MarkerShapeFromSeries)
117 updateMarkerShapeAndSize();
118 m_marker->invalidateLegend();
119}
120
121QFont LegendMarkerItem::font() const
122{
123 return m_font;
124}
125
126void LegendMarkerItem::setLabel(const QString label)
127{
128 m_label = label;
129 updateGeometry();
130}
131
132QString LegendMarkerItem::label() const
133{
134 return m_label;
135}
136
137void LegendMarkerItem::setLabelBrush(const QBrush &brush)
138{
139 m_textItem->setDefaultTextColor(brush.color());
140}
141
142QBrush LegendMarkerItem::labelBrush() const
143{
144 return QBrush(m_textItem->defaultTextColor());
145}
146
147void LegendMarkerItem::setGeometry(const QRectF &rect)
148{
149 if (!m_markerItem)
150 updateMarkerShapeAndSize();
151
152 const qreal width = rect.width();
153 const qreal markerWidth = effectiveMarkerWidth();
154 const qreal x = m_margin + markerWidth + m_space + m_margin;
155 QRectF truncatedRect;
156 const QString html = ChartPresenter::truncatedText(font: m_font, text: m_label, angle: qreal(0.0),
157 maxWidth: width - x, maxHeight: rect.height(), boundingRect&: truncatedRect);
158 m_textItem->setHtml(html);
159#if QT_CONFIG(tooltip)
160 if (m_marker->m_legend->showToolTips() && html != m_label)
161 m_textItem->setToolTip(m_label);
162 else
163 m_textItem->setToolTip(QString());
164#endif
165 m_textItem->setFont(m_font);
166 m_textItem->setTextWidth(truncatedRect.width());
167
168 qreal y = qMax(a: m_markerRect.height() + 2 * m_margin, b: truncatedRect.height() + 2 * m_margin);
169
170 const QRectF &textRect = m_textItem->boundingRect();
171
172 m_textItem->setPos(ax: x - m_margin, ay: y / 2 - textRect.height() / 2);
173 setItemRect();
174
175 // The textMargin adjustments to position are done to make default case rects less blurry with anti-aliasing
176 m_markerItem->setPos(ax: m_margin - ChartPresenter::textMargin()
177 + (markerWidth - m_markerRect.width()) / 2.0,
178 ay: y / 2.0 - m_markerRect.height() / 2.0 + ChartPresenter::textMargin());
179
180 prepareGeometryChange();
181 m_boundingRect = QRectF(0, 0, x + textRect.width() + m_margin, y);
182}
183
184QRectF LegendMarkerItem::boundingRect() const
185{
186 return m_boundingRect;
187}
188
189QRectF LegendMarkerItem::markerRect() const
190{
191 return m_markerRect;
192}
193
194void LegendMarkerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
195{
196 Q_UNUSED(option)
197 Q_UNUSED(widget)
198 Q_UNUSED(painter)
199}
200
201QSizeF LegendMarkerItem::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
202{
203 Q_UNUSED(constraint)
204
205 QSizeF sh;
206 const qreal markerWidth = effectiveMarkerWidth();
207
208 switch (which) {
209 case Qt::MinimumSize: {
210 const QRectF labelRect = ChartPresenter::textBoundingRect(font: m_font, QStringLiteral("..."));
211 sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth,
212 qMax(a: m_markerRect.height(), b: labelRect.height()) + (2.0 * m_margin));
213 break;
214 }
215 case Qt::PreferredSize: {
216 const QRectF labelRect = ChartPresenter::textBoundingRect(font: m_font, text: m_label);
217 sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth,
218 qMax(a: m_markerRect.height(), b: labelRect.height()) + (2.0 * m_margin));
219 break;
220 }
221 default:
222 break;
223 }
224
225 return sh;
226}
227
228void LegendMarkerItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
229{
230 Q_UNUSED(event)
231 m_hovering = true;
232 emit m_marker->q_ptr->hovered(status: true);
233}
234
235void LegendMarkerItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
236{
237 Q_UNUSED(event)
238 m_hovering = false;
239 emit m_marker->q_ptr->hovered(status: false);
240}
241
242QString LegendMarkerItem::displayedLabel() const
243{
244 return m_textItem->toHtml();
245}
246
247void LegendMarkerItem::setToolTip(const QString &tip)
248{
249#if QT_CONFIG(tooltip)
250 m_textItem->setToolTip(tip);
251#endif
252}
253
254QLegend::MarkerShape LegendMarkerItem::markerShape() const
255{
256 return m_markerShape;
257}
258
259void LegendMarkerItem::setMarkerShape(QLegend::MarkerShape shape)
260{
261 m_markerShape = shape;
262}
263
264void LegendMarkerItem::updateMarkerShapeAndSize()
265{
266 const QLegend::MarkerShape shape = effectiveMarkerShape();
267
268 ItemType itemType = TypeRect;
269 QRectF newRect = m_defaultMarkerRect;
270 if (shape == QLegend::MarkerShapeFromSeries) {
271 QScatterSeries *scatter = qobject_cast<QScatterSeries *>(object: m_marker->series());
272 if (scatter) {
273 newRect.setSize(QSizeF(scatter->markerSize(), scatter->markerSize()));
274 if (scatter->markerShape() == QScatterSeries::MarkerShapeCircle)
275 itemType = TypeCircle;
276 } else if (qobject_cast<QLineSeries *>(object: m_marker->series())
277 || qobject_cast<QSplineSeries *>(object: m_marker->series())) {
278 newRect.setHeight(m_seriesPen.width());
279 newRect.setWidth(qRound(d: m_defaultMarkerRect.width() * 1.5));
280 itemType = TypeLine;
281 }
282 } else if (shape == QLegend::MarkerShapeCircle) {
283 itemType = TypeCircle;
284 }
285
286 if (!m_markerItem || m_itemType != itemType) {
287 m_itemType = itemType;
288 QPointF oldPos;
289 if (m_markerItem) {
290 oldPos = m_markerItem->pos();
291 delete m_markerItem;
292 }
293 if (itemType == TypeRect)
294 m_markerItem = new QGraphicsRectItem(this);
295 else if (itemType == TypeCircle)
296 m_markerItem = new QGraphicsEllipseItem(this);
297 else
298 m_markerItem = new QGraphicsLineItem(this);
299 // Immediately update the position to the approximate correct position to avoid marker
300 // jumping around when changing markers
301 m_markerItem->setPos(oldPos);
302 }
303 setItemBrushAndPen();
304
305 if (newRect != m_markerRect) {
306 if (useMaxWidth() && m_marker->m_legend->d_ptr->maxMarkerWidth() < newRect.width())
307 m_marker->invalidateAllItems();
308 m_markerRect = newRect;
309 setItemRect();
310 emit markerRectChanged();
311 updateGeometry();
312 }
313}
314
315QLegend::MarkerShape LegendMarkerItem::effectiveMarkerShape() const
316{
317 QLegend::MarkerShape shape = m_markerShape;
318 if (shape == QLegend::MarkerShapeDefault)
319 shape = m_marker->m_legend->markerShape();
320 return shape;
321}
322
323qreal LegendMarkerItem::effectiveMarkerWidth() const
324{
325 return useMaxWidth() ? m_marker->m_legend->d_ptr->maxMarkerWidth()
326 : m_markerRect.width();
327}
328
329void LegendMarkerItem::setItemBrushAndPen()
330{
331 if (m_markerItem) {
332 QAbstractGraphicsShapeItem *shapeItem =
333 qgraphicsitem_cast<QGraphicsRectItem *>(item: m_markerItem);
334 if (!shapeItem)
335 shapeItem = qgraphicsitem_cast<QGraphicsEllipseItem *>(item: m_markerItem);
336 if (shapeItem) {
337 if (effectiveMarkerShape() == QLegend::MarkerShapeFromSeries) {
338 shapeItem->setPen(m_seriesPen);
339 shapeItem->setBrush(m_seriesBrush);
340 } else {
341 shapeItem->setPen(m_pen);
342 shapeItem->setBrush(m_brush);
343 }
344 } else {
345 // Must be line item, it has no brush.
346 QGraphicsLineItem *lineItem =
347 qgraphicsitem_cast<QGraphicsLineItem *>(item: m_markerItem);
348 if (lineItem)
349 lineItem->setPen(m_seriesPen);
350 }
351 }
352}
353
354void LegendMarkerItem::setItemRect()
355{
356 if (m_itemType == TypeRect) {
357 static_cast<QGraphicsRectItem *>(m_markerItem)->setRect(m_markerRect);
358 } else if (m_itemType == TypeCircle) {
359 static_cast<QGraphicsEllipseItem *>(m_markerItem)->setRect(m_markerRect);
360 } else {
361 qreal y = m_markerRect.height() / 2.0;
362 QLineF line(0.0, y, m_markerRect.width(), y);
363 static_cast<QGraphicsLineItem *>(m_markerItem)->setLine(line);
364 }
365}
366
367bool LegendMarkerItem::useMaxWidth() const
368{
369 return (m_marker->m_legend->alignment() == Qt::AlignLeft
370 || m_marker->m_legend->alignment() == Qt::AlignRight);
371}
372
373QT_CHARTS_END_NAMESPACE
374
375#include "moc_legendmarkeritem_p.cpp"
376

source code of qtcharts/src/charts/legend/legendmarkeritem.cpp