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};
345static_assert(avnd::painter<QPainterAdapter>);
346
347template <typename Item, typename Control = void>
348class CustomItem : public QGraphicsItem
349{
350public:
351 // Item may be T::item_type or T&
352 using item_type = std::decay_t<Item>;
353
354 // // Case T::item_type
355 // explicit CustomItem() { init(); }
356
357 // Case T&
358 explicit CustomItem(Item t)
359 : impl{t}
360 {
361 this->setFlag(QGraphicsItem::ItemClipsToShape);
362 this->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
363
364 if constexpr(requires { this->impl.update = [this] {}; })
365 {
366 this->impl.update = [this] { this->update(); };
367 }
368 }
369
370 QRectF boundingRect() const override
371 {
372 return {0., 0., item_type::width(), item_type::height()};
373 }
374
375 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
376 override
377 {
378 auto& skin = score::Skin::instance();
379 painter->setRenderHint(QPainter::Antialiasing, true);
380 painter->setPen(skin.Dark.main.pen1);
381 impl.paint(QPainterAdapter{*painter, *this, {}});
382 painter->setRenderHint(QPainter::Antialiasing, false);
383 }
384
386 {
387 enum button
388 {
389 no_button,
390 left = (1 << 1),
391 right = (1 << 2),
392 middle = (1 << 3)
393 };
394 friend button& operator|=(button& lhs, button rhs) noexcept
395 {
396 return (
397 enum button&)(reinterpret_cast<std::underlying_type_t<enum button>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum button>&>(rhs));
398 }
399 enum modifier
400 {
401 no_modifier,
402 shift = (1 << 1),
403 ctrl = (1 << 2),
404 alt = (1 << 3),
405 meta = (1 << 4)
406 };
407 friend modifier& operator|=(modifier& lhs, modifier rhs) noexcept
408 {
409 return (
410 enum modifier&)(reinterpret_cast<std::underlying_type_t<enum modifier>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum modifier>&>(rhs));
411 }
412
413 float x, y;
414
415 enum button button = {};
416 enum button held_buttons = {};
417 enum modifier modifiers = {};
418 };
419
420protected:
421 static custom_mouse_event make_event(QGraphicsSceneMouseEvent* event) noexcept
422 {
424
425 p.x = event->pos().x();
426 p.y = event->pos().y();
427
428 if(event->button() == Qt::LeftButton)
429 p.button = custom_mouse_event::left;
430 else if(event->button() == Qt::RightButton)
431 p.button = custom_mouse_event::right;
432 else if(event->button() == Qt::MiddleButton)
433 p.button = custom_mouse_event::middle;
434
435 if(event->buttons() & Qt::LeftButton)
436 p.held_buttons |= p.left;
437 if(event->buttons() & Qt::RightButton)
438 p.held_buttons |= p.right;
439 if(event->buttons() & Qt::MiddleButton)
440 p.held_buttons |= p.middle;
441
442 if(event->modifiers() & Qt::ShiftModifier)
443 p.modifiers |= p.shift;
444 if(event->modifiers() & Qt::AltModifier)
445 p.modifiers |= p.alt;
446 if(event->modifiers() & Qt::ControlModifier)
447 p.modifiers |= p.ctrl;
448 if(event->modifiers() & Qt::MetaModifier)
449 p.modifiers |= p.meta;
450
451 return p;
452 }
453
454 void mousePressEvent(QGraphicsSceneMouseEvent* event) override
455 {
456 if constexpr(requires { impl.mouse_press(0, 0); })
457 {
458 if(impl.mouse_press(event->pos().x(), event->pos().y()))
459 event->accept();
460 }
461 else if constexpr(requires { impl.mouse_press(custom_mouse_event{}); })
462 {
463 if(impl.mouse_press(make_event(event)))
464 event->accept();
465 }
466 update();
467 }
468
469 void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override
470 {
471 if constexpr(requires { impl.mouse_move(0, 0); })
472 {
473 impl.mouse_move(event->pos().x(), event->pos().y());
474 event->accept();
475 }
476 else if constexpr(requires { impl.mouse_move(custom_mouse_event{}); })
477 {
478 impl.mouse_move(make_event(event));
479 event->accept();
480 }
481 update();
482 }
483
484 void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override
485 {
486 if constexpr(requires { impl.mouse_release(0, 0); })
487 {
488 impl.mouse_release(event->pos().x(), event->pos().y());
489 event->accept();
490 }
491 else if constexpr(requires { impl.mouse_release(custom_mouse_event{}); })
492 {
493 impl.mouse_release(make_event(event));
494 event->accept();
495 }
496 update();
497 }
498
499protected:
500 Item impl;
501};
502
503template <typename Item>
504class CustomControl : public CustomItem<Item>
505{
506public:
508 explicit CustomControl(
509 Item item_init, Process::ControlInlet& ctl, const score::DocumentContext& ctx)
510 : CustomItem<Item>{item_init}
511 , cmd{ctx.commandStack}
512 {
513 if constexpr(requires { this->impl.transaction; })
514 {
515 this->impl.transaction.start = [] {
516
517 };
518 this->impl.transaction.update = [this, &ctl](const auto& value) {
519 auto val = oscr::to_ossia_value(value);
520 cmd.submit<Process::SetControlValue>(ctl, val);
521
522 this->impl.value = value; //impl.value_to_control(Control{}
523 this->update();
524 };
525 this->impl.transaction.commit = [this] { cmd.commit(); };
526 this->impl.transaction.rollback = [this] { cmd.rollback(); };
527 }
528 }
529};
530}
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:204
Definition SetControlValue.hpp:13
Definition Painter.hpp:505
Definition Painter.hpp:349
Definition Skin.hpp:94
Definition Factories.hpp:19
Definition Painter.hpp:386
Definition Painter.hpp:23
Definition Painter.hpp:19
Definition DocumentContext.hpp:18