Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgTextTypeSettingStrategy.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-3.0-or-later
5 */
6
8#include "SvgTextCursor.h"
12
13#include <KoToolBase.h>
14#include <KoCanvasBase.h>
15#include "KoSnapGuide.h"
16#include <QVector2D>
17#include <kis_algebra_2d.h>
18#include <QDebug>
19#include <KoViewConverter.h>
20
21SvgTextTypeSettingStrategy::SvgTextTypeSettingStrategy(KoToolBase *tool, KoSvgTextShape *textShape, SvgTextCursor *textCursor, const QRectF &regionOfInterest, Qt::KeyboardModifiers modifiers)
23 , m_shape(textShape)
24 , m_dragStart(regionOfInterest.center())
25 , m_deltaCalc(true)
26 , m_modifiers(modifiers)
27 , m_textData(textShape->getMemento())
28{
29 m_cursorPos = textCursor->getPos();
30 m_cursorAnchor = textCursor->getAnchor();
31 m_editingType = textCursor->typeSettingHandleAtPos(regionOfInterest);
33}
34
35void SvgTextTypeSettingStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
36{
37 QPointF delta = mouseLocation - m_dragStart;
38 m_modifiers = modifiers;
39
40 if (m_modifiers & Qt::ShiftModifier) {
41 delta = snapToClosestAxis(delta);
42 m_dragCurrent = m_dragStart + delta;
43 m_currentDelta = delta;
44 } else {
46 tool()->canvas()->snapGuide()->snap(mouseLocation, modifiers);
48 }
49
50
52 SvgTextShapeManagerBlocker blocker(tool()->canvas()->shapeManager());
53 // TODO: replace with KoShapeBulkActionLock (recursive locking is not supported right now)
54 QRectF updateRect = m_shape->boundingRect();
55 if (m_previousCmd) {
56 m_previousCmd->undo();
57 }
59 if (m_previousCmd) {
60 m_previousCmd->redo();
61 }
62 updateRect |= m_shape->boundingRect();
63 blocker.unlock();
64 m_shape->updateAbsolute(updateRect);
65 }
66}
67
69{
70 if (m_editingType == int(SvgTextCursor::NoHandle)) return nullptr;
71 QPointF delta = m_currentDelta;
72
74 if (originalTf.isEmpty()) return nullptr;
75
76 KUndo2Command *cmd = nullptr;
80
82 } else {
83 const QPointF dragStart = m_shape->documentToShape(m_dragStart);
84 const QPointF dragCurrent = m_shape->documentToShape(m_dragCurrent);
85 const int closestPos = m_referenceCursorPos;
86 const QList<KoSvgTextCharacterInfo> infos = m_shape->getPositionsAndRotationsForRange(closestPos, closestPos);
87
88 if (infos.empty()) return cmd;
89
90 const KoSvgTextCharacterInfo info = infos.first();
91 const QTransform tf = QTransform::fromTranslate(info.finalPos.x(), info.finalPos.y()) * QTransform().rotate(info.rotateDeg);
92 const QLineF line = tf.map(QLineF(QPointF(), info.advance));
93 const qreal distNew = kisDistanceToLine(dragCurrent, line);
94
96 KoSvgTextProperties oldProps = m_shape->propertiesForPos(closestPos, true);
97
99 QVector<QPointF> newPositions;
100 QVector<qreal> newRotations;
101
103 const qreal distOld = kisDistanceToLine(dragStart, line);
104 const qreal scale = qMax(0.000001, distNew/distOld);
106 length.value *= scale;
107 props.setFontSize(length);
108
110 // When we change font size, we need to correct the offset for the scaled advances.
111 // Technically, we need to test against the laid out text to get the correct advance, but that's really complicated...
112 QPointF diff = QPointF();
113
114 Q_FOREACH(const KoSvgTextCharacterInfo originalInfo, originalTf) {
115 const QTransform rotate = QTransform().rotate(originalInfo.rotateDeg);
116 const QPointF newPos = originalInfo.finalPos + diff;
117 const QTransform oTf = QTransform::fromTranslate(originalInfo.finalPos.x(), originalInfo.finalPos.y()) * rotate;
118 diff += (oTf.map(originalInfo.advance*scale) - oTf.map(originalInfo.advance));
119 newPositions.append(newPos);
120 newRotations.append(originalInfo.rotateDeg);
121 }
122 }
123 } else if (m_editingType == int(SvgTextCursor::BaselineShift)) {
125
126 const QLineF normal = line.normalVector();
127 qreal dot = QVector2D::dotProduct(QVector2D(normal.p2() - normal.p1()), QVector2D(dragCurrent-line.p1()));
128 length.value = dot < 0? -distNew: distNew;
133 const qreal metricsMultiplier = oldProps.fontSize().value/qreal(info.metrics.fontSize);
134
135 const qreal ascender = metricsMultiplier*info.metrics.ascender;
136 const qreal descender = metricsMultiplier*info.metrics.descender;
137 qreal lineGap = distNew - fabs(m_editingType == int(SvgTextCursor::LineHeightTop)? ascender: descender);
138 lineHeight.length.value = (ascender-descender)+lineGap+lineGap;
139 lineHeight.isNormal = false;
140 lineHeight.isNumber = false;
141
142 props.setProperty(KoSvgTextProperties::LineHeightId, QVariant::fromValue(lineHeight));
143 }
144 if (!props.isEmpty()) {
145 int pos = m_cursorPos == m_cursorAnchor? -1: m_cursorPos;
146 int anchor = m_cursorPos == m_cursorAnchor? -1: m_cursorAnchor;
147 if (!newPositions.isEmpty()) {
148 cmd = new KUndo2Command();
149 KUndo2Command *cmd2 = new SvgTextMergePropertiesRangeCommand(m_shape, props, pos, anchor, QSet<KoSvgTextProperties::PropertyId>(), cmd);
150 new SvgTextChangeTransformsOnRange(m_shape, m_cursorPos, m_cursorAnchor, newPositions, newRotations, m_deltaCalc, cmd);
151 cmd->setText(cmd2->text());
152 } else {
153 cmd = new SvgTextMergePropertiesRangeCommand(m_shape, props, pos, anchor);
154 }
155 }
156 }
157 return cmd;
158}
159
161{
162 tool()->canvas()->snapGuide()->reset();
163 QRectF updateRect = m_shape->boundingRect();
164 if (m_previousCmd) {
165 m_previousCmd->undo();
166 }
167 updateRect |= m_shape->boundingRect();
171}
172
173void SvgTextTypeSettingStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
174{
175 m_modifiers = modifiers;
177}
qreal metricsMultiplier(const CssQmlUnitConverter::UserUnits type, const KoSvgText::FontMetrics metrics, const qreal fontSize, const qreal percentageReference)
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
void setText(const KUndo2MagicString &text)
KUndo2MagicString text() const
KoSnapGuide * snapGuide
QPointF documentToShape(const QPointF &point) const
Transforms point from document coordinates to shape coordinates.
Definition KoShape.cpp:1016
virtual void updateAbsolute(const QRectF &rect) const
Definition KoShape.cpp:545
void reset()
Resets the snap guide.
QPointF snap(const QPointF &mousePosition, Qt::KeyboardModifiers modifiers)
snaps the mouse position, returns if mouse was snapped
@ LineHeightId
KoSvgText::AutoValue.
@ BaselineShiftModeId
KoSvgText::BaselineShiftMode.
void setFontSize(const KoSvgText::CssLengthPercentage length)
void setProperty(PropertyId id, const QVariant &value)
QVariant propertyOrDefault(PropertyId id) const
KoSvgText::CssLengthPercentage fontSize() const
void setMemento(const KoSvgTextShapeMementoSP memento)
Set the text data and layout info, reset listening cursors to 0.
QRectF boundingRect() const override
Get the bounding box of the shape.
@ PreformattedText
Text-on-Path falls under this or PrePositionedText depending on collapse of lines.
KoSvgTextProperties propertiesForPos(const int pos, bool inherited=false) const
Return the properties at a given position.
TextType textType() const
textType This enum gives an indication of what kind of text this shape is. The different text types a...
QList< KoSvgTextCharacterInfo > getPositionsAndRotationsForRange(const int startPos, const int endPos) const
getPositionsAndRotationsForRange
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
virtual void repaintDecorations()
The SvgTextMergePropertiesRangeCommand class This sets properties on a specific range in a single tex...
KUndo2Command * createCommand() override
void finishInteraction(Qt::KeyboardModifiers modifiers) override
SvgTextTypeSettingStrategy(KoToolBase *tool, KoSvgTextShape *textShape, SvgTextCursor *textCursor, const QRectF &regionOfInterest, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override
QScopedPointer< KUndo2Command > m_previousCmd
qreal kisDistanceToLine(const QPointF &m, const QLineF &line)
Definition kis_global.h:234
PointType snapToClosestAxis(PointType P)
Definition kis_global.h:199
@ ShiftLengthPercentage
Css Length Percentage, percentage is lh.
Definition KoSvgText.h:244
The KoSvgTextCharacterInfo class This is a small struct to convey information about character positio...
KoSvgText::FontMetrics metrics
<Whether the character is in the middle of a cluster.
qint32 fontSize
Currently set size, CSS unit 'em'.
Definition KoSvgText.h:329
qint32 descender
distance for origin to bottom.
Definition KoSvgText.h:340
qint32 ascender
distance from origin to top.
Definition KoSvgText.h:339
bool isNumber
Length or number.
Definition KoSvgText.h:695
CssLengthPercentage length
Definition KoSvgText.h:693
bool isNormal
It's a number indicating the lineHeight;.
Definition KoSvgText.h:696
The SvgTextCursor class.
int getAnchor()
Get the current selection anchor. This is the same as position, unless there's a selection.
TypeSettingModeHandle
Handles used by type setting mode.
@ BaselineShift
The text properties handles.
int getPos()
Get the current position.
int posForTypeSettingHandleAndRect(const TypeSettingModeHandle handle, const QRectF regionOfInterest)
posForHandleAndRect Returns the closest cursor position for a given region and typesetting handle....
TypeSettingModeHandle typeSettingHandleAtPos(const QRectF regionOfInterest)
Get typeSettingMode handle for text;.