1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtQuick/private/qquickrectangle_p.h>
5#include <QtGraphs/qbarseries.h>
6#include <QtGraphs/qbarset.h>
7#include <private/barsrenderer_p.h>
8#include <private/axisrenderer_p.h>
9#include <private/qbarseries_p.h>
10#include <private/qgraphsview_p.h>
11
12QT_BEGIN_NAMESPACE
13
14static const char* TAG_BAR_COLOR = "barColor";
15static const char* TAG_BAR_BORDER_COLOR = "barBorderColor";
16static const char* TAG_BAR_BORDER_WIDTH = "barBorderWidth";
17static const char* TAG_BAR_SELECTED = "barSelected";
18static const char* TAG_BAR_VALUE = "barValue";
19static const char* TAG_BAR_LABEL = "barLabel";
20
21BarsRenderer::BarsRenderer(QGraphsView *graph)
22 : QQuickItem(graph)
23 , m_graph(graph)
24{
25 setFlag(flag: QQuickItem::ItemHasContents);
26 setClip(true);
27}
28
29BarsRenderer::~BarsRenderer() {}
30
31// Returns color in this order:
32// 1) QBarSet::color if that is defined (alpha > 0).
33// 2) QBarSeries::seriesColors at index if that is defined.
34// 3) QGraphsTheme::seriesColors at index.
35// 4) Black if seriesColors is empty.
36QColor BarsRenderer::getSetColor(QBarSeries *series, QBarSet *set, qsizetype barSeriesIndex)
37{
38 const auto &seriesColors = !series->seriesColors().isEmpty()
39 ? series->seriesColors() : m_graph->theme()->seriesColors();
40 if (seriesColors.isEmpty())
41 return QColorConstants::Black;
42 qsizetype index = m_colorIndex + barSeriesIndex;
43 index = index % seriesColors.size();
44 QColor color = set->color().alpha() != 0
45 ? set->color()
46 : seriesColors.at(i: index);
47 return color;
48}
49
50QColor BarsRenderer::getSetSelectedColor(QBarSeries *series, QBarSet *set)
51{
52 Q_UNUSED(series);
53 auto theme = m_graph->theme();
54 QColor color = set->selectedColor().alpha() != 0
55 ? set->selectedColor()
56 : theme->singleHighlightColor();
57 return color;
58}
59
60QColor BarsRenderer::getSetBorderColor(QBarSeries *series, QBarSet *set, qsizetype barSeriesIndex)
61{
62 const auto &borderColors = !series->borderColors().isEmpty()
63 ? series->borderColors() : m_graph->theme()->borderColors();
64 if (borderColors.isEmpty())
65 return QColorConstants::Black;
66 qsizetype index = m_colorIndex + barSeriesIndex;
67 index = index % borderColors.size();
68 QColor color = set->borderColor().alpha() != 0
69 ? set->borderColor()
70 : borderColors.at(i: index);
71 return color;
72}
73
74qreal BarsRenderer::getSetBorderWidth(QBarSeries *series, QBarSet *set)
75{
76 Q_UNUSED(series);
77 auto theme = m_graph->theme();
78 qreal borderWidth = set->borderWidth();
79 if (qFuzzyCompare(p1: borderWidth, p2: qreal(-1.0)))
80 borderWidth = theme->borderWidth();
81 return borderWidth;
82}
83
84QString BarsRenderer::generateLabelText(QBarSeries *series, qreal value)
85{
86 static const QString valueTag(QLatin1String("@value"));
87 QString valueString = QString::number(value, format: 'f', precision: series->labelsPrecision());
88 QString valueLabel;
89 if (series->labelsFormat().isEmpty()) {
90 valueLabel = valueString;
91 } else {
92 valueLabel = series->labelsFormat();
93 valueLabel.replace(before: valueTag, after: valueString);
94 }
95 return valueLabel;
96}
97
98void BarsRenderer::positionLabelItem(QBarSeries *series, QQuickText *textItem, const BarSeriesData &d)
99{
100 auto pos = series->labelsPosition();
101 const bool vertical = m_graph->orientation() == Qt::Orientation::Vertical;
102 const float w = textItem->contentWidth() + series->labelsMargin() * 2;
103 const float h = textItem->contentHeight() + series->labelsMargin() * 2;
104 textItem->setWidth(w);
105 textItem->setHeight(h);
106 textItem->setHAlign(QQuickText::HAlignment::AlignHCenter);
107 textItem->setVAlign(QQuickText::VAlignment::AlignVCenter);
108 if (pos == QBarSeries::LabelsPosition::Center) {
109 textItem->setX(d.rect.x() + d.rect.width() * 0.5 - w * 0.5);
110 textItem->setY(d.rect.y() + d.rect.height() * 0.5 - h * 0.5);
111 } else if (pos == QBarSeries::LabelsPosition::InsideEnd) {
112 if (vertical) {
113 textItem->setX(d.rect.x() + d.rect.width() * 0.5 - w * 0.5);
114 textItem->setY(d.rect.y());
115 } else {
116 textItem->setX(d.rect.x() + d.rect.width() - w);
117 textItem->setY(d.rect.y() + d.rect.height() * 0.5 - h * 0.5);
118 }
119 } else if (pos == QBarSeries::LabelsPosition::InsideBase) {
120 if (vertical) {
121 textItem->setX(d.rect.x() + d.rect.width() * 0.5 - w * 0.5);
122 textItem->setY(d.rect.y() + d.rect.height() - h);
123 } else {
124 textItem->setX(d.rect.x());
125 textItem->setY(d.rect.y() + d.rect.height() * 0.5 - h * 0.5);
126 }
127 } else {
128 // OutsideEnd
129 if (vertical) {
130 textItem->setX(d.rect.x() + d.rect.width() * 0.5 - w * 0.5);
131 textItem->setY(d.rect.y() - h);
132 } else {
133 textItem->setX(d.rect.x() + d.rect.width());
134 textItem->setY(d.rect.y() + d.rect.height() * 0.5 - h * 0.5);
135 }
136 }
137 textItem->update();
138}
139
140void BarsRenderer::updateComponents(QBarSeries *series)
141{
142 int barIndex = 0;
143 auto &seriesData = m_seriesData[series];
144 auto &barItems = m_barItems[series];
145 for (auto i = seriesData.cbegin(), end = seriesData.cend(); i != end; ++i) {
146 if (barItems.size() <= barIndex) {
147 QQuickItem *item = nullptr;
148 // Create more components as needed
149 if (series->barDelegate()) {
150 item = qobject_cast<QQuickItem *>(
151 o: series->barDelegate()->create(context: series->barDelegate()->creationContext()));
152 }
153 if (!item)
154 item = new QQuickRectangle();
155 item->setParent(this);
156 item->setParentItem(this);
157 barItems << item;
158 }
159 if (barItems.size() > barIndex) {
160 BarSeriesData d = *i;
161 if (series->barDelegate()) {
162 // Set custom bar components
163 auto &barItem = barItems[barIndex];
164 barItem->setX(d.rect.x());
165 barItem->setY(d.rect.y());
166 barItem->setWidth(d.rect.width());
167 barItem->setHeight(d.rect.height());
168 barItem->setVisible(series->isVisible());
169 // Check for specific dynamic properties
170 if (barItem->property(name: TAG_BAR_COLOR).isValid())
171 barItem->setProperty(name: TAG_BAR_COLOR, value: d.color);
172 if (barItem->property(name: TAG_BAR_BORDER_COLOR).isValid())
173 barItem->setProperty(name: TAG_BAR_BORDER_COLOR, value: d.borderColor);
174 if (barItem->property(name: TAG_BAR_BORDER_WIDTH).isValid())
175 barItem->setProperty(name: TAG_BAR_BORDER_WIDTH, value: d.borderWidth);
176 if (barItem->property(name: TAG_BAR_SELECTED).isValid())
177 barItem->setProperty(name: TAG_BAR_SELECTED, value: d.isSelected);
178 if (barItem->property(name: TAG_BAR_VALUE).isValid())
179 barItem->setProperty(name: TAG_BAR_VALUE, value: d.value);
180 if (barItem->property(name: TAG_BAR_LABEL).isValid())
181 barItem->setProperty(name: TAG_BAR_LABEL, value: d.label);
182 } else {
183 // Set default rectangle bars
184 auto barItem = qobject_cast<QQuickRectangle *>(object: barItems[barIndex]);
185 if (barItem) {
186 barItem->setX(d.rect.x());
187 barItem->setY(d.rect.y());
188 barItem->setWidth(d.rect.width());
189 barItem->setHeight(d.rect.height());
190 barItem->setVisible(series->isVisible());
191 barItem->setColor(d.color);
192 barItem->border()->setColor(d.borderColor);
193 barItem->border()->setWidth(d.borderWidth);
194 barItem->setRadius(4.0);
195 }
196 }
197 }
198 barIndex++;
199 }
200}
201
202void BarsRenderer::updateValueLabels(QBarSeries *series)
203{
204 if (!series->barDelegate() && series->isVisible() && series->labelsVisible()) {
205 // Update default value labels
206 int barIndex = 0;
207 auto &seriesData = m_seriesData[series];
208 auto &labelTextItems = m_labelTextItems[series];
209 for (auto i = seriesData.cbegin(), end = seriesData.cend(); i != end; ++i) {
210 if (labelTextItems.size() <= barIndex) {
211 // Create more label items as needed
212 auto labelItem = new QQuickText(this);
213 labelTextItems << labelItem;
214 }
215 if (labelTextItems.size() > barIndex) {
216 // Set label item values
217 auto &textItem = labelTextItems[barIndex];
218 const auto d = *i;
219 if (qFuzzyIsNull(f: d.value)) {
220 textItem->setVisible(false);
221 } else {
222 textItem->setVisible(series->labelsVisible());
223 QString valueLabel = generateLabelText(series, value: d.value);
224 textItem->setText(valueLabel);
225 positionLabelItem(series, textItem, d);
226 QColor labelColor = d.labelColor;
227 if (labelColor.alpha() == 0) {
228 // TODO: Use graphs theme labels color.
229 labelColor = QColor(255, 255, 255);
230 }
231 textItem->setColor(labelColor);
232 textItem->setRotation(series->labelsAngle());
233 }
234 }
235 barIndex++;
236 }
237 } else {
238 // Hide all possibly existing label items
239 auto &labelTextItems = m_labelTextItems[series];
240 for (auto textItem : labelTextItems)
241 textItem->setVisible(false);
242 }
243}
244
245void calculateCategoryTotalValues(QBarSeries *series, QList<float> &totalValues, qsizetype valuesPerSet)
246{
247 totalValues.fill(t: 0, newSize: valuesPerSet);
248 for (auto s : series->barSets()) {
249 QVariantList v = s->values();
250 int setIndex = 0;
251 for (auto variantValue : std::as_const(t&: v)) {
252 if (setIndex < totalValues.size())
253 totalValues[setIndex] += variantValue.toReal();
254 setIndex++;
255 }
256 }
257}
258
259void BarsRenderer::updateVerticalBars(QBarSeries *series, qsizetype setCount, qsizetype valuesPerSet)
260{
261 bool stacked = series->barsType() == QBarSeries::BarsType::Stacked
262 || series->barsType() == QBarSeries::BarsType::StackedPercent;
263 bool percent = series->barsType() == QBarSeries::BarsType::StackedPercent;
264 // Bars area width & height
265 float w = width();
266 float h = height();
267 // Max width of a bar if no separation between sets.
268 float maxBarWidth = w / (setCount * valuesPerSet) - m_barMargin;
269 if (stacked)
270 maxBarWidth = w / valuesPerSet;
271 // Actual bar width.
272 float barWidth = maxBarWidth * series->barWidth();
273 // Helper to keep barsets centered when bar width is less than max width.
274 float barCentering = (maxBarWidth - barWidth) * setCount * 0.5;
275 if (stacked)
276 barCentering = (maxBarWidth - barWidth) * 0.5;
277
278 auto &seriesData = m_seriesData[series];
279 auto &rectNodesInputRects = m_rectNodesInputRects[series];
280 // Clear the selection rects
281 // These will be filled only if series is selectable
282 rectNodesInputRects.clear();
283 seriesData.clear();
284
285 float seriesPos = 0;
286 float posXInSet = 0;
287 QList<float> posYListInSet;
288 if (stacked)
289 posYListInSet.fill(t: 0, newSize: valuesPerSet);
290 QList<float> totalValuesListInSet;
291 if (percent)
292 calculateCategoryTotalValues(series, totalValues&: totalValuesListInSet, valuesPerSet);
293
294 int barIndexInSet = 0;
295 int barSeriesIndex = 0;
296 QList<QLegendData> legendDataList;
297 for (auto s : series->barSets()) {
298 QVariantList v = s->values();
299 qsizetype valuesCount = v.size();
300 if (valuesCount == 0)
301 continue;
302 seriesPos = 0;
303 barIndexInSet = 0;
304 BarSelectionRect *barSelectionRect = nullptr;
305 if (series->isSelectable() || series->isHoverable()) {
306 rectNodesInputRects << BarSelectionRect();
307 barSelectionRect = &rectNodesInputRects.last();
308 barSelectionRect->barSet = s;
309 barSelectionRect->series = series;
310 }
311
312 QColor color = getSetColor(series, set: s, barSeriesIndex);
313 QColor borderColor = getSetBorderColor(series, set: s, barSeriesIndex);
314 qreal borderWidth = getSetBorderWidth(series, set: s);
315
316 // Update legendData
317 legendDataList.push_back(t: {
318 .color: color,
319 .borderColor: borderColor,
320 .label: s->label()
321 });
322 // Apply series opacity
323 color.setAlpha(color.alpha() * series->opacity());
324 borderColor.setAlpha(borderColor.alpha() * series->opacity());
325 const auto selectedBars = s->selectedBars();
326 for (auto variantValue : std::as_const(t&: v)) {
327 const float realValue = variantValue.toReal();
328 float value = (realValue - m_graph->m_axisRenderer->m_axisVerticalMinValue) * series->valuesMultiplier();
329 if (percent) {
330 if (auto totalValue = totalValuesListInSet.at(i: barIndexInSet))
331 value *= (100.0 / totalValue);
332 }
333 const bool isSelected = selectedBars.contains(t: barIndexInSet);
334 double delta = m_graph->m_axisRenderer->m_axisVerticalMaxValue - m_graph->m_axisRenderer->m_axisVerticalMinValue;
335 double maxValues = delta > 0 ? 1.0 / delta : 100.0;
336 float barLength = h * value * maxValues;
337 float barY = h - barLength;
338 float barX = seriesPos + posXInSet + barCentering;
339 if (stacked) {
340 barY = h - barLength - posYListInSet[barIndexInSet];
341 barX = seriesPos + barCentering;
342 }
343 QRectF barRect(barX, barY, barWidth, barLength);
344 if (barSelectionRect)
345 barSelectionRect->rects << barRect;
346
347 // Collect the series data
348 BarSeriesData d;
349 d.rect = barRect;
350 d.color = isSelected ? getSetSelectedColor(series, set: s) : color;
351 d.borderColor = borderColor;
352 d.borderWidth = borderWidth;
353 d.isSelected = isSelected;
354 d.label = s->label();
355 d.labelColor = s->labelColor();
356 d.value = realValue;
357 seriesData << d;
358
359 if (stacked)
360 posYListInSet[barIndexInSet] += barLength;
361 barIndexInSet++;
362 seriesPos = ((float)barIndexInSet / valuesPerSet) * w;
363 }
364 posXInSet += barWidth + m_barMargin;
365 barSeriesIndex++;
366 }
367 series->d_func()->setLegendData(legendDataList);
368}
369
370void BarsRenderer::updateHorizontalBars(QBarSeries *series, qsizetype setCount, qsizetype valuesPerSet)
371{
372 bool stacked = series->barsType() == QBarSeries::BarsType::Stacked
373 || series->barsType() == QBarSeries::BarsType::StackedPercent;
374 bool percent = series->barsType() == QBarSeries::BarsType::StackedPercent;
375 // Bars area width & height
376 float w = width();
377 float h = height();
378 // Max width of a bar if no separation between sets.
379 float maxBarWidth = h / (setCount * valuesPerSet) - m_barMargin;
380 if (stacked)
381 maxBarWidth = h / valuesPerSet;
382 // Actual bar width.
383 float barWidth = maxBarWidth * series->barWidth();
384 // Helper to keep barsets centered when bar width is less than max width.
385 float barCentering = (maxBarWidth - barWidth) * setCount * 0.5;
386 if (stacked)
387 barCentering = (maxBarWidth - barWidth) * 0.5;
388
389 auto &seriesData = m_seriesData[series];
390 auto &rectNodesInputRects = m_rectNodesInputRects[series];
391 // Clear the selection rects
392 // These will be filled only if series is selectable
393 rectNodesInputRects.clear();
394 seriesData.clear();
395
396 float seriesPos = 0;
397 float posYInSet = 0;
398 QList<float> posXListInSet;
399 if (stacked)
400 posXListInSet.fill(t: 0, newSize: valuesPerSet);
401 QList<float> totalValuesListInSet;
402 if (percent)
403 calculateCategoryTotalValues(series, totalValues&: totalValuesListInSet, valuesPerSet);
404 int barIndexInSet = 0;
405 int barSerieIndex = 0;
406 QList<QLegendData> legendDataList;
407 for (auto s : series->barSets()) {
408 QVariantList v = s->values();
409 qsizetype valuesCount = v.size();
410 if (valuesCount == 0)
411 continue;
412 seriesPos = 0;
413 barIndexInSet = 0;
414 BarSelectionRect *barSelectionRect = nullptr;
415 if (series->isSelectable() || series->isHoverable()) {
416 rectNodesInputRects << BarSelectionRect();
417 barSelectionRect = &rectNodesInputRects.last();
418 barSelectionRect->barSet = s;
419 barSelectionRect->series = series;
420 }
421
422 QColor color = getSetColor(series, set: s, barSeriesIndex: barSerieIndex);
423 QColor borderColor = getSetBorderColor(series, set: s, barSeriesIndex: barSerieIndex);
424 qreal borderWidth = getSetBorderWidth(series, set: s);
425 // Update legendData
426 legendDataList.push_back(t: {
427 .color: color,
428 .borderColor: borderColor,
429 .label: s->label()
430 });
431 // Apply series opacity
432 color.setAlpha(color.alpha() * series->opacity());
433 borderColor.setAlpha(borderColor.alpha() * series->opacity());
434 const auto selectedBars = s->selectedBars();
435 for (auto variantValue : std::as_const(t&: v)) {
436 const float realValue = variantValue.toReal();
437 float value = (realValue - m_graph->m_axisRenderer->m_axisHorizontalMinValue) * series->valuesMultiplier();
438 if (percent) {
439 if (auto totalValue = totalValuesListInSet.at(i: barIndexInSet))
440 value *= (100.0 / totalValue);
441 }
442 const bool isSelected = selectedBars.contains(t: barIndexInSet);
443 double delta = m_graph->m_axisRenderer->m_axisHorizontalMaxValue - m_graph->m_axisRenderer->m_axisHorizontalMinValue;
444 double maxValues = delta > 0 ? 1.0 / delta : 100.0;
445 float barLength = w * value * maxValues;
446 float barY = seriesPos + posYInSet + barCentering;
447 float barX = 0;
448 if (stacked) {
449 barY = seriesPos + barCentering;
450 barX = posXListInSet[barIndexInSet];
451 }
452 QRectF barRect(barX, barY, barLength, barWidth);
453 if (barSelectionRect)
454 barSelectionRect->rects << barRect;
455
456 // Collect the series data
457 BarSeriesData d;
458 d.rect = barRect;
459 d.color = isSelected ? getSetSelectedColor(series, set: s) : color;
460 d.borderColor = borderColor;
461 d.borderWidth = borderWidth;
462 d.isSelected = isSelected;
463 d.label = s->label();
464 d.labelColor = s->labelColor();
465 d.value = realValue;
466 seriesData << d;
467
468 if (stacked)
469 posXListInSet[barIndexInSet] += barLength;
470 barIndexInSet++;
471 seriesPos = ((float)barIndexInSet / valuesPerSet) * h;
472 }
473 posYInSet += barWidth + m_barMargin;
474 barSerieIndex++;
475 }
476 series->d_func()->setLegendData(legendDataList);
477}
478
479void BarsRenderer::handlePolish(QBarSeries *series)
480{
481 auto theme = m_graph->theme();
482 if (!theme)
483 return;
484
485 if (!m_graph->m_axisRenderer)
486 return;
487
488 qsizetype setCount = series->barSets().size();
489 auto &seriesData = m_seriesData[series];
490 auto &barItems = m_barItems[series];
491 auto &rectNodesInputRects = m_rectNodesInputRects[series];
492 if (setCount == 0) {
493 for (int i = 0; i < barItems.size(); i++)
494 barItems[i]->deleteLater();
495 barItems.clear();
496
497 series->d_func()->clearLegendData();
498 rectNodesInputRects.clear();
499 seriesData.clear();
500 return;
501 }
502
503 if (m_colorIndex < 0)
504 m_colorIndex = m_graph->graphSeriesCount();
505 m_graph->setGraphSeriesCount(m_colorIndex + setCount);
506
507 if (series->barDelegateDirty() && !barItems.isEmpty()) {
508 // Bars delegate has changed, so remove the old items.
509 for (int i = 0; i < barItems.size(); i++)
510 barItems[i]->deleteLater();
511 barItems.clear();
512 series->setBarDelegateDirty(false);
513 }
514
515 // Get bars values
516 qsizetype valuesPerSet = series->barSets().first()->values().size();
517 if (m_graph->orientation() == Qt::Orientation::Vertical)
518 updateVerticalBars(series, setCount, valuesPerSet);
519 else
520 updateHorizontalBars(series, setCount, valuesPerSet);
521 updateComponents(series);
522 updateValueLabels(series);
523
524 // Remove additional components
525 for (qsizetype i = barItems.size() - 1; i >= seriesData.size(); --i)
526 barItems[i]->deleteLater();
527 const auto range = barItems.size() - seriesData.size();
528 if (range > 0)
529 barItems.remove(i: seriesData.size(), n: range);
530}
531
532void BarsRenderer::updateSeries(QBarSeries *series)
533{
534 Q_UNUSED(series);
535}
536
537void BarsRenderer::afterUpdate(QList<QAbstractSeries *> &cleanupSeries)
538{
539 Q_UNUSED(cleanupSeries);
540}
541
542void BarsRenderer::afterPolish(QList<QAbstractSeries *> &cleanupSeries)
543{
544 for (auto &cleanupSerie : cleanupSeries) {
545 auto series = qobject_cast<QBarSeries *>(object: cleanupSerie);
546 if (series && m_barItems.contains(key: series)) {
547 // Remove custom bar items
548 auto &barItems = m_barItems[series];
549 for (int i = 0; i < barItems.size(); i++)
550 barItems[i]->deleteLater();
551 barItems.clear();
552 m_barItems.remove(key: series);
553 }
554 if (series && m_labelTextItems.contains(key: series)) {
555 // Remove bar label items
556 auto &labelTextItems = m_labelTextItems[series];
557 for (int i = 0; i < labelTextItems.size(); i++)
558 labelTextItems[i]->deleteLater();
559 labelTextItems.clear();
560 m_labelTextItems.remove(key: series);
561 }
562 }
563}
564
565bool BarsRenderer::handleMousePress(QMouseEvent *event)
566{
567 bool handled = false;
568 for (auto &rectNodesInputRects : m_rectNodesInputRects) {
569 for (auto &barSelection : rectNodesInputRects) {
570 if (!barSelection.series->isSelectable())
571 continue;
572 qsizetype indexInSet = 0;
573 for (auto &rect : barSelection.rects) {
574 if (rect.contains(p: event->pos())) {
575 // TODO: Currently just toggling selection
576 QList<qsizetype> indexList = {indexInSet};
577 barSelection.barSet->toggleSelection(indexes: indexList);
578 handled = true;
579 }
580 indexInSet++;
581 }
582 }
583 }
584 return handled;
585}
586
587bool BarsRenderer::handleHoverMove(QHoverEvent *event)
588{
589 bool handled = false;
590 const QPointF &position = event->position();
591
592 bool hovering = false;
593 for (auto &rectNodesInputRects : m_rectNodesInputRects) {
594 for (auto &barSelection : rectNodesInputRects) {
595 int indexInSet = 0;
596
597 for (auto &rect : barSelection.rects) {
598 if (rect.contains(p: event->position().toPoint())) {
599 const QString &name = barSelection.series->name();
600 const QPointF point(indexInSet, barSelection.barSet->at(index: indexInSet));
601
602 if (!m_currentHoverSeries) {
603 m_currentHoverSeries = barSelection.series;
604 emit barSelection.series->hoverEnter(seriesName: name, position, value: point);
605 }
606
607 emit barSelection.series->hover(seriesName: name, position, value: point);
608 hovering = true;
609 handled = true;
610 }
611 indexInSet++;
612 }
613 }
614 }
615
616 if (!hovering && m_currentHoverSeries) {
617 emit m_currentHoverSeries->hoverExit(seriesName: m_currentHoverSeries->name(), position);
618 m_currentHoverSeries = nullptr;
619 handled = true;
620 }
621 return handled;
622}
623
624QT_END_NAMESPACE
625

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtgraphs/src/graphs2d/qsgrenderer/barsrenderer.cpp