| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Charts module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtGui/QPainter> |
| #include <QtWidgets/QGraphicsSceneEvent> |
| #include <QtWidgets/QGraphicsTextItem> |
| #include <QtWidgets/QGraphicsEllipseItem> |
| #include <QtWidgets/QGraphicsRectItem> |
| #include <QtWidgets/QGraphicsLineItem> |
| #include <QtGui/QTextDocument> |
| #include <QtCore/QtMath> |
| |
| #include <QtCharts/QLegend> |
| #include <QtCharts/QScatterSeries> |
| #include <QtCharts/QLineSeries> |
| #include <QtCharts/QSplineSeries> |
| #include <private/qlegend_p.h> |
| #include <QtCharts/QLegendMarker> |
| #include <private/qlegendmarker_p.h> |
| #include <private/legendmarkeritem_p.h> |
| #include <private/chartpresenter_p.h> |
| |
| QT_CHARTS_BEGIN_NAMESPACE |
| |
| LegendMarkerItem::LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent) : |
| QGraphicsObject(parent), |
| m_marker(marker), |
| m_defaultMarkerRect(0.0, 0.0, 10.0, 10.0), |
| m_markerRect(0.0, 0.0, -1.0, -1.0), |
| m_boundingRect(0,0,0,0), |
| m_textItem(new QGraphicsTextItem(this)), |
| m_markerItem(nullptr), |
| m_margin(3), |
| m_space(4), |
| m_markerShape(QLegend::MarkerShapeDefault), |
| m_hovering(false), |
| m_itemType(TypeRect) |
| { |
| m_textItem->document()->setDocumentMargin(ChartPresenter::textMargin()); |
| setAcceptHoverEvents(true); |
| } |
| |
| LegendMarkerItem::~LegendMarkerItem() |
| { |
| if (m_hovering) { |
| emit m_marker->q_ptr->hovered(false); |
| } |
| } |
| |
| void LegendMarkerItem::setPen(const QPen &pen) |
| { |
| m_pen = pen; |
| setItemBrushAndPen(); |
| } |
| |
| QPen LegendMarkerItem::pen() const |
| { |
| return m_pen; |
| } |
| |
| void LegendMarkerItem::setBrush(const QBrush &brush) |
| { |
| m_brush = brush; |
| setItemBrushAndPen(); |
| } |
| |
| QBrush LegendMarkerItem::brush() const |
| { |
| return m_brush; |
| } |
| |
| void LegendMarkerItem::setSeriesPen(const QPen &pen) |
| { |
| m_seriesPen = pen; |
| setItemBrushAndPen(); |
| } |
| |
| void LegendMarkerItem::setSeriesBrush(const QBrush &brush) |
| { |
| m_seriesBrush = brush; |
| setItemBrushAndPen(); |
| } |
| |
| void LegendMarkerItem::setFont(const QFont &font) |
| { |
| QFontMetrics fn(font); |
| m_font = font; |
| |
| m_defaultMarkerRect = QRectF(0, 0, fn.height() / 2, fn.height() / 2); |
| if (effectiveMarkerShape() != QLegend::MarkerShapeFromSeries) |
| updateMarkerShapeAndSize(); |
| m_marker->invalidateLegend(); |
| } |
| |
| QFont LegendMarkerItem::font() const |
| { |
| return m_font; |
| } |
| |
| void LegendMarkerItem::setLabel(const QString label) |
| { |
| m_label = label; |
| updateGeometry(); |
| } |
| |
| QString LegendMarkerItem::label() const |
| { |
| return m_label; |
| } |
| |
| void LegendMarkerItem::setLabelBrush(const QBrush &brush) |
| { |
| m_textItem->setDefaultTextColor(brush.color()); |
| } |
| |
| QBrush LegendMarkerItem::labelBrush() const |
| { |
| return QBrush(m_textItem->defaultTextColor()); |
| } |
| |
| void LegendMarkerItem::setGeometry(const QRectF &rect) |
| { |
| if (!m_markerItem) |
| updateMarkerShapeAndSize(); |
| |
| const qreal width = rect.width(); |
| const qreal markerWidth = effectiveMarkerWidth(); |
| const qreal x = m_margin + markerWidth + m_space + m_margin; |
| QRectF truncatedRect; |
| const QString html = ChartPresenter::truncatedText(m_font, m_label, qreal(0.0), |
| width - x, rect.height(), truncatedRect); |
| m_textItem->setHtml(html); |
| #if QT_CONFIG(tooltip) |
| if (m_marker->m_legend->showToolTips() && html != m_label) |
| m_textItem->setToolTip(m_label); |
| else |
| m_textItem->setToolTip(QString()); |
| #endif |
| m_textItem->setFont(m_font); |
| m_textItem->setTextWidth(truncatedRect.width()); |
| |
| qreal y = qMax(m_markerRect.height() + 2 * m_margin, truncatedRect.height() + 2 * m_margin); |
| |
| const QRectF &textRect = m_textItem->boundingRect(); |
| |
| m_textItem->setPos(x - m_margin, y / 2 - textRect.height() / 2); |
| setItemRect(); |
| |
| // The textMargin adjustments to position are done to make default case rects less blurry with anti-aliasing |
| m_markerItem->setPos(m_margin - ChartPresenter::textMargin() |
| + (markerWidth - m_markerRect.width()) / 2.0, |
| y / 2.0 - m_markerRect.height() / 2.0 + ChartPresenter::textMargin()); |
| |
| prepareGeometryChange(); |
| m_boundingRect = QRectF(0, 0, x + textRect.width() + m_margin, y); |
| } |
| |
| QRectF LegendMarkerItem::boundingRect() const |
| { |
| return m_boundingRect; |
| } |
| |
| QRectF LegendMarkerItem::markerRect() const |
| { |
| return m_markerRect; |
| } |
| |
| void LegendMarkerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
| { |
| Q_UNUSED(option) |
| Q_UNUSED(widget) |
| Q_UNUSED(painter) |
| } |
| |
| QSizeF LegendMarkerItem::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const |
| { |
| Q_UNUSED(constraint) |
| |
| QSizeF sh; |
| const qreal markerWidth = effectiveMarkerWidth(); |
| |
| switch (which) { |
| case Qt::MinimumSize: { |
| const QRectF labelRect = ChartPresenter::textBoundingRect(m_font, QStringLiteral("...")); |
| sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth, |
| qMax(m_markerRect.height(), labelRect.height()) + (2.0 * m_margin)); |
| break; |
| } |
| case Qt::PreferredSize: { |
| const QRectF labelRect = ChartPresenter::textBoundingRect(m_font, m_label); |
| sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth, |
| qMax(m_markerRect.height(), labelRect.height()) + (2.0 * m_margin)); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| return sh; |
| } |
| |
| void LegendMarkerItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) |
| { |
| Q_UNUSED(event) |
| m_hovering = true; |
| emit m_marker->q_ptr->hovered(true); |
| } |
| |
| void LegendMarkerItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) |
| { |
| Q_UNUSED(event) |
| m_hovering = false; |
| emit m_marker->q_ptr->hovered(false); |
| } |
| |
| QString LegendMarkerItem::displayedLabel() const |
| { |
| return m_textItem->toHtml(); |
| } |
| |
| void LegendMarkerItem::setToolTip(const QString &tip) |
| { |
| #if QT_CONFIG(tooltip) |
| m_textItem->setToolTip(tip); |
| #endif |
| } |
| |
| QLegend::MarkerShape LegendMarkerItem::markerShape() const |
| { |
| return m_markerShape; |
| } |
| |
| void LegendMarkerItem::setMarkerShape(QLegend::MarkerShape shape) |
| { |
| m_markerShape = shape; |
| } |
| |
| void LegendMarkerItem::updateMarkerShapeAndSize() |
| { |
| const QLegend::MarkerShape shape = effectiveMarkerShape(); |
| |
| ItemType itemType = TypeRect; |
| QRectF newRect = m_defaultMarkerRect; |
| if (shape == QLegend::MarkerShapeFromSeries) { |
| QScatterSeries *scatter = qobject_cast<QScatterSeries *>(m_marker->series()); |
| if (scatter) { |
| newRect.setSize(QSizeF(scatter->markerSize(), scatter->markerSize())); |
| if (scatter->markerShape() == QScatterSeries::MarkerShapeCircle) |
| itemType = TypeCircle; |
| } else if (qobject_cast<QLineSeries *>(m_marker->series()) |
| || qobject_cast<QSplineSeries *>(m_marker->series())) { |
| newRect.setHeight(m_seriesPen.width()); |
| newRect.setWidth(qRound(m_defaultMarkerRect.width() * 1.5)); |
| itemType = TypeLine; |
| } |
| } else if (shape == QLegend::MarkerShapeCircle) { |
| itemType = TypeCircle; |
| } |
| |
| if (!m_markerItem || m_itemType != itemType) { |
| m_itemType = itemType; |
| QPointF oldPos; |
| if (m_markerItem) { |
| oldPos = m_markerItem->pos(); |
| delete m_markerItem; |
| } |
| if (itemType == TypeRect) |
| m_markerItem = new QGraphicsRectItem(this); |
| else if (itemType == TypeCircle) |
| m_markerItem = new QGraphicsEllipseItem(this); |
| else |
| m_markerItem = new QGraphicsLineItem(this); |
| // Immediately update the position to the approximate correct position to avoid marker |
| // jumping around when changing markers |
| m_markerItem->setPos(oldPos); |
| } |
| setItemBrushAndPen(); |
| |
| if (newRect != m_markerRect) { |
| if (useMaxWidth() && m_marker->m_legend->d_ptr->maxMarkerWidth() < newRect.width()) |
| m_marker->invalidateAllItems(); |
| m_markerRect = newRect; |
| setItemRect(); |
| emit markerRectChanged(); |
| updateGeometry(); |
| } |
| } |
| |
| QLegend::MarkerShape LegendMarkerItem::effectiveMarkerShape() const |
| { |
| QLegend::MarkerShape shape = m_markerShape; |
| if (shape == QLegend::MarkerShapeDefault) |
| shape = m_marker->m_legend->markerShape(); |
| return shape; |
| } |
| |
| qreal LegendMarkerItem::effectiveMarkerWidth() const |
| { |
| return useMaxWidth() ? m_marker->m_legend->d_ptr->maxMarkerWidth() |
| : m_markerRect.width(); |
| } |
| |
| void LegendMarkerItem::setItemBrushAndPen() |
| { |
| if (m_markerItem) { |
| QAbstractGraphicsShapeItem *shapeItem = |
| qgraphicsitem_cast<QGraphicsRectItem *>(m_markerItem); |
| if (!shapeItem) |
| shapeItem = qgraphicsitem_cast<QGraphicsEllipseItem *>(m_markerItem); |
| if (shapeItem) { |
| if (effectiveMarkerShape() == QLegend::MarkerShapeFromSeries) { |
| shapeItem->setPen(m_seriesPen); |
| shapeItem->setBrush(m_seriesBrush); |
| } else { |
| shapeItem->setPen(m_pen); |
| shapeItem->setBrush(m_brush); |
| } |
| } else { |
| // Must be line item, it has no brush. |
| QGraphicsLineItem *lineItem = |
| qgraphicsitem_cast<QGraphicsLineItem *>(m_markerItem); |
| if (lineItem) |
| lineItem->setPen(m_seriesPen); |
| } |
| } |
| } |
| |
| void LegendMarkerItem::setItemRect() |
| { |
| if (m_itemType == TypeRect) { |
| static_cast<QGraphicsRectItem *>(m_markerItem)->setRect(m_markerRect); |
| } else if (m_itemType == TypeCircle) { |
| static_cast<QGraphicsEllipseItem *>(m_markerItem)->setRect(m_markerRect); |
| } else { |
| qreal y = m_markerRect.height() / 2.0; |
| QLineF line(0.0, y, m_markerRect.width(), y); |
| static_cast<QGraphicsLineItem *>(m_markerItem)->setLine(line); |
| } |
| } |
| |
| bool LegendMarkerItem::useMaxWidth() const |
| { |
| return (m_marker->m_legend->alignment() == Qt::AlignLeft |
| || m_marker->m_legend->alignment() == Qt::AlignRight); |
| } |
| |
| QT_CHARTS_END_NAMESPACE |
| |
| #include "moc_legendmarkeritem_p.cpp" |