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