File indexing completed on 2024-06-23 04:28:11
0001 /* This file is part of the KDE project 0002 0003 SPDX-FileCopyrightText: 2006 Thorsten Zachmann <zachmann@kde.org> 0004 SPDX-FileCopyrightText: 2006-2007 Thomas Zander <zander@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "SelectionDecorator.h" 0010 0011 #include <QPainterPath> 0012 0013 #include <KoShape.h> 0014 #include <KoSelection.h> 0015 #include <KoResourcePaths.h> 0016 #include "kis_algebra_2d.h" 0017 0018 #include "kis_debug.h" 0019 #include <KisHandlePainterHelper.h> 0020 #include <KoCanvasResourceProvider.h> 0021 #include <KisQPainterStateSaver.h> 0022 #include "KoShapeGradientHandles.h" 0023 #include <KoCanvasBase.h> 0024 #include <KoSvgTextShape.h> 0025 0026 #include "kis_painting_tweaks.h" 0027 #include "kis_coordinates_converter.h" 0028 #include "kis_icon_utils.h" 0029 0030 0031 0032 #define HANDLE_DISTANCE 10 0033 0034 SelectionDecorator::SelectionDecorator(KoCanvasResourceProvider *resourceManager) 0035 : m_hotPosition(KoFlake::Center) 0036 , m_handleRadius(7) 0037 , m_decorationThickness(1) 0038 , m_showFillGradientHandles(false) 0039 , m_showStrokeFillGradientHandles(false) 0040 , m_forceShapeOutlines(false) 0041 { 0042 m_hotPosition = 0043 KoFlake::AnchorPosition( 0044 resourceManager->resource(KoFlake::HotPosition).toInt()); 0045 } 0046 0047 void SelectionDecorator::setSelection(KoSelection *selection) 0048 { 0049 m_selection = selection; 0050 } 0051 0052 void SelectionDecorator::setHandleRadius(int radius) 0053 { 0054 m_handleRadius = radius; 0055 } 0056 0057 void SelectionDecorator::setDecorationThickness(int thickness) 0058 { 0059 m_decorationThickness = thickness; 0060 } 0061 0062 void SelectionDecorator::setShowFillGradientHandles(bool value) 0063 { 0064 m_showFillGradientHandles = value; 0065 } 0066 0067 void SelectionDecorator::setShowStrokeFillGradientHandles(bool value) 0068 { 0069 m_showStrokeFillGradientHandles = value; 0070 } 0071 0072 void SelectionDecorator::setShowFillMeshGradientHandles(bool value) 0073 { 0074 m_showFillMeshGradientHandles = value; 0075 } 0076 0077 void SelectionDecorator::setCurrentMeshGradientHandles(const KoShapeMeshGradientHandles::Handle &selectedHandle, 0078 const KoShapeMeshGradientHandles::Handle &hoveredHandle) 0079 { 0080 m_selectedMeshHandle = selectedHandle; 0081 m_currentHoveredMeshHandle = hoveredHandle; 0082 } 0083 0084 void SelectionDecorator::paint(QPainter &painter, const KoViewConverter &converter) 0085 { 0086 QList<KoShape*> selectedShapes = m_selection->selectedVisibleShapes(); 0087 if (selectedShapes.isEmpty()) return; 0088 0089 const bool haveOnlyOneEditableShape = 0090 m_selection->selectedEditableShapes().size() == 1 && 0091 selectedShapes.size() == 1; 0092 0093 bool editable = false; 0094 bool forceBoundingRubberLine = false; 0095 0096 Q_FOREACH (KoShape *shape, KoShape::linearizeSubtree(selectedShapes)) { 0097 if (!haveOnlyOneEditableShape || !m_showStrokeFillGradientHandles) { 0098 KisHandlePainterHelper helper = 0099 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius, m_decorationThickness); 0100 0101 helper.setHandleStyle(KisHandleStyle::secondarySelection()); 0102 0103 if (!m_forceShapeOutlines) { 0104 helper.drawRubberLine(shape->outlineRect()); 0105 } else { 0106 QList<QPolygonF> polys = shape->outline().toSubpathPolygons(); 0107 0108 if (polys.size() == 1) { 0109 const QPolygonF poly1 = polys[0]; 0110 const QPolygonF poly2 = QPolygonF(polys[0].boundingRect()); 0111 const QPolygonF nonoverlap = poly2.subtracted(poly1); 0112 0113 forceBoundingRubberLine |= !nonoverlap.isEmpty(); 0114 } 0115 0116 Q_FOREACH (const QPolygonF &poly, polys) { 0117 helper.drawRubberLine(poly); 0118 } 0119 } 0120 } 0121 0122 if (shape->isShapeEditable()) { 0123 editable = true; 0124 } 0125 } 0126 0127 const QRectF handleArea = m_selection->outlineRect(); 0128 0129 // draw extra rubber line around all the shapes 0130 if (selectedShapes.size() > 1 || forceBoundingRubberLine) { 0131 KisHandlePainterHelper helper = 0132 KoShape::createHandlePainterHelperView(&painter, m_selection, converter, m_handleRadius, m_decorationThickness); 0133 0134 helper.setHandleStyle(KisHandleStyle::primarySelection()); 0135 helper.drawRubberLine(handleArea); 0136 } 0137 0138 // if we have no editable shape selected there 0139 // is no need drawing the selection handles 0140 if (editable) { 0141 KisHandlePainterHelper helper = 0142 KoShape::createHandlePainterHelperView(&painter, m_selection, converter, m_handleRadius, m_decorationThickness); 0143 helper.setHandleStyle(KisHandleStyle::primarySelection()); 0144 0145 QPolygonF outline = handleArea; 0146 0147 { 0148 helper.drawHandleRect(outline.value(0)); 0149 helper.drawHandleRect(outline.value(1)); 0150 helper.drawHandleRect(outline.value(2)); 0151 helper.drawHandleRect(outline.value(3)); 0152 helper.drawHandleRect(0.5 * (outline.value(0) + outline.value(1))); 0153 helper.drawHandleRect(0.5 * (outline.value(1) + outline.value(2))); 0154 helper.drawHandleRect(0.5 * (outline.value(2) + outline.value(3))); 0155 helper.drawHandleRect(0.5 * (outline.value(3) + outline.value(0))); 0156 0157 QPointF hotPos = KoFlake::anchorToPoint(m_hotPosition, handleArea); 0158 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles()); 0159 helper.drawHandleRect(hotPos); 0160 } 0161 } 0162 0163 if (haveOnlyOneEditableShape) { 0164 KoShape *shape = selectedShapes.first(); 0165 0166 if (m_showFillGradientHandles) { 0167 paintGradientHandles(shape, KoFlake::Fill, painter, converter); 0168 } else if (m_showStrokeFillGradientHandles) { 0169 paintGradientHandles(shape, KoFlake::StrokeFill, painter, converter); 0170 } 0171 0172 // paint meshgradient handles 0173 if(m_showFillMeshGradientHandles) { 0174 paintMeshGradientHandles(shape, KoFlake::Fill, painter, converter); 0175 } 0176 } 0177 0178 } 0179 0180 void SelectionDecorator::paintGradientHandles(KoShape *shape, KoFlake::FillVariant fillVariant, QPainter &painter, const KoViewConverter &converter) 0181 { 0182 KoShapeGradientHandles gradientHandles(fillVariant, shape); 0183 QVector<KoShapeGradientHandles::Handle> handles = gradientHandles.handles(); 0184 0185 KisHandlePainterHelper helper = 0186 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius, m_decorationThickness); 0187 0188 const QTransform t = shape->absoluteTransformation().inverted(); 0189 0190 if (gradientHandles.type() == QGradient::LinearGradient) { 0191 KIS_SAFE_ASSERT_RECOVER_NOOP(handles.size() == 2); 0192 0193 if (handles.size() == 2) { 0194 helper.setHandleStyle(KisHandleStyle::gradientArrows()); 0195 helper.drawGradientArrow(t.map(handles[0].pos), t.map(handles[1].pos), 1.5 * m_handleRadius); 0196 } 0197 } 0198 0199 helper.setHandleStyle(KisHandleStyle::gradientHandles()); 0200 0201 Q_FOREACH (const KoShapeGradientHandles::Handle &h, handles) { 0202 if (h.type == KoShapeGradientHandles::Handle::RadialCenter) { 0203 helper.drawGradientCrossHandle(t.map(h.pos), 1.2 * m_handleRadius); 0204 } else { 0205 helper.drawGradientHandle(t.map(h.pos), 1.2 * m_handleRadius); 0206 } 0207 } 0208 } 0209 0210 void SelectionDecorator::paintMeshGradientHandles(KoShape *shape, 0211 KoFlake::FillVariant fillVariant, 0212 QPainter &painter, 0213 const KoViewConverter &converter) 0214 { 0215 KoShapeMeshGradientHandles gradientHandles(fillVariant, shape); 0216 0217 KisHandlePainterHelper helper = 0218 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius, m_decorationThickness); 0219 helper.setHandleStyle(KisHandleStyle::secondarySelection()); 0220 0221 helper.drawPath(gradientHandles.path()); 0222 0223 // invert them, because we draw in logical coordinates. 0224 const QTransform t = shape->absoluteTransformation().inverted(); 0225 const QVector<KoShapeMeshGradientHandles::Handle> cornerHandles = gradientHandles.handles(); 0226 for (const auto& corner: cornerHandles) { 0227 const QPointF point = t.map(corner.pos); 0228 if (corner.type == KoShapeMeshGradientHandles::Handle::BezierHandle) { 0229 helper.drawConnectionLine(gradientHandles.getAttachedCorner(corner), point); 0230 helper.drawHandleSmallCircle(point); 0231 } else if (corner.type == KoShapeMeshGradientHandles::Handle::Corner) { 0232 helper.drawHandleCircle(point); 0233 } 0234 } 0235 0236 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandlesWithSolidOutline()); 0237 0238 // highlight the selected handle (only corner) 0239 if (m_selectedMeshHandle.type == KoShapeMeshGradientHandles::Handle::Corner) { 0240 helper.drawHandleRect(t.map(gradientHandles.getHandle(m_selectedMeshHandle.getPosition()).pos)); 0241 } 0242 0243 // highlight the path which is being hovered/moved 0244 if (m_currentHoveredMeshHandle.type != KoShapeMeshGradientHandles::Handle::None) { 0245 QVector<QPainterPath> paths = gradientHandles.getConnectedPath(m_currentHoveredMeshHandle); 0246 for (const auto &path: paths) { 0247 helper.drawPath(path); 0248 } 0249 } 0250 } 0251 0252 void SelectionDecorator::setForceShapeOutlines(bool value) 0253 { 0254 m_forceShapeOutlines = value; 0255 }