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