diff --git a/example/qml/page/T_Rectangle.qml b/example/qml/page/T_Rectangle.qml index edd8d153..08a016ca 100644 --- a/example/qml/page/T_Rectangle.qml +++ b/example/qml/page/T_Rectangle.qml @@ -58,6 +58,22 @@ FluScrollablePage{ color:"#b4009e" radius:[0,0,0,15] } + FluRectangle{ + width: 50 + height: 50 + color:"#a8d5ba" + radius:[15,15,15,15] + borderWidth: 3 + borderColor: "#5b8a72" + } + FluRectangle{ + width: 50 + height: 50 + color:"#dbe2ef" + radius:[15,0,0,0] + borderWidth: 2 + borderColor: "#3f72af" + } } } } @@ -66,6 +82,8 @@ FluScrollablePage{ Layout.topMargin: -6 code:'FluRectangle{ radius: [25,25,25,25] + borderWidth: 2 + borderColor: "#000000" width: 50 height: 50 }' diff --git a/src/FluRectangle.cpp b/src/FluRectangle.cpp index 00890a76..fdd69d5f 100644 --- a/src/FluRectangle.cpp +++ b/src/FluRectangle.cpp @@ -2,33 +2,61 @@ #include FluRectangle::FluRectangle(QQuickItem *parent) : QQuickPaintedItem(parent) { - color(QColor(255, 255, 255, 255)); + color(Qt::white); radius({0, 0, 0, 0}); + borderWidth(0); + borderColor(Qt::black); connect(this, &FluRectangle::colorChanged, this, [=] { update(); }); connect(this, &FluRectangle::radiusChanged, this, [=] { update(); }); + connect(this, &FluRectangle::borderWidthChanged, this, [=] { update(); }); + connect(this, &FluRectangle::borderColorChanged, this, [=] { update(); }); } +bool FluRectangle::borderValid() const { + return qRound(_borderWidth) >= 1 && _color.isValid() && _color.alpha() > 0; +} void FluRectangle::paint(QPainter *painter) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); - QPainterPath path; + QRectF rect = boundingRect(); - path.moveTo(rect.bottomRight() - QPointF(0, _radius[2])); - path.lineTo(rect.topRight() + QPointF(0, _radius[1])); - path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(_radius[1] * 2, 0)), - QSize(_radius[1] * 2, _radius[1] * 2)), - 0, 90); - path.lineTo(rect.topLeft() + QPointF(_radius[0], 0)); - path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(_radius[0] * 2, _radius[0] * 2)), 90, 90); - path.lineTo(rect.bottomLeft() - QPointF(0, _radius[3])); - path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, _radius[3] * 2)), - QSize(_radius[3] * 2, _radius[3] * 2)), - 180, 90); - path.lineTo(rect.bottomRight() - QPointF(_radius[2], 0)); - path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(_radius[2] * 2, _radius[2] * 2)), - QSize(_radius[2] * 2, _radius[2] * 2)), - 270, 90); + bool valid = borderValid(); + if (valid) { + // 绘制边框时画笔的宽度从路径向两侧扩充 + // 因此实际绘制的矩形应向内侧收缩边框宽度的一半,避免边框裁剪导致不完整 + qreal halfBorderWidth = _borderWidth / 2.0; + rect.adjust(halfBorderWidth, halfBorderWidth, -halfBorderWidth, -halfBorderWidth); + } + + QPainterPath path; + QList r = _radius; + + while (r.size() < 4) { + r.append(0); + } + + // 从右下角开始逆时针绘制圆角矩形路径 + path.moveTo(rect.bottomRight() - QPointF(0, r[2])); + path.lineTo(rect.topRight() + QPointF(0, r[1])); + path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(r[1] * 2, 0)), QSize(r[1] * 2, r[1] * 2)), 0, 90); + + path.lineTo(rect.topLeft() + QPointF(r[0], 0)); + path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(r[0] * 2, r[0] * 2)), 90, 90); + + path.lineTo(rect.bottomLeft() - QPointF(0, r[3])); + path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, r[3] * 2)), QSize(r[3] * 2, r[3] * 2)), 180, 90); + + path.lineTo(rect.bottomRight() - QPointF(r[2], 0)); + path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(r[2] * 2, r[2] * 2)), QSize(r[2] * 2, r[2] * 2)), 270, 90); + + // 填充背景 painter->fillPath(path, _color); + + // 绘制边框 + if (valid) { + painter->strokePath(path, QPen(_borderColor, _borderWidth)); + } + painter->restore(); } diff --git a/src/FluRectangle.h b/src/FluRectangle.h index 4690ff39..571af4f6 100644 --- a/src/FluRectangle.h +++ b/src/FluRectangle.h @@ -12,9 +12,13 @@ class FluRectangle : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY_AUTO(QColor, color) Q_PROPERTY_AUTO(QList, radius) + Q_PROPERTY_AUTO(qreal, borderWidth) + Q_PROPERTY_AUTO(QColor, borderColor) QML_NAMED_ELEMENT(FluRectangle) public: explicit FluRectangle(QQuickItem *parent = nullptr); + bool borderValid() const; + void paint(QPainter *painter) override; }; diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes index 26d694db..17e31c99 100644 --- a/src/Qt5/imports/FluentUI/plugins.qmltypes +++ b/src/Qt5/imports/FluentUI/plugins.qmltypes @@ -231,6 +231,8 @@ Module { exportMetaObjectRevisions: [0] Property { name: "color"; type: "QColor" } Property { name: "radius"; type: "QList" } + Property { name: "borderWidth"; type: "double" } + Property { name: "borderColor"; type: "QColor" } } Component { name: "FluSheetType" @@ -2776,7 +2778,7 @@ Module { } Property { name: "layoutMacosButtons" - type: "FluLoader_QMLTYPE_14" + type: "FluLoader_QMLTYPE_11" isReadonly: true isPointer: true } @@ -2797,12 +2799,16 @@ Module { Property { name: "items"; type: "QVariant" } Property { name: "emptyText"; type: "string" } Property { name: "autoSuggestBoxReplacement"; type: "int" } + Property { name: "itemHeight"; type: "int" } + Property { name: "itemRows"; type: "int" } + Property { name: "showSuggestWhenPressed"; type: "bool" } Property { name: "textRole"; type: "string" } Property { name: "filter"; type: "QVariant" } Signal { name: "itemClicked" Parameter { name: "data"; type: "QVariant" } } + Method { name: "showSuggest"; type: "QVariant" } Method { name: "updateText" type: "QVariant" @@ -2830,8 +2836,9 @@ Module { defaultProperty: "data" Property { name: "isDot"; type: "bool" } Property { name: "showZero"; type: "bool" } - Property { name: "count"; type: "int" } - Property { name: "topRight"; type: "bool" } + Property { name: "count"; type: "QVariant" } + Property { name: "max"; type: "int" } + Property { name: "position"; type: "string" } } Component { prototype: "QQuickItem" @@ -3479,8 +3486,8 @@ Module { Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true } Property { name: "topPadding"; type: "int" } Property { name: "pageMode"; type: "int" } - Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_47"; isPointer: true } - Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_47"; isPointer: true } + Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_37"; isPointer: true } + Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_37"; isPointer: true } Property { name: "navCompactWidth"; type: "int" } Property { name: "navTopMargin"; type: "int" } Property { name: "cellHeight"; type: "int" }