OpenCV zGUI
CrossPlatform C++ GUI libarary based on OpenCV
zgui.h
1 #pragma once
2 #include<map>
3 #include<type_traits>
4 #include<functional>
5 #include<spdlog/spdlog.h>
6 #include<source_location>
7 #include<opencv2/highgui.hpp>
8 #include<opencv2/freetype.hpp>
9 #include<opencv2/imgproc.hpp>
10 #include<list>
11 #include<hangul.h>
12 #define EVENT_ENTER 8000
13 #define EVENT_LEAVE 8001
14 #define EVENT_KEYBOARD 8002
15 
16 namespace z {
17 
18 std::string source_loc(const std::source_location &location = std::source_location::current());
19 class Window;
20 
22 class Widget : public cv::Rect_<int>
23 {
24 public:
25  Widget(cv::Rect_<int> r);
26  Widget& operator=(const Widget &r);
27  bool focus() const;
28  virtual void focus(bool);
29  virtual void event_callback(int event, int x, int y);
30  virtual void show();
31  virtual std::string type() const { return "Widget"; }
32  void resize(cv::Rect2i r);
34  std::map<int, std::function<void(int, int)>> gui_callback_;
39  std::map<int, std::function<void(int, int)>> user_callback_;
40  cv::Mat3b mat_;
41  void update();
42  Window *parent_ = nullptr;
43  virtual void on_register() {}
44  virtual bool is_window() const {return false;}
45  int zIndex() {return zIndex_;}
46  void zIndex(int v) {zIndex_ = v; }
47  void hidden(bool v) {hidden_ = v;}
48  void activated(bool v) {activated_ = v;}
49  bool hidden() const {return hidden_;}
50  bool activated() const {return activated_;}
51  void hide();
52  float alpha() const {return alpha_;}
53  void alpha(float f) {alpha_ = f;}
54  void shade_rect(cv::Rect2i r, int shade = 3, cv::Vec3b color = widget_color_,
55  cv::Vec3b upper_left = highlight_color_, cv::Vec3b lower_right = click_color_);
56 
57 protected:
58  float alpha_ = 1;
59  bool hidden_ = false;
60  bool activated_ = true;
61  static const cv::Vec3b background_color_, widget_color_, highlight_color_, click_color_;
62  bool focus_ = false;
66  static cv::Ptr<cv::freetype::FreeType2> ft2_;
67  int zIndex_ = 0;
68 };
69 
70 class Label : public Widget
71 {
72 public:
73  Label(std::string text, cv::Rect2i r);
74  void text(std::string s);
75  std::string text() const;
76 protected:
77  std::string text_;
78 };
79 
80 class Button : public Widget
81 {
82 public:
83  Button(std::string text, cv::Rect_<int> r);
84  void click(std::function<void()> f);
85  void text(std::string s);
86  void draw(cv::Vec3b color = widget_color_, bool repaint = true);
87 protected:
88  std::string text_;
89 };
90 
91 class CheckBox : public Widget
92 {
93 public:
94  CheckBox(cv::Rect2i r);
95  bool checked() const;
96  void checked(bool);
97  void on_change(std::function<void(bool)> f);
98 protected:
99  bool checked_ = false;
100 private:
101  void click(int, int);
102 };
103 
105 class TextInput : public Widget
106 {
107 public:
108  TextInput(cv::Rect2i r);
109  std::string value() const;
110  void value(std::string s);
111  void enter(std::function<void(std::string)> f);
112  void move_cursor(bool right);
113  std::string type() const {return "TextInput";}
114 protected:
115  std::string fore_, back_, editting_;
116  void show_cursor();
117  bool draw();
118  void key_event(int key, int);
119  static HangulInputContext* hic_;
120  static HanjaTable* table_;
121  static bool hangul_mode_;// = false;
122  virtual bool on_overflow(std::string, std::string, std::string) { return true;}
123 
124 private:
125  void flush(), del(), hanja(), backspace(), hangul();
126  void popup(std::vector<std::string> v);
127  const cv::Vec3b white = cv::Vec3b{255, 255, 255};
128 };
129 
130 struct Wrapped
131 {
132  cv::Rect2i rect;
133  int fontsize;
134  std::string title;
135 };
136 
137 template<class T> class TwoD : public std::vector<std::vector<T>>
138 {
139 public:
140  void push_back(T a) {
141  tmp_.push_back(a);
142  }
143  void done() {
144  std::vector<std::vector<T>>::push_back(std::move(tmp_));
145  }
146 protected:
147  std::vector<T> tmp_;
148 };
149 
150 template<class T> auto to_number(std::string n) {
151  try {
152  if constexpr(std::is_same_v<int, T>) return stoi(n);
153  else if constexpr(std::is_same_v<float, T>) return stof(n);
154  else if constexpr(std::is_same_v<double, T>) return stod(n);
155  } catch(...) {
156  T n = 0;
157  return n;
158  }
159 }
160 
162 class Window : public Widget
163 {
164 public:
165  Window(std::string title, cv::Rect_<int> r);
166  void show();
167  void popup(Window &w, std::function<void(int)> f = [](int){});
168  void popdown(int value);
169  std::string type() const {return "Window"; }
170  int open(int flag = cv::WINDOW_AUTOSIZE, int x = -1, int y = -1);
171  void quit(int r);
172  void focus(bool v);
173  Window &operator+(Widget &w);
174  Window &operator-(Widget &w);
175  Window &operator<<(Widget &r);
176  Window &operator>>(Widget &r);
177  int loop();
178  std::vector<Widget*>::iterator begin(), end();
179  void close();
180  void start(int flag = cv::WINDOW_AUTOSIZE | cv::WINDOW_KEEPRATIO);
181  void keyboard_callback(int key, int level = 0);
182  //void update(const Widget &r);
183  std::string title() const;
184  //void resize(cv::Rect2i r);
185  void tie(TextInput &t, Button &b, const std::vector<std::string> &v, int font_size);
186  template<class T> auto tie(TextInput &t, Button &b1, Button &b2, T start, T step)
187  {
190  b1.text("\u25b2"); b2.text("\u25bc");
191  if(t.value() == "") t.value(std::to_string(start));
192  *this << b1; *this << b2; *this << t;
193  b1.click([&, step](){t.value(std::to_string(to_number<T>(t.value()) + step)); *this << t; show();});
194  b2.click([&, step](){t.value(std::to_string(to_number<T>(t.value()) - step)); *this << t; show();});
195  return [&t]() { return to_number<T>(t.value()); };
196  }
197  template<class... T> void tabs(int xpos, int ypos, T&... wins)
198  {
201  static TwoD<z::Window*> v;
202  static TwoD<std::shared_ptr<z::Button>> bts;
203  static std::vector<std::shared_ptr<z::Widget>> panels;
204  const int button_width = 100;
205  panels.push_back(std::make_shared<z::Widget>(cv::Rect2i{0,0,1,1}));
206  static std::vector<int> max_zindex;
207  int k = sizeof...(wins);
208  (v.push_back(&wins), ...);
209  v.done();
210  v.back().front()->zIndex(2);
211  panels.back()->zIndex(1);
212  max_zindex.push_back(2);
213  int sz = v.size(), shift = 0, max_w = 0, max_h = 0;
214  for(auto *pw : v.back()) {
215  pw->scrolled_rect_ = {0,0,0,0};
216  pw->x = xpos; pw->y = ypos + 40;
217  if(pw->width > max_w) max_w = pw->width;
218  if(pw->height > max_h) max_h = pw->height;
219  auto p = std::make_shared<z::Button>(pw->title(),
220  cv::Rect2i{xpos + shift++ * button_width, ypos, button_width, 30});
221  *this + *p + *pw;
222  bts.push_back(p);
223  p->click([this, sz, pw]() {
224  int z = max_zindex[sz-1];
225  panels[sz-1]->zIndex(z+1);
226  pw->zIndex(z+2);
227  max_zindex[sz - 1] = z + 2;
229  show();
230  });
231  }
232  bts.done();
233  panels.back()->resize(cv::Rect2i{xpos, ypos + 40, max_w, max_h});
234  *this + *panels.back();
235  }
236 
238  void move_widget(Widget &w, cv::Point2i p);
239  void on_register();
240  bool is_window() const {return true;}
241  template<class... T> auto tie(T&... checks)
242  {
244  static std::vector<z::CheckBox*> v;
245  int k = sizeof...(checks);
246  int sz = v.size();
247  (v.push_back(&checks), ...);
248  for(int i=sz; i < sz + k; i++) v[i]->on_change([i, k, sz, this](bool) {
249  for(int j=sz; j < sz + k; j++) {
250  if(i != j) v[j]->checked(false);
251  else v[j]->checked(true);
252  v[j]->update();
253  }
254  });
255  return [sz, k, &v]() {
256  for(int i=sz; i<sz+k; i++) if(v[i]->checked()) return i - sz;
257  };
258  }
259  template<class... T> void wrap(const char* title, int font, int N, const T&... widgets)
260  {
262  std::vector<int> xs, ys;
263  (xs.push_back(widgets.x), ...);
264  (xs.push_back(widgets.br().x), ...);
265  (ys.push_back(widgets.y), ...);
266  (ys.push_back(widgets.br().y), ...);
267  auto p = std::minmax_element(xs.begin(), xs.end());
268  auto q = std::minmax_element(ys.begin(), ys.end());
269  cv::Point2i ul = {*p.first -N, *q.first -N};
270  wrapped_.push_back({cv::Rect2i{ul, cv::Point{*p.second + N, *q.second + N}}, font, title});
271  draw_wrapped(wrapped_.back());
272  }
273  cv::Rect2i scrolled_rect_ = cv::Rect2i{0,0,0,0};
274  std::vector<Widget*> widgets_, backup_;
275  void draw_all_wrapped();
276  void scroll_to(cv::Rect2i r);
277  void set_ul(cv::Point2i p);
278  void set_br(cv::Point2i p);
279 protected:
280  std::string title_;
281  void draw_wrapped(const Wrapped &wr);
282  bool closed_ = false;
283  int result_ = -1;
284 private:
285  z::Window *popup_on_ = nullptr;
286  std::function<void(int)> popup_exit_func_;
287  std::vector<Wrapped> wrapped_;
288  void copy_widget_to_mat(const Widget &r);
289 };
290 
291 class Handle;
292 
293 class VHandle : public Widget
294 {
295 public:
296  VHandle(Handle&);
297  const static int widget_width_ = 15;
298  void draw();
299  int starty() const;
300  int endy() const;
301 private:
302  Handle &handle_;
303  bool mouse_down_ = false;
304  int press_y_ = 0;
305  void on_register();
306  void scroll_window(int ypos);
307  int scroll_delta(int yd);
308  friend class Handle;
309 };
310 
311 class HHandle : public Widget
312 {
313 public:
314  HHandle(Handle&);
315  const static int widget_height_ = 15;
316  void draw();
317  int startx() const;
318  int endx() const;
319 private:
320  Handle &handle_;
321  bool mouse_down_ = false;
322  int press_x_ = 0;
323  void on_register();
324  int scroll_delta(int xd);
325  void scroll_window(int xpos);
326  friend class Handle;
327 };
328 
330 class Handle : public Widget
331 {
332 public:
333  Handle();
334  static const int widget_size_ = 30;
335  VHandle vh_;
336  HHandle hh_;
337  void position_widgets();
338  void show_widgets();
339 private:
340  bool mouse_down_ = false;
341  Window *scwin_;
342  cv::Rect2i scroll_backup_ = {0,0,0,0};
343  void on_register();
344  void routine(int, int);
345 };
346 
347 struct Line {
348  std::string fore, editting, back;
349  bool new_line = false;
350  std::string value() const { return fore + editting + back; }
351 };
352 
353 class TextInput2 : public TextInput
354 {
355 public:
356  using iter = std::list<Line>::iterator;
357  TextInput2(cv::Rect2i r);
358  bool empty() const;
359  Line line() const;
360  void line(Line l);
361 protected:
362  void focus(bool tf);
363  void set_iter(iter it);
364  TextInput2 *prev_ = nullptr, *next_ = nullptr;
365  std::list<Line> *contents_ptr_ = nullptr;
366  bool end_new_line_ = false;
367  iter it_;
368  //std::list<std::array<std::string, 3>> *contents_ = nullptr;
369 private:
370  void down_stream(iter it, int level = 0);
371  void up_stream(iter it);
372  bool on_overflow(std::string fore, std::string editting, std::string back);
373  void up(), down(), new_line();
374  bool del(), backsp();
375  void keyboard_callback(int, int);
376  bool is_end() const { return contents_ptr_->end() == it_;}
377  friend class TextBox;
378 };
379 
380 class TextBox : public Window
381 {
382 public:
383  TextBox(cv::Rect2i r, int lines);
384  void set_max_character(int max);
385  std::string type() const {return "TextBox";}
386  std::string value() const;
387  void value(std::string s);
388 protected:
389  int top_line_index_ = 0, focus_line_ = 0;
390  std::list<Line> contents_;
391  std::vector<std::shared_ptr<TextInput2>> inputs_;
392  void draw();
393 private:
394  Line line() const;
395  void line(Line l);
396  bool empty() const;
397 };
398 
399 class Image : public Widget
400 {
401 public:
402  Image(cv::Rect2i r);
403  cv::Mat &operator=(const cv::Mat &r);
404 };
405 
406 class Slider : public Widget
407 {
408 public:
409  Slider(cv::Rect2i r, int start, int stop, int step);
410  int value();
411  void value(int);
412  void on_change(std::function<void(int)> f);//front int = value, second int = discard
413  void draw();
414 protected:
415  int value_, start_, end_, step_, logical_length_, physical_length_;
416  bool hold_ = false;
417 private:
418  std::function<void(int)> on_change_;
419  void key_event(int key, int);
420  void move(int x, int y);
421  void ldown(int x, int y);
422  void lup(int x, int y);
423  int to_pos(int val), to_val(int pos);
424  bool user_hold_ = false;
425 };
426 
427 class Progress : public Widget
428 {
429 public:
430  Progress(cv::Rect2i r);
431  void value(int val);
432  int value() const;
433 protected:
434  int value_ = 0;
435 };
436 
437 class AsciiWindow : public Window
438 {
439 public:
440  AsciiWindow(const char *asciiart, int unit_width = 10, int unit_height = 15,
441  int margin = 1);//, int x = 0, int y = 0);
442  void title(std::string t) { title_ = t; }
443 protected:
444  std::vector<std::shared_ptr<Slider>> S;
445  std::vector<std::shared_ptr<Button>> B;
446  std::vector<std::shared_ptr<TextInput>> T;
447  std::vector<std::shared_ptr<CheckBox>> C;
448  std::vector<std::shared_ptr<Label>> L;
449  std::vector<std::shared_ptr<Image>> I;
450  std::vector<std::shared_ptr<Progress>> P;
451  std::vector<std::shared_ptr<Widget>> Z;
452  std::vector<std::shared_ptr<TextBox>> E;
453 private:
454  int get_size(char c);
455  bool parse_widget_area(int y, int x);
456  void parse_art();
457  void cjk_correction();
458  int uw_, uh_, margin_;
459  std::vector<std::string> art_, parsed_;
460 };
461 
462 class PopupInterface {
463 public:
464  PopupInterface(Window *p);
465  int open(int flag = cv::WINDOW_AUTOSIZE, int x = -1, int y = -1);
466  void quit(int r);
467 protected:
468  bool closed_ = false;
469  int result_ = -1;
470 private:
471  Window *window_ptr_ = nullptr;
472 };
473 
474 class WButton : public Widget
475 {
476 public:
477  WButton(cv::Rect2i r);
478 };
479 
480 }
Definition: zgui.h:438
AsciiWindow(const char *asciiart, int unit_width=10, int unit_height=15, int margin=1)
Definition: asciiwindow.cc:26
Definition: zgui.h:81
void draw(cv::Vec3b color=widget_color_, bool repaint=true)
Definition: button.cc:18
void click(std::function< void()> f)
Definition: button.cc:28
Button(std::string text, cv::Rect_< int > r)
Definition: button.cc:8
void text(std::string s)
Definition: button.cc:34
Definition: zgui.h:92
void on_change(std::function< void(bool)> f)
Definition: checkbox.cc:35
bool checked() const
Definition: checkbox.cc:23
CheckBox(cv::Rect2i r)
Definition: checkbox.cc:6
add Handle for Scrolled Window. If you add Handle widget to a window, it will become a scrolled Windo...
Definition: zgui.h:331
Definition: zgui.h:400
cv::Mat & operator=(const cv::Mat &r)
Definition: image.cc:8
Definition: zgui.h:71
Label(std::string text, cv::Rect2i r)
Definition: label.cc:5
std::string text() const
Definition: label.cc:10
Definition: zgui.h:428
void value(int val)
Definition: progress.cc:10
Definition: zgui.h:407
void draw()
Definition: slider.cc:22
int value()
Definition: slider.cc:58
Slider(cv::Rect2i r, int start, int stop, int step)
Definition: slider.cc:6
Definition: zgui.h:381
CJK font, libhangul needed.
Definition: zgui.h:106
bool draw()
Definition: textinput.cc:274
std::string value() const
Definition: textinput.cc:243
TextInput(cv::Rect2i r)
Definition: textinput.cc:91
void enter(std::function< void(std::string)> f)
Definition: textinput.cc:238
void key_event(int key, int)
Definition: textinput.cc:142
void show_cursor()
Definition: textinput.cc:176
void move_cursor(bool right)
Definition: textinput.cc:184
base class for all widgets
Definition: zgui.h:23
void resize(cv::Rect2i r)
Definition: widget.cc:77
int zIndex_
higher zIndex widget will be in front of lower zIndex widget.
Definition: zgui.h:67
cv::Mat3b mat_
Matrix that contains widget shape.
Definition: zgui.h:40
bool focus_
Definition: zgui.h:62
std::map< int, std::function< void(int, int)> > gui_callback_
map<int : event, function(x, y)>. library will draw change in widget graphics after gui_callback
Definition: zgui.h:34
Widget(cv::Rect_< int > r)
Definition: widget.cc:19
std::map< int, std::function< void(int, int)> > user_callback_
Definition: zgui.h:39
bool activated_
deactivated widget cannot get focus so that it cannot react to events.
Definition: zgui.h:60
bool hidden_
if hidden, a widget will not show on window, but still can react to event.
Definition: zgui.h:59
Window * parent_
pointer to parent window that is containing this widget
Definition: zgui.h:42
bool focus() const
Definition: widget.cc:44
Widget & operator=(const Widget &r)
Definition: widget.cc:38
virtual void on_register()
will be called when widget is added to a window
Definition: zgui.h:43
static cv::Ptr< cv::freetype::FreeType2 > ft2_
freetype2 font. CJK font needed
Definition: zgui.h:66
float alpha_
alpha value of widget. Widgets can be overlapped. Alpha value will be calculated in such cases.
Definition: zgui.h:58
void update()
Definition: widget.cc:83
Definition: zgui.h:163
auto tie(T &... checks)
Definition: zgui.h:241
int open(int flag=cv::WINDOW_AUTOSIZE, int x=-1, int y=-1)
Definition: window.cc:258
Window(std::string title, cv::Rect_< int > r)
Definition: window.cc:38
void wrap(const char *title, int font, int N, const T &... widgets)
Definition: zgui.h:259
Window & operator>>(Widget &r)
Definition: window.cc:133
Window & operator+(Widget &w)
Definition: window.cc:57
int loop()
Definition: window.cc:236
std::string title() const
Definition: window.cc:92
void move_widget(Widget &w, cv::Point2i p)
Definition: window.cc:76
void tabs(int xpos, int ypos, T &... wins)
Definition: zgui.h:197
auto tie(TextInput &t, Button &b1, Button &b2, T start, T step)
Definition: zgui.h:186
void scroll_to(cv::Rect2i r)
Definition: scrolledwindow.cc:5
void draw_all_wrapped()
Definition: window.cc:152
void close()
Definition: window.cc:166
Window & operator<<(Widget &r)
Definition: window.cc:119
void on_register()
will be called when widget is added to a window
Definition: window.cc:279
void organize_accordingto_zindex()
Definition: window.cc:111
void start(int flag=cv::WINDOW_AUTOSIZE|cv::WINDOW_KEEPRATIO)
Definition: window.cc:228
void popdown(int value)
Definition: window.cc:204
Window & operator-(Widget &w)
Definition: window.cc:65
void show()
draw mat_
Definition: window.cc:157
void popup(Window &w, std::function< void(int)> f=[](int){})
Definition: window.cc:171