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