DefaultGraphicsSpinboxImpl.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  painter->setPen(skin.NoPen);
33  painter->setBrush(skin.Emphasis2.main.brush);
34 
35  // Draw rect
36  const QRectF brect = self.boundingRect();
37  painter->drawRoundedRect(brect, 1, 1);
38 
39  // Draw text
40  painter->setPen(skin.Base4.main.pen1);
41  painter->setFont(skin.Medium8Pt);
42  const auto textrect = brect.adjusted(2, 3, -2, -2);
43  painter->drawText(textrect, text, QTextOption(Qt::AlignLeft));
44 
45  painter->setRenderHint(QPainter::Antialiasing, false);
46  }
47 
48  template <typename T>
49  static void mousePressEvent(T& self, QGraphicsSceneMouseEvent* event)
50  {
51  if(event->button() == Qt::LeftButton)
52  {
53  self.m_grab = true;
54  InfiniteScroller::start(self, (self.max - self.min) * self.m_value + self.min);
55  }
56 
57  event->accept();
58  }
59 
60  template <typename T>
61  static double mapValue(T& self, QGraphicsSceneMouseEvent* event) noexcept
62  {
63  InfiniteScroller::move_free(event);
64  const auto speed
65  = std::pow(10., std::log10(1. + std::abs(InfiniteScroller::currentDelta)));
66 
67  auto v = InfiniteScroller::origValue
68  - speed * InfiniteScroller::currentDelta
69  / double(InfiniteScroller::currentGeometry.height());
70  v = (v - self.min) / (self.max - self.min);
71  return std::clamp(v, 0., 1.);
72  }
73 
74  template <typename T>
75  static void mouseMoveEvent(T& self, QGraphicsSceneMouseEvent* event)
76  {
77  if((event->buttons() & Qt::LeftButton) && self.m_grab)
78  {
79  if(const auto v = mapValue(self, event); v != self.m_value)
80  {
81  self.m_value = v;
82  if(!self.m_noValueChangeOnMove)
83  self.sliderMoved();
84  self.update();
85  }
86  }
87  event->accept();
88  }
89 
90  template <typename T>
91  static void mouseReleaseEvent(T& self, QGraphicsSceneMouseEvent* event)
92  {
93  InfiniteScroller::stop(self, event);
94  if(self.m_grab)
95  {
96  if(const auto v = mapValue(self, event); v != self.m_value)
97  {
98  self.m_value = v;
99  self.update();
100  }
101  }
102 
103  if(self.m_noValueChangeOnMove)
104  self.sliderMoved();
105  self.m_grab = false;
106  self.sliderReleased();
107 
108  if(event->button() == Qt::RightButton)
109  {
110  contextMenuEvent(self, event->scenePos());
111  }
112 
113  event->accept();
114  }
115 
116  template <typename T>
117  requires std::is_integral_v<std::decay_t<decltype(std::declval<T>().value())>>
118  static void contextMenuEvent(T& self, QPointF pos)
119  {
120  QTimer::singleShot(0, &self, [&, self_p = &self, pos] {
121  auto w = new SpinboxWithEnter;
122  w->setRange(self.min, self.max);
123 
124  w->setValue(self.map(self.m_value));
125  auto obj = self.scene()->addWidget(
126  w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
127  obj->setPos(pos);
128 
129  QTimer::singleShot(0, w, [w] { w->setFocus(); });
130 
131  auto con = QObject::connect(
132  w, SignalUtils::QSpinBox_valueChanged_int(), &self,
133  [&self, obj, scene = self.scene()](double v) {
134  DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
135  });
136 
137  QObject::connect(
138  w, &SpinboxWithEnter::editingFinished, &self, [obj, con, self_p]() mutable {
139  if(obj != nullptr)
140  {
141  if(self_p->m_noValueChangeOnMove)
142  self_p->sliderMoved();
143  self_p->sliderReleased();
144  QObject::disconnect(con);
145  QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
146  scene->removeItem(obj);
147  delete obj;
148  });
149  }
150  obj = nullptr;
151  });
152  });
153  }
154 
155  template <typename T>
156  requires std::is_floating_point_v<std::decay_t<decltype(std::declval<T>().value())>>
157  static void contextMenuEvent(T& self, QPointF pos)
158  {
159  // FIXME to be safe we have to locate the object by path on every click as
160  // some control changes may cause entire GUI rebuilds
161  QTimer::singleShot(0, &self, [&, self_p = &self, pos] {
162  auto w = new DoubleSpinboxWithEnter;
163  w->setRange(self.min, self.max);
164 
165  w->setDecimals(6);
166  w->setValue(self.map(self.m_value));
167  auto obj = self.scene()->addWidget(
168  w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
169  obj->setPos(pos);
170 
171  QTimer::singleShot(0, w, [w] { w->setFocus(); });
172 
173  auto con = QObject::connect(
174  w, SignalUtils::QDoubleSpinBox_valueChanged_double(), &self,
175  [&self, obj, scene = self.scene()](double v) {
176  DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
177  });
178 
179  QObject::connect(
180  w, &DoubleSpinboxWithEnter::editingFinished, &self,
181  [obj, con, self_p]() mutable {
182  if(obj != nullptr)
183  {
184  if(self_p->m_noValueChangeOnMove)
185  self_p->sliderMoved();
186  self_p->sliderReleased();
187  QObject::disconnect(con);
188  QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
189  scene->removeItem(obj);
190  delete obj;
191  });
192  }
193  obj = nullptr;
194  });
195  });
196  }
197 
198  template <typename T>
199  static void mouseDoubleClickEvent(T& self, QGraphicsSceneMouseEvent* event)
200  {
201  self.m_value = self.unmap(self.init);
202 
203  self.m_grab = true;
204  self.sliderMoved();
205  self.sliderReleased();
206  self.m_grab = false;
207 
208  self.update();
209 
210  event->accept();
211  }
212 };
213 }
Definition: Skin.hpp:93
Base toolkit upon which the software is built.
Definition: Application.cpp:90
Definition: DefaultGraphicsSpinboxImpl.hpp:24
Definition: DoubleSpinBox.hpp:17
Definition: DoubleSpinBox.hpp:9