Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextShapeOutlineHelper.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
7
8#include <KoCanvasBase.h>
9#include <kis_icon.h>
11
12#include <KoSvgTextShape.h>
14#include <KoShapeManager.h>
15#include <KoSelection.h>
16#include <QApplication>
17#include <QPalette>
18
19const int BUTTON_ICON_SIZE = 16;
20const int BUTTON_PADDING = 4;
21const int BUTTON_CORNER_ROUND = 1;
22const QString ICON_EXIT = "object-ungroup-calligra";
23const QString ICON_ENTER = "object-group-calligra";
24
26 Private(KoCanvasBase *canvasBase): canvas(canvasBase) {
27 }
29 int handleRadius = 7;
31
32 bool drawBoundingRect = true;
34 bool drawOutline = false;
36
40
41 KoSvgTextShape *getPotentialTextShape(const QPointF &point) {
43 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
44 if (drawButton(text)) {
45 if (getButtonRectCorrected(text->boundingRect()).contains(point)) {
46 return text;
47 }
48 }
49 }
50 return nullptr;
51 }
52
54 return canvas->viewConverter();
55 }
56
57 QRectF getButtonRect(QRectF base) {
59 return QRectF(base.topRight(), QSizeF(buttonSize, buttonSize));
60 }
61 QRectF getButtonRectCorrected(QRectF base) {
62 return converter()->viewToDocument().mapRect(getButtonRect(converter()->documentToView().mapRect(base)));
63 }
64
66 return text && !text->internalShapeManager()->shapes().isEmpty();
67 }
68};
69
74
79
81 QList<QLineF> lines;
82 if (areas.size() <= 1) return lines;
83 for (int i = 1; i < areas.size(); i++) {
84 const QPainterPath previous = areas.at(i-1);
85 const QPainterPath next = areas.at(i);
86 const bool overlap = previous.intersects(next);
87 QLineF arrow(previous.boundingRect().center(), next.boundingRect().center());
88 if (!overlap) {
89
90 Q_FOREACH (QPolygonF p, previous.toSubpathPolygons()) {
91 if (p.size() == 1) continue;
92 for (int j = 1; j < p.size(); j++) {
93 QLineF l2(p.at(j-1), p.at(j));
94 QPointF intersect;
95 if (l2.intersects(arrow, &intersect) == QLineF::BoundedIntersection) {
96 arrow.setP1(intersect);
97 break;
98 }
99 }
100 }
101 Q_FOREACH (QPolygonF p, next.toSubpathPolygons()) {
102 if (p.size() == 1) continue;
103 for (int j = 1; j < p.size(); j++) {
104 QLineF l2(p.at(j-1), p.at(j));
105 QPointF intersect;
106 if (l2.intersects(arrow, &intersect) == QLineF::BoundedIntersection) {
107 arrow.setP2(intersect);
108 break;
109 }
110 }
111 }
112
113 }
114 lines.append(arrow);
115 }
116 return lines;
117}
118
119void KoSvgTextShapeOutlineHelper::paintTextShape(QPainter *painter, const KoViewConverter &converter,
120 const QPalette &pal, KoSvgTextShape *text,
121 bool contourModeActive) {
122 painter->save();
124 KoShape::createHandlePainterHelperView(painter, text, converter, d->handleRadius, d->decorationThickness);
126 if (contourModeActive) {
127 if (d->drawOutline) {
128
129 Q_FOREACH(KoShape *shape, text->internalShapeManager()->shapes()) {
130 QPainterPath p = shape->transformation().map(shape->outline());
131 helper.drawPath(p);
132 }
134 Q_FOREACH(const KoShape *shape, text->shapesInside()) {
135 areas.append(shape->transformation().map(shape->outline()));
136 }
137 Q_FOREACH(QLineF arrow, getTextAreaOrderArrows(areas)) {
138 helper.drawGradientArrow(arrow.p1(), arrow.p2(), 1.5 * d->handleRadius);
139 }
140 }
141 if (d->drawBoundingRect) {
142 QPainterPath rect;
143 rect.addRect(text->outlineRect());
144 helper.drawPath(rect);
145 }
146 }
147 if (d->drawTextWrappingArea) {
148 if (d->textWrappingAreasHovered) {
150 }
152 Q_FOREACH(QLineF arrow, getTextAreaOrderArrows(areas)) {
153 helper.drawGradientArrow(arrow.p1(), arrow.p2(), 1.5 * d->handleRadius);
154 }
155 Q_FOREACH(const QPainterPath path, areas) {
156 helper.drawPath(path);
157 }
158 }
159 painter->restore();
160
161 painter->save();
162 QIcon icon = contourModeActive? KisIconUtils::loadIcon(ICON_EXIT): KisIconUtils::loadIcon(ICON_ENTER);
163 QPixmap pm = icon.pixmap(BUTTON_ICON_SIZE, BUTTON_ICON_SIZE);
164 painter->setBrush(contourModeActive? pal.highlight(): pal.button());
165 QPen pen;
166 pen.setColor(contourModeActive? pal.highlightedText().color(): pal.buttonText().color());
167 pen.setCosmetic(true);
168 pen.setWidthF(d->decorationThickness);
169 painter->setPen(pen);
170 const QRectF buttonRect = d->getButtonRect(converter.documentToView().mapRect(text->boundingRect()));
171 painter->drawRoundedRect(buttonRect, BUTTON_CORNER_ROUND, BUTTON_CORNER_ROUND);
172 painter->drawPixmap(buttonRect.topLeft()+QPointF(BUTTON_PADDING, BUTTON_PADDING), pm);
173 painter->restore();
174}
175
176void KoSvgTextShapeOutlineHelper::paint(QPainter *painter, const KoViewConverter &converter)
177{
178 const QPalette pal = qApp->palette();
179 KoSvgTextShape *text = d->getTextModeShape();
180 if (text) {
181 paintTextShape(painter, converter, pal, text, true);
182 } else {
183 Q_FOREACH(KoShape* shape, d->canvas->shapeManager()->selection()->selectedEditableShapes()) {
184 text = dynamic_cast<KoSvgTextShape*>(shape);
185 if (d->drawButton(text)) {
186 paintTextShape(painter, converter, pal, text, false);
187 }
188 }
189 }
190}
191
193{
194 QRectF decorationRect;
195 KoSvgTextShape *text = d->getTextModeShape();
196 if (text) {
197 QRectF base = text->boundingRect();
198 base |= d->getButtonRectCorrected(base);
199 decorationRect = base;
200 } else {
201 Q_FOREACH(KoShape* shape, d->canvas->shapeManager()->selection()->selectedEditableShapes()) {
202 text = dynamic_cast<KoSvgTextShape*>(shape);
203 if (d->drawButton(text)) {
204 QRectF base = text->boundingRect();
205 base |= d->getButtonRectCorrected(base);
206 decorationRect |= base;
207 }
208 }
209 }
210 return decorationRect;
211}
212
214{
215 d->drawBoundingRect = enable;
216}
217
219{
220 return d->drawBoundingRect;
221}
222
224{
225 d->drawTextWrappingArea = enable;
226}
227
229{
230 d->drawOutline = enable;
231}
232
234{
235 return d->drawOutline;
236}
237
239{
240 d->handleRadius = radius;
241}
242
244{
245 d->decorationThickness = thickness;
246}
247
249{
250 KoSvgTextShape *text = d->getTextModeShape();
251 if (text) {
252 if (d->getButtonRect(text->boundingRect()).contains(point)) {
253 return text;
254 }
255 }
256 return d->getPotentialTextShape(point);
257}
258
260{
261 KoSvgTextShape *text = d->getTextModeShape();
262 if (text && !d->drawButton(text)) {
263 toggleTextContourMode(nullptr);
264 return true;
265 }
266 return false;
267}
268
270{
271 if (d->canvas) {
272 if (shape == d->canvas->currentShapeManagerOwnerShape()) {
273 d->canvas->setCurrentShapeManagerOwnerShape(nullptr);
274 } else {
275 d->canvas->setCurrentShapeManagerOwnerShape(shape);
276 }
277 }
278}
279
281{
282 d->textWrappingAreasHovered = enabled;
283}
const Params2D p
const int BUTTON_CORNER_ROUND
const QString ICON_ENTER
QList< QLineF > getTextAreaOrderArrows(QList< QPainterPath > areas)
const int BUTTON_ICON_SIZE
const QString ICON_EXIT
const int BUTTON_PADDING
static int buttonSize(int screen)
Definition KoToolBox.cpp:41
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawPath(const QPainterPath &path)
void setHandleStyle(const KisHandleStyle &style)
void drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius)
static KisHandleStyle & partiallyHighlightedPrimaryHandles()
static KisHandleStyle & secondarySelection()
virtual KoShape * currentShapeManagerOwnerShape() const
the shape that owns the currently active shape manager
virtual KoShapeManager * shapeManager() const =0
virtual const KoViewConverter * viewConverter() const =0
const QList< KoShape * > selectedEditableShapes() const
QList< KoShape * > shapes
KoSelection * selection
virtual QPainterPath outline() const
Definition KoShape.cpp:559
static KisHandlePainterHelper createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius=0.0, int decorationThickness=1)
Definition KoShape.cpp:982
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:383
void paintTextShape(QPainter *painter, const KoViewConverter &converter, const QPalette &pal, KoSvgTextShape *text, bool contourModeActive=false)
void setDrawShapeOutlines(bool enable)
setDrawShapeOutlines Draw the shape outlines instead of only the rect.
KoSvgTextShape * contourModeButtonHovered(const QPointF &point)
KoSvgTextShapeOutlineHelper(KoCanvasBase *canvas)
void setDrawBoundingRect(bool enable)
setDrawBoundingRect Whether to draw the bounding rect of the shapes.
void toggleTextContourMode(KoSvgTextShape *shape)
bool updateTextContourMode()
updateTextContourMode This checks if the text contour mode still has shapes, and otherwise switches o...
void setDrawTextWrappingArea(bool enable)
setDrawTextWrappingArea draw the wrapping area. The wrapping area is computed from the shapes as well...
void paint(QPainter *painter, const KoViewConverter &converter)
QRectF boundingRect() const override
Get the bounding box of the shape.
QList< QPainterPath > textWrappingAreas() const
textWrappingAreas The text wrapping areas are computed from shapesInside() and shapesSubtract(),...
QList< KoShape * > shapesInside
KoShapeManager * internalShapeManager() const
internalShapeManager
QRectF outlineRect() const override
virtual QPointF viewToDocument(const QPointF &viewPoint) const
virtual QPointF documentToView(const QPointF &documentPoint) const
QIcon loadIcon(const QString &name)
KoSvgTextShape * getPotentialTextShape(const QPointF &point)