Loading...
Searching...
No Matches
DefaultGraphicsKnobImpl.hpp
1#pragma once
2#include <score/graphics/DefaultControlImpl.hpp>
3#include <score/graphics/InfiniteScroller.hpp>
4#include <score/graphics/widgets/Constants.hpp>
5#include <score/model/Skin.hpp>
6#include <score/tools/Cursor.hpp>
7#include <score/widgets/DoubleSpinBox.hpp>
8#include <score/widgets/SignalUtils.hpp>
9
10#include <ossia/detail/math.hpp>
11
12#include <QDoubleSpinBox>
13#include <QGraphicsProxyWidget>
14#include <QGraphicsScene>
15#include <QGraphicsSceneMouseEvent>
16#include <QGuiApplication>
17#include <QPainter>
18#include <QPointer>
19#include <QScreen>
20#include <QTimer>
21
22namespace score
23{
25{
26 template <typename T>
27 static void paint(
28 T& self, const score::Skin& skin, const QString& text, QPainter* painter,
29 QWidget* widget)
30 {
31 painter->setRenderHint(QPainter::Antialiasing, true);
32
33 constexpr const double adj = 6.;
34 constexpr const double space = 50.;
35 constexpr const double start = (270. - space) * 16.;
36 constexpr const double totalSpan = (360. - 2. * space) * 16.;
37
38 const QRectF srect = defaultKnobSize;
39 const QRectF r = srect.adjusted(adj, adj, -adj, -adj);
40 const double rw = r.width();
41
42 // Draw knob
43 painter->setPen(skin.Emphasis2.main.pen1);
44 painter->setBrush(skin.Emphasis2.main.brush);
45 painter->drawChord(r, start, -totalSpan);
46
47 const double valueSpan = -self.m_value * totalSpan;
48 double textDelta = 0.;
49 if(rw >= 30.)
50 {
51 painter->setPen(skin.Base4.main.pen3_solid_round_round);
52 textDelta = -10;
53 }
54 else if(rw >= 20.)
55 {
56 painter->setPen(skin.Base4.main.pen2_solid_round_round);
57 textDelta = -9;
58 }
59 else if(rw >= 10.)
60 {
61 painter->setPen(skin.Base4.main.pen1_5);
62 textDelta = -8;
63 }
64 else if(rw >= 5.)
65 {
66 painter->setPen(skin.Base4.main.pen1);
67 textDelta = -7;
68 }
69 painter->drawArc(r, start, valueSpan);
70
71 // Draw knob indicator
72 const double r1 = 0.5 * rw;
73 const double x0 = r.center().x();
74 const double y0 = r.center().y();
75 const double theta = -0.0174533 * (start + valueSpan) / 16.;
76 const double x1 = r.center().x() + r1 * cos(theta);
77 const double y1 = r.center().y() + r1 * sin(theta);
78
79 painter->drawLine(QPointF{x0, y0}, QPointF{x1, y1});
80
81 painter->setPen(skin.Base4.lighter180.pen1);
82 if(self.m_hasExec)
83 {
84 const QRectF er = r.adjusted(1.5, 1.5, -1.5, -1.5);
85 const double valueSpan = -self.m_execValue * totalSpan;
86 painter->drawArc(er, start, valueSpan);
87 }
88
89 // Draw text
90 painter->setFont(skin.Medium8Pt);
91 painter->drawText(
92 QRectF{0., srect.height() + textDelta, srect.width(), 10.}, text,
93 QTextOption(Qt::AlignCenter));
94
95 painter->setRenderHint(QPainter::Antialiasing, false);
96 }
97
98 template <typename T>
99 static void mousePressEvent(T& self, QGraphicsSceneMouseEvent* event)
100 {
101 if(event->button() == Qt::LeftButton)
102 {
103 self.m_grab = true;
104 InfiniteScroller::start(self, self.m_value);
105 }
106
107 event->accept();
108 }
109
110 template <typename T>
111 static void mouseMoveEvent(T& self, QGraphicsSceneMouseEvent* event)
112 {
113 if((event->buttons() & Qt::LeftButton) && self.m_grab)
114 {
115 double v = InfiniteScroller::move(event);
116 if(v != self.m_value)
117 {
118 self.m_value = v;
119 self.sliderMoved();
120 self.update();
121 }
122 }
123 event->accept();
124 }
125
126 template <typename T>
127 static void mouseReleaseEvent(T& self, QGraphicsSceneMouseEvent* event)
128 {
129 InfiniteScroller::stop(self, event);
130 if(self.m_grab)
131 {
132 double v = InfiniteScroller::move(event);
133 if(v != self.m_value)
134 {
135 self.m_value = v;
136 self.update();
137 }
138 }
139
140 self.m_grab = false;
141 self.sliderReleased();
142
143 if(event->button() == Qt::RightButton)
144 {
145 contextMenuEvent(self, event->scenePos());
146 }
147
148 event->accept();
149 }
150
151 template <typename T>
152 requires std::is_integral_v<std::decay_t<decltype(std::declval<T>().value())>>
153 static void contextMenuEvent(T& self, QPointF pos)
154 {
155 QTimer::singleShot(0, &self, [&, self_p = &self, pos] {
156 auto w = new SpinboxWithEnter;
157 w->setRange(self.min, self.max);
158
159 w->setValue(self.map(self.m_value));
160 auto obj = self.scene()->addWidget(
161 w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
162 obj->setPos(pos);
163
164 QTimer::singleShot(0, w, [w] { w->setFocus(); });
165
166 auto con = QObject::connect(
167 w, SignalUtils::QSpinBox_valueChanged_int(), &self,
168 [&self, obj, scene = self.scene()](double v) {
169 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
170 });
171
172 QObject::connect(
173 w, &SpinboxWithEnter::editingFinished, &self, [obj, con, self_p]() mutable {
174 if(obj != nullptr)
175 {
176 self_p->sliderReleased();
177 QObject::disconnect(con);
178 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
179 scene->removeItem(obj);
180 delete obj;
181 });
182 }
183 obj = nullptr;
184 });
185 });
186 }
187
188 template <typename T>
189 requires std::is_floating_point_v<std::decay_t<decltype(std::declval<T>().value())>>
190 static void contextMenuEvent(T& self, QPointF pos)
191 {
192 QTimer::singleShot(0, &self, [&, self_p = &self, pos] {
193 auto w = new DoubleSpinboxWithEnter;
194 w->setRange(self.min, self.max);
195
196 w->setDecimals(6);
197 w->setValue(self.map(self.m_value));
198 auto obj = self.scene()->addWidget(
199 w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
200 obj->setPos(pos);
201
202 QTimer::singleShot(0, w, [w] { w->setFocus(); });
203
204 auto con = QObject::connect(
205 w, SignalUtils::QDoubleSpinBox_valueChanged_double(), &self,
206 [&self, obj, scene = self.scene()](double v) {
207 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
208 });
209
210 QObject::connect(
211 w, &DoubleSpinboxWithEnter::editingFinished, &self,
212 [obj, con, self_p]() mutable {
213 if(obj != nullptr)
214 {
215 self_p->sliderReleased();
216 QObject::disconnect(con);
217 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
218 scene->removeItem(obj);
219 delete obj;
220 });
221 }
222 obj = nullptr;
223 });
224 });
225 }
226
227 template <typename T>
228 static void mouseDoubleClickEvent(T& self, QGraphicsSceneMouseEvent* event)
229 {
230 self.m_value = self.unmap(self.init);
231
232 self.m_grab = true;
233 self.sliderMoved();
234 self.sliderReleased();
235 self.m_grab = false;
236
237 self.update();
238
239 event->accept();
240 }
241};
242}
Definition Skin.hpp:93
Base toolkit upon which the software is built.
Definition Application.cpp:97
Definition DefaultGraphicsKnobImpl.hpp:25
Definition DoubleSpinBox.hpp:17
Definition DoubleSpinBox.hpp:9