GLUI: implement auto volume gain value text editor
[spectmorph.git] / glui / smwidget.hh
1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #ifndef SPECTMORPH_WIDGET_HH
4 #define SPECTMORPH_WIDGET_HH
5
6 #include <vector>
7 #include <cairo.h>
8 #include <stdio.h>
9
10 #include "pugl/pugl.h"
11 #include "smsignal.hh"
12
13 namespace SpectMorph
14 {
15
16 enum class TextAlign {
17   LEFT,
18   CENTER,
19   RIGHT
20 };
21
22 struct Window;
23 class ScrollView;
24
25 class Rect
26 {
27   double m_x;
28   double m_y;
29   double m_width;
30   double m_height;
31 public:
32   Rect() :
33     m_x (0), m_y (0), m_width (0), m_height (0)
34   {
35   }
36   Rect (double x, double y, double width, double height) :
37     m_x (x), m_y (y), m_width (width), m_height (height)
38   {
39   }
40   void
41   move_to (double x, double y)
42   {
43     m_x = x;
44     m_y = y;
45   }
46   bool
47   contains (double x, double y)
48   {
49     return (x >= m_x) && (y >= m_y) && (x < m_x + m_width) && (y < m_y + m_height);
50   }
51   double
52   x() const
53   {
54     return m_x;
55   }
56   double
57   y() const
58   {
59     return m_y;
60   }
61   double
62   width() const
63   {
64     return m_width;
65   }
66   double
67   height() const
68   {
69     return m_height;
70   }
71   bool
72   empty() const
73   {
74     return m_width * m_height == 0;
75   }
76   Rect
77   intersection (const Rect& r)
78   {
79     // upper left corner
80     double x1 = std::max (m_x, r.m_x);
81     double y1 = std::max (m_y, r.m_y);
82
83     // lower right corner
84     double x2 = std::min (m_x + m_width,  r.m_x + r.m_width);
85     double y2 = std::min (m_y + m_height, r.m_y + r.m_height);
86
87     // FIXME: maybe special case the "no intersection at all" rectangle
88     return Rect (x1, y1, std::max (x2 - x1, 0.0), std::max (y2 - y1, 0.0));
89   }
90   Rect
91   rect_union (const Rect &r)
92   {
93     if (r.empty())
94       return *this;
95     if (empty())
96       return r;
97
98     // upper left corner
99     double x1 = std::min (m_x, r.m_x);
100     double y1 = std::min (m_y, r.m_y);
101
102     // lower right corner
103     double x2 = std::max (m_x + m_width,  r.m_x + r.m_width);
104     double y2 = std::max (m_y + m_height, r.m_y + r.m_height);
105
106     return Rect (x1, y1, x2 - x1, y2 - y1);
107   }
108 };
109
110 class Point
111 {
112   double m_x;
113   double m_y;
114
115 public:
116   Point() :
117     m_x (0), m_y (0)
118   {
119   }
120   Point (double x, double y) :
121     m_x (x), m_y (y)
122   {
123   }
124   double
125   x() const
126   {
127     return m_x;
128   }
129   double
130   y() const
131   {
132     return m_y;
133   }
134 };
135
136 enum class ThemeColor
137 {
138   FRAME,
139   MENU_BG,
140   MENU_ITEM,
141   CHECKBOX,
142   SLIDER,
143   WINDOW_BG,
144   OPERATOR_BG,
145   TEXT
146 };
147
148 class Color
149 {
150   bool   m_valid = false;
151   double m_red = 0;
152   double m_green = 0;
153   double m_blue = 0;
154
155 public:
156   Color()
157   {
158   }
159   Color (ThemeColor theme_color)
160   {
161     switch (theme_color)
162       {
163         case ThemeColor::FRAME:     set_rgb (0.8, 0.8, 0.8);  break;
164         case ThemeColor::MENU_BG:   set_rgb (0.3, 0.3, 0.3);  break;
165         case ThemeColor::MENU_ITEM: set_rgb (1, 0.6, 0.0);    break;
166         case ThemeColor::CHECKBOX:  set_rgb (0.1, 0.7, 0.1);  break;
167         case ThemeColor::SLIDER:    set_rgb (0.1, 0.7, 0.1);  break;
168         case ThemeColor::WINDOW_BG: set_rgb (0.2, 0.2, 0.2);  break;
169         case ThemeColor::OPERATOR_BG: set_rgb (0.2, 0.2, 0.2); break;
170         case ThemeColor::TEXT:      set_rgb (1.0, 1.0, 1.0);  break;
171       }
172   }
173   Color (double r, double g, double b)
174   {
175     set_rgb (r, g, b);
176   }
177   operator bool()
178   {
179     return m_valid;
180   }
181   static Color null()
182   {
183     return Color(); // not valid
184   }
185   double red() const
186   {
187     return m_red;
188   }
189   double green() const
190   {
191     return m_green;
192   }
193   double blue() const
194   {
195     return m_blue;
196   }
197   void
198   set_rgb (double r, double g, double b)
199   {
200     m_red = r;
201     m_green = g;
202     m_blue = b;
203     m_valid = true;
204   }
205   void set_hsv (double h, double s, double v);
206   void get_hsv (double *h, double *s, double *v);
207   Color lighter (double factor = 130);
208   Color darker (double factor = 130);
209
210   /* comparision */
211   bool
212   operator== (const Color& other) const
213   {
214     return other.m_valid == m_valid && other.m_red == m_red && other.m_green == m_green && other.m_blue == m_blue;
215   }
216   bool
217   operator!= (const Color& other) const
218   {
219     return !(other == *this);
220   }
221 };
222
223 struct Widget : public SignalReceiver
224 {
225 private:
226   bool m_enabled = true;
227   bool m_visible = true;
228   Color m_background_color;
229
230 protected:
231   void remove_child (Widget *child);
232
233 public:
234   Widget *parent;
235   double x, y, width, height;
236
237   std::vector<Widget *> children;
238
239   void debug_fill (cairo_t *cr)
240   {
241     cairo_rectangle (cr, 0, 0, width, height);
242     cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
243     cairo_fill (cr);
244   }
245
246   Widget (Widget *parent, double x, double y, double width, double height);
247   Widget (Widget *parent) :
248     Widget (parent, 0, 0, 300, 100)
249   {
250   }
251   virtual ~Widget();
252
253   struct DrawEvent
254   {
255     cairo_t *cr;
256
257     Rect     rect; // only valid for clipping() == true
258   };
259
260   virtual void draw (const DrawEvent& draw);
261
262   virtual bool
263   clipping()
264   {
265     // clipping for draw() - enabled by default
266     return true;
267   }
268
269   virtual void motion (double x, double y)
270   {
271   }
272   virtual void
273   mouse_press (double x, double y)
274   {
275   }
276   virtual void
277   mouse_release (double x, double y)
278   {
279   }
280   virtual bool
281   scroll (double dx, double dy)
282   {
283     return false;
284   }
285   virtual void
286   enter_event()
287   {
288   }
289   virtual void
290   leave_event()
291   {
292   }
293   virtual void
294   focus_event()
295   {
296   }
297   virtual void
298   focus_out_event()
299   {
300   }
301   virtual void
302   key_press_event (const PuglEventKey& key_event)
303   {
304   }
305   virtual Window *
306   window()
307   {
308     return parent ? parent->window() : nullptr;
309   }
310   virtual ScrollView *
311   scroll_view()
312   {
313     return parent ? parent->scroll_view() : nullptr;
314   }
315   void
316   set_enabled (bool e)
317   {
318     if (e == m_enabled)
319       return;
320
321     m_enabled = e;
322     update_with_children();
323   }
324   bool
325   enabled() const
326   {
327     return m_enabled;
328   }
329   void
330   set_visible (bool v)
331   {
332     if (v == m_visible)
333       return;
334
335     m_visible = v;
336     update_with_children();
337   }
338   bool
339   visible() const
340   {
341     return m_visible;
342   }
343   void
344   set_background_color (Color color)
345   {
346     m_background_color = color;
347   }
348   Color
349   background_color() const
350   {
351     return m_background_color;
352   }
353   bool
354   recursive_enabled() const
355   {
356     if (!m_enabled)
357       return false;
358     if (parent)
359       return parent->recursive_enabled();
360     return true;
361   }
362   double abs_x() const;
363   double abs_y() const;
364
365   Rect   abs_visible_rect();
366   void   update (double x, double y, double width, double height);
367   void   update();
368   void   update_with_children();
369   void   update_full();
370 };
371
372 }
373
374 #endif