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#include <avnd/wrappers/colors.hpp>
13
14#include <cmath>
15
16namespace oscr
17{
19{
20 uint8_t r, g, b, a;
21};
23{
24 QPainter& painter;
25 QGraphicsItem& item;
26 QPainterPath path;
27 score::Skin& skin = score::Skin::instance();
28
29 void begin_path() { path = QPainterPath{}; }
30 void close_path() { path.closeSubpath(); }
31 void stroke() { painter.strokePath(path, painter.pen()); }
32 void fill() { painter.fillPath(path, painter.brush()); }
33 void update() { item.update(); }
34
35 void move_to(double x, double y) { path.moveTo(x, y); }
36 void line_to(double x, double y) { path.lineTo(x, y); }
37 void arc_to(double x, double y, double w, double h, double start, double length)
38 {
39 path.arcTo(x, y, w, h, start, length);
40 }
41
42 void cubic_to(double c1x, double c1y, double c2x, double c2y, double endx, double endy)
43 {
44 path.cubicTo(c1x, c1y, c2x, c2y, endx, endy);
45 }
46
47 void quad_to(double x1, double y1, double x2, double y2)
48 {
49 path.quadTo(x1, y1, x2, y2);
50 }
51
52 void translate(double x, double y) { painter.translate(x, y); }
53 void scale(double x, double y) { painter.scale(x, y); }
54 void rotate(double a) { painter.rotate(a); }
55 void reset_transform() { painter.resetTransform(); }
56
57 // Colors:
58 void unset_stroke() { painter.setPen(score::Skin::instance().NoPen); }
59
60 rgba_color to_rgba(auto c)
61 {
62 const auto standard_color = avnd::get_color(c);
63 static constexpr auto convert = [](QColor c) constexpr -> rgba_color {
64 const auto rgba = c.rgba();
65 return {
66 (uint8_t)qRed(rgba),
67 (uint8_t)qGreen(rgba),
68 (uint8_t)qBlue(rgba),
69 (uint8_t)qAlpha(rgba),
70 };
71 };
72
73 using enum avnd::color_type;
74 switch(standard_color)
75 {
76 case dark:
77 return convert(skin.Dark.main.brush.color());
78 case darker:
79 return convert(skin.HalfDark.main.brush.color());
80 case mid:
81 return convert(skin.Gray.main.brush.color());
82 case lighter:
83 return convert(skin.HalfLight.main.brush.color());
84 case light:
85 return convert(skin.Light.main.brush.color());
86
87 case background_darker:
88 return convert(skin.Background2.darker300.brush.color());
89 case background_dark:
90 return convert(skin.Background2.darker.brush.color());
91 case background_mid:
92 return convert(skin.Background2.main.brush.color());
93 case background_light:
94 return convert(skin.Background2.lighter.brush.color());
95 case background_lighter:
96 return convert(skin.Background2.lighter180.brush.color());
97
98 case runtime_value_dark:
99 return convert(skin.Base4.darker.brush.color());
100 case runtime_value_mid:
101 return convert(skin.Base4.main.brush.color());
102 case runtime_value_light:
103 return convert(skin.Base4.lighter.brush.color());
104
105 case editable_value_dark:
106 return convert(skin.Emphasis2.darker.brush.color());
107 case editable_value_mid:
108 return convert(skin.Emphasis2.main.brush.color());
109 case editable_value_light:
110 return convert(skin.Emphasis2.lighter.brush.color());
111 }
112
113 return {};
114 }
115
116 void set_stroke_color(rgba_color c)
117 {
118 QPen p = painter.pen();
119 p.setColor(QColor::fromRgb(c.r, c.g, c.b, c.a));
120 painter.setPen(p);
121 }
122
123 template <typename E>
124 requires std::is_enum_v<E>
125 void set_stroke_color(E c)
126 {
127 set_stroke_color(to_rgba(c));
128 }
129
130 void set_stroke_width(double w)
131 {
132 QPen p = painter.pen();
133 p.setWidth(w);
134 painter.setPen(p);
135 }
136
137 void set_fill_color(rgba_color c)
138 {
139 painter.setBrush(QColor::fromRgb(c.r, c.g, c.b, c.a));
140 }
141
142 template <typename E>
143 requires std::is_enum_v<E>
144 void set_fill_color(E c)
145 {
146 set_fill_color(to_rgba(c));
147 }
148
149 void set_linear_gradient(
150 double x1, double y1, double x2, double y2, rgba_color c1, rgba_color c2)
151 {
152 QLinearGradient gradient(QPointF(x1, y1), QPointF(x2, y2));
153 gradient.setColorAt(0, QColor::fromRgb(c1.r, c1.g, c1.b, c1.a));
154 gradient.setColorAt(1, QColor::fromRgb(c2.r, c2.g, c2.b, c2.a));
155 painter.setBrush(gradient);
156 }
157
158 void set_radial_gradient(double cx, double cy, double cr, rgba_color c1, rgba_color c2)
159 {
160 QRadialGradient gradient(cx, cy, cr);
161 gradient.setColorAt(0, QColor::fromRgb(c1.r, c1.g, c1.b, c1.a));
162 gradient.setColorAt(1, QColor::fromRgb(c2.r, c2.g, c2.b, c2.a));
163 painter.setBrush(gradient);
164 }
165
166 void set_conical_gradient(double x, double y, double a, rgba_color c1, rgba_color c2)
167 {
168 QConicalGradient gradient(x, y, a);
169 gradient.setColorAt(0, QColor::fromRgb(c1.r, c1.g, c1.b, c1.a));
170 gradient.setColorAt(1, QColor::fromRgb(c2.r, c2.g, c2.b, c2.a));
171 painter.setBrush(gradient);
172 }
173
174 template <typename E>
175 requires std::is_enum_v<E>
176 void
177 set_linear_gradient(double x1, double y1, double x2, double y2, E c1, rgba_color c2)
178 {
179 set_linear_gradient(x1, y1, x2, y2, to_rgba(c1), c2);
180 }
181
182 template <typename E>
183 requires std::is_enum_v<E>
184 void
185 set_linear_gradient(double x1, double y1, double x2, double y2, rgba_color c1, E c2)
186 {
187 set_linear_gradient(x1, y1, x2, y2, c1, to_rgba(c2));
188 }
189
190 template <typename E1, typename E2>
191 requires std::is_enum_v<E1> && std::is_enum_v<E2>
192 void set_linear_gradient(double x1, double y1, double x2, double y2, E1 c1, E2 c2)
193 {
194 set_linear_gradient(x1, y1, x2, y2, to_rgba(c1), to_rgba(c2));
195 }
196
197 template <typename E>
198 requires std::is_enum_v<E>
199 void
200 set_radial_gradient(double x1, double y1, double x2, double y2, E c1, rgba_color c2)
201 {
202 set_radial_gradient(x1, y1, x2, y2, to_rgba(c1), c2);
203 }
204
205 template <typename E>
206 requires std::is_enum_v<E>
207 void
208 set_radial_gradient(double x1, double y1, double x2, double y2, rgba_color c1, E c2)
209 {
210 set_radial_gradient(x1, y1, x2, y2, c1, to_rgba(c2));
211 }
212
213 template <typename E1, typename E2>
214 requires std::is_enum_v<E1> && std::is_enum_v<E2>
215 void set_radial_gradient(double x1, double y1, double x2, double y2, E1 c1, E2 c2)
216 {
217 set_radial_gradient(x1, y1, x2, y2, to_rgba(c1), to_rgba(c2));
218 }
219
220 template <typename E>
221 requires std::is_enum_v<E>
222 void
223 set_conical_gradient(double x1, double y1, double x2, double y2, E c1, rgba_color c2)
224 {
225 set_conical_gradient(x1, y1, x2, y2, to_rgba(c1), c2);
226 }
227
228 template <typename E>
229 requires std::is_enum_v<E>
230 void
231 set_conical_gradient(double x1, double y1, double x2, double y2, rgba_color c1, E c2)
232 {
233 set_conical_gradient(x1, y1, x2, y2, c1, to_rgba(c2));
234 }
235
236 template <typename E1, typename E2>
237 requires std::is_enum_v<E1> && std::is_enum_v<E2>
238 void set_conical_gradient(double x1, double y1, double x2, double y2, E1 c1, E2 c2)
239 {
240 set_conical_gradient(x1, y1, x2, y2, to_rgba(c1), to_rgba(c2));
241 }
242
243 // Text:
244 void set_font(std::string_view f)
245 {
246 auto font = painter.font();
247 font.setFamily(QString::fromUtf8(f.data(), f.size()));
248 painter.setFont(font);
249 }
250
251 void set_font_size(double f)
252 {
253 auto font = painter.font();
254 font.setPointSize(f);
255 painter.setFont(font);
256 }
257
258 void draw_text(double x, double y, std::string_view str)
259 {
260 path.addText(x, y, painter.font(), QString::fromUtf8(str.data(), str.size()));
261 }
262
263 void draw_text(double x, double y, double w, double h, std::string_view str)
264 {
265 QRectF rect{x, y, w, h};
266 const auto& m = painter.fontMetrics();
267 auto txt = QString::fromUtf8(str.data(), str.size());
268 auto text_rect = m.boundingRect(txt);
269 auto text_half_diagonal = (text_rect.center() - text_rect.topLeft()) / 2.;
270 auto pos = rect.center() + text_half_diagonal;
271 path.addText(pos.x(), pos.y(), painter.font(), txt);
272 }
273
274 // Drawing
275 void draw_line(double x1, double y1, double x2, double y2)
276 {
277 path.moveTo(x1, y1);
278 path.lineTo(x2, y2);
279 }
280
281 // x1, y1, x2 , y2, x3, y3
282 void draw_triangle(double x1, double y1, double x2, double y2, double x3, double y3)
283 {
284 path.moveTo(x1, y1);
285 path.lineTo(x2, y2);
286 path.lineTo(x3, y3);
287 path.lineTo(x1, y1);
288 painter.drawPath(path);
289 }
290
291 // x , y , w , h
292 void draw_rect(double x, double y, double w, double h) { path.addRect(x, y, w, h); }
293
294 // x , y , w , h
295 void draw_rounded_rect(double x, double y, double w, double h, double r)
296 {
297 path.addRoundedRect(x, y, w, h, r, r);
298 }
299
300 // x , y , filename
301 void draw_pixmap(double x, double y, const QString& str)
302 {
303 painter.drawPixmap(x, y, str);
304 }
305
306 // x , y , w , h
307 void draw_ellipse(double x, double y, double w, double h)
308 {
309 path.addEllipse(x, y, w, h);
310 }
311
312 // cx, cy, radius
313 void draw_circle(double cx, double cy, double cr)
314 {
315 path.addEllipse(QPointF{cx, cy}, cr, cr);
316 }
317
318 // tab, count
319 void draw_polygon(const double* tab, int count)
320 {
321 QPolygonF poly;
322 double x, y;
323 for(int i = 0; i < count * 2; i += 2)
324 {
325 x = tab[i];
326 y = tab[i + 1];
327 poly << QPointF(x, y);
328 }
329 path.addPolygon(poly);
330 }
331
332 void draw_bytes(
333 int x, int y, int w, int h, const unsigned char* image, int img_w, int img_h,
334 bool smooth = false)
335 {
336 auto img = QImage(image, img_w, img_h, QImage::Format_RGBA8888);
337 auto prev = painter.renderHints() & QPainter::SmoothPixmapTransform;
338 painter.setRenderHint(QPainter::SmoothPixmapTransform, smooth);
339 painter.drawImage(
340 QRect(x, y, w, h), img, QRect(0, 0, img_w, img_h), Qt::ImageConversionFlags{});
341
342 painter.setRenderHint(QPainter::SmoothPixmapTransform, prev);
343 }
344 void draw_bytes(
345 int x, int y, int w, int h, const float* image, int img_w, int img_h,
346 bool smooth = false)
347 {
348 auto img
349 = QImage((const unsigned char*)image, img_w, img_h, QImage::Format_RGBA32FPx4);
350 auto prev = painter.renderHints() & QPainter::SmoothPixmapTransform;
351 painter.setRenderHint(QPainter::SmoothPixmapTransform, smooth);
352 painter.drawImage(
353 QRect(x, y, w, h), img, QRect(0, 0, img_w, img_h), Qt::ImageConversionFlags{});
354
355 painter.setRenderHint(QPainter::SmoothPixmapTransform, prev);
356 }
357};
358static_assert(avnd::painter<QPainterAdapter>);
359
360template <typename Item, typename Control = void>
361class CustomItem : public QGraphicsItem
362{
363public:
364 // Item may be T::item_type or T&
365 using item_type = std::decay_t<Item>;
366
367 // // Case T::item_type
368 // explicit CustomItem() { init(); }
369
370 // Case T&
371 explicit CustomItem(Item t)
372 : impl{t}
373 {
374 this->setFlag(QGraphicsItem::ItemClipsToShape);
375 this->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
376
377 if constexpr(requires { this->impl.update = [this] {}; })
378 {
379 this->impl.update = [this] { this->update(); };
380 }
381 }
382
383 QRectF boundingRect() const override
384 {
385 return {0., 0., item_type::width(), item_type::height()};
386 }
387
388 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
389 override
390 {
391 auto& skin = score::Skin::instance();
392 painter->setRenderHint(QPainter::Antialiasing, true);
393 painter->setPen(skin.Dark.main.pen1);
394 impl.paint(QPainterAdapter{*painter, *this, {}});
395 painter->setRenderHint(QPainter::Antialiasing, false);
396 }
397
399 {
400 enum button
401 {
402 no_button,
403 left = (1 << 1),
404 right = (1 << 2),
405 middle = (1 << 3)
406 };
407 friend button& operator|=(button& lhs, button rhs) noexcept
408 {
409 return (
410 enum button&)(reinterpret_cast<std::underlying_type_t<enum button>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum button>&>(rhs));
411 }
412 enum modifier
413 {
414 no_modifier,
415 shift = (1 << 1),
416 ctrl = (1 << 2),
417 alt = (1 << 3),
418 meta = (1 << 4)
419 };
420 friend modifier& operator|=(modifier& lhs, modifier rhs) noexcept
421 {
422 return (
423 enum modifier&)(reinterpret_cast<std::underlying_type_t<enum modifier>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum modifier>&>(rhs));
424 }
425
426 float x, y;
427
428 enum button button = {};
429 enum button held_buttons = {};
430 enum modifier modifiers = {};
431 };
432
433protected:
434 static custom_mouse_event make_event(QGraphicsSceneMouseEvent* event) noexcept
435 {
437
438 p.x = event->pos().x();
439 p.y = event->pos().y();
440
441 if(event->button() == Qt::LeftButton)
442 p.button = custom_mouse_event::left;
443 else if(event->button() == Qt::RightButton)
444 p.button = custom_mouse_event::right;
445 else if(event->button() == Qt::MiddleButton)
446 p.button = custom_mouse_event::middle;
447
448 if(event->buttons() & Qt::LeftButton)
449 p.held_buttons |= p.left;
450 if(event->buttons() & Qt::RightButton)
451 p.held_buttons |= p.right;
452 if(event->buttons() & Qt::MiddleButton)
453 p.held_buttons |= p.middle;
454
455 if(event->modifiers() & Qt::ShiftModifier)
456 p.modifiers |= p.shift;
457 if(event->modifiers() & Qt::AltModifier)
458 p.modifiers |= p.alt;
459 if(event->modifiers() & Qt::ControlModifier)
460 p.modifiers |= p.ctrl;
461 if(event->modifiers() & Qt::MetaModifier)
462 p.modifiers |= p.meta;
463
464 return p;
465 }
466
467 void mousePressEvent(QGraphicsSceneMouseEvent* event) override
468 {
469 if constexpr(requires { impl.mouse_press(0, 0); })
470 {
471 if(impl.mouse_press(event->pos().x(), event->pos().y()))
472 event->accept();
473 }
474 else if constexpr(requires { impl.mouse_press(custom_mouse_event{}); })
475 {
476 if(impl.mouse_press(make_event(event)))
477 event->accept();
478 }
479 else
480 {
481 event->ignore();
482 }
483 update();
484 }
485
486 void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override
487 {
488 if constexpr(requires { impl.mouse_move(0, 0); })
489 {
490 impl.mouse_move(event->pos().x(), event->pos().y());
491 event->accept();
492 }
493 else if constexpr(requires { impl.mouse_move(custom_mouse_event{}); })
494 {
495 impl.mouse_move(make_event(event));
496 event->accept();
497 }
498 else
499 {
500 event->ignore();
501 }
502 update();
503 }
504
505 void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override
506 {
507 if constexpr(requires { impl.mouse_release(0, 0); })
508 {
509 impl.mouse_release(event->pos().x(), event->pos().y());
510 event->accept();
511 }
512 else if constexpr(requires { impl.mouse_release(custom_mouse_event{}); })
513 {
514 impl.mouse_release(make_event(event));
515 event->accept();
516 }
517 else
518 {
519 event->ignore();
520 }
521 update();
522 }
523
524protected:
525 Item impl;
526};
527
528template <typename Item>
529class CustomControl : public CustomItem<Item>
530{
531public:
533 explicit CustomControl(
534 Item item_init, Process::ControlInlet& ctl, const score::DocumentContext& ctx)
535 : CustomItem<Item>{item_init}
536 , cmd{ctx.commandStack}
537 {
538 if constexpr(requires { this->impl.transaction; })
539 {
540 this->impl.transaction.start = [] {
541
542 };
543 this->impl.transaction.update = [this, &ctl](const auto& value) {
544 auto val = oscr::to_ossia_value(value);
545 cmd.submit<Process::SetControlValue>(ctl, val);
546
547 this->impl.value = value; //impl.value_to_control(Control{}
548 this->update();
549 };
550 this->impl.transaction.commit = [this] { cmd.commit(); };
551 this->impl.transaction.rollback = [this] { cmd.rollback(); };
552 }
553 }
554};
555}
The OngoingCommandDispatcher class.
Definition OngoingCommandDispatcher.hpp:27
void submit(Watched &&watched, Args &&... args)
Definition OngoingCommandDispatcher.hpp:39
void commit()
Definition Document.cpp:51
void rollback()
If the command has to be reverted, for instance when pressing escape.
Definition Document.cpp:64
Definition Port.hpp:205
Definition SetControlValue.hpp:13
Definition Painter.hpp:530
Definition Painter.hpp:362
Definition Skin.hpp:94
Definition Factories.hpp:19
Definition Painter.hpp:399
Definition Painter.hpp:23
Definition Painter.hpp:19
Definition DocumentContext.hpp:18