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>
19
20const int BUTTON_ICON_SIZE = 16;
21const int BUTTON_PADDING = 4;
22const int BUTTON_CORNER_ROUND = 1;
23const QString ICON_EXIT = "object-ungroup-calligra";
24const QString ICON_ENTER = "object-group-calligra";
25
27 Private(KoCanvasBase *canvasBase): canvas(canvasBase) {
28 }
30 int handleRadius = 7;
32
33 bool drawBoundingRect = true;
35 bool drawOutline = false;
37
41
42 KoSvgTextShape *getPotentialTextShape(const QPointF &point) {
44 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
45 if (drawButton(text)) {
46 if (getButtonRectCorrected(text->boundingRect()).contains(point)) {
47 return text;
48 }
49 }
50 }
51 return nullptr;
52 }
53
55 return canvas->viewConverter();
56 }
57
58 QRectF getButtonRect(QRectF base) {
60 return QRectF(base.topRight(), QSizeF(buttonSize, buttonSize));
61 }
62 QRectF getButtonRectCorrected(QRectF base) {
63 return converter()->viewToDocument().mapRect(getButtonRect(converter()->documentToView().mapRect(base)));
64 }
65
67 return text && !text->internalShapeManager()->shapes().isEmpty();
68 }
69
73};
74
79
84
86 QList<QLineF> lines;
87 if (areas.size() <= 1) return lines;
88 for (int i = 1; i < areas.size(); i++) {
89 const QPainterPath previous = areas.at(i-1);
90 const QPainterPath next = areas.at(i);
91 const bool overlap = previous.intersects(next);
92 QLineF arrow(previous.boundingRect().center(), next.boundingRect().center());
93 if (!overlap) {
94
95 Q_FOREACH (QPolygonF p, previous.toSubpathPolygons()) {
96 if (p.size() == 1) continue;
97 for (int j = 1; j < p.size(); j++) {
98 QLineF l2(p.at(j-1), p.at(j));
99 QPointF intersect;
100 if (l2.intersects(arrow, &intersect) == QLineF::BoundedIntersection) {
101 arrow.setP1(intersect);
102 break;
103 }
104 }
105 }
106 Q_FOREACH (QPolygonF p, next.toSubpathPolygons()) {
107 if (p.size() == 1) continue;
108 for (int j = 1; j < p.size(); j++) {
109 QLineF l2(p.at(j-1), p.at(j));
110 QPointF intersect;
111 if (l2.intersects(arrow, &intersect) == QLineF::BoundedIntersection) {
112 arrow.setP2(intersect);
113 break;
114 }
115 }
116 }
117
118 }
119 lines.append(arrow);
120 }
121 return lines;
122}
123
124void KoSvgTextShapeOutlineHelper::paintTextShape(QPainter *painter, const KoViewConverter &converter,
125 KoSvgTextShape *text, bool contourModeActive) {
126 painter->save();
127 KisHandlePalette handlePalette = d->renderInterface()->handlePaletteForDisplayColorSpace();
129 KoShape::createHandlePainterHelperView(painter, text, converter, d->handleRadius, d->decorationThickness);
131 if (contourModeActive) {
132 if (d->drawOutline) {
133
134 Q_FOREACH(KoShape *shape, text->internalShapeManager()->shapes()) {
135 QPainterPath p = shape->transformation().map(shape->outline());
136 helper.drawPath(p);
137 }
139 Q_FOREACH(const KoShape *shape, text->shapesInside()) {
140 areas.append(shape->transformation().map(shape->outline()));
141 }
142 Q_FOREACH(QLineF arrow, getTextAreaOrderArrows(areas)) {
143 helper.drawGradientArrow(arrow.p1(), arrow.p2(), 1.5 * d->handleRadius);
144 }
145 }
146 if (d->drawBoundingRect) {
147 QPainterPath rect;
148 rect.addRect(text->outlineRect());
149 helper.drawPath(rect);
150 }
151 }
152 if (d->drawTextWrappingArea) {
153 if (d->textWrappingAreasHovered) {
155 }
157 Q_FOREACH(QLineF arrow, getTextAreaOrderArrows(areas)) {
158 helper.drawGradientArrow(arrow.p1(), arrow.p2(), 1.5 * d->handleRadius);
159 }
160 Q_FOREACH(const QPainterPath path, areas) {
161 helper.drawPath(path);
162 }
163 }
164 painter->restore();
165
166 painter->save();
167 QPalette systemPalette = d->renderInterface()->systemPaletteForDisplayColorSpace();
168 QIcon icon = contourModeActive? KisIconUtils::loadIcon(ICON_EXIT): KisIconUtils::loadIcon(ICON_ENTER);
169 QImage pm = d->renderInterface()->convertImageToDisplayColorSpace(icon.pixmap(BUTTON_ICON_SIZE, BUTTON_ICON_SIZE).toImage());
170 painter->setBrush(contourModeActive? systemPalette.highlight(): systemPalette.button());
171 QPen pen;
172 pen.setColor(contourModeActive? systemPalette.highlightedText().color(): systemPalette.buttonText().color());
173 pen.setCosmetic(true);
174 pen.setWidthF(d->decorationThickness);
175 painter->setPen(pen);
176 const QRectF buttonRect = d->getButtonRect(converter.documentToView().mapRect(text->boundingRect()));
177 painter->drawRoundedRect(buttonRect, BUTTON_CORNER_ROUND, BUTTON_CORNER_ROUND);
178 painter->drawImage(buttonRect.topLeft()+QPointF(BUTTON_PADDING, BUTTON_PADDING), pm);
179 painter->restore();
180}
181
182void KoSvgTextShapeOutlineHelper::paint(QPainter *painter, const KoViewConverter &converter)
183{
184 KoSvgTextShape *text = d->getTextModeShape();
185 if (text) {
186 paintTextShape(painter, converter, text, true);
187 } else {
188 Q_FOREACH(KoShape* shape, d->canvas->shapeManager()->selection()->selectedEditableShapes()) {
189 text = dynamic_cast<KoSvgTextShape*>(shape);
190 if (d->drawButton(text)) {
191 paintTextShape(painter, converter, text, false);
192 }
193 }
194 }
195}
196
198{
199 QRectF decorationRect;
200 KoSvgTextShape *text = d->getTextModeShape();
201 if (text) {
202 QRectF base = text->boundingRect();
203 base |= d->getButtonRectCorrected(base);
204 decorationRect = base;
205 } else {
206 Q_FOREACH(KoShape* shape, d->canvas->shapeManager()->selection()->selectedEditableShapes()) {
207 text = dynamic_cast<KoSvgTextShape*>(shape);
208 if (d->drawButton(text)) {
209 QRectF base = text->boundingRect();
210 base |= d->getButtonRectCorrected(base);
211 decorationRect |= base;
212 }
213 }
214 }
215 return decorationRect;
216}
217
219{
220 d->drawBoundingRect = enable;
221}
222
224{
225 return d->drawBoundingRect;
226}
227
229{
230 d->drawTextWrappingArea = enable;
231}
232
234{
235 d->drawOutline = enable;
236}
237
239{
240 return d->drawOutline;
241}
242
244{
245 d->handleRadius = radius;
246}
247
249{
250 d->decorationThickness = thickness;
251}
252
254{
255 KoSvgTextShape *text = d->getTextModeShape();
256 if (text) {
257 if (d->getButtonRect(text->boundingRect()).contains(point)) {
258 return text;
259 }
260 }
261 return d->getPotentialTextShape(point);
262}
263
265{
266 KoSvgTextShape *text = d->getTextModeShape();
267 if (text && !d->drawButton(text)) {
268 toggleTextContourMode(nullptr);
269 return true;
270 }
271 return false;
272}
273
275{
276 if (d->canvas) {
277 if (shape == d->canvas->currentShapeManagerOwnerShape()) {
278 d->canvas->setCurrentShapeManagerOwnerShape(nullptr);
279 } else {
280 d->canvas->setCurrentShapeManagerOwnerShape(shape);
281 }
282 }
283}
284
286{
287 d->textWrappingAreasHovered = enabled;
288}
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(KisHandlePalette palette=KisHandlePalette())
static KisHandleStyle & secondarySelection(KisHandlePalette palette=KisHandlePalette())
virtual KoShape * currentShapeManagerOwnerShape() const
the shape that owns the currently active shape manager
virtual KoShapeManager * shapeManager() const =0
virtual const KoViewConverter * viewConverter() const =0
virtual KoColorDisplayRendererInterface * displayRendererInterface() const
displayRendererInterface The display renderer interface has a number of color conversion functions wh...
const QList< KoShape * > selectedEditableShapes() const
QList< KoShape * > shapes
KoSelection * selection
virtual QPainterPath outline() const
Definition KoShape.cpp:554
static KisHandlePainterHelper createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius=0.0, int decorationThickness=1)
Definition KoShape.cpp:977
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:378
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)
void paintTextShape(QPainter *painter, const KoViewConverter &converter, KoSvgTextShape *text, bool contourModeActive=false)
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)
KoColorDisplayRendererInterface * renderInterface()
KoSvgTextShape * getPotentialTextShape(const QPointF &point)