QPainterPath::addRoundedRect with 4 different radii?
-
I was looking at the documentation for
QPainterPath::addRoundedRect()
, expecting one of the overloads to take 4 radius parameters (one for top left, another for top right, another for bottom left, another for bottom right). I was surprised to see that they only take 2 parameters, X radius and Y radius -- how can I add a rounded rectangle with corner radii that aren't in pairs? -
This implementation suits my purposes just fine.
inset
can be replaced with 0s or omitted for the subtraction; I included it because it was convenient for my implementation since I'm using this to draw rounded rectangles with borders (the normalQPainter
way w/ pen and brush overlaps the two which is bad when translucency is present)const int w = width(); const int h = height(); auto drawRoundedRect = [this, w, h](QPainterPath &p, const BorderRadius &r, int inset) { // clang-format off p.arcMoveTo( inset, inset, inset, r.topLeft, 0); p.arcTo(inset, inset, r.topLeft, r.topLeft, 180, -90); p.arcTo(w - r.topRight - inset, inset, r.topRight, r.topRight, 90, -90); p.arcTo(w - r.bottomRight - inset, h - r.bottomRight - inset, r.bottomRight, r.bottomRight, 0, -90); p.arcTo(inset, h - r.bottomLeft - inset, r.bottomLeft, r.bottomLeft, 270, -90); p.arcTo(inset, inset, r.topLeft, r.topLeft, 180, 0); // clang-format on };
I used a BorderRadius struct, as follows. Includes convenience overloads and operators, would work with just the radius members
struct BorderRadius { template <typename T> explicit BorderRadius(T a) { topLeft = a; topRight = a; bottomLeft = a; bottomRight = a; } template <typename T> explicit BorderRadius(T top, T bottom) { topLeft = top; topRight = top; bottomLeft = bottom; bottomRight = bottom; } template <typename T> explicit BorderRadius(T topLeft, T topRight, T bottomLeft, T bottomRight) { this->topLeft = topLeft; this->topRight = topRight; this->bottomLeft = bottomLeft; this->bottomRight = bottomRight; } bool operator==(BorderRadius x) { return this->topLeft == x.topLeft && this->topRight == x.topRight && this->bottomLeft == x.bottomLeft && this->bottomRight == x.bottomRight; } template <typename T> bool operator==(T x) { return topLeft == x && topRight == x && bottomLeft == x && bottomRight == x; } float topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0; };
When invoking like this, it yields the following screenshot
// Default values provided for clarity QPainter p(this); QColor m_border_brush = Qt::red; m_border_brush.setAlphaF(0.5); QColor m_background_brush = Qt::blue; m_border_brush.setAlphaF(0.5); BorderRadius m_border_outer_radius(10, 20, 30, 40); int m_border_thickness = 4; BorderRadius m_border_inner_radius( m_border_outer_radius.topLeft - m_border_thickness, m_border_outer_radius.topRight - m_border_thickness, m_border_outer_radius.bottomLeft - m_border_thickness, m_border_outer_radius.bottomRight - m_border_thickness); // End defaults QPainterPath path, path2; drawRoundedRect(path, m_border_outer_radius, 0); drawRoundedRect(path2, m_border_inner_radius, m_border_thickness); p.setCompositionMode(QPainter::CompositionMode_Xor); // For demonstration; overlap would be made very apparent p.setPen(Qt::NoPen); p.setBrush(m_border_brush); p.drawPath(path.subtracted(path2)); p.setBrush(m_background_brush); p.drawPath(path2.intersected(path));
-
const int w = width(); const int h = height(); auto drawRoundedRect = [this, w, h](QPainterPath &p, const BorderRadius &r, int inset) { // clang-format off p.arcMoveTo( inset, inset, inset, r.topLeft, 0); p.arcTo(inset, inset, r.topLeft, r.topLeft, 180, -90); p.arcTo(w - r.topRight - inset, inset, r.topRight, r.topRight, 90, -90); p.arcTo(w - r.bottomRight - inset, h - r.bottomRight - inset, r.bottomRight, r.bottomRight, 0, -90); p.arcTo(inset, h - r.bottomLeft - inset, r.bottomLeft, r.bottomLeft, 270, -90); p.arcTo(inset, inset, r.topLeft, r.topLeft, 180, 0); // clang-format on };
Where should this code be placed? How should it be used?