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