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 
21 namespace 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