Loading...
Searching...
No Matches
Painter.hpp
1#pragma once
2#include <score/model/Skin.hpp>
3
4#include <QFlags>
5#include <QGradient>
6#include <QGraphicsItem>
7#include <QGraphicsSceneMouseEvent>
8#include <QPainter>
9#include <QPolygon>
10
11#include <avnd/concepts/painter.hpp>
12
13#include <cmath>
14
15namespace oscr
16{
18{
19 uint8_t r, g, b, a;
20};
22{
23 QPainter& painter;
24 QGraphicsItem& item;
25 QPainterPath path;
26
27 void begin_path() { path = QPainterPath{}; }
28 void close_path() { path.closeSubpath(); }
29 void stroke() { painter.strokePath(path, painter.pen()); }
30 void fill() { painter.fillPath(path, painter.brush()); }
31 void update() { item.update(); }
32
33 void move_to(double x, double y) { path.moveTo(x, y); }
34 void line_to(double x, double y) { path.lineTo(x, y); }
35 void arc_to(double x, double y, double w, double h, double start, double length)
36 {
37 path.arcTo(x, y, w, h, start, length);
38 }
39
40 void cubic_to(double c1x, double c1y, double c2x, double c2y, double endx, double endy)
41 {
42 path.cubicTo(c1x, c1y, c2x, c2y, endx, endy);
43 }
44
45 void quad_to(double x1, double y1, double x2, double y2)
46 {
47 path.quadTo(x1, y1, x2, y2);
48 }
49
50 void translate(double x, double y) { painter.translate(x, y); }
51 void scale(double x, double y) { painter.scale(x, y); }
52 void rotate(double a) { painter.rotate(a); }
53 void reset_transform() { painter.resetTransform(); }
54
55 // Colors:
56 void unset_stroke() { painter.setPen(score::Skin::instance().NoPen); }
57 void set_stroke_color(rgba_color c)
58 {
59 QPen p = painter.pen();
60 p.setColor(qRgba(c.r, c.g, c.b, c.a));
61 painter.setPen(p);
62 }
63
64 void set_stroke_width(double w)
65 {
66 QPen p = painter.pen();
67 p.setWidth(w);
68 painter.setPen(p);
69 }
70
71 void set_fill_color(rgba_color c)
72 {
73 painter.setBrush(QColor(qRgba(c.r, c.g, c.b, c.a)));
74 }
75
76 void set_linear_gradient(
77 double x1, double y1, double x2, double y2, rgba_color c1, rgba_color c2)
78 {
79 QLinearGradient gradient(QPointF(x1, y1), QPointF(x2, y2));
80 gradient.setColorAt(0, QColor(qRgba(c1.r, c1.g, c1.b, c1.a)));
81 gradient.setColorAt(1, QColor(qRgba(c2.r, c2.g, c2.b, c2.a)));
82 painter.setBrush(gradient);
83 }
84
85 void set_radial_gradient(double cx, double cy, double cr, rgba_color c1, rgba_color c2)
86 {
87 QRadialGradient gradient(cx, cy, cr);
88 gradient.setColorAt(0, QColor(qRgba(c1.r, c1.g, c1.b, c1.a)));
89 gradient.setColorAt(1, QColor(qRgba(c2.r, c2.g, c2.b, c2.a)));
90 painter.setBrush(gradient);
91 }
92
93 void set_conical_gradient(double x, double y, double a, rgba_color c1, rgba_color c2)
94 {
95 QConicalGradient gradient(x, y, a);
96 gradient.setColorAt(0, QColor(qRgba(c1.r, c1.g, c1.b, c1.a)));
97 gradient.setColorAt(1, QColor(qRgba(c2.r, c2.g, c2.b, c2.a)));
98 painter.setBrush(gradient);
99 }
100
101 // Text:
102 void set_font(std::string_view f)
103 {
104 auto font = painter.font();
105 font.setFamily(QString::fromUtf8(f.data(), f.size()));
106 painter.setFont(font);
107 }
108
109 void set_font_size(double f)
110 {
111 auto font = painter.font();
112 font.setPointSize(f);
113 painter.setFont(font);
114 }
115
116 void draw_text(double x, double y, std::string_view str)
117 {
118 path.addText(x, y, painter.font(), QString::fromUtf8(str.data(), str.size()));
119 }
120
121 void draw_text(double x, double y, double w, double h, std::string_view str)
122 {
123 QRectF rect{x, y, w, h};
124 const auto& m = painter.fontMetrics();
125 auto txt = QString::fromUtf8(str.data(), str.size());
126 auto text_rect = m.boundingRect(txt);
127 auto text_half_diagonal = (text_rect.center() - text_rect.topLeft()) / 2.;
128 auto pos = rect.center() + text_half_diagonal;
129 path.addText(pos.x(), pos.y(), painter.font(), txt);
130 }
131
132 // Drawing
133 void draw_line(double x1, double y1, double x2, double y2)
134 {
135 path.moveTo(x1, y1);
136 path.lineTo(x2, y2);
137 }
138
139 // x1, y1, x2 , y2, x3, y3
140 void draw_triangle(double x1, double y1, double x2, double y2, double x3, double y3)
141 {
142 path.moveTo(x1, y1);
143 path.lineTo(x2, y2);
144 path.lineTo(x3, y3);
145 path.lineTo(x1, y1);
146 painter.drawPath(path);
147 }
148
149 // x , y , w , h
150 void draw_rect(double x, double y, double w, double h) { path.addRect(x, y, w, h); }
151
152 // x , y , w , h
153 void draw_rounded_rect(double x, double y, double w, double h, double r)
154 {
155 path.addRoundedRect(x, y, w, h, r, r);
156 }
157
158 // x , y , filename
159 void draw_pixmap(double x, double y, const QString& str)
160 {
161 painter.drawPixmap(x, y, str);
162 }
163
164 // x , y , w , h
165 void draw_ellipse(double x, double y, double w, double h)
166 {
167 path.addEllipse(x, y, w, h);
168 }
169
170 // cx, cy, radius
171 void draw_circle(double cx, double cy, double cr)
172 {
173 path.addEllipse(QPointF{cx, cy}, cr, cr);
174 }
175
176 // tab, count
177 void draw_polygon(const double* tab, int count)
178 {
179 QPolygonF poly;
180 double x, y;
181 for(int i = 0; i < count * 2; i += 2)
182 {
183 x = tab[i];
184 y = tab[i + 1];
185 poly << QPointF(x, y);
186 }
187 path.addPolygon(poly);
188 }
189
190 void draw_bytes(
191 int x, int y, int w, int h, const unsigned char* image, int img_w, int img_h,
192 bool smooth = false)
193 {
194 auto img = QImage(image, img_w, img_h, QImage::Format_RGBA8888);
195 auto prev = painter.renderHints() & QPainter::SmoothPixmapTransform;
196 painter.setRenderHint(QPainter::SmoothPixmapTransform, smooth);
197 painter.drawImage(
198 QRect(x, y, w, h), img, QRect(0, 0, img_w, img_h), Qt::ImageConversionFlags{});
199
200 painter.setRenderHint(QPainter::SmoothPixmapTransform, prev);
201 }
202};
203static_assert(avnd::painter<QPainterAdapter>);
204
205template <typename Item, typename Control = void>
206class CustomItem : public QGraphicsItem
207{
208public:
209 // Item may be T::item_type or T&
210 using item_type = std::decay_t<Item>;
211
212 // // Case T::item_type
213 // explicit CustomItem() { init(); }
214
215 // Case T&
216 explicit CustomItem(Item t)
217 : impl{t}
218 {
219 this->setFlag(QGraphicsItem::ItemClipsToShape);
220 this->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
221
222 if constexpr(requires { this->impl.update = [this] {}; })
223 {
224 this->impl.update = [this] { this->update(); };
225 }
226 }
227
228 QRectF boundingRect() const override
229 {
230 return {0., 0., item_type::width(), item_type::height()};
231 }
232
233 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
234 override
235 {
236 auto& skin = score::Skin::instance();
237 painter->setRenderHint(QPainter::Antialiasing, true);
238 painter->setPen(skin.Dark.main.pen1);
239 impl.paint(QPainterAdapter{*painter, *this, {}});
240 painter->setRenderHint(QPainter::Antialiasing, false);
241 }
242
244 {
245 enum button
246 {
247 no_button,
248 left = (1 << 1),
249 right = (1 << 2),
250 middle = (1 << 3)
251 };
252 friend button& operator|=(button& lhs, button rhs) noexcept
253 {
254 return (
255 enum button&)(reinterpret_cast<std::underlying_type_t<enum button>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum button>&>(rhs));
256 }
257 enum modifier
258 {
259 no_modifier,
260 shift = (1 << 1),
261 ctrl = (1 << 2),
262 alt = (1 << 3),
263 meta = (1 << 4)
264 };
265 friend modifier& operator|=(modifier& lhs, modifier rhs) noexcept
266 {
267 return (
268 enum modifier&)(reinterpret_cast<std::underlying_type_t<enum modifier>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum modifier>&>(rhs));
269 }
270
271 float x, y;
272
273 enum button button = {};
274 enum button held_buttons = {};
275 enum modifier modifiers = {};
276 };
277
278protected:
279 static custom_mouse_event make_event(QGraphicsSceneMouseEvent* event) noexcept
280 {
282
283 p.x = event->pos().x();
284 p.y = event->pos().y();
285
286 if(event->button() == Qt::LeftButton)
287 p.button = custom_mouse_event::left;
288 else if(event->button() == Qt::RightButton)
289 p.button = custom_mouse_event::right;
290 else if(event->button() == Qt::MiddleButton)
291 p.button = custom_mouse_event::middle;
292
293 if(event->buttons() & Qt::LeftButton)
294 p.held_buttons |= p.left;
295 if(event->buttons() & Qt::RightButton)
296 p.held_buttons |= p.right;
297 if(event->buttons() & Qt::MiddleButton)
298 p.held_buttons |= p.middle;
299
300 if(event->modifiers() & Qt::ShiftModifier)
301 p.modifiers |= p.shift;
302 if(event->modifiers() & Qt::AltModifier)
303 p.modifiers |= p.alt;
304 if(event->modifiers() & Qt::ControlModifier)
305 p.modifiers |= p.ctrl;
306 if(event->modifiers() & Qt::MetaModifier)
307 p.modifiers |= p.meta;
308
309 return p;
310 }
311
312 void mousePressEvent(QGraphicsSceneMouseEvent* event) override
313 {
314 if constexpr(requires { impl.mouse_press(0, 0); })
315 {
316 if(impl.mouse_press(event->pos().x(), event->pos().y()))
317 event->accept();
318 }
319 else if constexpr(requires { impl.mouse_press(custom_mouse_event{}); })
320 {
321 if(impl.mouse_press(make_event(event)))
322 event->accept();
323 }
324 update();
325 }
326
327 void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override
328 {
329 if constexpr(requires { impl.mouse_move(0, 0); })
330 {
331 impl.mouse_move(event->pos().x(), event->pos().y());
332 event->accept();
333 }
334 else if constexpr(requires { impl.mouse_move(custom_mouse_event{}); })
335 {
336 impl.mouse_move(make_event(event));
337 event->accept();
338 }
339 update();
340 }
341
342 void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override
343 {
344 if constexpr(requires { impl.mouse_release(0, 0); })
345 {
346 impl.mouse_release(event->pos().x(), event->pos().y());
347 event->accept();
348 }
349 else if constexpr(requires { impl.mouse_release(custom_mouse_event{}); })
350 {
351 impl.mouse_release(make_event(event));
352 event->accept();
353 }
354 update();
355 }
356
357protected:
358 Item impl;
359};
360
361template <typename Item>
362class CustomControl : public CustomItem<Item>
363{
364public:
366 explicit CustomControl(
367 Item item_init, Process::ControlInlet& ctl, const score::DocumentContext& ctx)
368 : CustomItem<Item>{item_init}
369 , cmd{ctx.commandStack}
370 {
371 if constexpr(requires { this->impl.transaction; })
372 {
373 this->impl.transaction.start = [] {
374
375 };
376 this->impl.transaction.update = [this, &ctl](const auto& value) {
377 auto val = oscr::to_ossia_value(value);
378 cmd.submit<Process::SetControlValue>(ctl, val);
379
380 this->impl.value = value; //impl.value_to_control(Control{}
381 this->update();
382 };
383 this->impl.transaction.commit = [this] { cmd.commit(); };
384 this->impl.transaction.rollback = [this] { cmd.rollback(); };
385 }
386 }
387};
388}
The OngoingCommandDispatcher class.
Definition OngoingCommandDispatcher.hpp:27
void submit(Args &&... args)
Definition OngoingCommandDispatcher.hpp:37
void commit()
Definition OngoingCommandDispatcher.hpp:61
void rollback()
If the command has to be reverted, for instance when pressing escape.
Definition OngoingCommandDispatcher.hpp:71
Definition Port.hpp:203
Definition SetControlValue.hpp:13
Definition Painter.hpp:363
Definition Painter.hpp:207
Definition Factories.hpp:19
Definition Painter.hpp:244
Definition Painter.hpp:22
Definition Painter.hpp:18
Definition DocumentContext.hpp:18