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 auto build = [&, 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#if defined(__EMSCRIPTEN__)
165 w->setFocus();
166#else
167 QTimer::singleShot(0, w, [w] { w->setFocus(); });
168#endif
169
170 auto con = QObject::connect(
171 w, SignalUtils::QSpinBox_valueChanged_int(), &self,
172 [&self, obj, scene = self.scene()](double v) {
173 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
174 });
175
176 QObject::connect(
177 w, &SpinboxWithEnter::editingFinished, &self, [obj, con, self_p]() mutable {
178 if(obj != nullptr)
179 {
180 self_p->sliderReleased();
181 QObject::disconnect(con);
182 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
183 scene->removeItem(obj);
184 delete obj;
185 });
186 }
187 obj = nullptr;
188 });
189 };
190#if defined(__EMSCRIPTEN__)
191 build();
192#else
193 QTimer::singleShot(0, &self, build);
194#endif
195 }
196
197 template <typename T>
198 requires std::is_floating_point_v<std::decay_t<decltype(std::declval<T>().value())>>
199 static void contextMenuEvent(T& self, QPointF pos)
200 {
201 auto build = [&, self_p = &self, pos] {
202 auto w = new DoubleSpinboxWithEnter;
203 w->setRange(self.min, self.max);
204
205 w->setDecimals(6);
206 w->setValue(self.map(self.m_value));
207 auto obj = self.scene()->addWidget(
208 w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
209 obj->setPos(pos);
210
211#if defined(__EMSCRIPTEN__)
212 w->setFocus();
213#else
214 QTimer::singleShot(0, w, [w] { w->setFocus(); });
215#endif
216
217 auto con = QObject::connect(
218 w, SignalUtils::QDoubleSpinBox_valueChanged_double(), &self,
219 [&self, obj, scene = self.scene()](double v) {
220 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
221 });
222
223 QObject::connect(
224 w, &DoubleSpinboxWithEnter::editingFinished, &self,
225 [obj, con, self_p]() mutable {
226 if(obj != nullptr)
227 {
228 self_p->sliderReleased();
229 QObject::disconnect(con);
230 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
231 scene->removeItem(obj);
232 delete obj;
233 });
234 }
235 obj = nullptr;
236 });
237 };
238#if defined(__EMSCRIPTEN__)
239 build();
240#else
241 QTimer::singleShot(0, &self, build);
242#endif
243 }
244
245 template <typename T>
246 static void mouseDoubleClickEvent(T& self, QGraphicsSceneMouseEvent* event)
247 {
248 self.m_value = self.unmap(self.init);
249
250 self.m_grab = true;
251 self.sliderMoved();
252 self.sliderReleased();
253 self.m_grab = false;
254
255 self.update();
256
257 event->accept();
258 }
259};
260}
Definition Skin.hpp:94
Base toolkit upon which the software is built.
Definition Application.cpp:113
Definition DefaultGraphicsKnobImpl.hpp:25
Definition DoubleSpinBox.hpp:17
Definition DoubleSpinBox.hpp:9