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 "callout.h" |
31 | #include <QtGui/QPainter> |
32 | #include <QtGui/QFontMetrics> |
33 | #include <QtWidgets/QGraphicsSceneMouseEvent> |
34 | #include <QtGui/QMouseEvent> |
35 | #include <QtCharts/QChart> |
36 | |
37 | Callout::Callout(QChart *chart): |
38 | QGraphicsItem(chart), |
39 | m_chart(chart) |
40 | { |
41 | } |
42 | |
43 | QRectF Callout::boundingRect() const |
44 | { |
45 | QPointF anchor = mapFromParent(point: m_chart->mapToPosition(value: m_anchor)); |
46 | QRectF rect; |
47 | rect.setLeft(qMin(a: m_rect.left(), b: anchor.x())); |
48 | rect.setRight(qMax(a: m_rect.right(), b: anchor.x())); |
49 | rect.setTop(qMin(a: m_rect.top(), b: anchor.y())); |
50 | rect.setBottom(qMax(a: m_rect.bottom(), b: anchor.y())); |
51 | return rect; |
52 | } |
53 | |
54 | void Callout::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
55 | { |
56 | Q_UNUSED(option) |
57 | Q_UNUSED(widget) |
58 | QPainterPath path; |
59 | path.addRoundedRect(rect: m_rect, xRadius: 5, yRadius: 5); |
60 | |
61 | QPointF anchor = mapFromParent(point: m_chart->mapToPosition(value: m_anchor)); |
62 | if (!m_rect.contains(p: anchor)) { |
63 | QPointF point1, point2; |
64 | |
65 | // establish the position of the anchor point in relation to m_rect |
66 | bool above = anchor.y() <= m_rect.top(); |
67 | bool aboveCenter = anchor.y() > m_rect.top() && anchor.y() <= m_rect.center().y(); |
68 | bool belowCenter = anchor.y() > m_rect.center().y() && anchor.y() <= m_rect.bottom(); |
69 | bool below = anchor.y() > m_rect.bottom(); |
70 | |
71 | bool onLeft = anchor.x() <= m_rect.left(); |
72 | bool leftOfCenter = anchor.x() > m_rect.left() && anchor.x() <= m_rect.center().x(); |
73 | bool rightOfCenter = anchor.x() > m_rect.center().x() && anchor.x() <= m_rect.right(); |
74 | bool onRight = anchor.x() > m_rect.right(); |
75 | |
76 | // get the nearest m_rect corner. |
77 | qreal x = (onRight + rightOfCenter) * m_rect.width(); |
78 | qreal y = (below + belowCenter) * m_rect.height(); |
79 | bool cornerCase = (above && onLeft) || (above && onRight) || (below && onLeft) || (below && onRight); |
80 | bool vertical = qAbs(t: anchor.x() - x) > qAbs(t: anchor.y() - y); |
81 | |
82 | qreal x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * !vertical * (onLeft * 10 - onRight * 20); |
83 | qreal y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20);; |
84 | point1.setX(x1); |
85 | point1.setY(y1); |
86 | |
87 | qreal x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * !vertical * (onLeft * 20 - onRight * 10);; |
88 | qreal y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10);; |
89 | point2.setX(x2); |
90 | point2.setY(y2); |
91 | |
92 | path.moveTo(p: point1); |
93 | path.lineTo(p: anchor); |
94 | path.lineTo(p: point2); |
95 | path = path.simplified(); |
96 | } |
97 | painter->setBrush(QColor(255, 255, 255)); |
98 | painter->drawPath(path); |
99 | painter->drawText(r: m_textRect, text: m_text); |
100 | } |
101 | |
102 | void Callout::mousePressEvent(QGraphicsSceneMouseEvent *event) |
103 | { |
104 | event->setAccepted(true); |
105 | } |
106 | |
107 | void Callout::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
108 | { |
109 | if (event->buttons() & Qt::LeftButton){ |
110 | setPos(mapToParent(point: event->pos() - event->buttonDownPos(button: Qt::LeftButton))); |
111 | event->setAccepted(true); |
112 | } else { |
113 | event->setAccepted(false); |
114 | } |
115 | } |
116 | |
117 | void Callout::setText(const QString &text) |
118 | { |
119 | m_text = text; |
120 | QFontMetrics metrics(m_font); |
121 | m_textRect = metrics.boundingRect(r: QRect(0, 0, 150, 150), flags: Qt::AlignLeft, text: m_text); |
122 | m_textRect.translate(dx: 5, dy: 5); |
123 | prepareGeometryChange(); |
124 | m_rect = m_textRect.adjusted(xp1: -5, yp1: -5, xp2: 5, yp2: 5); |
125 | } |
126 | |
127 | void Callout::setAnchor(QPointF point) |
128 | { |
129 | m_anchor = point; |
130 | } |
131 | |
132 | void Callout::updateGeometry() |
133 | { |
134 | prepareGeometryChange(); |
135 | setPos(m_chart->mapToPosition(value: m_anchor) + QPoint(10, -50)); |
136 | } |
137 | |