Loading...
Searching...
No Matches
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
21namespace 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