Skip to content

Commit cb607c1

Browse files
serebryakov02serebryakov02
andauthored
Add arrow style selector (#4769)
* Add arrow style selector * Fix curved arrow artifact at large thickness --------- Co-authored-by: serebryakov02 <nik04ta01@gmail.com>
1 parent b92a578 commit cb607c1

5 files changed

Lines changed: 125 additions & 3 deletions

File tree

flameshot.example.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
;; Last used tool thickness; same thickness shared by Pencil, Line, Arrow, Rectangular Selection, Circle (int)
6565
;drawThickness=3
6666
;
67+
;; Arrow style: 0 = default, 1 = curved (int)
68+
;arrowStyle=0
69+
;
6770
;; Last used font size (int)
6871
;drawFontSize=8
6972
;

src/tools/arrow/arrowtool.cpp

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@
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

913
namespace {
1014
const int ArrowWidth = 10;
1115
const 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

1324
QPainterPath 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

63125
ArrowTool::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+
138226
CaptureTool* ArrowTool::copy(QObject* parent)
139227
{
140228
auto* tool = new ArrowTool(parent);
@@ -145,7 +233,8 @@ CaptureTool* ArrowTool::copy(QObject* parent)
145233
void 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

151240
void 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

165262
void 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+
}

src/tools/arrow/arrowtool.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class ArrowTool : public AbstractTwoPointTool
1818
QString name() const override;
1919
QString description() const override;
2020
QRect boundingRect() const override;
21+
QWidget* configurationWidget() override;
2122

2223
CaptureTool* copy(QObject* parent = nullptr) override;
2324
void process(QPainter& painter, const QPixmap& pixmap) override;
@@ -29,6 +30,16 @@ class ArrowTool : public AbstractTwoPointTool
2930
public slots:
3031
void pressed(CaptureContext& context) override;
3132

33+
private slots:
34+
void setArrowStyle(int style);
35+
3236
private:
37+
enum class ArrowStyle
38+
{
39+
Default = 0,
40+
Curved = 1,
41+
};
42+
3343
QPainterPath m_arrowPath;
44+
ArrowStyle m_arrowStyle = ArrowStyle::Default;
3445
};

src/utils/confighandler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ static QMap<class QString, QSharedPointer<ValueHandler>>
136136
OPTION("showSelectionGeometryHideTime", LowerBoundedInt ( 0, 3000 )),
137137
OPTION("jpegQuality" , BoundedInt ( 0,100,75 )),
138138
OPTION("reverseArrow" ,Bool ( false )),
139+
OPTION("arrowStyle" ,BoundedInt ( 0, 1, 0 )),
139140
OPTION("insecurePixelate" ,Bool ( false )),
140141
#if defined(Q_OS_WIN)
141142
// Not visible on settings dialog

src/utils/confighandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class ConfigHandler : public QObject
139139
CONFIG_GETTER_SETTER(showSelectionGeometry, setShowSelectionGeometry, int)
140140
CONFIG_GETTER_SETTER(jpegQuality, setJpegQuality, int)
141141
CONFIG_GETTER_SETTER(reverseArrow, setReverseArrow, bool)
142+
CONFIG_GETTER_SETTER(arrowStyle, setArrowStyle, int)
142143
CONFIG_GETTER_SETTER(insecurePixelate, setInsecurePixelate, bool)
143144
CONFIG_GETTER_SETTER(showSelectionGeometryHideTime,
144145
showSelectionGeometryHideTime,

0 commit comments

Comments
 (0)