##// END OF EJS Templates
Fix crash when using linuxfb and 2D renderer...
Fix crash when using linuxfb and 2D renderer No longer should call any OpenGL functions on QML charts applications if we don't have a valid OpenGL context. Task-number: QTBUG-51387 Change-Id: Ic393f69921e300f19dc42c83fc07677ce7869273 Reviewed-by: Mika Salmela <mika.salmela@theqtcompany.com> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@theqtcompany.com>

File last commit:

r2845:ae12522d475c
r2868:f4ba2fb98402
Show More
piesliceitem.cpp
337 lines | 12.2 KiB | text/x-c | CppLexer
/******************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Charts module.
**
** $QT_BEGIN_LICENSE:COMM$
**
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** $QT_END_LICENSE$
**
******************************************************************************/
#include <private/piesliceitem_p.h>
#include <private/piechartitem_p.h>
#include <QtCharts/QPieSeries>
#include <QtCharts/QPieSlice>
#include <private/chartpresenter_p.h>
#include <QtGui/QPainter>
#include <QtCore/QtMath>
#include <QtWidgets/QGraphicsSceneEvent>
#include <QtCore/QTime>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>
QT_CHARTS_BEGIN_NAMESPACE
QPointF offset(qreal angle, qreal length)
{
qreal dx = qSin(angle * (M_PI / 180)) * length;
qreal dy = qCos(angle * (M_PI / 180)) * length;
return QPointF(dx, -dy);
}
PieSliceItem::PieSliceItem(QGraphicsItem *parent)
: QGraphicsObject(parent),
m_hovered(false),
m_mousePressed(false)
{
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::MouseButtonMask);
setZValue(ChartPresenter::PieSeriesZValue);
setFlag(QGraphicsItem::ItemIsSelectable);
m_labelItem = new QGraphicsTextItem(this);
m_labelItem->document()->setDocumentMargin(1.0);
}
PieSliceItem::~PieSliceItem()
{
// If user is hovering over the slice and it gets destroyed we do
// not get a hover leave event. So we must emit the signal here.
if (m_hovered)
emit hovered(false);
}
QRectF PieSliceItem::boundingRect() const
{
return m_boundingRect;
}
QPainterPath PieSliceItem::shape() const
{
// Don't include the label and label arm.
// This is used to detect a mouse clicks. We do not want clicks from label.
return m_slicePath;
}
void PieSliceItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
painter->save();
painter->setClipRect(parentItem()->boundingRect());
painter->setPen(m_data.m_slicePen);
painter->setBrush(m_data.m_sliceBrush);
painter->drawPath(m_slicePath);
painter->restore();
if (m_data.m_isLabelVisible) {
painter->save();
// Pen for label arm not defined in the QPieSeries api, let's use brush's color instead
painter->setBrush(m_data.m_labelBrush);
if (m_data.m_labelPosition == QPieSlice::LabelOutside) {
painter->setClipRect(parentItem()->boundingRect());
painter->strokePath(m_labelArmPath, m_data.m_labelBrush.color());
}
painter->restore();
}
}
void PieSliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
{
m_hovered = true;
emit hovered(true);
}
void PieSliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
m_hovered = false;
emit hovered(false);
}
void PieSliceItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
emit pressed(event->buttons());
m_mousePressed = true;
}
void PieSliceItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
emit released(event->buttons());
if (m_mousePressed)
emit clicked(event->buttons());
}
void PieSliceItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
// For Pie slice a press signal needs to be explicitly fired for mouseDoubleClickEvent
emit pressed(event->buttons());
emit doubleClicked(event->buttons());
}
void PieSliceItem::setLayout(const PieSliceData &sliceData)
{
m_data = sliceData;
updateGeometry();
update();
}
void PieSliceItem::updateGeometry()
{
if (m_data.m_radius <= 0)
return;
prepareGeometryChange();
// slice path
qreal centerAngle;
QPointF armStart;
m_slicePath = slicePath(m_data.m_center, m_data.m_radius, m_data.m_startAngle, m_data.m_angleSpan, &centerAngle, &armStart);
m_labelItem->setVisible(m_data.m_isLabelVisible);
if (m_data.m_isLabelVisible) {
// text rect
m_labelTextRect = ChartPresenter::textBoundingRect(m_data.m_labelFont,
m_data.m_labelText,
0);
QString label(m_data.m_labelText);
m_labelItem->setDefaultTextColor(m_data.m_labelBrush.color());
m_labelItem->setFont(m_data.m_labelFont);
// text position
if (m_data.m_labelPosition == QPieSlice::LabelOutside) {
setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
// label arm path
QPointF labelTextStart;
m_labelArmPath = labelArmPath(armStart, centerAngle,
m_data.m_radius * m_data.m_labelArmLengthFactor,
m_labelTextRect.width(), &labelTextStart);
m_labelTextRect.moveBottomLeft(labelTextStart);
if (m_labelTextRect.left() < 0)
m_labelTextRect.setLeft(0);
else if (m_labelTextRect.left() < parentItem()->boundingRect().left())
m_labelTextRect.setLeft(parentItem()->boundingRect().left());
if (m_labelTextRect.right() > parentItem()->boundingRect().right())
m_labelTextRect.setRight(parentItem()->boundingRect().right());
label = ChartPresenter::truncatedText(m_data.m_labelFont, m_data.m_labelText,
qreal(0.0), m_labelTextRect.width(),
m_labelTextRect.height(), m_labelTextRect);
m_labelArmPath = labelArmPath(armStart, centerAngle,
m_data.m_radius * m_data.m_labelArmLengthFactor,
m_labelTextRect.width(), &labelTextStart);
m_labelTextRect.moveBottomLeft(labelTextStart);
m_labelItem->setTextWidth(m_labelTextRect.width()
+ m_labelItem->document()->documentMargin());
m_labelItem->setHtml(label);
m_labelItem->setRotation(0);
m_labelItem->setPos(m_labelTextRect.x(), m_labelTextRect.y() + 1.0);
} else {
// label inside
setFlag(QGraphicsItem::ItemClipsChildrenToShape);
m_labelItem->setTextWidth(m_labelTextRect.width()
+ m_labelItem->document()->documentMargin());
m_labelItem->setHtml(label);
QPointF textCenter;
if (m_data.m_holeRadius > 0) {
textCenter = m_data.m_center + offset(centerAngle, m_data.m_holeRadius
+ (m_data.m_radius
- m_data.m_holeRadius) / 2);
} else {
textCenter = m_data.m_center + offset(centerAngle, m_data.m_radius / 2);
}
m_labelItem->setPos(textCenter.x() - m_labelItem->boundingRect().width() / 2,
textCenter.y() - m_labelTextRect.height() / 2);
QPointF labelCenter = m_labelItem->boundingRect().center();
m_labelItem->setTransformOriginPoint(labelCenter);
if (m_data.m_labelPosition == QPieSlice::LabelInsideTangential) {
m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2);
} else if (m_data.m_labelPosition == QPieSlice::LabelInsideNormal) {
if (m_data.m_startAngle + m_data.m_angleSpan / 2 < 180)
m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2 - 90);
else
m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2 + 90);
} else {
m_labelItem->setRotation(0);
}
}
// Hide label if it's outside the bounding rect of parent item
QRectF labelRect(m_labelItem->boundingRect());
labelRect.moveTopLeft(m_labelItem->pos());
if ((parentItem()->boundingRect().left()
< (labelRect.left() + m_labelItem->document()->documentMargin() + 1.0))
&& (parentItem()->boundingRect().right()
> (labelRect.right() - m_labelItem->document()->documentMargin() - 1.0))
&& (parentItem()->boundingRect().top()
< (labelRect.top() + m_labelItem->document()->documentMargin() + 1.0))
&& (parentItem()->boundingRect().bottom()
> (labelRect.bottom() - m_labelItem->document()->documentMargin() - 1.0)))
m_labelItem->show();
else
m_labelItem->hide();
}
// bounding rect
if (m_data.m_isLabelVisible)
m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
else
m_boundingRect = m_slicePath.boundingRect();
// Inflate bounding rect by 2/3 pen width to make sure it encompasses whole slice also for thick pens
// and miter joins.
int penWidth = (m_data.m_slicePen.width() * 2) / 3;
m_boundingRect = m_boundingRect.adjusted(-penWidth, -penWidth, penWidth, penWidth);
}
QPointF PieSliceItem::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
{
if (slice->isExploded()) {
qreal centerAngle = slice->startAngle() + (slice->angleSpan() / 2);
qreal len = radius * slice->explodeDistanceFactor();
point += offset(centerAngle, len);
}
return point;
}
QPainterPath PieSliceItem::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal *centerAngle, QPointF *armStart)
{
// calculate center angle
*centerAngle = startAngle + (angleSpan / 2);
// calculate slice rectangle
QRectF rect(center.x() - radius, center.y() - radius, radius * 2, radius * 2);
// slice path
QPainterPath path;
if (m_data.m_holeRadius > 0) {
QRectF insideRect(center.x() - m_data.m_holeRadius, center.y() - m_data.m_holeRadius, m_data.m_holeRadius * 2, m_data.m_holeRadius * 2);
path.arcMoveTo(rect, -startAngle + 90);
path.arcTo(rect, -startAngle + 90, -angleSpan);
path.arcTo(insideRect, -startAngle + 90 - angleSpan, angleSpan);
path.closeSubpath();
} else {
path.moveTo(rect.center());
path.arcTo(rect, -startAngle + 90, -angleSpan);
path.closeSubpath();
}
// calculate label arm start point
*armStart = center;
*armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
return path;
}
QPainterPath PieSliceItem::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF *textStart)
{
// Normalize the angle to 0-360 range
// NOTE: We are using int here on purpose. Depenging on platform and hardware
// qreal can be a double, float or something the user gives to the Qt configure
// (QT_COORD_TYPE). Compilers do not seem to support modulo for double or float
// but there are fmod() and fmodf() functions for that. So instead of some #ifdef
// that might break we just use int. Precision for this is just fine for our needs.
int normalized = angle * 10.0;
normalized = normalized % 3600;
if (normalized < 0)
normalized += 3600;
angle = (qreal) normalized / 10.0;
// prevent label arm pointing straight down because it will look bad
if (angle < 180 && angle > 170)
angle = 170;
if (angle > 180 && angle < 190)
angle = 190;
// line from slice to label
QPointF parm1 = start + offset(angle, length);
// line to underline the label
QPointF parm2 = parm1;
if (angle < 180) { // arm swings the other way on the left side
parm2 += QPointF(textWidth, 0);
*textStart = parm1;
} else {
parm2 += QPointF(-textWidth, 0);
*textStart = parm2;
}
QPainterPath path;
path.moveTo(start);
path.lineTo(parm1);
path.lineTo(parm2);
return path;
}
#include "moc_piesliceitem_p.cpp"
QT_CHARTS_END_NAMESPACE