|
#include "RoundedRectangle.h" |
|
|
|
#include <QPainter> |
|
#include <QPainterPath> |
|
|
|
RoundedRectangle::RoundedRectangle(QQuickItem* parent) : QQuickPaintedItem(parent) { |
|
_borderPen = QPen(Qt::black, 1., Qt::PenStyle::SolidLine, Qt::PenCapStyle::SquareCap, Qt::PenJoinStyle::MiterJoin); |
|
|
|
// Enable antialiasing by default, as on QML's Rectangle. |
|
setAntialiasing(true); |
|
|
|
// Forward signals and update accordingly. |
|
QObject::connect(&_corners, &RoundedCorners::topLeftChanged, this, [this](){ |
|
emit radiusTopLeftChanged(); |
|
update(); |
|
}); |
|
QObject::connect(&_corners, &RoundedCorners::bottomLeftChanged, this, [this](){ |
|
emit radiusBottomLeftChanged(); |
|
update(); |
|
}); |
|
QObject::connect(&_corners, &RoundedCorners::bottomRightChanged, this, [this](){ |
|
emit radiusBottomRightChanged(); |
|
update(); |
|
}); |
|
QObject::connect(&_corners, &RoundedCorners::topRightChanged, this, [this](){ |
|
emit radiusTopRightChanged(); |
|
update(); |
|
}); |
|
} |
|
|
|
void RoundedRectangle::paint(QPainter *painter) { |
|
if (antialiasing()) { |
|
painter->setRenderHint(QPainter::RenderHint::Antialiasing); |
|
} |
|
|
|
const QSizeF itemSize = size(); |
|
const auto borderWidth = _borderPen.widthF(); |
|
|
|
if (borderWidth > 0.) { |
|
const auto halfBorderWidth = borderWidth / 2; |
|
const auto borderCorners = RoundedCorners { |
|
std::max(0., _corners.topLeft() - halfBorderWidth), |
|
std::max(0., _corners.bottomLeft() - halfBorderWidth), |
|
std::max(0., _corners.bottomRight() - halfBorderWidth), |
|
std::max(0., _corners.topRight() - halfBorderWidth), |
|
}; |
|
|
|
// Fill. |
|
if (_color.alpha() > 0) { |
|
painter->setBrush(_color); |
|
painter->setPen(Qt::PenStyle::NoPen); |
|
if (_borderPen.color().alpha() == 255) { |
|
drawRect(*painter, |
|
halfBorderWidth, halfBorderWidth, |
|
itemSize.width() - borderWidth, |
|
itemSize.height() - borderWidth, |
|
borderCorners); |
|
} else { |
|
drawRect(*painter, 0., 0., itemSize.width(), itemSize.height(), _corners); |
|
} |
|
} |
|
|
|
// Border. |
|
if (_borderPen.color().alpha() > 0) { |
|
painter->setBrush(Qt::BrushStyle::NoBrush); |
|
painter->setPen(_borderPen); |
|
drawRect(*painter, |
|
halfBorderWidth, halfBorderWidth, |
|
itemSize.width() - borderWidth , |
|
itemSize.height() - borderWidth, |
|
borderCorners); |
|
} |
|
} else if (_color.alpha() > 0) { |
|
painter->setBrush(_color); |
|
painter->setPen(Qt::PenStyle::NoPen); |
|
drawRect(*painter, 0., 0., itemSize.width(), itemSize.height(), _corners); |
|
} |
|
} |
|
|
|
void RoundedRectangle::drawRect(QPainter &painter, const qreal x, const qreal y, |
|
const qreal w, const qreal h, const RoundedCorners &corners) { |
|
QPainterPath path; |
|
const auto minSide = std::min(w, h) / 2; |
|
qreal radius; |
|
qreal diameter; |
|
|
|
// To-left corner. |
|
if (corners.topLeft() > 0) { |
|
radius = std::min(minSide, corners.topLeft()); |
|
diameter = 2 * radius; |
|
path.moveTo(x + diameter, y); |
|
path.arcTo(x, y, diameter, diameter, 90, 90); |
|
} else { |
|
path.moveTo(x, y); |
|
} |
|
|
|
// Left side & Bottom-left corner. |
|
if (corners.bottomLeft() > 0) { |
|
radius = std::min(minSide, corners.bottomLeft()); |
|
diameter = 2 * radius; |
|
path.arcTo(x, y + h - diameter, diameter, diameter, 180, 90); |
|
} else { |
|
path.lineTo(x, y + h); |
|
} |
|
|
|
// Bottom side & Bottom-right corner. |
|
if (corners.bottomRight() > 0) { |
|
radius = std::min(minSide, corners.bottomRight()); |
|
diameter = 2 * radius; |
|
path.arcTo(x + w - diameter, y + h - diameter, diameter, diameter, 270, 90); |
|
} else { |
|
path.lineTo(x + w, y + h); |
|
} |
|
|
|
// Right side & Top-right corner. |
|
if (corners.topRight()) { |
|
radius = std::min(minSide, corners.topRight()); |
|
diameter = 2 * radius; |
|
path.arcTo(x + w - diameter, y, diameter, diameter, 360, 90); |
|
} else { |
|
path.lineTo(x + w, y); |
|
} |
|
|
|
path.closeSubpath(); |
|
painter.drawPath(path); |
|
} |
|
|
|
const QColor &RoundedRectangle::color() const { |
|
return _color; |
|
} |
|
|
|
void RoundedRectangle::setColor(const QColor &value) { |
|
if (value != _color) { |
|
_color = value; |
|
emit colorChanged(); |
|
update(); |
|
} |
|
} |
|
|
|
QColor RoundedRectangle::borderColor() const { |
|
return _borderPen.color(); |
|
} |
|
|
|
void RoundedRectangle::setBorderColor(const QColor &value) { |
|
if (value != _borderPen.color()) { |
|
_borderPen.setColor(value); |
|
emit borderColorChanged(); |
|
update(); |
|
} |
|
} |
|
|
|
qreal RoundedRectangle::borderWidth() const { |
|
return _borderPen.widthF(); |
|
} |
|
|
|
void RoundedRectangle::setBorderWidth(qreal value) { |
|
value = std::max(0., value); |
|
if (value != _borderPen.widthF()) { |
|
_borderPen.setWidthF(value); |
|
emit borderWidthChanged(); |
|
update(); |
|
} |
|
} |
|
|
|
qreal RoundedRectangle::radiusTopLeft() const { |
|
return _corners.topLeft(); |
|
} |
|
|
|
void RoundedRectangle::setRadiusTopLeft(qreal value) { |
|
_corners.setTopLeft(value); |
|
} |
|
|
|
qreal RoundedRectangle::radiusBottomLeft() const { |
|
return _corners.bottomLeft(); |
|
} |
|
|
|
void RoundedRectangle::setRadiusBottomLeft(qreal value) { |
|
_corners.setBottomLeft(value); |
|
} |
|
|
|
qreal RoundedRectangle::radiusBottomRight() const { |
|
return _corners.bottomRight(); |
|
} |
|
|
|
void RoundedRectangle::setRadiusBottomRight(qreal value) { |
|
_corners.setBottomRight(value); |
|
} |
|
|
|
qreal RoundedRectangle::radiusTopRight() const { |
|
return _corners.topRight(); |
|
} |
|
|
|
void RoundedRectangle::setRadiusTopRight(qreal value) { |
|
_corners.setTopRight(value); |
|
} |