30 T& self,
const score::Skin& skin,
const QString& text, QPainter* painter,
31 QWidget* widget,
const QStyleOptionGraphicsItem* option =
nullptr)
33 painter->setRenderHint(QPainter::Antialiasing,
true);
35 constexpr const double space = 50.;
36 constexpr const double start = (270. - space) * 16.;
37 constexpr const double totalSpan = (360. - 2. * space) * 16.;
40 QRectF srect = defaultKnobSize;
41 if constexpr(
requires { self.m_rect; })
44 const double side = std::min(srect.width(), srect.height());
52 const double k = side / 35.;
53 const QPointF c{side / 2., side / 2.};
54 const double arcRadius = 10.5 * k;
56 const double arcWidth = std::max(1., 2. * k);
57 const double tickWidth = arcWidth;
61 const QTransform& dt = painter->deviceTransform();
63 = 1. / std::max(1e-6, std::sqrt(std::abs(dt.m11() * dt.m22() - dt.m12() * dt.m21())));
64 const double tickOuter
65 = arcRadius + arcWidth / 2. - tickWidth / 2. - 0.5 * devicePx;
66 const double tickInner = 4.94 * k + tickWidth / 2.;
68 c.x() - arcRadius, c.y() - arcRadius, 2. * arcRadius, 2. * arcRadius};
69 const double bodyRadius = arcRadius + arcWidth / 2.;
71 c.x() - bodyRadius, c.y() - bodyRadius, 2. * bodyRadius, 2. * bodyRadius};
73 const QColor accent = skin.Base4.main.brush.color();
76 painter->setPen(Qt::NoPen);
77 painter->setBrush(skin.Emphasis2.main.brush);
78 painter->drawChord(body, start, -totalSpan);
79 painter->setBrush(Qt::NoBrush);
82 const double valueSpan = -self.m_value * totalSpan;
83 painter->setPen(QPen{accent, arcWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin});
85 painter->drawArc(r, start, valueSpan);
88 const double theta = -0.0174533 * (start + valueSpan) / 16.;
89 const QPointF dir{std::cos(theta), std::sin(theta)};
90 painter->drawLine(c + tickInner * dir, c + tickOuter * dir);
95 painter->setPen(skin.Base4.lighter180.pen1);
96 const double er = arcRadius - arcWidth / 2. - 1.;
97 const QRectF erect{c.x() - er, c.y() - er, 2. * er, 2. * er};
98 painter->drawArc(erect, start, -self.m_execValue * totalSpan);
101 const double textDelta = side >= 30. ? -10. : side >= 20. ? -9. : -8.;
103 painter->setPen(skin.Base4.lighter180.pen1);
107 bool showValue =
true;
108 if constexpr(std::is_base_of_v<QGraphicsItem, std::remove_cvref_t<T>>)
110 bool grabbed =
false;
111 if constexpr(
requires { self.m_grab; })
112 grabbed = self.m_grab;
113 showValue =
showsValue(self, grabbed, option);
115 if(showValue && !text.isEmpty())
117 painter->setFont(skin.Medium8Pt);
119 = srect.height() > srect.width() ? side : srect.height() + textDelta;
121 QRectF{0., text_y, srect.width(), 10.}, text, QTextOption(Qt::AlignCenter));
124 painter->setRenderHint(QPainter::Antialiasing,
false);
127 template <
typename T>
128 static void mousePressEvent(T& self, QGraphicsSceneMouseEvent* event)
130 if(event->button() == Qt::LeftButton)
133 InfiniteScroller::start(self, self.m_value);
139 template <
typename T>
140 static void mouseMoveEvent(T& self, QGraphicsSceneMouseEvent* event)
142 if((event->buttons() & Qt::LeftButton) && self.m_grab)
144 double v = InfiniteScroller::move(event);
145 if(v != self.m_value)
155 template <
typename T>
156 static void mouseReleaseEvent(T& self, QGraphicsSceneMouseEvent* event)
164 double v = InfiniteScroller::move(event);
165 if(v != self.m_value)
170 InfiniteScroller::stop(self, event);
174 self.sliderReleased();
176 if(event->button() == Qt::RightButton)
178 contextMenuEvent(self, event->scenePos());
187 template <
typename T>
195 self.sliderReleased();
198 template <
typename T>
199 requires std::is_integral_v<std::decay_t<decltype(std::declval<T>().value())>>
200 static void contextMenuEvent(T& self, QPointF pos)
202 auto build = [&, self_p = &self, pos] {
207 w->setRange(self.min, self.max);
209 w->setValue(self.map(self.m_value));
210 auto obj = self.scene()->addWidget(
211 w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
215#if defined(__EMSCRIPTEN__)
218 QTimer::singleShot(0, w, [w] { w->setFocus(); });
221 auto con = QObject::connect(
222 w, SignalUtils::QSpinBox_valueChanged_int(), &self,
223 [&self, obj, scene = self.scene()](
double v) {
224 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
228 w, &SpinboxWithEnter::editingFinished, &self, [obj, con, self_p]()
mutable {
231 self_p->sliderReleased();
232 QObject::disconnect(con);
233 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
234 scene->removeItem(obj);
241#if defined(__EMSCRIPTEN__)
244 QTimer::singleShot(0, &self, build);
250 template <
typename T>
251 requires(!std::is_integral_v<std::decay_t<decltype(std::declval<T>().value())>>)
254 auto build = [&, self_p = &self, pos] {
259 w->setRange(self.min, self.max);
262 w->setValue(self.map(self.m_value));
263 auto obj = self.scene()->addWidget(
264 w, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
268#if defined(__EMSCRIPTEN__)
271 QTimer::singleShot(0, w, [w] { w->setFocus(); });
274 auto con = QObject::connect(
275 w, SignalUtils::QDoubleSpinBox_valueChanged_double(), &self,
276 [&self, obj, scene = self.scene()](
double v) {
277 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
281 w, &DoubleSpinboxWithEnter::editingFinished, &self,
282 [obj, con, self_p]()
mutable {
285 self_p->sliderReleased();
286 QObject::disconnect(con);
287 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
288 scene->removeItem(obj);
295#if defined(__EMSCRIPTEN__)
298 QTimer::singleShot(0, &self, build);
302 template <
typename T>
303 static void mouseDoubleClickEvent(T& self, QGraphicsSceneMouseEvent* event)
305 self.m_value = self.unmap(self.init);
309 self.sliderReleased();