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