qskinny/playground/shapes/ShapeItem.cpp

233 lines
5.1 KiB
C++
Raw Normal View History

2022-09-26 17:51:28 +02:00
/******************************************************************************
2024-01-17 14:31:45 +01:00
* QSkinny - Copyright (C) The authors
2023-04-06 09:23:37 +02:00
* SPDX-License-Identifier: BSD-3-Clause
2022-09-26 17:51:28 +02:00
*****************************************************************************/
#include "ShapeItem.h"
#include "Stroke.h"
2022-09-26 17:51:28 +02:00
#include <QskGradient.h>
2022-09-26 17:51:28 +02:00
#include <QskStrokeNode.h>
#include <QskShapeNode.h>
#include <QskSGNode.h>
#include <qpainterpath.h>
#include <qpen.h>
2023-01-10 12:09:27 +01:00
#define STROKE_ADJUSTMENT 0
2022-09-29 16:50:46 +02:00
static inline QTransform transformForRects( const QRectF& r1, const QRectF& r2 )
{
return QTransform::fromTranslate( -r1.x(), -r1.y() )
* QTransform::fromScale( r2.width() / r1.width(), r2.height() / r1.height() )
* QTransform::fromTranslate( r2.x(), r2.y() );
}
2023-01-10 12:09:27 +01:00
#if STROKE_ADJUSTMENT
static inline qreal effectiveStrokeWidth(
const Stroke& stroke, const QRectF& r1, const QRectF& r2 )
2022-09-29 16:50:46 +02:00
{
qreal width = qMax( stroke.width(), 0.0 );
2022-09-29 16:50:46 +02:00
if ( !stroke.isCosmetic() )
2022-09-30 15:09:52 +02:00
{
const qreal sx = r1.width() / r2.width();
const qreal sy = r1.height() / r2.height();
2022-09-29 16:50:46 +02:00
2022-09-30 15:09:52 +02:00
width *= std::min( sx, sy );
}
2022-09-29 16:50:46 +02:00
2022-09-30 15:09:52 +02:00
return width;
2022-09-29 16:50:46 +02:00
}
2023-01-10 12:09:27 +01:00
#endif
class ShapeItem::PrivateData
{
public:
inline PrivateData()
: stroke( Qt::black, -1.0 ) // no stroke
, gradient( Qt::white )
{
}
Stroke stroke;
QskGradient gradient;
QPainterPath path;
};
2022-09-26 17:51:28 +02:00
ShapeItem::ShapeItem( QQuickItem* parent )
: QskControl( parent )
, m_data( new PrivateData() )
2022-09-26 17:51:28 +02:00
{
setMargins( 20 );
setSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Ignored );
2022-09-26 17:51:28 +02:00
}
ShapeItem::~ShapeItem()
{
}
void ShapeItem::setStroke( const QColor& color, qreal width )
2022-09-26 17:51:28 +02:00
{
setStroke( Stroke( color, width ) );
}
void ShapeItem::setStroke( const Stroke& stroke )
{
if ( stroke != m_data->stroke )
2022-09-26 17:51:28 +02:00
{
m_data->stroke = stroke;
2022-09-26 17:51:28 +02:00
update();
Q_EMIT strokeChanged( m_data->stroke );
2022-09-26 17:51:28 +02:00
}
}
void ShapeItem::resetStroke()
2022-09-26 17:51:28 +02:00
{
setStroke( Stroke( Qt::black, -1.0 ) );
}
Stroke ShapeItem::stroke() const
{
return m_data->stroke;
2022-09-26 17:51:28 +02:00
}
2022-10-31 14:42:08 +01:00
void ShapeItem::setGradient( const QskGradient& gradient )
2022-09-26 17:51:28 +02:00
{
if ( gradient != m_data->gradient )
2022-09-26 17:51:28 +02:00
{
m_data->gradient = gradient;
2022-09-26 17:51:28 +02:00
update();
Q_EMIT gradientChanged( m_data->gradient );
2022-09-26 17:51:28 +02:00
}
}
void ShapeItem::resetGradient()
2022-09-29 16:50:46 +02:00
{
setGradient( Qt::white );
}
QskGradient ShapeItem::gradient() const
{
return m_data->gradient;
2022-09-29 16:50:46 +02:00
}
2022-09-26 17:51:28 +02:00
void ShapeItem::setPath( const QPainterPath& path )
{
if ( path != m_data->path )
2022-09-26 17:51:28 +02:00
{
m_data->path = path;
2022-09-26 17:51:28 +02:00
update();
Q_EMIT pathChanged( m_data->path );
2022-09-26 17:51:28 +02:00
}
}
void ShapeItem::resetPath()
{
setPath( QPainterPath() );
}
2022-09-26 17:51:28 +02:00
QPainterPath ShapeItem::path() const
{
return m_data->path;
2022-09-26 17:51:28 +02:00
}
void ShapeItem::updateNode( QSGNode* parentNode )
{
enum NodeRole
{
2022-09-27 15:06:44 +02:00
FillRole,
BorderRole
2022-09-26 17:51:28 +02:00
};
const auto rect = contentsRect();
const auto pathRect = m_data->path.controlPointRect();
2022-09-26 17:51:28 +02:00
2022-10-31 14:42:08 +01:00
auto fillNode = static_cast< QskShapeNode* >(
2022-09-27 15:06:44 +02:00
QskSGNode::findChildNode( parentNode, FillRole ) );
2022-09-26 17:51:28 +02:00
2022-09-29 16:50:46 +02:00
auto borderNode = static_cast< QskStrokeNode* >(
QskSGNode::findChildNode( parentNode, BorderRole ) );
if ( rect.isEmpty() || pathRect.isEmpty() )
2022-09-26 17:51:28 +02:00
{
2022-09-27 15:06:44 +02:00
delete fillNode;
2022-09-29 16:50:46 +02:00
delete borderNode;
return;
2022-09-26 17:51:28 +02:00
}
2022-09-29 16:50:46 +02:00
if ( m_data->gradient.isVisible() )
2022-09-26 17:51:28 +02:00
{
2022-09-27 15:06:44 +02:00
if ( fillNode == nullptr )
2022-09-26 17:51:28 +02:00
{
2022-10-31 14:42:08 +01:00
fillNode = new QskShapeNode;
2022-09-27 15:06:44 +02:00
QskSGNode::setNodeRole( fillNode, FillRole );
2022-09-29 16:50:46 +02:00
parentNode->prependChildNode( fillNode );
2022-09-26 17:51:28 +02:00
}
2022-09-29 16:50:46 +02:00
auto fillRect = rect;
2023-01-10 12:09:27 +01:00
#if STROKE_ADJUSTMENT
/*
when the stroke is not opaque ( transparent color or dashed ) we
would see, that the filling is not inside. But simply adjusting
by the half of the stroke width is only correct for rectangles. TODO ...
*/
const auto pw2 = 0.5 * ::effectiveStrokeWidth( m_data->stroke, rect, pathRect );
fillRect.adjust( pw2, pw2, -pw2, -pw2 );
#endif
2022-09-29 16:50:46 +02:00
const auto transform = ::transformForRects( pathRect, fillRect );
fillNode->updatePath( m_data->path, transform, fillRect, m_data->gradient );
2022-09-26 17:51:28 +02:00
}
2022-09-29 16:50:46 +02:00
else
2022-09-26 17:51:28 +02:00
{
2022-09-29 16:50:46 +02:00
delete fillNode;
2022-09-26 17:51:28 +02:00
}
2022-09-29 16:50:46 +02:00
if ( m_data->stroke.isVisible() )
2022-09-26 17:51:28 +02:00
{
const auto pen = m_data->stroke.toPen();
2022-09-30 15:09:52 +02:00
#if 0
if ( pen.widthF() > 1.0 )
2022-09-29 16:50:46 +02:00
{
if ( !( pen.isSolid() && pen.color().alpha() == 255 ) )
2022-09-29 16:50:46 +02:00
{
/*
We might end up with overlapping parts
at corners with angles < 180°
What about translating the stroke into
a path ( QPainterPathStroker ) and using
a QskShapeNode then. TODO ...
*/
}
}
2022-09-30 15:09:52 +02:00
#endif
2022-09-29 16:50:46 +02:00
2022-09-27 15:06:44 +02:00
if ( borderNode == nullptr )
2022-09-26 17:51:28 +02:00
{
2022-09-27 15:06:44 +02:00
borderNode = new QskStrokeNode;
QskSGNode::setNodeRole( borderNode, BorderRole );
2022-09-26 17:51:28 +02:00
2022-09-27 15:06:44 +02:00
parentNode->appendChildNode( borderNode );
2022-09-29 16:50:46 +02:00
}
2022-09-27 15:06:44 +02:00
2022-09-29 16:50:46 +02:00
const auto transform = ::transformForRects( pathRect, rect );
borderNode->updatePath( m_data->path, transform, pen );
2022-09-29 16:50:46 +02:00
}
else
{
delete borderNode;
}
2022-09-27 15:06:44 +02:00
}
2022-09-26 17:51:28 +02:00
#include "moc_ShapeItem.cpp"