Loading...
Searching...
No Matches
DefaultGraphicsSliderImpl.hpp
1#pragma once
2#include <score/graphics/DefaultControlImpl.hpp>
3#include <score/graphics/RightClickWidget.hpp>
4#include <score/model/Skin.hpp>
5#include <score/widgets/DoubleSpinBox.hpp>
6#include <score/widgets/SignalUtils.hpp>
7
8#include <ossia/detail/math.hpp>
9
10#include <QGraphicsProxyWidget>
11#include <QGraphicsScene>
12#include <QGraphicsSceneMouseEvent>
13#include <QGuiApplication>
14#include <QKeyEvent>
15#include <QPainter>
16#include <QPointer>
17#include <QTimer>
18
19#include <score_lib_base_export.h>
20
21#include <verdigris>
22namespace score
23{
24
26{
27 QPointer<DoubleSpinboxWithEnter> spinbox{};
28 QPointer<QGraphicsProxyWidget> spinboxProxy{};
29};
30
32{
33 template <typename T>
34 static void paint(
35 T& self, const score::Skin& skin, const QString& text, QPainter* painter,
36 QWidget* widget)
37 {
38 painter->setRenderHint(QPainter::Antialiasing, false);
39
40 painter->setPen(skin.NoPen);
41 painter->setBrush(skin.Emphasis2.main.brush);
42
43 // Draw rect
44 const QRectF brect = self.boundingRect();
45 const QRectF srect = self.sliderRect();
46 painter->drawRoundedRect(srect, 1, 1);
47
48 // Draw text
49#if defined(__linux__)
50 // widget is null when painting through QGraphicsScene::render
51 static const auto dpi_adjust
52 = (widget ? widget->devicePixelRatioF() : painter->device()->devicePixelRatioF())
53 > 1
54 ? 0
55 : -1;
56#elif defined(_WIN32)
57 static const constexpr auto dpi_adjust = 0;
58#else
59 static const constexpr auto dpi_adjust = -2;
60#endif
61 painter->setPen(skin.Base4.lighter180.pen1);
62 painter->setFont(skin.Medium8Pt);
63 const auto textrect = brect.adjusted(2, srect.height() + 2 + dpi_adjust, -2, -1);
64 painter->drawText(textrect, text, QTextOption(Qt::AlignCenter));
65
66 // Draw handle
67 painter->fillRect(self.handleRect(), skin.Base4);
68 if(self.m_hasExec)
69 painter->fillRect(self.execHandleRect(), skin.Base4.lighter180.brush);
70 // painter->setPen(QPen(Qt::green, 1));
71 // painter->setBrush(QBrush(Qt::transparent));
72 // painter->drawRect(textrect);
73 //
74 // painter->setPen(QPen(Qt::red, 1));
75 // painter->setBrush(QBrush(Qt::transparent));
76 // painter->drawRect(self.boundingRect());
77 }
78
79 template <typename T>
80 static void mousePressEvent(T& self, QGraphicsSceneMouseEvent* event)
81 {
82 if(event->button() == Qt::LeftButton && self.isInHandle(event->pos()))
83 {
84 self.m_grab = true;
85
86 const auto srect = self.sliderRect();
87 const auto posX = event->pos().x() - srect.x();
88 double curPos = self.from01(ossia::clamp(posX, 0., srect.width()) / srect.width());
89 if(curPos != self.m_value)
90 {
91 self.m_value = curPos;
92 self.sliderMoved();
93 self.update();
94 }
95 }
96
97 event->accept();
98 }
99
100 template <typename T>
101 static void mouseMoveEvent(T& self, QGraphicsSceneMouseEvent* event)
102 {
103 if((event->buttons() & Qt::LeftButton) && self.m_grab)
104 {
105 const auto srect = self.sliderRect();
106 const auto posX = event->pos().x() - srect.x();
107 double curPos = self.from01(ossia::clamp(posX, 0., srect.width()) / srect.width());
108 if(curPos != self.m_value)
109 {
110 if constexpr(std::is_same_v<decltype(self.m_value), int>)
111 {
112 self.m_value = curPos;
113 }
114 else
115 {
116 double ratio = qGuiApp->keyboardModifiers() & Qt::CTRL ? 0.1 : 1.;
117 self.m_value = std::clamp(
118 double(self.m_value + ((curPos - self.m_value) * ratio)), 0., 1.);
119 }
120
121 self.sliderMoved();
122 self.update();
123 }
124 }
125 event->accept();
126 }
127
128 template <typename T>
129 static void mouseReleaseEvent(T& self, QGraphicsSceneMouseEvent* event)
130 {
131 if(self.m_grab)
132 {
133 const auto srect = self.sliderRect();
134 const auto posX = event->pos().x() - srect.x();
135 double curPos = self.from01(ossia::clamp(posX, 0., srect.width()) / srect.width());
136 if(curPos != self.m_value)
137 {
138 if constexpr(std::is_same_v<decltype(self.m_value), int>)
139 {
140 self.m_value = curPos;
141 }
142 else
143 {
144 double ratio = qGuiApp->keyboardModifiers() & Qt::CTRL ? 0.1 : 1.;
145 self.m_value = std::clamp(
146 double(self.m_value + ((curPos - self.m_value) * ratio)), 0., 1.);
147 }
148 self.update();
149 }
150 }
151
152 self.m_grab = false;
153 self.sliderReleased();
154
155 if(event->button() == Qt::RightButton)
156 {
157 contextMenuEvent(self, event->scenePos());
158 }
159 event->accept();
160 }
161
170 template <typename T>
171 static void ungrabMouseEvent(T& self, QEvent* event)
172 {
173 if(!self.m_grab)
174 return;
175
176 self.m_grab = false;
177 self.sliderReleased();
178 }
179
180 template <typename T>
181 static void contextMenuEvent(T& self, QPointF pos)
182 {
183 auto build = [&, self_p = &self, pos] {
184 // Whatever was open belongs to the previous right-click, including one
185 // on this same control.
187 self_p->impl->spinbox = nullptr;
188 self_p->impl->spinboxProxy = nullptr;
189
190 auto w = new DoubleSpinboxWithEnter;
191 self.impl->spinbox = w;
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 self.impl->spinboxProxy = obj;
201
202#if defined(__EMSCRIPTEN__)
203 w->setFocus();
204#else
205 QTimer::singleShot(0, w, [w] { w->setFocus(); });
206#endif
207
208 auto con = QObject::connect(
209 w, SignalUtils::QDoubleSpinBox_valueChanged_double(), &self,
210 [&self, obj, scene = self.scene()](double v) {
211 DefaultControlImpl::editWidgetInContextMenu(self, scene, obj, v);
212 });
213
214 QObject::connect(
215 w, &DoubleSpinboxWithEnter::editingFinished, &self, [obj, con, self_p] {
216 if(self_p->impl->spinbox)
217 {
218 self_p->sliderReleased();
219 QObject::disconnect(con);
220 // obj is the timer's context, not self: if the next right-click
221 // took this box down first, through closeRightClickWidget(), the
222 // teardown goes with it rather than freeing it a second time.
223 QTimer::singleShot(0, obj, [scene = self_p->scene(), obj] {
224 scene->removeItem(obj);
225 delete obj;
226 });
227 self_p->impl->spinbox = nullptr;
228 self_p->impl->spinboxProxy = nullptr;
229 }
230 });
231 };
232#if defined(__EMSCRIPTEN__)
233 build();
234#else
235 QTimer::singleShot(0, &self, build);
236#endif
237 }
238
239 template <typename T>
240 static void mouseDoubleClickEvent(T& self, QGraphicsSceneMouseEvent* event)
241 {
242 self.m_value = self.unmap(self.init);
243
244 self.m_grab = true;
245 self.sliderMoved();
246 self.sliderReleased();
247 self.m_grab = false;
248
249 self.update();
250
251 event->accept();
252 }
253};
254
255}
Definition Skin.hpp:100
Base toolkit upon which the software is built.
Definition Application.cpp:117
QPointer< QGraphicsProxyWidget > & currentRightClickWidget()
The one type-in box a right-click may have open.
Definition GraphicWidgets.cpp:26
void closeRightClickWidget()
Takes down whatever right-click box is open, if any.
Definition RightClickWidget.hpp:23
Definition DefaultGraphicsSliderImpl.hpp:32
static void ungrabMouseEvent(T &self, QEvent *event)
Definition DefaultGraphicsSliderImpl.hpp:171
Definition DoubleSpinBox.hpp:17
Definition DefaultGraphicsSliderImpl.hpp:26