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 <halp/texture.hpp>
13 
14 #include <cmath>
15 
16 namespace oscr
17 {
19 {
20  QPainter& painter;
21  QGraphicsItem& item;
22  QPainterPath path;
23 
24  void begin_path() { path = QPainterPath{}; }
25  void close_path() { path.closeSubpath(); }
26  void stroke() { painter.strokePath(path, painter.pen()); }
27  void fill() { painter.fillPath(path, painter.brush()); }
28  void update() { item.update(); }
29 
30  void move_to(double x, double y) { path.moveTo(x, y); }
31  void line_to(double x, double y) { path.lineTo(x, y); }
32  void arc_to(double x, double y, double w, double h, double start, double length)
33  {
34  path.arcTo(x, y, w, h, start, length);
35  }
36 
37  void cubic_to(double c1x, double c1y, double c2x, double c2y, double endx, double endy)
38  {
39  path.cubicTo(c1x, c1y, c2x, c2y, endx, endy);
40  }
41 
42  void quad_to(double x1, double y1, double x2, double y2)
43  {
44  path.quadTo(x1, y1, x2, y2);
45  }
46 
47  void translate(double x, double y) { painter.translate(x, y); }
48  void scale(double x, double y) { painter.scale(x, y); }
49  void rotate(double a) { painter.rotate(a); }
50  void reset_transform() { painter.resetTransform(); }
51 
52  // Colors:
53  void set_stroke_color(halp::rgba_color c)
54  {
55  QPen p = painter.pen();
56  p.setColor(qRgba(c.r, c.g, c.b, c.a));
57  painter.setPen(p);
58  }
59 
60  void set_stroke_width(double w)
61  {
62  QPen p = painter.pen();
63  p.setWidth(w);
64  painter.setPen(p);
65  }
66 
67  void set_fill_color(halp::rgba_color c)
68  {
69  painter.setBrush(QColor(qRgba(c.r, c.g, c.b, c.a)));
70  }
71 
72  void set_linear_gradient(
73  double x1, double y1, double x2, double y2, halp::rgba_color c1,
74  halp::rgba_color c2)
75  {
76  QLinearGradient gradient(QPointF(x1, y1), QPointF(x2, y2));
77  gradient.setColorAt(0, QColor(qRgba(c1.r, c1.g, c1.b, c1.a)));
78  gradient.setColorAt(1, QColor(qRgba(c2.r, c2.g, c2.b, c2.a)));
79  painter.setBrush(gradient);
80  }
81 
82  void set_radial_gradient(
83  double cx, double cy, double cr, halp::rgba_color c1, halp::rgba_color c2)
84  {
85  QRadialGradient gradient(cx, cy, cr);
86  gradient.setColorAt(0, QColor(qRgba(c1.r, c1.g, c1.b, c1.a)));
87  gradient.setColorAt(1, QColor(qRgba(c2.r, c2.g, c2.b, c2.a)));
88  painter.setBrush(gradient);
89  }
90 
91  void set_conical_gradient(
92  double x, double y, double a, halp::rgba_color c1, halp::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_RGB32);
182  painter.drawImage(QRect(x, y, w, h), img, QRect(0, 0, img_w, img_h));
183  }
184 };
185 static_assert(avnd::painter<QPainterAdapter>);
186 
187 template <typename Item, typename Control = void>
188 class CustomItem : public QGraphicsItem
189 {
190 public:
191  // Item may be T::item_type or T&
192  using item_type = std::decay_t<Item>;
193 
194  // // Case T::item_type
195  // explicit CustomItem() { init(); }
196 
197  // Case T&
198  explicit CustomItem(Item t)
199  : impl{t}
200  {
201  this->setFlag(QGraphicsItem::ItemClipsToShape);
202  this->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
203 
204  if constexpr(requires { this->impl.update = [this] {}; })
205  {
206  this->impl.update = [this] { this->update(); };
207  }
208  }
209 
210  QRectF boundingRect() const override
211  {
212  return {0., 0., item_type::width(), item_type::height()};
213  }
214 
215  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
216  override
217  {
218  auto& skin = score::Skin::instance();
219  painter->setRenderHint(QPainter::Antialiasing, true);
220  painter->setPen(skin.Dark.main.pen1);
221  impl.paint(QPainterAdapter{*painter, *this, {}});
222  painter->setRenderHint(QPainter::Antialiasing, false);
223  }
224 
226  {
227  enum button
228  {
229  no_button,
230  left = (1 << 1),
231  right = (1 << 2),
232  middle = (1 << 3)
233  };
234  friend button& operator|=(button& lhs, button rhs) noexcept
235  {
236  return (
237  enum button&)(reinterpret_cast<std::underlying_type_t<enum button>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum button>&>(rhs));
238  }
239  enum modifier
240  {
241  no_modifier,
242  shift = (1 << 1),
243  ctrl = (1 << 2),
244  alt = (1 << 3),
245  meta = (1 << 4)
246  };
247  friend modifier& operator|=(modifier& lhs, modifier rhs) noexcept
248  {
249  return (
250  enum modifier&)(reinterpret_cast<std::underlying_type_t<enum modifier>&>(lhs) |= reinterpret_cast<std::underlying_type_t<enum modifier>&>(rhs));
251  }
252 
253  float x, y;
254 
255  enum button button = {};
256  enum button held_buttons = {};
257  enum modifier modifiers = {};
258  };
259 
260 protected:
261  static custom_mouse_event make_event(QGraphicsSceneMouseEvent* event) noexcept
262  {
264 
265  p.x = event->pos().x();
266  p.y = event->pos().y();
267 
268  if(event->button() == Qt::LeftButton)
269  p.button = custom_mouse_event::left;
270  else if(event->button() == Qt::RightButton)
271  p.button = custom_mouse_event::right;
272  else if(event->button() == Qt::MiddleButton)
273  p.button = custom_mouse_event::middle;
274 
275  if(event->buttons() & Qt::LeftButton)
276  p.held_buttons |= p.left;
277  if(event->buttons() & Qt::RightButton)
278  p.held_buttons |= p.right;
279  if(event->buttons() & Qt::MiddleButton)
280  p.held_buttons |= p.middle;
281 
282  if(event->modifiers() & Qt::ShiftModifier)
283  p.modifiers |= p.shift;
284  if(event->modifiers() & Qt::AltModifier)
285  p.modifiers |= p.alt;
286  if(event->modifiers() & Qt::ControlModifier)
287  p.modifiers |= p.ctrl;
288  if(event->modifiers() & Qt::MetaModifier)
289  p.modifiers |= p.meta;
290 
291  return p;
292  }
293 
294  void mousePressEvent(QGraphicsSceneMouseEvent* event) override
295  {
296  if constexpr(requires { impl.mouse_press(0, 0); })
297  {
298  if(impl.mouse_press(event->pos().x(), event->pos().y()))
299  event->accept();
300  }
301  else if constexpr(requires { impl.mouse_press(custom_mouse_event{}); })
302  {
303  if(impl.mouse_press(make_event(event)))
304  event->accept();
305  }
306  update();
307  }
308 
309  void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override
310  {
311  if constexpr(requires { impl.mouse_move(0, 0); })
312  {
313  impl.mouse_move(event->pos().x(), event->pos().y());
314  event->accept();
315  }
316  else if constexpr(requires { impl.mouse_move(custom_mouse_event{}); })
317  {
318  impl.mouse_move(make_event(event));
319  event->accept();
320  }
321  update();
322  }
323 
324  void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override
325  {
326  if constexpr(requires { impl.mouse_release(0, 0); })
327  {
328  impl.mouse_release(event->pos().x(), event->pos().y());
329  event->accept();
330  }
331  else if constexpr(requires { impl.mouse_release(custom_mouse_event{}); })
332  {
333  impl.mouse_release(make_event(event));
334  event->accept();
335  }
336  update();
337  }
338 
339 protected:
340  Item impl;
341 };
342 
343 template <typename Item>
344 class CustomControl : public CustomItem<Item>
345 {
346 public:
348  explicit CustomControl(
349  Item item_init, Process::ControlInlet& ctl, const score::DocumentContext& ctx)
350  : CustomItem<Item>{item_init}
351  , cmd{ctx.commandStack}
352  {
353  if constexpr(requires { this->impl.transaction; })
354  {
355  this->impl.transaction.start = [] {
356 
357  };
358  this->impl.transaction.update = [this, &ctl](const auto& value) {
359  auto val = oscr::to_ossia_value(value);
360  cmd.submit<Process::SetControlValue>(ctl, val);
361 
362  this->impl.value = value; //impl.value_to_control(Control{}
363  this->update();
364  };
365  this->impl.transaction.commit = [this] { cmd.commit(); };
366  this->impl.transaction.rollback = [this] { cmd.rollback(); };
367  }
368  }
369 };
370 }
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: score-lib-process/Process/Dataflow/Port.hpp:202
Definition: SetControlValue.hpp:13
Definition: Painter.hpp:345
Definition: Painter.hpp:189
Definition: Painter.hpp:226
Definition: Painter.hpp:19
Definition: DocumentContext.hpp:18