1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses |
5 | |
6 | #include <QtCharts/QLegend> |
7 | #include <private/qlegend_p.h> |
8 | #include <QtCharts/QAbstractSeries> |
9 | #include <private/qabstractseries_p.h> |
10 | #include <private/qchart_p.h> |
11 | #include <private/legendlayout_p.h> |
12 | #include <private/chartpresenter_p.h> |
13 | #include <private/abstractchartlayout_p.h> |
14 | #include <QtCharts/QLegendMarker> |
15 | #include <private/qlegendmarker_p.h> |
16 | #include <private/legendmarkeritem_p.h> |
17 | #include <private/legendmoveresizehandler_p.h> |
18 | #include <private/chartdataset_p.h> |
19 | #include <QtGui/QPainter> |
20 | #include <QtGui/QPen> |
21 | #include <QtWidgets/QGraphicsItemGroup> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | /*! |
26 | \class QLegend |
27 | \inmodule QtCharts |
28 | \inherits QGraphicsWidget |
29 | \brief The QLegend class displays the legend of a chart. |
30 | |
31 | A legend is a graphical object that displays the legend of a chart. The legend state is updated |
32 | by QChart when series change. By default, the legend is attached to the chart, but it can be |
33 | detached to make it independent of chart layout. Legend objects cannot be created or deleted, |
34 | but they can be referenced via the QChart class. |
35 | |
36 | \image examples_percentbarchart_legend.png |
37 | |
38 | \sa QChart |
39 | */ |
40 | /*! |
41 | \qmltype Legend |
42 | \nativetype QLegend |
43 | \inqmlmodule QtCharts |
44 | |
45 | \brief Displays the legend of a chart. |
46 | |
47 | A legend is a graphical object that displays the legend of a chart. The legend state is updated |
48 | by the ChartView type when series change. The \l Legend type properties can be attached to the |
49 | ChartView type. For example: |
50 | \code |
51 | ChartView { |
52 | legend.visible: true |
53 | legend.alignment: Qt.AlignBottom |
54 | // Add a few series... |
55 | } |
56 | \endcode |
57 | |
58 | \image examples_percentbarchart_legend.png |
59 | |
60 | \note There is no QML API available for modifying legend markers. Markers |
61 | can be modified by creating a custom legend. For more information, see |
62 | \l {Using Legend Markers}. |
63 | */ |
64 | |
65 | /*! |
66 | \property QLegend::alignment |
67 | \brief How the legend is aligned with the chart. |
68 | |
69 | Can be Qt::AlignTop, Qt::AlignBottom, Qt::AlignLeft, Qt::AlignRight. If you set more than one |
70 | flag, the result is undefined. |
71 | */ |
72 | /*! |
73 | \qmlproperty alignment Legend::alignment |
74 | Defines how the legend is aligned with the chart. Can be \l{Qt::AlignLeft}{Qt.AlignLeft}, |
75 | \l{Qt::AlignRight}{Qt.AlignRight}, \l{Qt::AlignBottom}{Qt.AlignBottom}, or |
76 | \l{Qt::AlignTop}{Qt.AlignTop}. If you set more than one flag, the result is undefined. |
77 | */ |
78 | |
79 | /*! |
80 | \property QLegend::backgroundVisible |
81 | \brief Whether the legend background is visible. |
82 | */ |
83 | /*! |
84 | \qmlproperty bool Legend::backgroundVisible |
85 | Whether the legend background is visible. |
86 | */ |
87 | |
88 | /*! |
89 | \qmlproperty bool Legend::visible |
90 | Whether the legend is visible. |
91 | |
92 | By default, this property is \c true. |
93 | \sa QGraphicsObject::visible |
94 | */ |
95 | |
96 | /*! |
97 | \property QLegend::color |
98 | \brief The background (brush) color of the legend. |
99 | |
100 | If you change the color of the legend, the style of the legend brush is set to |
101 | Qt::SolidPattern. |
102 | */ |
103 | /*! |
104 | \qmlproperty color Legend::color |
105 | The background (brush) color of the legend. |
106 | */ |
107 | |
108 | /*! |
109 | \property QLegend::borderColor |
110 | \brief The line color of the legend. |
111 | */ |
112 | /*! |
113 | \qmlproperty color Legend::borderColor |
114 | The line color of the legend. |
115 | */ |
116 | |
117 | /*! |
118 | \property QLegend::font |
119 | \brief The font of the markers used by the legend. |
120 | */ |
121 | /*! |
122 | \qmlproperty Font Legend::font |
123 | The font of the markers used by the legend. |
124 | */ |
125 | |
126 | /*! |
127 | \property QLegend::labelColor |
128 | \brief The color of the brush used to draw labels. |
129 | */ |
130 | /*! |
131 | \qmlproperty color Legend::labelColor |
132 | The color of the brush used to draw labels. |
133 | */ |
134 | |
135 | /*! |
136 | \property QLegend::reverseMarkers |
137 | \brief Whether reverse order is used for the markers in the legend. |
138 | |
139 | This property is \c false by default. |
140 | */ |
141 | /*! |
142 | \qmlproperty bool Legend::reverseMarkers |
143 | Whether reverse order is used for the markers in the legend. This property |
144 | is \c false by default. |
145 | */ |
146 | |
147 | /*! |
148 | \property QLegend::showToolTips |
149 | \brief Whether tooltips are shown when the text is truncated. |
150 | |
151 | This property is \c false by default. |
152 | */ |
153 | |
154 | /*! |
155 | \enum QLegend::MarkerShape |
156 | |
157 | This enum describes the shape used when rendering legend marker items. |
158 | |
159 | \value MarkerShapeDefault Default shape determined by QLegend is used for the marker. |
160 | This value is supported only for individual QLegendMarker items. |
161 | \value MarkerShapeRectangle Rectangular markers are used. |
162 | Marker size is determined by font size. |
163 | \value MarkerShapeCircle Circular markers are used. |
164 | Marker size is determined by font size. |
165 | \value MarkerShapeRotatedRectangle Rotated rectangle shaped markers are used. |
166 | Marker size is determined by font size. |
167 | \value MarkerShapeTriangle Triangular markers are used. |
168 | Marker size is determined by font size. |
169 | \value MarkerShapeStar Star shaped markers are used. |
170 | Marker size is determined by font size. |
171 | \value MarkerShapePentagon Pentagon shaped markers are used. |
172 | Marker size is determined by font size. |
173 | \value MarkerShapeFromSeries The marker shape is determined by the series. |
174 | In case of a scatter series, the legend marker looks like a scatter dot and is the same |
175 | size as the dot. In case of a line or spline series, the legend marker looks like a |
176 | small segment of the line. For other series types, rectangular markers are shown. |
177 | If a \c lightMarker is specified for a series, the \c lightMarker will be shown and |
178 | its size will be determined by the series marker size. |
179 | |
180 | \sa markerShape |
181 | */ |
182 | |
183 | /*! |
184 | \qmlproperty enumeration Legend::markerShape |
185 | \since 5.9 |
186 | |
187 | The default shape of the legend markers. |
188 | The default value is \c{MarkerShapeRectangle}. |
189 | |
190 | \value Legend.MarkerShapeRectangle Legend markers are rectangular |
191 | \value Legend.MarkerShapeCircle Legend markers are circular |
192 | \value MarkerShapeRotatedRectangle Legend markers are rotated rectangle shaped. |
193 | \value MarkerShapeTriangle Legend markers are triangular. |
194 | \value MarkerShapeStar Legend markers are star shaped. |
195 | \value MarkerShapePentagon Legend markers are pentagon shaped. |
196 | \value Legend.MarkerShapeFromSeries Legend marker shape is determined by the series |
197 | |
198 | \sa QLegend::MarkerShape |
199 | */ |
200 | |
201 | /*! |
202 | \property QLegend::markerShape |
203 | \since 5.9 |
204 | |
205 | The default shape of the legend markers. |
206 | The default value is \c{MarkerShapeRectangle}. |
207 | */ |
208 | |
209 | /*! |
210 | \qmlproperty bool Legend::showToolTips |
211 | Whether tooltips are shown when the text is truncated. This property is \c false by default. |
212 | This property currently has no effect as there is no support for tooltips in QML. |
213 | */ |
214 | |
215 | /*! |
216 | \fn void QLegend::attachedToChartChanged(bool attached) |
217 | This signal is emitted when the legend is \a attached to or detached from the chart. |
218 | \since 6.2 |
219 | */ |
220 | |
221 | /*! |
222 | \fn void QLegend::backgroundVisibleChanged(bool) |
223 | This signal is emitted when the visibility of the legend background changes to \a visible. |
224 | */ |
225 | |
226 | /*! |
227 | \fn void QLegend::colorChanged(QColor) |
228 | This signal is emitted when the color of the legend background changes to \a color. |
229 | */ |
230 | |
231 | /*! |
232 | \fn void QLegend::borderColorChanged(QColor) |
233 | This signal is emitted when the border color of the legend background changes to \a color. |
234 | */ |
235 | |
236 | /*! |
237 | \fn void QLegend::fontChanged(QFont) |
238 | This signal is emitted when the font of the markers of the legend changes to \a font. |
239 | */ |
240 | |
241 | /*! |
242 | \fn void QLegend::labelColorChanged(QColor color) |
243 | This signal is emitted when the color of the brush used to draw the legend |
244 | labels changes to \a color. |
245 | */ |
246 | |
247 | /*! |
248 | \fn void QLegend::reverseMarkersChanged(bool) |
249 | This signal is emitted when the use of reverse order for the markers in the |
250 | legend is changed to \a reverseMarkers. |
251 | */ |
252 | |
253 | /*! |
254 | \fn void QLegend::showToolTipsChanged(bool showToolTips) |
255 | This signal is emitted when the visibility of tooltips is changed to \a showToolTips. |
256 | */ |
257 | |
258 | QLegend::QLegend(QChart *chart): QGraphicsWidget(chart), |
259 | d_ptr(new QLegendPrivate(chart->d_ptr->m_presenter, chart, this)) |
260 | { |
261 | setZValue(ChartPresenter::LegendZValue); |
262 | setFlags(QGraphicsItem::ItemClipsChildrenToShape); |
263 | QObject::connect(sender: chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), receiver: d_ptr.data(), SLOT(handleSeriesAdded(QAbstractSeries*))); |
264 | QObject::connect(sender: chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), receiver: d_ptr.data(), SLOT(handleSeriesRemoved(QAbstractSeries*))); |
265 | setLayout(d_ptr->m_layout); |
266 | } |
267 | |
268 | /*! |
269 | Destroys the legend object. The legend is always owned by a QChart, so an application |
270 | should never call this function. |
271 | */ |
272 | QLegend::~QLegend() |
273 | { |
274 | } |
275 | |
276 | /*! |
277 | \internal |
278 | */ |
279 | void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
280 | { |
281 | Q_UNUSED(option); |
282 | Q_UNUSED(widget); |
283 | |
284 | if (!d_ptr->m_backgroundVisible) |
285 | return; |
286 | |
287 | painter->setOpacity(opacity()); |
288 | painter->setPen(d_ptr->m_pen); |
289 | painter->setBrush(d_ptr->m_brush); |
290 | painter->drawRoundedRect(rect: rect(), xRadius: d_ptr->roundness(size: rect().width()), |
291 | yRadius: d_ptr->roundness(size: rect().height()), mode: Qt::RelativeSize); |
292 | } |
293 | |
294 | |
295 | /*! |
296 | Sets the \a brush that is used to draw the background of the legend. |
297 | */ |
298 | void QLegend::setBrush(const QBrush &brush) |
299 | { |
300 | if (d_ptr->m_brush != brush) { |
301 | d_ptr->m_brush = brush; |
302 | update(); |
303 | emit colorChanged(color: brush.color()); |
304 | } |
305 | } |
306 | |
307 | /*! |
308 | Returns the brush used by the legend. |
309 | */ |
310 | QBrush QLegend::brush() const |
311 | { |
312 | if (d_ptr->m_brush == QChartPrivate::defaultBrush()) |
313 | return QBrush(); |
314 | else |
315 | return d_ptr->m_brush; |
316 | } |
317 | |
318 | void QLegend::setColor(QColor color) |
319 | { |
320 | QBrush b = brush(); |
321 | if (b.style() != Qt::SolidPattern || b.color() != color) { |
322 | b.setStyle(Qt::SolidPattern); |
323 | b.setColor(color); |
324 | setBrush(b); |
325 | } |
326 | } |
327 | |
328 | QColor QLegend::color() |
329 | { |
330 | return d_ptr->m_brush.color(); |
331 | } |
332 | |
333 | /*! |
334 | Sets the \a pen that is used to draw the legend borders. |
335 | */ |
336 | void QLegend::setPen(const QPen &pen) |
337 | { |
338 | if (d_ptr->m_pen != pen) { |
339 | d_ptr->m_pen = pen; |
340 | update(); |
341 | emit borderColorChanged(color: pen.color()); |
342 | } |
343 | } |
344 | |
345 | /*! |
346 | Returns the pen used by the legend. |
347 | */ |
348 | |
349 | QPen QLegend::pen() const |
350 | { |
351 | if (d_ptr->m_pen == QChartPrivate::defaultPen()) |
352 | return QPen(); |
353 | else |
354 | return d_ptr->m_pen; |
355 | } |
356 | |
357 | void QLegend::setFont(const QFont &font) |
358 | { |
359 | if (d_ptr->m_font != font) { |
360 | // Hide items to avoid flickering |
361 | d_ptr->items()->setVisible(false); |
362 | d_ptr->m_font = font; |
363 | const auto markers = d_ptr->markers(); |
364 | for (QLegendMarker *marker : markers) { |
365 | marker->setFont(d_ptr->m_font); |
366 | } |
367 | layout()->invalidate(); |
368 | emit fontChanged(font); |
369 | } |
370 | } |
371 | |
372 | QFont QLegend::font() const |
373 | { |
374 | return d_ptr->m_font; |
375 | } |
376 | |
377 | void QLegend::setBorderColor(QColor color) |
378 | { |
379 | QPen p = pen(); |
380 | if (p.color() != color) { |
381 | p.setColor(color); |
382 | setPen(p); |
383 | } |
384 | } |
385 | |
386 | QColor QLegend::borderColor() |
387 | { |
388 | return d_ptr->m_pen.color(); |
389 | } |
390 | |
391 | /*! |
392 | Sets the brush used to draw the legend labels to \a brush. |
393 | */ |
394 | void QLegend::setLabelBrush(const QBrush &brush) |
395 | { |
396 | if (d_ptr->m_labelBrush != brush) { |
397 | d_ptr->m_labelBrush = brush; |
398 | const auto markers = d_ptr->markers(); |
399 | for (QLegendMarker *marker : markers) { |
400 | marker->setLabelBrush(d_ptr->m_labelBrush); |
401 | // Note: The pen of the marker rectangle could be exposed in the public QLegend API |
402 | // instead of mapping it from label brush color |
403 | marker->setPen(brush.color()); |
404 | } |
405 | emit labelColorChanged(color: brush.color()); |
406 | } |
407 | } |
408 | |
409 | /*! |
410 | Returns the brush used to draw labels. |
411 | */ |
412 | QBrush QLegend::labelBrush() const |
413 | { |
414 | if (d_ptr->m_labelBrush == QChartPrivate::defaultBrush()) |
415 | return QBrush(); |
416 | else |
417 | return d_ptr->m_labelBrush; |
418 | } |
419 | |
420 | void QLegend::setLabelColor(QColor color) |
421 | { |
422 | QBrush b = labelBrush(); |
423 | if (b.style() != Qt::SolidPattern || b.color() != color) { |
424 | b.setStyle(Qt::SolidPattern); |
425 | b.setColor(color); |
426 | setLabelBrush(b); |
427 | } |
428 | } |
429 | |
430 | QColor QLegend::labelColor() const |
431 | { |
432 | return d_ptr->m_labelBrush.color(); |
433 | } |
434 | |
435 | |
436 | void QLegend::setAlignment(Qt::Alignment alignment) |
437 | { |
438 | if (d_ptr->m_alignment != alignment) { |
439 | d_ptr->m_alignment = alignment; |
440 | layout()->invalidate(); |
441 | } |
442 | } |
443 | |
444 | Qt::Alignment QLegend::alignment() const |
445 | { |
446 | return d_ptr->m_alignment; |
447 | } |
448 | |
449 | /*! |
450 | Detaches the legend from the chart. The chart will no longer adjust the layout of the legend. |
451 | */ |
452 | void QLegend::detachFromChart() |
453 | { |
454 | bool changed = d_ptr->m_attachedToChart == true; |
455 | d_ptr->m_attachedToChart = false; |
456 | d_ptr->m_chart->layout()->invalidate(); |
457 | setParent(0); |
458 | |
459 | if (changed) |
460 | emit attachedToChartChanged(attachedToChart: false); |
461 | } |
462 | |
463 | /*! |
464 | Attaches the legend to a chart. The chart may adjust the layout of the legend. |
465 | */ |
466 | void QLegend::attachToChart() |
467 | { |
468 | bool changed = d_ptr->m_attachedToChart == false; |
469 | d_ptr->m_attachedToChart = true; |
470 | d_ptr->m_chart->layout()->invalidate(); |
471 | setParent(d_ptr->m_chart); |
472 | |
473 | if (changed) |
474 | emit attachedToChartChanged(attachedToChart: true); |
475 | } |
476 | |
477 | /*! |
478 | Returns \c true, if the legend is attached to a chart. |
479 | */ |
480 | bool QLegend::isAttachedToChart() |
481 | { |
482 | return d_ptr->m_attachedToChart; |
483 | } |
484 | |
485 | /*! |
486 | Sets the visibility of the legend background to \a visible. |
487 | */ |
488 | void QLegend::setBackgroundVisible(bool visible) |
489 | { |
490 | if (d_ptr->m_backgroundVisible != visible) { |
491 | d_ptr->m_backgroundVisible = visible; |
492 | update(); |
493 | emit backgroundVisibleChanged(visible); |
494 | } |
495 | } |
496 | |
497 | /*! |
498 | Returns the visibility of the legend background. |
499 | */ |
500 | bool QLegend::isBackgroundVisible() const |
501 | { |
502 | return d_ptr->m_backgroundVisible; |
503 | } |
504 | |
505 | /*! |
506 | Returns the list of markers in the legend. The list can be filtered by specifying |
507 | the \a series for which the markers are returned. |
508 | */ |
509 | QList<QLegendMarker*> QLegend::markers(QAbstractSeries *series) const |
510 | { |
511 | return d_ptr->markers(series); |
512 | } |
513 | |
514 | bool QLegend::reverseMarkers() |
515 | { |
516 | return d_ptr->m_reverseMarkers; |
517 | } |
518 | |
519 | void QLegend::setReverseMarkers(bool reverseMarkers) |
520 | { |
521 | if (d_ptr->m_reverseMarkers != reverseMarkers) { |
522 | d_ptr->m_reverseMarkers = reverseMarkers; |
523 | layout()->invalidate(); |
524 | emit reverseMarkersChanged(reverseMarkers); |
525 | } |
526 | } |
527 | |
528 | /*! |
529 | Returns whether the tooltips are shown for the legend labels |
530 | when they are elided. |
531 | */ |
532 | |
533 | bool QLegend::showToolTips() const |
534 | { |
535 | return d_ptr->m_showToolTips; |
536 | } |
537 | |
538 | /*! |
539 | When \a show is \c true, the legend labels will show a tooltip when |
540 | the mouse hovers over them if the label itself is shown elided. |
541 | This is \c false by default. |
542 | */ |
543 | |
544 | void QLegend::setShowToolTips(bool show) |
545 | { |
546 | if (d_ptr->m_showToolTips != show) { |
547 | d_ptr->m_showToolTips = show; |
548 | d_ptr->updateToolTips(); |
549 | emit showToolTipsChanged(showToolTips: show); |
550 | } |
551 | } |
552 | |
553 | /*! |
554 | Returns whether the legend can be dragged or resized using a mouse when it is detached. |
555 | |
556 | \sa QLegend::setInteractive() |
557 | \since 6.2 |
558 | */ |
559 | |
560 | bool QLegend::isInteractive() const |
561 | { |
562 | return d_ptr->m_interactive; |
563 | } |
564 | |
565 | /*! |
566 | When \a interactive is \c true and the legend is detached, the legend is able to be moved and |
567 | resized with a mouse in a similar way to a window. |
568 | |
569 | The legend will automatically attach to an edge of the chart by dragging it off of that edge. |
570 | Double clicking an attached legend will detach it. |
571 | This is \c false by default. |
572 | |
573 | \sa QLegend::isInteractive() |
574 | \since 6.2 |
575 | */ |
576 | |
577 | void QLegend::setInteractive(bool interactive) |
578 | { |
579 | if (d_ptr->m_interactive != interactive) { |
580 | d_ptr->m_interactive = interactive; |
581 | update(); |
582 | emit interactiveChanged(interactive); |
583 | } |
584 | } |
585 | |
586 | QLegend::MarkerShape QLegend::markerShape() const |
587 | { |
588 | return d_ptr->m_markerShape; |
589 | } |
590 | |
591 | void QLegend::setMarkerShape(QLegend::MarkerShape shape) |
592 | { |
593 | QLegend::MarkerShape newShape = shape; |
594 | if (newShape == MarkerShapeDefault) |
595 | newShape = MarkerShapeRectangle; |
596 | if (d_ptr->m_markerShape != newShape) { |
597 | d_ptr->m_markerShape = newShape; |
598 | layout()->invalidate(); |
599 | emit markerShapeChanged(shape: newShape); |
600 | } |
601 | } |
602 | |
603 | /*! |
604 | \internal |
605 | \a event, see QGraphicsWidget for details. |
606 | */ |
607 | void QLegend::hideEvent(QHideEvent *event) |
608 | { |
609 | if (isAttachedToChart()) |
610 | d_ptr->m_presenter->layout()->invalidate(); |
611 | QGraphicsWidget::hideEvent(event); |
612 | } |
613 | /*! |
614 | \internal |
615 | \a event, see QGraphicsWidget for details. |
616 | */ |
617 | void QLegend::showEvent(QShowEvent *event) |
618 | { |
619 | if (isAttachedToChart()) |
620 | layout()->invalidate(); |
621 | QGraphicsWidget::showEvent(event); |
622 | //layout activation will show the items |
623 | } |
624 | |
625 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
626 | |
627 | QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend *q) |
628 | : q_ptr(q), |
629 | m_presenter(presenter), |
630 | m_layout(new LegendLayout(q)), |
631 | m_resizer(new LegendMoveResizeHandler(q)), |
632 | m_chart(chart), |
633 | m_items(new QGraphicsItemGroup(q)), |
634 | m_alignment(Qt::AlignTop), |
635 | m_brush(QChartPrivate::defaultBrush()), |
636 | m_pen(QChartPrivate::defaultPen()), |
637 | m_labelBrush(QChartPrivate::defaultBrush()), |
638 | m_diameter(5), |
639 | m_attachedToChart(true), |
640 | m_backgroundVisible(false), |
641 | m_reverseMarkers(false), |
642 | m_showToolTips(false), |
643 | m_interactive(false), |
644 | m_markerShape(QLegend::MarkerShapeRectangle) |
645 | { |
646 | m_items->setHandlesChildEvents(false); |
647 | } |
648 | |
649 | QLegendPrivate::~QLegendPrivate() |
650 | { |
651 | delete m_resizer; |
652 | } |
653 | |
654 | void QLegendPrivate::setOffset(const QPointF &offset) |
655 | { |
656 | m_layout->setOffset(x: offset.x(), y: offset.y()); |
657 | } |
658 | |
659 | QPointF QLegendPrivate::offset() const |
660 | { |
661 | return m_layout->offset(); |
662 | } |
663 | |
664 | int QLegendPrivate::roundness(qreal size) |
665 | { |
666 | return 100 * m_diameter / int(size); |
667 | } |
668 | |
669 | QList<QLegendMarker*> QLegendPrivate::markers(QAbstractSeries *series) |
670 | { |
671 | // Return all markers |
672 | if (!series) { |
673 | return m_markers; |
674 | } |
675 | |
676 | // Create filtered list |
677 | QList<QLegendMarker *> markers; |
678 | foreach (QLegendMarker *marker, m_markers) { |
679 | if (marker->series() == series) { |
680 | markers.append(t: marker); |
681 | } |
682 | } |
683 | return markers; |
684 | } |
685 | |
686 | qreal QLegendPrivate::maxMarkerWidth() const |
687 | { |
688 | qreal maxWidth = 0.0; |
689 | for (int i = 0; i < m_markers.size(); i++) { |
690 | LegendMarkerItem *item = m_markers.at(i)->d_ptr->item(); |
691 | if (item) |
692 | maxWidth = qMax(a: item->markerRect().width(), b: maxWidth); |
693 | } |
694 | return maxWidth; |
695 | } |
696 | |
697 | void QLegendPrivate::handleSeriesAdded(QAbstractSeries *series) |
698 | { |
699 | if (m_series.contains(t: series)) { |
700 | return; |
701 | } |
702 | |
703 | QList<QLegendMarker*> newMarkers = series->d_ptr->createLegendMarkers(legend: q_ptr); |
704 | decorateMarkers(markers: newMarkers); |
705 | addMarkers(markers: newMarkers); |
706 | |
707 | QObject::connect(sender: series->d_ptr.data(), SIGNAL(countChanged()), receiver: this, SLOT(handleCountChanged())); |
708 | QObject::connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged())); |
709 | |
710 | m_series.append(t: series); |
711 | m_items->setVisible(false); |
712 | m_layout->invalidate(); |
713 | } |
714 | |
715 | void QLegendPrivate::handleSeriesRemoved(QAbstractSeries *series) |
716 | { |
717 | if (m_series.contains(t: series)) { |
718 | m_series.removeOne(t: series); |
719 | } |
720 | |
721 | // Find out, which markers to remove |
722 | QList<QLegendMarker *> removed; |
723 | foreach (QLegendMarker *m, m_markers) { |
724 | if (m->series() == series) { |
725 | removed << m; |
726 | } |
727 | } |
728 | removeMarkers(markers: removed); |
729 | |
730 | QObject::disconnect(sender: series->d_ptr.data(), SIGNAL(countChanged()), receiver: this, SLOT(handleCountChanged())); |
731 | QObject::disconnect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged())); |
732 | |
733 | m_layout->invalidate(); |
734 | } |
735 | |
736 | void QLegendPrivate::handleSeriesVisibleChanged() |
737 | { |
738 | QAbstractSeries *series = qobject_cast<QAbstractSeries *> (object: sender()); |
739 | Q_ASSERT(series); |
740 | |
741 | foreach (QLegendMarker *marker, m_markers) { |
742 | if (marker->series() == series) { |
743 | marker->setVisible(series->isVisible()); |
744 | } |
745 | } |
746 | |
747 | if (m_chart->isVisible()) |
748 | m_layout->invalidate(); |
749 | } |
750 | |
751 | QObject *QLegendPrivate::relatedObject(const QLegendMarker *l) |
752 | { |
753 | return l->d_ptr->relatedObject(); |
754 | } |
755 | |
756 | // Find equivalent QLegendMarker by checking for relatedObject() |
757 | static int indexOfEquivalent(const QLegendMarker *needle, |
758 | const QList<QLegendMarker *> &hayStack) |
759 | { |
760 | const QObject *needleObject = QLegendPrivate::relatedObject(l: needle); |
761 | for (int i = 0, size = hayStack.size(); i < size; ++i) { |
762 | if (QLegendPrivate::relatedObject(l: hayStack.at(i)) == needleObject) |
763 | return i; |
764 | } |
765 | return -1; |
766 | } |
767 | |
768 | // Find QLegendMarker for series |
769 | static int indexOfSeries(const QAbstractSeries *series, |
770 | const QList<QLegendMarker *> &hayStack) |
771 | { |
772 | for (int i = 0, size = hayStack.size(); i < size; ++i) { |
773 | if (hayStack.at(i)->series() == series) |
774 | return i; |
775 | } |
776 | return -1; |
777 | } |
778 | |
779 | void QLegendPrivate::handleCountChanged() |
780 | { |
781 | // Here we handle the changes in marker count. |
782 | // Can happen for example when pieslice(s) have been added to or removed from pieseries. |
783 | |
784 | QAbstractSeriesPrivate *seriesP = qobject_cast<QAbstractSeriesPrivate *>(object: sender()); |
785 | QAbstractSeries *series = seriesP->q_ptr; |
786 | QList<QLegendMarker *> createdMarkers = seriesP->createLegendMarkers(legend: q_ptr); |
787 | QList<bool> isNew(createdMarkers.size(), true); |
788 | |
789 | const int pos = indexOfSeries(series, hayStack: m_markers); |
790 | // Remove markers of the series from m_markers and check against the newly |
791 | // created ones. |
792 | if (pos != -1) { |
793 | while (pos < m_markers.size() && m_markers.at(i: pos)->series() == series) { |
794 | QLegendMarker *oldMarker = m_markers.takeAt(i: pos); |
795 | const int newIndex = indexOfEquivalent(needle: oldMarker, hayStack: createdMarkers); |
796 | if (newIndex == -1) { |
797 | removeMarkerHelper(marker: oldMarker); // no longer exists |
798 | } else { |
799 | // Replace newly created marker by its equivalent |
800 | delete createdMarkers[newIndex]; |
801 | createdMarkers[newIndex] = oldMarker; |
802 | isNew[newIndex] = false; |
803 | } |
804 | } |
805 | } |
806 | |
807 | for (int i = 0, size = createdMarkers.size(); i < size; ++i) { |
808 | if (isNew.at(i)) { |
809 | insertMarkerHelper(marker: createdMarkers.at(i)); |
810 | decorateMarker(marker: createdMarkers.at(i)); |
811 | } |
812 | } |
813 | |
814 | // Re-insert createdMarkers into m_markers in correct order. |
815 | if (pos == -1 || pos == m_markers.size()) { |
816 | m_markers.append(l: createdMarkers); |
817 | } else { |
818 | for (int c = createdMarkers.size() - 1; c >= 0; --c) |
819 | m_markers.insert(i: pos, t: createdMarkers.at(i: c)); |
820 | } |
821 | |
822 | q_ptr->layout()->invalidate(); |
823 | } |
824 | |
825 | // Helper function for marker insertion except m_markers handling |
826 | void QLegendPrivate::insertMarkerHelper(QLegendMarker *marker) |
827 | { |
828 | LegendMarkerItem *item = marker->d_ptr->item(); |
829 | m_items->addToGroup(item); |
830 | m_markerHash.insert(key: item, value: marker); |
831 | } |
832 | |
833 | void QLegendPrivate::addMarkers(const QList<QLegendMarker *> &markers) |
834 | { |
835 | for (auto *marker : markers) { |
836 | insertMarkerHelper(marker); |
837 | m_markers << marker; |
838 | } |
839 | } |
840 | |
841 | // Helper function for marker removal except m_markers handling |
842 | void QLegendPrivate::removeMarkerHelper(QLegendMarker *marker) |
843 | { |
844 | LegendMarkerItem *item = marker->d_ptr->item(); |
845 | item->setVisible(false); |
846 | m_items->removeFromGroup(item); |
847 | m_markerHash.remove(key: item); |
848 | delete marker; |
849 | } |
850 | |
851 | void QLegendPrivate::removeMarkers(const QList<QLegendMarker *> &markers) |
852 | { |
853 | for (auto *marker : markers) { |
854 | m_markers.removeOne(t: marker); |
855 | removeMarkerHelper(marker); |
856 | } |
857 | } |
858 | |
859 | void QLegendPrivate::decorateMarker(QLegendMarker *marker) |
860 | { |
861 | marker->setFont(m_font); |
862 | marker->setLabelBrush(m_labelBrush); |
863 | } |
864 | |
865 | void QLegendPrivate::decorateMarkers(const QList<QLegendMarker *> &markers) |
866 | { |
867 | for (auto *marker : markers) |
868 | decorateMarker(marker); |
869 | } |
870 | |
871 | void QLegendPrivate::updateToolTips() |
872 | { |
873 | foreach (QLegendMarker *m, m_markers) { |
874 | if (m->d_ptr->m_item->displayedLabel() != m->label()) |
875 | m->d_ptr->m_item->setToolTip(m->label()); |
876 | else |
877 | m->d_ptr->m_item->setToolTip(QString()); |
878 | } |
879 | } |
880 | |
881 | QT_END_NAMESPACE |
882 | |
883 | #include "moc_qlegend.cpp" |
884 | #include "moc_qlegend_p.cpp" |
885 |
Definitions
- QLegend
- ~QLegend
- paint
- setBrush
- brush
- setColor
- color
- setPen
- pen
- setFont
- font
- setBorderColor
- borderColor
- setLabelBrush
- labelBrush
- setLabelColor
- labelColor
- setAlignment
- alignment
- detachFromChart
- attachToChart
- isAttachedToChart
- setBackgroundVisible
- isBackgroundVisible
- markers
- reverseMarkers
- setReverseMarkers
- showToolTips
- setShowToolTips
- isInteractive
- setInteractive
- markerShape
- setMarkerShape
- hideEvent
- showEvent
- QLegendPrivate
- ~QLegendPrivate
- setOffset
- offset
- roundness
- markers
- maxMarkerWidth
- handleSeriesAdded
- handleSeriesRemoved
- handleSeriesVisibleChanged
- relatedObject
- indexOfEquivalent
- indexOfSeries
- handleCountChanged
- insertMarkerHelper
- addMarkers
- removeMarkerHelper
- removeMarkers
- decorateMarker
- decorateMarkers
Start learning QML with our Intro Training
Find out more