Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAngleGauge.cpp
Go to the documentation of this file.
1/*
2 * KDE. Krita Project.
3 *
4 * SPDX-FileCopyrightText: 2020 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <QPainter>
10#include <QMouseEvent>
11#include <cmath>
12
13#include "KisAngleGauge.h"
14
25
27 : QWidget(parent)
28 , m_d(new Private)
29{
30 m_d->angle = 0.0;
31 m_d->snapAngle = 15.0;
32 m_d->resetAngle = 0.0;
33 m_d->increasingDirection = IncreasingDirection_CounterClockwise;
34 m_d->isPressed = false;
35 m_d->isMouseHover = false;
36
37 setFocusPolicy(Qt::WheelFocus);
38}
39
42
44{
45 return m_d->angle;
46}
47
49{
50 return m_d->snapAngle;
51}
52
54{
55 return m_d->resetAngle;
56}
57
59{
60 return m_d->increasingDirection;
61}
62
63void KisAngleGauge::setAngle(qreal newAngle)
64{
65 if (qFuzzyCompare(newAngle, m_d->angle)) {
66 return;
67 }
68
69 m_d->angle = newAngle;
70 update();
71 Q_EMIT angleChanged(newAngle);
72}
73
74void KisAngleGauge::setSnapAngle(qreal newSnapAngle)
75{
76 m_d->snapAngle = newSnapAngle;
77}
78
79void KisAngleGauge::setResetAngle(qreal newResetAngle)
80{
81 m_d->resetAngle = newResetAngle;
82}
83
85{
86 m_d->increasingDirection = newIncreasingDirection;
87 update();
88}
89
94
95void KisAngleGauge::paintEvent(QPaintEvent *e)
96{
97 QPainter painter(this);
98 const QPointF center(width() / 2.0, height() / 2.0);
99 const qreal minSide = std::min(center.x(), center.y());
100 const qreal radius = minSide * 0.9;
101 const qreal lineMarkerRadius = minSide * 0.1;
102 const qreal angleInRadians = m_d->angle * M_PI / 180.0;
103 const QPointF d(
104 center.x() + std::cos(angleInRadians) * radius,
105 m_d->increasingDirection == IncreasingDirection_CounterClockwise
106 ? center.y() - std::sin(angleInRadians) * radius
107 : center.y() + std::sin(angleInRadians) * radius
108 );
109
110 painter.setRenderHint(QPainter::Antialiasing, true);
111
112 QColor backgroundColor, circleColor, axesColor, angleLineColor, angleLineMarkerColor;
113 if (palette().color(QPalette::Window).lightness() < 128) {
114 circleColor = palette().color(QPalette::Light);
115 axesColor = palette().color(QPalette::Light);
116 axesColor.setAlpha(200);
117 if (isEnabled()) {
118 backgroundColor = palette().color(QPalette::Dark);
119 angleLineColor = QColor(255, 255, 255, 128);
120 angleLineMarkerColor = QColor(255, 255, 255, 200);
121 } else {
122 backgroundColor = palette().color(QPalette::Window);
123 angleLineColor = palette().color(QPalette::Light);
124 angleLineMarkerColor = palette().color(QPalette::Light);
125 }
126 } else {
127 circleColor = palette().color(QPalette::Dark);
128 axesColor = palette().color(QPalette::Dark);
129 axesColor.setAlpha(200);
130 if (isEnabled()) {
131 backgroundColor = palette().color(QPalette::Light);
132 angleLineColor = QColor(0, 0, 0, 128);
133 angleLineMarkerColor = QColor(0, 0, 0, 200);
134 } else {
135 backgroundColor = palette().color(QPalette::Window);
136 angleLineColor = palette().color(QPalette::Dark);
137 angleLineMarkerColor = palette().color(QPalette::Dark);
138 }
139 }
140
141 // Background
142 painter.setPen(Qt::transparent);
143 painter.setBrush(backgroundColor);
144 painter.drawEllipse(center, radius, radius);
145
146 // Axes lines
147 painter.setPen(QPen(axesColor, 1.0, Qt::DotLine));
148 painter.drawLine(center.x(), center.y() - radius + 1.0, center.x(), center.y() + radius - 1.0);
149 painter.drawLine(center.x() - radius + 1.0, center.y(), center.x() + radius - 1.0, center.y());
150
151 // Outer circle
152 if (this->hasFocus()) {
153 painter.setPen(QPen(palette().color(QPalette::Highlight), 2.0));
154 } else {
155 if (m_d->isMouseHover && isEnabled()) {
156 painter.setPen(QPen(palette().color(QPalette::Highlight), 1.0));
157 } else {
158 painter.setPen(QPen(circleColor, 1.0));
159 }
160 }
161 painter.setBrush(Qt::transparent);
162 painter.drawEllipse(center, radius, radius);
163
164 // Angle line
165 painter.setPen(QPen(angleLineColor, 1.0));
166 painter.drawLine(center, d);
167
168 // Inner line marker
169 painter.setPen(Qt::transparent);
170 painter.setBrush(angleLineMarkerColor);
171 painter.drawEllipse(center, lineMarkerRadius, lineMarkerRadius);
172
173 // Outer line marker
174 painter.setBrush(angleLineMarkerColor);
175 painter.drawEllipse(d, lineMarkerRadius, lineMarkerRadius);
176
177 e->accept();
178}
179
181{
182 if (e->button() != Qt::LeftButton) {
183 e->ignore();
184 return;
185 }
186
187 const QPointF center(width() / 2.0, height() / 2.0);
188 const qreal radius = std::min(center.x(), center.y());
189 const qreal radiusSquared = radius * radius;
190 const QPointF delta(e->x() - center.x(), e->y() - center.y());
191 const qreal distanceSquared = delta.x() * delta.x() + delta.y() * delta.y();
192
193 if (distanceSquared > radiusSquared) {
194 e->ignore();
195 return;
196 }
197
198 qreal angle =
199 std::atan2(
200 m_d->increasingDirection == IncreasingDirection_CounterClockwise ? -delta.y() : delta.y(),
201 delta.x()
202 );
203
204 if (e->modifiers() & Qt::ControlModifier) {
205 const qreal sa = m_d->snapAngle * M_PI / 180.0;
206 angle = std::round(angle / sa) * sa;
207 }
208
209 setAngle(angle * 180.0 / M_PI);
210
211 m_d->isPressed = true;
212
213 e->accept();
214}
215
217{
218 if (e->button() == Qt::LeftButton && m_d->isPressed) {
219 m_d->isPressed = false;
220 e->accept();
221 return;
222 }
223 e->ignore();
224}
225
227{
228 if (!(e->buttons() & Qt::LeftButton) || !m_d->isPressed) {
229 e->ignore();
230 return;
231 }
232
233 const QPointF center(width() / 2.0, height() / 2.0);
234 const qreal radius = std::min(center.x(), center.y());
235 const qreal radiusSquared = radius * radius;
236 const QPointF delta(e->x() - center.x(), e->y() - center.y());
237 const qreal distanceSquared = delta.x() * delta.x() + delta.y() * delta.y();
238 qreal angle =
239 std::atan2(
240 m_d->increasingDirection == IncreasingDirection_CounterClockwise ? -delta.y() : delta.y(),
241 delta.x()
242 ) * 180.0 / M_PI;
243
244 const qreal snapDistance = qMax(m_d->minimumSnapDistance * m_d->minimumSnapDistance, radiusSquared * 4.0);
245 const bool controlPressed = e->modifiers() & Qt::ControlModifier;
246 const bool shiftPressed = e->modifiers() & Qt::ShiftModifier;
247
248 if (controlPressed && shiftPressed) {
249 angle = std::round(angle);
250 } else if (!shiftPressed && (controlPressed || distanceSquared < snapDistance)) {
251 angle = std::round(angle / m_d->snapAngle) * m_d->snapAngle;
252 }
253
255
256 e->accept();
257}
258
260{
261 if (e->button() == Qt::LeftButton) {
262 reset();
263 e->accept();
264 } else {
265 e->ignore();
266 }
267}
268
269void KisAngleGauge::wheelEvent(QWheelEvent *e)
270{
271 if (e->angleDelta().y() > 0) {
272 if (e->modifiers() & Qt::ControlModifier) {
273 setAngle(std::floor((m_d->angle + m_d->snapAngle) / m_d->snapAngle) * m_d->snapAngle);
274 } else {
275 setAngle(m_d->angle + 1.0);
276 }
277 } else if (e->angleDelta().y() < 0) {
278 if (e->modifiers() & Qt::ControlModifier) {
279 setAngle(std::ceil((m_d->angle - m_d->snapAngle) / m_d->snapAngle) * m_d->snapAngle);
280 } else {
281 setAngle(m_d->angle - 1.0);
282 }
283 }
284 e->accept();
285}
286
288{
289 if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Right) {
290 if (e->modifiers() & Qt::ControlModifier) {
291 setAngle(std::floor((m_d->angle + m_d->snapAngle) / m_d->snapAngle) * m_d->snapAngle);
292 } else {
293 setAngle(m_d->angle + 1.0);
294 }
295 e->accept();
296 } else if (e->key() == Qt::Key_Down || e->key() == Qt::Key_Left) {
297 if (e->modifiers() & Qt::ControlModifier) {
298 setAngle(std::ceil((m_d->angle - m_d->snapAngle) / m_d->snapAngle) * m_d->snapAngle);
299 } else {
300 setAngle(m_d->angle - 1.0);
301 }
302 e->accept();
303 } else {
304 e->ignore();
305 }
306}
307
308#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
309void KisAngleGauge::enterEvent(QEvent *e)
310#else
311void KisAngleGauge::enterEvent(QEnterEvent *e)
312#endif
313{
314 m_d->isMouseHover = true;
315 update();
316 QWidget::enterEvent(e);
317}
318
320{
321 m_d->isMouseHover = false;
322 update();
323 QWidget::leaveEvent(e);
324}
void mouseReleaseEvent(QMouseEvent *e) override
void enterEvent(QEnterEvent *e) override
IncreasingDirection increasingDirection() const
Gets the direction in which the angle increases.
void mouseDoubleClickEvent(QMouseEvent *e) override
void angleChanged(qreal angle)
Signal emitted when the angle has changed.
void setIncreasingDirection(IncreasingDirection newIncreasingDirection)
Sets the increasing direction.
qreal resetAngle() const
Gets the angle that is used to reset the current angle.
void keyPressEvent(QKeyEvent *e) override
KisAngleGauge(QWidget *parent=0)
Construct a new KisAngleGauge widget.
void wheelEvent(QWheelEvent *e) override
void setSnapAngle(qreal newSnapAngle)
Sets the angle to which multiples the selected angle will snap.
void reset()
Sets the current angle to the reset angle.
qreal snapAngle() const
Gets the angle to which multiples the selected angle will snap.
void setResetAngle(qreal newResetAngle)
Sets the angle that is used to reset the current angle.
const QScopedPointer< Private > m_d
void mousePressEvent(QMouseEvent *e) override
void leaveEvent(QEvent *e) override
void paintEvent(QPaintEvent *e) override
@ IncreasingDirection_CounterClockwise
void mouseMoveEvent(QMouseEvent *e) override
void setAngle(qreal newAngle)
Sets the current angle.
qreal angle() const
Gets the current angle.
static bool qFuzzyCompare(half p1, half p2)
#define M_PI
Definition kis_global.h:111
rgba palette[MAX_PALETTE]
Definition palette.c:35
static constexpr qreal minimumSnapDistance
IncreasingDirection increasingDirection