44#include " arrowtool.h"
55#include " utils/confighandler.h"
66
7+ #include < QComboBox>
8+ #include < QHBoxLayout>
9+ #include < QLabel>
10+ #include < QWidget>
711#include < cmath>
812
913namespace {
1014const int ArrowWidth = 10 ;
1115const int ArrowHeight = 18 ;
16+ const int MinArrowStyle = 0 ;
17+ const int MaxArrowStyle = 1 ;
18+
19+ bool isValidArrowStyle (int style)
20+ {
21+ return style >= MinArrowStyle && style <= MaxArrowStyle;
22+ }
1223
1324QPainterPath getArrowHead (QPoint p1, QPoint p2, const int thickness)
1425{
@@ -58,11 +69,67 @@ QLine getShorterLine(QPoint p1, QPoint p2, const int thickness)
5869 return l.toLine ();
5970}
6071
72+ QPainterPath getCurvedArrowHead (QPointF p1, QPointF p2, const int thickness)
73+ {
74+ QLineF line (p1, p2);
75+ if (line.length () <= 0 ) {
76+ return {};
77+ }
78+
79+ const QPointF direction = (p2 - p1) / line.length ();
80+ const QPointF normal (-direction.y (), direction.x ());
81+ const QPointF baseCenter =
82+ getShorterLine (p1.toPoint (), p2.toPoint (), thickness).p2 ();
83+ const qreal halfWidth = (ArrowWidth + thickness * 2 ) / 2.0 ;
84+ const qreal baseDistance = QLineF (baseCenter, p2).length ();
85+ const qreal notchDepth = std::min<qreal>(baseDistance * 0.45 , halfWidth);
86+
87+ const QPointF baseLeft = baseCenter + normal * halfWidth;
88+ const QPointF baseRight = baseCenter - normal * halfWidth;
89+ const QPointF notch = baseCenter + direction * notchDepth;
90+ const QPointF leftControl = baseCenter + normal * halfWidth * 0.25 ;
91+ const QPointF rightControl = baseCenter - normal * halfWidth * 0.25 ;
92+
93+ QPainterPath path;
94+ path.moveTo (p2);
95+ path.lineTo (baseLeft);
96+ path.quadTo (leftControl, notch);
97+ path.quadTo (rightControl, baseRight);
98+ path.lineTo (p2);
99+ return path;
100+ }
101+
102+ QLineF getCurvedArrowShaft (QPointF p1, QPointF p2, const int thickness)
103+ {
104+ QLineF line (p1, p2);
105+ if (line.length () <= 0 ) {
106+ return {};
107+ }
108+
109+ const QPointF direction = (p2 - p1) / line.length ();
110+ QLineF shaft (getShorterLine (p1.toPoint (), p2.toPoint (), thickness));
111+ const qreal notchDepth =
112+ std::min<qreal>(QLineF (shaft.p2 (), p2).length () * 0.45 ,
113+ (ArrowWidth + thickness * 2 ) / 2.0 );
114+ constexpr qreal overlap = 1.0 ;
115+
116+ // The curved head has a concave back, so extend the straight shaft
117+ // slightly into the head to avoid a visible gap without leaking past
118+ // the head outline at large thicknesses.
119+ shaft.setP2 (shaft.p2 () + direction * (notchDepth + overlap));
120+ return shaft;
121+ }
122+
61123} // unnamed namespace
62124
63125ArrowTool::ArrowTool (QObject* parent)
64126 : AbstractTwoPointTool(parent)
65127{
128+ const int configuredArrowStyle = ConfigHandler ().arrowStyle ();
129+ if (isValidArrowStyle (configuredArrowStyle)) {
130+ m_arrowStyle = static_cast <ArrowStyle>(configuredArrowStyle);
131+ }
132+
66133 setPadding (ArrowWidth / 2 );
67134 m_supportsOrthogonalAdj = true ;
68135 m_supportsDiagonalAdj = true ;
@@ -135,6 +202,27 @@ QRect ArrowTool::boundingRect() const
135202 return rect.normalized ();
136203}
137204
205+ QWidget* ArrowTool::configurationWidget ()
206+ {
207+ auto * widget = new QWidget ();
208+ auto * layout = new QHBoxLayout (widget);
209+ auto * label = new QLabel (tr (" Arrow style:" ), widget);
210+ auto * styleSelector = new QComboBox (widget);
211+
212+ styleSelector->addItem (tr (" Default" ));
213+ styleSelector->addItem (tr (" Curved" ));
214+ styleSelector->setCurrentIndex (static_cast <int >(m_arrowStyle));
215+ connect (styleSelector,
216+ qOverload<int >(&QComboBox::currentIndexChanged),
217+ this ,
218+ &ArrowTool::setArrowStyle);
219+
220+ layout->addWidget (label);
221+ layout->addWidget (styleSelector);
222+
223+ return widget;
224+ }
225+
138226CaptureTool* ArrowTool::copy (QObject* parent)
139227{
140228 auto * tool = new ArrowTool (parent);
@@ -145,7 +233,8 @@ CaptureTool* ArrowTool::copy(QObject* parent)
145233void ArrowTool::copyParams (const ArrowTool* from, ArrowTool* to)
146234{
147235 AbstractTwoPointTool::copyParams (from, to);
148- to->m_arrowPath = this ->m_arrowPath ;
236+ to->m_arrowPath = from->m_arrowPath ;
237+ to->m_arrowStyle = from->m_arrowStyle ;
149238}
150239
151240void ArrowTool::process (QPainter& painter, const QPixmap& pixmap)
@@ -157,12 +246,29 @@ void ArrowTool::process(QPainter& painter, const QPixmap& pixmap)
157246
158247 Q_UNUSED (pixmap)
159248 painter.setPen (QPen (color (), size ()));
160- painter.drawLine (getShorterLine (head, tail, size ()));
161- m_arrowPath = getArrowHead (head, tail, size ());
249+ if (m_arrowStyle == ArrowStyle::Default) {
250+ painter.drawLine (getShorterLine (head, tail, size ()));
251+ m_arrowPath = getArrowHead (head, tail, size ());
252+ painter.fillPath (m_arrowPath, QBrush (color ()));
253+ return ;
254+ }
255+
256+ painter.setPen (QPen (color (), size (), Qt::SolidLine, Qt::FlatCap));
257+ painter.drawLine (getCurvedArrowShaft (head, tail, size ()));
258+ m_arrowPath = getCurvedArrowHead (head, tail, size ());
162259 painter.fillPath (m_arrowPath, QBrush (color ()));
163260}
164261
165262void ArrowTool::pressed (CaptureContext& context)
166263{
167264 Q_UNUSED (context)
168265}
266+
267+ void ArrowTool::setArrowStyle (int style)
268+ {
269+ if (!isValidArrowStyle (style)) {
270+ style = static_cast <int >(ArrowStyle::Default);
271+ }
272+ m_arrowStyle = static_cast <ArrowStyle>(style);
273+ ConfigHandler ().setArrowStyle (style);
274+ }
0 commit comments