WINDEX
wex.h
1 #pragma once
2 #define _USE_MATH_DEFINES
3 #include <cmath>
4 #include <iostream>
5 #include <thread>
6 #include <vector>
7 #include <map>
8 #include <functional>
9 #include <algorithm>
10 #include <windows.h>
11 #include <CommCtrl.h>
12 #include <Shellapi.h>
13 #include "cxy.h"
14 
15 #ifndef M_PI
16 #define M_PI 3.14159265358979323846
17 #endif
18 
19 namespace wex
20 {
21 
22  class gui;
23 
24  typedef std::map<HWND, gui *> mgui_t;
25  typedef std::vector<gui *> children_t;
26 
28  struct sMouse
29  {
30  int x;
31  int y;
32  bool left;
33  bool right;
34  bool shift;
35  };
36 
37  enum eventMsgID
38  {
39  asyncReadComplete = WM_APP + 1,
40  tcpServerAccept,
41  tcpServerReadComplete,
42  };
43 
44  class modalMgr
45  {
46  public:
48  static modalMgr &get()
49  {
50  static modalMgr theModalMgr;
51  return theModalMgr;
52  }
53 
61  bool set(int id, HWND h)
62  {
63  if (!id)
64  {
65  myModalID = 0;
66  return true;
67  }
68  if (myModalID)
69  {
70  std::cout << "App tried to show two modal windows\n";
71  SetFocus(myModalHandle);
72  return false;
73  }
74 
75  myModalID = id;
76  myModalHandle = h;
77  return true;
78  }
79 
87  bool canClose(int id)
88  {
89  if (!myModalID)
90  return true; // no modal window is running
91 
92  if (myModalID == id)
93  {
94  // the modal window is being closed
95  myModalID = 0;
96  return true;
97  }
98 
99  // attempt to close other window while modal window is running
100  // give the modal window focus
101  SetFocus(myModalHandle);
102  return false;
103  }
104 
105  private:
106  int myModalID;
107  HWND myModalHandle;
108 
109  modalMgr()
110  : myModalID(0)
111  {
112  }
113  };
114 
117  {
118  public:
119  eventhandler()
120  : myfClickPropogate(false), myfMouseTracking(false)
121  {
122  // initialize functions with no-ops
123  click([] {});
124  clickDouble([] {});
125  clickWex([] {});
126  clickRight([] {});
127  draw([](PAINTSTRUCT &ps) {});
128  resize([](int w, int h) {});
129  scrollH([](int c) {});
130  scrollV([](int c) {});
131  keydown([](int c) {});
132  mouseEnter([] {});
133  mouseLeave([] {});
134  mouseMove([](sMouse &m) {});
135  mouseWheel([](int dist) {});
136  mouseUp([] {});
137  timer([](int id) {});
138  slid([](int pos) {});
139  dropStart([](HDROP hDrop) {});
140  drop([](const std::vector<std::string> &files) {});
141  asyncReadComplete([](int id) {});
142  tcpServerAccept([] {});
143  tcpRead([] {});
144  quitApp([]
145  { return true; });
146  datePick([](int id, LPNMDATETIMECHANGE date) {});
147  }
148  bool onLeftdown()
149  {
150  myClickFunWex();
151  myClickFunctionApp();
152  return !myfClickPropogate;
153  }
154  void onRightDown()
155  {
156  myClickRightFunction();
157  }
158  void onMouseUp()
159  {
160  myMouseUpFunction();
161  }
162  void onDoubleClick()
163  {
164  myClickDoubleFunction();
165  }
166  void onDraw(PAINTSTRUCT &ps)
167  {
168  myDrawFunction(ps);
169  }
170  void onResize(int w, int h)
171  {
172  myResizeFunction(w, h);
173  }
174  void onScrollH(int code)
175  {
176  myScrollHFunction(code);
177  }
178  void onScrollV(int code)
179  {
180  myScrollVFunction(code);
181  }
182  void onMenuCommand(int id)
183  {
184  if (0 > id || id >= (int)myVectorMenuFunction.size())
185  return;
186  myVectorMenuFunction[id](myVectorMenuTitle[id]);
187  }
188  void onKeydown(int keycode)
189  {
190  myKeydownFunction(keycode);
191  }
192  void onMouseMove(WPARAM wParam, LPARAM lParam)
193  {
194  sMouse m;
195  m.x = LOWORD(lParam);
196  m.y = HIWORD(lParam);
197 
198  // left button or shift key ( never both )
199  m.left = (wParam == MK_LBUTTON);
200  m.shift = (wParam == MK_SHIFT);
201 
202  myMouseMoveFunction(m);
203  }
204  void onMouseEnter()
205  {
206  myMouseEnterFunction();
207  }
208  void onMouseWheel(int dist)
209  {
210  myMouseWheelFunction(dist);
211  }
212  void onMouseLeave()
213  {
214  myMouseLeaveFunction();
215  }
216  void onTimer(int id)
217  {
218  myTimerFunction(id);
219  }
220  bool onSelect(
221  unsigned short id)
222  {
223  auto it = mapControlFunction().find(std::make_pair(id, CBN_SELCHANGE));
224  if (it == mapControlFunction().end())
225  return true;
226  it->second();
227  return true;
228  }
229  bool onChange(
230  unsigned short id)
231  {
232  auto it = mapControlFunction().find(std::make_pair(id, EN_CHANGE));
233  if (it == mapControlFunction().end())
234  return true;
235  it->second();
236  return true;
237  }
238  void onSlid(unsigned short id)
239  {
240  mySlidFunction((int)id);
241  }
242  void onDropStart(HDROP hDrop)
243  {
244  myDropStartFunction(hDrop);
245  }
246  void onDrop(const std::vector<std::string> &files)
247  {
248  myDropFunction(files);
249  }
250  void onAsyncReadComplete(int id)
251  {
252  myAsyncReadCompleteFunction(id);
253  }
254  void onTcpServerAccept()
255  {
256  myTcpServerAcceptFunction();
257  }
258  void onTcpServerReadComplete()
259  {
260  myTcpServerReadCompleteFunction();
261  }
262  bool onQuitApp()
263  {
264  return myQuitAppFunction();
265  }
266  void onDatePicked(
267  int idFrom,
268  LPNMDATETIMECHANGE date)
269  {
270  myDatePickFunction(idFrom, date);
271  }
273 
280  void click(
281  std::function<void(void)> f,
282  bool propogate = false)
283  {
284  myClickFunctionApp = f;
285  myfClickPropogate = propogate;
286  }
295  void clickWex(std::function<void(void)> f)
296  {
297  myClickFunWex = f;
298  }
300  void clickPropogate(bool f = true)
301  {
302  myfClickPropogate = f;
303  }
304 
305  void clickRight(std::function<void(void)> f)
306  {
307  myClickRightFunction = f;
308  }
309 
310  void clickDouble(std::function<void(void)> f)
311  {
312  myClickDoubleFunction = f;
313  }
314 
315  void draw(std::function<void(PAINTSTRUCT &ps)> f)
316  {
317  myDrawFunction = f;
318  }
319  void resize(std::function<void(int w, int h)> f)
320  {
321  myResizeFunction = f;
322  }
323  void scrollH(std::function<void(int code)> f)
324  {
325  myScrollHFunction = f;
326  }
327  void scrollV(std::function<void(int code)> f)
328  {
329  myScrollVFunction = f;
330  }
337  std::function<void(const std::string &title)> f,
338  const std::string &title)
339  {
340  int id = (int)myVectorMenuFunction.size();
341  myVectorMenuFunction.push_back(f);
342  myVectorMenuTitle.push_back(title);
343  return id;
344  }
345  void select(
346  int id,
347  std::function<void(void)> f)
348  {
349  mapControlFunction().insert(
350  std::make_pair(std::make_pair(id, CBN_SELCHANGE), f));
351  }
355  void change(
356  int id,
357  std::function<void(void)> f)
358  {
359  mapControlFunction().insert(
360  std::make_pair(std::make_pair(id, EN_CHANGE), f));
361  }
363  void keydown(std::function<void(int keydown)> f)
364  {
365  myKeydownFunction = f;
366  }
367  void mouseEnter(std::function<void(void)> f)
368  {
369  myMouseEnterFunction = f;
370  }
371  void mouseMove(std::function<void(sMouse &m)> f)
372  {
373  myMouseMoveFunction = f;
374  }
375  void mouseWheel(std::function<void(int dist)> f)
376  {
377  myMouseWheelFunction = f;
378  }
379  void mouseUp(std::function<void(void)> f)
380  {
381  myMouseUpFunction = f;
382  }
383  void mouseLeave(std::function<void(void)> f)
384  {
385  myMouseLeaveFunction = f;
386  }
387  void timer(std::function<void(int id)> f)
388  {
389  myTimerFunction = f;
390  }
391  void slid(std::function<void(int pos)> f)
392  {
393  mySlidFunction = f;
394  }
396  void dropStart(std::function<void(HDROP hDrop)> f)
397  {
398  myDropStartFunction = f;
399  }
401  void drop(std::function<void(const std::vector<std::string> &files)> f)
402  {
403  myDropFunction = f;
404  }
408  void asyncReadComplete(std::function<void(int id)> f)
409  {
410  myAsyncReadCompleteFunction = f;
411  }
412  void tcpServerAccept(std::function<void(void)> f)
413  {
414  myTcpServerAcceptFunction = f;
415  }
417  void tcpRead(std::function<void(void)> f)
418  {
419  myTcpServerReadCompleteFunction = f;
420  }
424  void quitApp(std::function<bool(void)> f)
425  {
426  myQuitAppFunction = f;
427  }
454  void datePick(std::function<void(int, LPNMDATETIMECHANGE)> f)
455  {
456  myDatePickFunction = f;
457  }
458 
459  private:
460  bool myfClickPropogate;
461  bool myfMouseTracking;
462 
463  // event handlers registered by application code
464  std::function<void(void)> myClickFunctionApp;
465  std::function<void(void)> myClickRightFunction;
466  std::function<void(void)> myClickDoubleFunction;
467  std::function<void(PAINTSTRUCT &ps)> myDrawFunction;
468  std::function<void(int w, int h)> myResizeFunction;
469  std::function<void(int code)> myScrollHFunction;
470  std::function<void(int code)> myScrollVFunction;
471  std::vector<std::function<void(const std::string &title)>> myVectorMenuFunction;
472  std::vector<std::string> myVectorMenuTitle;
473  std::function<void(int keycode)> myKeydownFunction;
474  std::function<void(sMouse &m)> myMouseMoveFunction;
475  std::function<void(void)> myMouseEnterFunction;
476  std::function<void(int dist)> myMouseWheelFunction;
477  std::function<void(int id)> myTimerFunction;
478  std::function<void(void)> myMouseUpFunction;
479  std::function<void(void)> myMouseLeaveFunction;
480  std::function<void(int pos)> mySlidFunction;
481  std::function<void(HDROP hDrop)> myDropStartFunction;
482  std::function<void(const std::vector<std::string> &files)> myDropFunction;
483  std::function<void(int id)> myAsyncReadCompleteFunction;
484  std::function<void(void)> myTcpServerAcceptFunction;
485  std::function<void(void)> myTcpServerReadCompleteFunction;
486  std::function<bool(void)> myQuitAppFunction;
487  std::function<void(int, LPNMDATETIMECHANGE)> myDatePickFunction;
488 
489  // event handlers registered by windex class
490  std::function<void(void)> myClickFunWex;
491 
494  std::map<std::pair<int, unsigned short>, std::function<void(void)>> &
495  mapControlFunction()
496  {
497  static std::map<std::pair<int, unsigned short>, std::function<void(void)>>
498  myMapControlFunction;
499  return myMapControlFunction;
500  }
501  };
502 
528  class shapes
529  {
530  public:
534  shapes(PAINTSTRUCT &ps)
535  : myHDC(ps.hdc), myPenThick(1), myFill(false)
536  {
537  hPen = CreatePen(
538  PS_SOLID,
539  myPenThick,
540  RGB(0, 0, 0));
541  hPenOld = SelectObject(myHDC, hPen);
542 
543  myLogfont = {0};
544  HANDLE hFont;
545  ZeroMemory(&myLogfont, sizeof(LOGFONT));
546  myLogfont.lfWeight = FW_NORMAL;
547  strcpy(myLogfont.lfFaceName, "Tahoma");
548  myLogfont.lfHeight = 20;
549  hFont = CreateFontIndirect(&myLogfont);
550  hFont = (HFONT)SelectObject(myHDC, hFont);
551  DeleteObject(hFont);
552  }
553  ~shapes()
554  {
555  HGDIOBJ pen = SelectObject(myHDC, hPenOld);
556  DeleteObject(pen);
557  }
563  void color(int r, int g, int b)
564  {
565  color(RGB(r, g, b));
566  }
567  void color(int c)
568  {
569  myColor = c;
570  hPen = CreatePen(
571  PS_SOLID,
572  myPenThick,
573  c);
574  HGDIOBJ old = SelectObject(myHDC, hPen);
575  DeleteObject(old);
576  SetTextColor(myHDC, c);
577  HBRUSH brush = CreateSolidBrush(c);
578  old = SelectObject(myHDC, brush);
579  DeleteObject(old);
580  }
582  void bgcolor(int c)
583  {
584  SetBkColor(
585  myHDC,
586  c);
587  }
588  void bgcolor(int r, int g, int b)
589  {
590  bgcolor(RGB(r, g, b));
591  }
593  void transparent(bool f = true)
594  {
595  SetBkMode(
596  myHDC,
597  TRANSPARENT);
598  }
599 
601  void penThick(int t)
602  {
603  myPenThick = t;
604  color(myColor);
605  }
606 
608  void fill(bool f = true)
609  {
610  myFill = f;
611  }
613  void pixel(int x, int y)
614  {
615  SetPixel(myHDC, x, y, myColor);
616  }
620  void line(const std::vector<int> &v)
621  {
622  MoveToEx(
623  myHDC,
624  v[0],
625  v[1],
626  NULL);
627  LineTo(
628  myHDC,
629  v[2],
630  v[3]);
631  }
632 
633  void polyLine(POINT *pp, int n)
634  {
635  Polyline(
636  myHDC,
637  pp,
638  n);
639  }
640  void line(const cxy &p1, const cxy &p2)
641  {
642  line({(int)p1.x, (int)p1.y,
643  (int)p2.x, (int)p2.y});
644  }
648  void rectangle(const std::vector<int> &v)
649  {
650  if (!myFill)
651  {
652  MoveToEx(
653  myHDC,
654  v[0],
655  v[1],
656  NULL);
657  LineTo(
658  myHDC,
659  v[0] + v[2],
660  v[1]);
661  LineTo(
662  myHDC,
663  v[0] + v[2],
664  v[1] + v[3]);
665  LineTo(
666  myHDC,
667  v[0],
668  v[1] + v[3]);
669  LineTo(
670  myHDC,
671  v[0],
672  v[1]);
673  }
674  else
675  {
676  // std::cout << "wex rectangle fill " << v[0]<<" "<< v[1]<<" "<< v[0]+v[2]<<" "<< v[1]+v[3] << "\n";
677  Rectangle(
678  myHDC,
679  v[0], v[1], v[0] + v[2], v[1] + v[3]);
680  }
681  }
682 
687  void polygon(const std::vector<int> &v)
688  {
689  // 'empty' polygons are filled with a null brush
690  HGDIOBJ oldBrush;
691  if (!myFill)
692  oldBrush = SelectObject(myHDC, GetStockObject(NULL_BRUSH));
693  Polygon(myHDC, (const POINT *)&(v[0]), v.size() / 2);
694  if (!myFill)
695  SelectObject(myHDC, oldBrush);
696  }
697  void polygon(const std::vector<cxy> v)
698  {
699  std::vector<int> vi;
700  for (auto &xy : v)
701  {
702  vi.push_back((int)xy.x);
703  vi.push_back((int)xy.y);
704  }
705 
706  polygon(vi);
707  }
708 
719  void arc(
720  int x, int y, double r,
721  double sa, double ea)
722  {
723  int xl = round(x - r);
724  int yt = round(y - r);
725  int xr = round(x + r);
726  int yb = round(y + r);
727  int xs = round(x + r * cos(sa * M_PI / 180));
728  int ys = round(y - r * sin(sa * M_PI / 180));
729  int xe = round(x + r * cos(ea * M_PI / 180));
730  int ye = round(y - r * sin(ea * M_PI / 180));
731  Arc(
732  myHDC,
733  xl, yt, xr, yb, xs, ys, xe, ye);
734  }
740  void circle(int x0, int y0, double r)
741  {
742  // 'empty' circles are filled with a black brush
743  HGDIOBJ oldBrush;
744  if (!myFill)
745  oldBrush = SelectObject(myHDC, GetStockObject(NULL_BRUSH));
746  int ir = r;
747  Ellipse(
748  myHDC,
749  x0 - ir, y0 - ir,
750  x0 + ir, y0 + ir);
751  if (!myFill)
752  SelectObject(myHDC, oldBrush);
753  }
754  void circle(const cxy &xy, double r)
755  {
756  circle(xy.x, xy.y, r);
757  }
763  void text(
764  const std::string &t,
765  const std::vector<int> &v)
766  {
767  if (myLogfont.lfEscapement)
768  {
769  // rotated text
770  TextOut(
771  myHDC,
772  v[0],
773  v[1],
774  t.c_str(),
775  t.length());
776  return;
777  }
778  RECT rect;
779  switch ((int)v.size())
780  {
781  case 2:
782  TextOut(
783  myHDC,
784  v[0],
785  v[1],
786  t.c_str(),
787  t.length());
788  break;
789  case 4:
790  rect.left = v[0];
791  rect.top = v[1];
792  rect.right = v[0] + v[2];
793  rect.bottom = v[1] + v[3];
794  DrawText(
795  myHDC,
796  t.c_str(),
797  -1,
798  &rect,
799  DT_WORDBREAK);
800  break;
801  }
802  return;
803  }
804 
805  void textxy(
806  const std::string &t,
807  const cxy &xy)
808  {
809  text(
810  t,
811  {(int)xy.x, (int)xy.y});
812  }
813  void textxy(
814  const std::string &t,
815  const cxy &topleft,
816  const cxy &widthHeight)
817  {
818  text(
819  t,
820  {(int)topleft.x, (int)topleft.y,
821  (int)widthHeight.x, (int)widthHeight.y});
822  }
823  void rectangle(
824  const cxy &topleft,
825  const cxy &widthHeight)
826  {
827  rectangle(
828  {(int)topleft.x, (int)topleft.y,
829  (int)widthHeight.x, (int)widthHeight.y});
830  }
831 
832  void textCenterHz(
833  const std::string &t,
834  const std::vector<int> &v)
835  {
836  int ws = textWidthPixels(t);
837  int pad = (v[2] - ws) / 2;
838  if (pad < 0)
839  pad = 0;
840  std::vector<int> vc = v;
841  vc[0] += pad;
842  text(t, vc);
843  }
847  void textVertical(bool f = true)
848  {
849  if (f)
850  myLogfont.lfEscapement = 2700;
851  else
852  myLogfont.lfEscapement = 0;
853  myLogfont.lfOrientation = myLogfont.lfEscapement;
854  HANDLE hFont = CreateFontIndirect(&myLogfont);
855  hFont = (HFONT)SelectObject(myHDC, hFont);
856  DeleteObject(hFont);
857  }
858 
862  void textHeight(int h)
863  {
864  myLogfont.lfHeight = h;
865  HANDLE hFont = CreateFontIndirect(&myLogfont);
866  hFont = (HFONT)SelectObject(myHDC, hFont);
867  DeleteObject(hFont);
868  }
870  void textFontName(const std::string &fn)
871  {
872  strcpy(myLogfont.lfFaceName, fn.c_str());
873  HANDLE hFont = CreateFontIndirect(&myLogfont);
874  hFont = (HFONT)SelectObject(myHDC, hFont);
875  DeleteObject(hFont);
876  }
877  int textWidthPixels(const std::string &t)
878  {
879  SIZE sz;
880  GetTextExtentPoint32A(
881  myHDC,
882  t.c_str(),
883  t.length(),
884  &sz);
885  return sz.cx;
886  }
887 
888  private:
889  HDC myHDC;
890  int myPenThick;
891  HGDIOBJ hPen;
892  HGDIOBJ hPenOld;
893  bool myFill;
894  LOGFONT myLogfont;
895  int myColor; // foreground color
896  };
897 
899  class gui
900  {
901  public:
907  gui()
908  : myParent(NULL), myBGColor(0xC8C8C8), myBGBrush(CreateSolidBrush(myBGColor)), myTextColor(0),
909  myDeleteList(0), myfModal(false), myfEnabled(true), myfnobgerase(false), myToolTip(NULL), myAsyncReadCompleteMsgID(0), myCursorID(0)
910  {
911  myID = NewID();
912  Create(
913  NULL,
914  "windex",
915  WS_OVERLAPPEDWINDOW, WS_EX_CONTROLPARENT,
916  0);
917 
918  /* default resize event handler
919  simply forces a refresh so partially visible widgets are correctly drawn
920  Application code, if it needs to move child windows around,
921  should overwrite this event handler. Remember to call update() at end of event handler.
922  */
923  events().resize([this](int w, int h)
924  { update(); });
925 
926  /* Construct font, initialized with default GUI font
927 
928  Each top level window keeps a font and associated logfont
929  so that the font can be changed and inherited by all child windows
930  */
931  myLogFont = {0};
932  GetObject(
933  GetStockObject(DEFAULT_GUI_FONT),
934  sizeof(myLogFont), &myLogFont);
935 
936  // default font clips descenders ( p, q, y) so increase height
937  myLogFont.lfHeight = 18;
938 
939  myFont = CreateFontIndirectA(&myLogFont);
940  }
948  gui *parent,
949  const char *window_class = "windex",
950  unsigned long style = WS_CHILD,
951  unsigned long exstyle = WS_EX_CONTROLPARENT) : myParent(parent),
952  myDeleteList(0),
953  myfEnabled(true),
954  myToolTip(NULL),
955  myCursorID(IDC_ARROW)
956  {
957  // get a new unique ID
958  myID = NewID();
959 
960  // create the window as requested
961  Create(parent->handle(), window_class, style, exstyle, myID);
962 
963  // tell the parent that it has a new child
964  parent->child(this);
965 
966  // by default show text with ID ( helps debugging )
967  text("???" + std::to_string(myID));
968 
969  // inherit background color from parent
970  bgcolor(parent->bgcolor());
971 
972  // inherit font from parent
973  parent->font(myLogFont, myFont);
974  SendMessage(
975  myHandle,
976  WM_SETFONT,
977  (WPARAM)myFont,
978  0);
979 
980  myTextColor = 0;
981  }
982  virtual ~gui()
983  {
984  // std::cout << "deleting " << myText << "\n";
985  DestroyWindow(myHandle);
986  if (myDeleteList)
987  myDeleteList->push_back(myHandle);
988  }
989 
991  void child(gui *w)
992  {
993  myChild.push_back(w);
994  }
995 
997  children_t &children()
998  {
999  return myChild;
1000  }
1001 
1002  gui *parent()
1003  {
1004  return myParent;
1005  }
1006 
1008  gui *find(int id)
1009  {
1010  for (auto w : myChild)
1011  {
1012  if (w->id() == id)
1013  return (gui *)w;
1014  }
1015  return nullptr;
1016  }
1017 
1018  void focus()
1019  {
1020  SetFocus(myHandle);
1021  }
1025  void bgcolor(int color)
1026  {
1027  myBGColor = color;
1028  DeleteObject(myBGBrush);
1029  myBGBrush = CreateSolidBrush(color);
1030  for (auto w : myChild)
1031  w->bgcolor(color);
1032  }
1033 
1034  void nobgerase()
1035  {
1036  myfnobgerase = true;
1037  }
1039  void enable(bool f = true)
1040  {
1041  myfEnabled = f;
1042  EnableWindow(myHandle, myfEnabled);
1043  update();
1044  }
1045  bool isEnabled() const
1046  {
1047  return myfEnabled;
1048  }
1049 
1051  void fontHeight(int h)
1052  {
1053  myLogFont.lfHeight = h;
1054  createNewFont();
1055  setfont(myLogFont, myFont);
1056  }
1057  void fontName(const std::string &name)
1058  {
1059  strcpy(myLogFont.lfFaceName, name.c_str());
1060  createNewFont();
1061  setfont(myLogFont, myFont);
1062  }
1063 
1069  void icon(const std::string &iconfilename)
1070  {
1071  HICON hIcon = ExtractIconA(
1072  NULL,
1073  iconfilename.c_str(),
1074  0);
1075  SetClassLongPtr(
1076  myHandle,
1077  GCLP_HICON,
1078  (LONG_PTR)hIcon);
1079 
1080  SendMessage(myHandle, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
1081  SendMessage(myHandle, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
1082  SendMessage(GetWindow(myHandle, GW_OWNER), WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
1083  SendMessage(GetWindow(myHandle, GW_OWNER), WM_SETICON, ICON_BIG, (LPARAM)hIcon);
1084  }
1085  void cursor(char *cursorID)
1086  {
1087  myCursorID = cursorID;
1088  }
1089  int id()
1090  {
1091  return myID;
1092  }
1093  int bgcolor() const
1094  {
1095  return myBGColor;
1096  }
1101  void textColor(int c)
1102  {
1103  myTextColor = c;
1104  }
1105  void text(const std::string &text)
1106  {
1107  myText = text;
1108  SetWindowText(myHandle, myText.c_str());
1109  }
1110 
1111  std::string text() const
1112  {
1113  return myText;
1114  }
1120  void scroll(bool fHoriz = true)
1121  {
1122  myfScrollHoriz = fHoriz;
1123 
1124  // Add scrollbars to window style
1125  LONG_PTR extra;
1126  if (fHoriz)
1127  extra = WS_HSCROLL | WS_VSCROLL;
1128  else
1129  extra = WS_VSCROLL;
1130  SetWindowLongPtr(
1131  myHandle,
1132  GWL_STYLE,
1133  GetWindowLongPtr(myHandle, GWL_STYLE) | extra);
1134 
1135  // Set the scrolling range and page size to defaaults
1136  scrollRange(100, 100);
1137 
1138  // horizontal scroll handler
1139  events().scrollH([this](int code)
1140  {
1141  SCROLLINFO si;
1142  si.cbSize = sizeof(si);
1143  si.fMask = SIF_POS | SIF_TRACKPOS | SIF_PAGE;
1144  if (!GetScrollInfo(myHandle, SB_HORZ, &si))
1145  return;
1146 
1147  int oldPos = scrollMove(si, code);
1148 
1149  si.fMask = SIF_POS;
1150  SetScrollInfo(myHandle, SB_HORZ, &si, TRUE);
1151  GetScrollInfo(myHandle, SB_CTL, &si);
1152 
1153  RECT rect;
1154  GetClientRect(myHandle, &rect);
1155  int xs = oldPos - si.nPos;
1156  //std::cout << "scrollH " << xs <<" "<< oldPos <<" "<< si.nPos << "\n";
1157  ScrollWindow(
1158  myHandle,
1159  xs,
1160  0, NULL, NULL);
1161  UpdateWindow( myHandle );
1162 
1163  for (auto &w : myChild)
1164  w->update(); });
1165 
1166  // vertical scroll handler
1167  events().scrollV([this](int code)
1168  {
1169  SCROLLINFO si;
1170  si.cbSize = sizeof(si);
1171  si.fMask = SIF_POS | SIF_TRACKPOS | SIF_PAGE;
1172  if (!GetScrollInfo(myHandle, SB_VERT, &si))
1173  return;
1174 
1175  int oldPos = scrollMove(si, code);
1176 
1177  si.fMask = SIF_POS;
1178  SetScrollInfo(myHandle, SB_VERT, &si, TRUE);
1179  GetScrollInfo(myHandle, SB_VERT, &si);
1180  RECT rect;
1181  GetClientRect(myHandle, &rect);
1182  int ys = oldPos - si.nPos;
1183  ScrollWindow(
1184  myHandle,
1185  0,
1186  ys, // amount to scroll
1187  NULL, NULL);
1188 
1189  // update entire window and all children
1190  // this prevents visual artefacts on fast scrolling
1191  // but creates an unpleasant flicker
1192  // so it is commented out
1193  //update();
1194 
1195  // update any child windows
1196  // this has a fast and smooth appearance
1197  // but sometimes leaves fragments littering the window
1198  for (auto &w : myChild)
1199  w->update(); });
1200  }
1211  void scrollRange(int width, int height)
1212  {
1213 
1214  /* maximum scroll position
1215 
1216  We want the max scroll position
1217  which is the top of the visible portion
1218  to be placed where the bottom if the underlying window is just visible
1219  */
1220 
1221  RECT r;
1222  GetClientRect(myHandle, &r);
1223  int xmax = width - r.right;
1224  if (xmax < 0)
1225  xmax = 0;
1226  int ymax = height - (r.bottom - r.top) + 60;
1227  if (ymax < 0)
1228  ymax = 0;
1229  SCROLLINFO si;
1230  si.cbSize = sizeof(si);
1231  si.fMask = SIF_RANGE | SIF_PAGE;
1232  si.nMin = 0;
1233  si.nMax = ymax;
1234  si.nPage = ymax / 10;
1235  SetScrollInfo(myHandle, SB_VERT, &si, TRUE);
1236  if (myfScrollHoriz)
1237  {
1238  si.nMax = xmax;
1239  si.nPage = xmax / 10;
1240  SetScrollInfo(myHandle, SB_HORZ, &si, TRUE);
1241  }
1242  }
1243 
1248  {
1249  sMouse m;
1250  POINT p;
1251  GetCursorPos(&p);
1252  if (!ScreenToClient(myHandle, &p))
1253  {
1254  m.x = -1;
1255  m.y = -1;
1256  }
1257  m.x = p.x;
1258  m.y = p.y;
1259  m.left = (GetKeyState(VK_LBUTTON) < 0);
1260  m.right = (GetKeyState(VK_RBUTTON) < 0);
1261  m.shift = (GetKeyState(VK_SHIFT) < 0);
1262  return m;
1263  }
1274  void run()
1275  {
1276  MSG msg = {};
1277  while (GetMessage(&msg, NULL, 0, 0))
1278  {
1279  // std::cout << "gui::run " << msg.message << "\n";
1280  // if( msg.message == 256 )
1281  // {
1282  // std::cout << "widget text: " << myText << "\n";
1283  // int dbg = 0;
1284  // continue;
1285  // }
1286  if (!IsDialogMessage(myHandle, &msg))
1287  {
1288  TranslateMessage(&msg);
1289  DispatchMessage(&msg);
1290  }
1291  }
1292  }
1293 
1298  void tooltip(const std::string &text, int width = 0)
1299  {
1300  TOOLINFO toolInfo = {0};
1301  toolInfo.cbSize = sizeof(toolInfo);
1302  toolInfo.hwnd = myHandle;
1303  toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
1304  toolInfo.uId = (UINT_PTR)myHandle;
1305  toolInfo.lpszText = (char *)text.c_str();
1306 
1307  // check for existing tooltip
1308  if (!myToolTip)
1309  {
1310  // Create the tooltip.
1311  myToolTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL,
1312  WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
1313  CW_USEDEFAULT, CW_USEDEFAULT,
1314  CW_USEDEFAULT, CW_USEDEFAULT,
1315  myHandle, NULL, NULL, NULL);
1316  SendMessage(myToolTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
1317  }
1318 
1319  else
1320 
1321  // change tooltip
1322  SendMessage(myToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&toolInfo);
1323 
1324  // std::cout << "tooltip: " << width << "\n" << text << "\n";
1325 
1326  if (width > 0)
1327  SendMessage(myToolTip, TTM_SETMAXTIPWIDTH, 0, width);
1328  }
1329 
1330  bool isWindowMessageWaiting()
1331  {
1332  MSG msg;
1333  return PeekMessage(
1334  &msg,
1335  myHandle,
1336  0, 0,
1337  PM_NOREMOVE | PM_NOYIELD);
1338  }
1339 
1340  virtual LRESULT WindowMessageHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1341  {
1342  // if( uMsg != 132 && uMsg != 275 )
1343  // std::cout << " widget " << myText << " WindowMessageHandler " << uMsg << "\n";
1344  if (hwnd == myHandle)
1345  {
1346  switch (uMsg)
1347  {
1348  case WM_CLOSE:
1349 
1350  // Premission to close window requested
1351 
1352  if (!modalMgr::get().canClose(myID))
1353  {
1354  // Cannot close when modal window running
1355  return true;
1356  }
1357  if (myID == 1)
1358  {
1359  // check with registered QuitApp function
1360  if (!myEvents.onQuitApp())
1361  {
1362  return true;
1363  }
1364  }
1365  // close permission granted
1366  DestroyWindow(myHandle);
1367 
1368  return true;
1369 
1370  case WM_DESTROY:
1371 
1372  // if this is the appliction window
1373  // then quit application when destroyed
1374  if (myID == 1)
1375  {
1376  PostQuitMessage(0);
1377  }
1378  return true;
1379 
1380  case WM_NCDESTROY:
1381 
1382  // all the children are gone
1383  // so a modal display can close
1384  myfModal = false;
1385  return false;
1386 
1387  // case WM_SETFOCUS:
1388  // std::cout << myText << " got focus\n";
1389  // return true;
1390 
1391  case WM_ERASEBKGND:
1392  {
1393  if (myfnobgerase)
1394  return true;
1395  RECT rc;
1396  GetWindowRect(hwnd, &rc);
1397  FillRect((HDC)wParam, &rc, myBGBrush);
1398  return true;
1399  }
1400 
1401  case WM_PAINT:
1402  {
1403  PAINTSTRUCT ps;
1404  BeginPaint(myHandle, &ps);
1405  if (!myfnobgerase)
1406  FillRect(ps.hdc, &ps.rcPaint, myBGBrush);
1407  draw(ps);
1408 
1409  EndPaint(myHandle, &ps);
1410  }
1411  return true;
1412 
1413  case WM_CTLCOLORSTATIC:
1414  {
1415  // SetBkColor((HDC)wParam, myBGColor);
1416  // return (INT_PTR)myBGBrush;
1417  RECT r;
1418  GetBoundsRect(GetDC(myHandle), &r, 0);
1419  FillRect(GetDC(myHandle), &r, myBGBrush);
1420  SetBkMode((HDC)wParam, TRANSPARENT);
1421 
1422  return (INT_PTR)GetStockObject(NULL_BRUSH);
1423  }
1424 
1425  case WM_NOTIFY:
1426  {
1427  NMHDR *pnmhdr = reinterpret_cast<NMHDR *>(lParam);
1428  if (pnmhdr->code == DTN_DATETIMECHANGE)
1429  {
1430  myEvents.onDatePicked(
1431  pnmhdr->idFrom,
1432  (LPNMDATETIMECHANGE)(lParam));
1433  }
1434  }
1435  break;
1436 
1437  case WM_LBUTTONDOWN:
1438  // std::cout << "click on " << myText << "\n";
1439  if (!myfEnabled)
1440  return true;
1441  if (myEvents.onLeftdown())
1442  return true;
1443  // the event was not completely handled, maybe the parent can look after it
1444  if (myParent)
1445  {
1446  if (myParent->WindowMessageHandler(
1447  myParent->handle(),
1448  uMsg, wParam, lParam))
1449  return true;
1450  }
1451  break;
1452 
1453  case WM_RBUTTONDOWN:
1454  myEvents.onRightDown();
1455  break;
1456 
1457  case WM_LBUTTONUP:
1458  case WM_RBUTTONUP:
1459  myEvents.onMouseUp();
1460  return true;
1461 
1462  case WM_LBUTTONDBLCLK:
1463  std::cout << "WM_LBUTTONDBLCLK\n";
1464  myEvents.onDoubleClick();
1465  return true;
1466 
1467  case WM_MOUSEMOVE:
1468  myEvents.onMouseMove(wParam, lParam);
1469  break;
1470 
1471  case WM_MOUSEWHEEL:
1472  {
1473  int d = HIWORD(wParam);
1474  if (d > 0xEFFF)
1475  d = -120;
1476  myEvents.onMouseWheel(d);
1477  }
1478  break;
1479 
1480  case WM_MOUSELEAVE:
1481  myEvents.onMouseLeave();
1482  break;
1483 
1484  case WM_SIZE:
1485  myEvents.onResize(LOWORD(lParam), HIWORD(lParam));
1486  return true;
1487 
1488  case WM_HSCROLL:
1489  if (lParam)
1490  trackbarMessageHandler((HWND)lParam);
1491  else
1492  myEvents.onScrollH(LOWORD(wParam));
1493  return true;
1494 
1495  case WM_VSCROLL:
1496  std::cout << "VSCROLL\n";
1497  if (lParam)
1498  trackbarMessageHandler((HWND)lParam);
1499  else
1500  myEvents.onScrollV(LOWORD(wParam));
1501  return true;
1502 
1503  case WM_COMMAND:
1504  {
1505  // https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command
1506 
1507  auto wp_hi = HIWORD(wParam);
1508  if (!wp_hi)
1509  {
1510  events().onMenuCommand(wParam);
1511  return true;
1512  }
1513 
1514  if (wp_hi == CBN_SELCHANGE || wp_hi == LBN_SELCHANGE)
1515  {
1516  return events().onSelect(LOWORD(wParam));
1517  }
1518 
1519  if (wp_hi == EN_CHANGE)
1520  {
1521  return events().onChange(LOWORD(wParam));
1522  }
1523  return true;
1524  }
1525 
1526  case WM_TIMER:
1527  events().onTimer((int)wParam);
1528  return true;
1529 
1530  case WM_DROPFILES:
1531  events().onDropStart((HDROP)wParam);
1532  return true;
1533 
1534  case WM_GETDLGCODE:
1535  return DLGC_WANTARROWS;
1536 
1537  case WM_KEYDOWN:
1538  events().onKeydown((int)wParam);
1539  return true;
1540 
1541  case WM_SETCURSOR:
1542  if (myCursorID)
1543  {
1544  SetCursor(LoadCursor(NULL, (LPCSTR)myCursorID));
1545  return true;
1546  }
1547  return false;
1548 
1549  case eventMsgID::asyncReadComplete:
1550  events().onAsyncReadComplete(wParam);
1551  return true;
1552 
1553  case eventMsgID::tcpServerAccept:
1554  events().onTcpServerAccept();
1555  return true;
1556 
1557  case eventMsgID::tcpServerReadComplete:
1558  events().onTcpServerReadComplete();
1559  return true;
1560  }
1561  }
1562  else
1563  {
1564  for (auto w : myChild)
1565  {
1566  if (w->WindowMessageHandler(hwnd, uMsg, wParam, lParam))
1567  return true;
1568  }
1569  }
1570 
1571  return false;
1572  }
1573 
1579  virtual void show(bool f = true)
1580  {
1581  int cmd = SW_SHOWDEFAULT;
1582  if (!f)
1583  cmd = SW_HIDE;
1584  // std::cout << "show " << myText <<" "<< myHandle <<" "<< myChild.size() << "\n"; ;
1585  ShowWindow(myHandle, cmd);
1586  // display any children
1587  for (auto w : myChild)
1588  w->show(f);
1589  }
1590 
1599  void showModal(gui &appWindow)
1600  {
1601  myfModal = true;
1602  if (!modalMgr::get().set(myID, myHandle))
1603  {
1604  // problem, abandon
1605  myfModal = false;
1606  return;
1607  }
1608 
1609  appWindow.enable(false);
1610 
1611  show();
1612 
1613  // prevent other windows from interaction
1614  // by running our own message loop
1615  // std::cout << "-> modal msg loop\n";
1616  MSG msg = {};
1617  while (GetMessage(&msg, NULL, 0, 0))
1618  {
1619  if (!IsDialogMessage(myHandle, &msg))
1620  {
1621  TranslateMessage(&msg);
1622  DispatchMessage(&msg);
1623  }
1624  else
1625  {
1626  switch (msg.message)
1627  {
1628  case WM_CLOSE:
1629  std::cout << myText << " WM_CLOSE";
1630  break;
1631 
1632  case WM_DESTROY:
1633  std::cout << myText << " WM_DESTROY";
1634  myfModal = false;
1635  break;
1636  }
1637  }
1638  // window no longer modal
1639  // so break out of our own message loop
1640  if (!myfModal)
1641  break;
1642  }
1643  // std::cout << "<- modal msg loop\n";
1644 
1645  // restore rest of application
1646  appWindow.enable(true);
1647  appWindow.focus();
1648  appWindow.update();
1649  }
1651  void endModal()
1652  {
1653  myfModal = false;
1654  modalMgr::get().set(0, 0);
1655  DestroyWindow(myHandle);
1656  if (myDeleteList)
1657  myDeleteList->push_back(myHandle);
1658  }
1659 
1668  void update()
1669  {
1670  InvalidateRect(myHandle, NULL, true);
1671  UpdateWindow(myHandle);
1672  for (auto g : myChild)
1673  g->update();
1674  }
1675 
1680  void move(const std::vector<int> &r)
1681  {
1682  if (r.size() != 4)
1683  return;
1684  MoveWindow(myHandle,
1685  r[0], r[1], r[2], r[3], false);
1686  }
1691  void size(int w, int h)
1692  {
1693  RECT rect;
1694  GetWindowRect(myHandle, &rect);
1695  MoveWindow(myHandle,
1696  rect.left, rect.top, w, h,
1697  false);
1698  }
1703  void move(int x, int y)
1704  {
1705  RECT rect;
1706  GetClientRect(myHandle, &rect);
1707  MoveWindow(myHandle,
1708  x, y, rect.right - rect.left, rect.bottom - rect.top,
1709  false);
1710  }
1711  void move(int x, int y, int w, int h)
1712  {
1713  MoveWindow(myHandle,
1714  x, y, w, h, false);
1715  }
1719  std::vector<int> size()
1720  {
1721  RECT r;
1722  GetClientRect(myHandle, &r);
1723  std::vector<int> ret{
1724  r.right - r.left, r.bottom - r.top};
1725  return ret;
1726  }
1727  std::vector<int> lefttop()
1728  {
1729  RECT rp;
1730  GetWindowRect(myParent->handle(), &rp);
1731  RECT r;
1732  GetWindowRect(myHandle, &r);
1733  // std::cout << "parent " << rp.left <<" "<< rp.top
1734  // << " child " << r.left <<" "<< r.top << "\n";
1735  static std::vector<int> ret(2);
1736  ret[0] = r.left - rp.left;
1737  ret[1] = r.top - rp.top;
1738  return ret;
1739  }
1740 
1743  {
1744  return myEvents;
1745  }
1746 
1748  HWND handle()
1749  {
1750  return myHandle;
1751  }
1752 
1754  void delete_list(std::vector<HWND> *list)
1755  {
1756  myDeleteList = list;
1757  }
1758 
1760  void setfont(LOGFONT &logfont, HFONT &font)
1761  {
1762  myLogFont = logfont;
1763  myFont = font;
1764  SendMessage(
1765  myHandle,
1766  WM_SETFONT,
1767  (WPARAM)myFont,
1768  0);
1769  for (auto w : myChild)
1770  w->setfont(myLogFont, myFont);
1771  }
1772  void setAsyncReadCompleteMsgID(int id)
1773  {
1775  }
1776 
1777  protected:
1778  HWND myHandle;
1779  gui *myParent;
1780  eventhandler myEvents;
1781  int myBGColor;
1782  int myTextColor;
1783  HBRUSH myBGBrush;
1784  LOGFONT myLogFont;
1785  HFONT myFont;
1786  std::vector<HWND> *myDeleteList;
1787  std::string myText;
1788  int myID;
1789  std::vector<gui *> myChild;
1790  bool myfModal;
1791  bool myfEnabled;
1792  bool myfnobgerase;
1793  HWND myToolTip;
1795  char *myCursorID;
1796  bool myfScrollHoriz;
1797 
1805  void Create(
1806  HWND parent,
1807  const char *window_class,
1808  DWORD style, DWORD exstyle = 0,
1809  int id = 0)
1810  {
1811  myHandle = CreateWindowEx(
1812  exstyle, // Optional window styles.
1813  window_class, // Window class
1814  "widget", // Window text
1815  style, // Window style
1816 
1817  // Size and position
1818  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1819 
1820  parent, // Parent window
1821  reinterpret_cast<HMENU>(id), // Menu or control id
1822  NULL, // Instance handle
1823  NULL // Additional application data
1824  );
1825 
1826  if (!myHandle)
1827  throw std::runtime_error(
1828  "Create Window failed");
1829  }
1830 
1838  void font(LOGFONT &logfont, HFONT &font)
1839  {
1840  logfont = myLogFont;
1841  font = myFont;
1842  }
1843 
1846  {
1847  DeleteObject(myFont);
1848  myFont = CreateFontIndirectA(&myLogFont);
1849  }
1850 
1851  virtual void draw(PAINTSTRUCT &ps)
1852  {
1853  SetBkColor(
1854  ps.hdc,
1855  myBGColor);
1856  int color = 0x000000;
1857  if (!myfEnabled)
1858  color = 0xAAAAAA;
1859  SetTextColor(
1860  ps.hdc,
1861  myTextColor);
1862  if (myParent)
1863  {
1864  SelectObject(ps.hdc, myFont);
1865 
1866  RECT r(ps.rcPaint);
1867  auto hbrBkgnd = CreateSolidBrush(myBGColor);
1868  FillRect(
1869  ps.hdc,
1870  &r,
1871  hbrBkgnd);
1872  DeleteObject(hbrBkgnd);
1873  r.left += 1;
1874  r.top += 1;
1875  DrawText(
1876  ps.hdc,
1877  myText.c_str(),
1878  -1,
1879  &r,
1880  0);
1881  }
1882  myEvents.onDraw(ps);
1883  }
1884 
1890  int NewID()
1891  {
1892  static int lastID = 0;
1893  lastID++;
1894  return lastID;
1895  }
1896  int scrollMove(SCROLLINFO &si, int code)
1897  {
1898  int oldPos = si.nPos;
1899  switch (code)
1900  {
1901  // User clicked the left arrow.
1902  case SB_LINELEFT:
1903  si.nPos -= 1;
1904  break;
1905 
1906  // User clicked the right arrow.
1907  case SB_LINERIGHT:
1908  si.nPos += 1;
1909  break;
1910 
1911  // User clicked the scroll bar shaft left of the scroll box.
1912  case SB_PAGELEFT:
1913  si.nPos -= si.nPage;
1914  break;
1915 
1916  // User clicked the scroll bar shaft right of the scroll box.
1917  case SB_PAGERIGHT:
1918  si.nPos += si.nPage;
1919  break;
1920 
1921  // User dragged the scroll box.
1922  case SB_THUMBTRACK:
1923  si.nPos = si.nTrackPos;
1924  break;
1925  }
1926  return oldPos;
1927  }
1928 
1929  private:
1930  void trackbarMessageHandler(HWND hwnd)
1931  {
1932  std::cout << "trackbarMessageHandler\n";
1933  // trackbar notifications are sent to trackbar's parent window
1934  // find the child that generated this notification
1935  // get the tackbar position and call the slid event handler
1936  for (auto c : myChild)
1937  {
1938  if (c->handle() == hwnd)
1939  {
1940  c->events().onSlid(
1941  SendMessage(
1942  hwnd,
1943  TBM_GETPOS,
1944  (WPARAM)0, (LPARAM)0));
1945  }
1946  }
1947  }
1948  };
1949 
1951  class panel : public gui
1952  {
1953  public:
1954  panel(gui *parent)
1955  : gui(parent)
1956  {
1957  text("");
1958  }
1959  };
1990  class drop : public gui
1991  {
1992  public:
1993  drop(gui *parent)
1994  : gui(parent)
1995  {
1996  text("");
1997 
1998  // register as drop recipient
1999  DragAcceptFiles(myHandle, true);
2000 
2001  // handle drop event
2002  myEvents.dropStart([this](HDROP hDrop)
2003  {
2004  int count = DragQueryFileA(hDrop, 0xFFFFFFFF, NULL, 0);
2005  if (count)
2006  {
2007  // extract files from drop structure
2008  std::vector<std::string> files;
2009  char fname[MAX_PATH];
2010  for (int k = 0; k < count; k++)
2011  {
2012  DragQueryFileA(hDrop, k, fname, MAX_PATH);
2013  files.push_back(fname);
2014  }
2015  // call app code's event handler
2016  myEvents.onDrop(files);
2017  }
2018  DragFinish(hDrop); });
2019  }
2020  };
2021 
2023  class groupbox : public panel
2024  {
2025  public:
2026  groupbox(gui *parent)
2027  : panel(parent)
2028  {
2029  }
2033  void move(const std::vector<int> &r)
2034  {
2035  if (r.size() != 4)
2036  return;
2037 
2038  // store location and size of groupbox
2039  myRect.left = r[0];
2040  myRect.top = r[1];
2041  myRect.right = r[0] + r[2];
2042  myRect.bottom = r[1] + r[3];
2043 
2044  // set location and size of groupbox label
2045  MoveWindow(myHandle,
2046  r[0] + 5, r[1] + 2, 60, 25, false);
2047  }
2048  virtual void draw(PAINTSTRUCT &ps)
2049  {
2050  // Draw group box on parent window
2051  HDC hdc = GetDC(myParent->handle());
2052  DrawEdge(
2053  hdc,
2054  &myRect,
2055  EDGE_BUMP,
2056  BF_RECT);
2057  ReleaseDC(myParent->handle(), hdc);
2058 
2059  // Draw label
2060  SetBkColor(
2061  ps.hdc,
2062  myBGColor);
2063  SelectObject(ps.hdc, myFont);
2064  DrawText(
2065  ps.hdc,
2066  myText.c_str(),
2067  myText.length(),
2068  &ps.rcPaint,
2069  0);
2070  }
2071 
2072  private:
2073  RECT myRect;
2074  };
2075 
2077  class layout : public panel
2078  {
2079  public:
2080  layout(gui *parent)
2081  : panel(parent), myColCount(2), myfWidthsSpecified(false), myfColFirst(false)
2082  {
2083  }
2095  void grid(int cols)
2096  {
2097  myColCount = cols;
2098  }
2102  void colWidths(const std::vector<int> &vw)
2103  {
2104  myWidths = vw;
2105  myfWidthsSpecified = true;
2106  }
2126  void colfirst(bool f = true)
2127  {
2128  myfColFirst = f;
2129  }
2130  void draw(PAINTSTRUCT &ps)
2131  {
2132  if (!myChild.size())
2133  return;
2134  RECT r;
2135  GetClientRect(myHandle, &r);
2136  if (!myfWidthsSpecified)
2137  {
2138  // col widths not specified, default to all the same width to fill panel
2139  int colwidth = (r.right - r.left) / myColCount;
2140  myWidths.clear();
2141  for (int k = 0; k < myColCount; k++)
2142  {
2143  myWidths.push_back(colwidth);
2144  }
2145  }
2146  int rowheight;
2147  if (!myfColFirst)
2148  {
2149  // rowheight = (r.bottom - r.top) / ((myChild.size() + 1) / myColCount);
2150  int rowCount = (myChild.size() + 1) / myColCount;
2151  if (!rowCount)
2152  rowCount = 1;
2153  rowheight = (r.bottom - r.top) / rowCount;
2154  }
2155  else
2156  rowheight = 50;
2157 
2158  // display the children laid out in a grid
2159  int colcount = 0;
2160  int rowcount = 0;
2161  int x = 0;
2162 
2163  if (!myfColFirst)
2164  {
2165  for (auto w : myChild)
2166  {
2167  w->move(x, rowcount * rowheight);
2168  w->update();
2169 
2170  x += myWidths[colcount];
2171  colcount++;
2172  if (colcount >= myColCount)
2173  {
2174  colcount = 0;
2175  x = 0;
2176  rowcount++;
2177  }
2178  }
2179  }
2180  else
2181  {
2182  for (auto w : myChild)
2183  {
2184  w->move(x, rowcount * rowheight);
2185  w->update();
2186  rowcount++;
2187  if (rowcount >= (int)myChild.size() / myColCount)
2188  {
2189  rowcount = 0;
2190  x += myWidths[colcount];
2191  colcount++;
2192  }
2193  }
2194  }
2195  }
2196 
2197  private:
2198  int myColCount;
2199  std::vector<int> myWidths;
2200  bool myfWidthsSpecified; // true if app code specified column widths
2201  bool myfColFirst; // true if columns should be filled first
2202  };
2203 
2205  class button : public gui
2206  {
2207  public:
2208  button(gui *parent)
2209  : gui(parent), myBitmap(NULL)
2210  {
2211  myBGColor = 0xC8C8C8;
2212  }
2213 
2217  void imageFile(const std::string &name)
2218  {
2219  myBitmap = (HBITMAP)LoadImage(
2220  NULL, name.c_str(), IMAGE_BITMAP,
2221  0, 0, LR_LOADFROMFILE);
2222  }
2240  int imageResource(const std::string &name)
2241  {
2242  int ret = 0;
2243  auto h = GetModuleHandleA(NULL);
2244  if (!h)
2245  ret = 1;
2246  myBitmap = LoadBitmap(
2247  h, name.c_str());
2248  if (!myBitmap)
2249  ret = 2;
2250  if (ret)
2251  text("");
2252  return ret;
2253  }
2254 
2255  protected:
2256  HBITMAP myBitmap;
2257 
2259  virtual void draw(PAINTSTRUCT &ps)
2260  {
2261  if (!myBitmap)
2262  {
2263  // button with text
2264 
2265  int color = 0x000000;
2266  if (!myfEnabled)
2267  color = 0xAAAAAA;
2268  SetTextColor(
2269  ps.hdc,
2270  myTextColor);
2271  SetBkColor(
2272  ps.hdc,
2273  myBGColor);
2274 
2275  SelectObject(ps.hdc, myFont);
2276 
2277  RECT r(ps.rcPaint);
2278  auto hbrBkgnd = CreateSolidBrush(myBGColor);
2279  FillRect(
2280  ps.hdc,
2281  &r,
2282  hbrBkgnd);
2283  DeleteObject(hbrBkgnd);
2284 
2285  r.left += 1;
2286  r.top += 1;
2287  DrawText(
2288  ps.hdc,
2289  myText.c_str(),
2290  -1,
2291  &r,
2292  DT_SINGLELINE | DT_CENTER | DT_VCENTER);
2293 
2294  DrawEdge(
2295  ps.hdc,
2296  &ps.rcPaint,
2297  EDGE_RAISED,
2298  BF_RECT);
2299  }
2300  else
2301  {
2302  // button with bitmap
2303 
2304  HDC hLocalDC = CreateCompatibleDC(ps.hdc);
2305  BITMAP qBitmap;
2306  GetObject(reinterpret_cast<HGDIOBJ>(myBitmap), sizeof(BITMAP),
2307  reinterpret_cast<LPVOID>(&qBitmap));
2308  HBITMAP hOldBmp = (HBITMAP)SelectObject(hLocalDC, myBitmap);
2309  BitBlt(
2310  ps.hdc, 0, 0, qBitmap.bmWidth, qBitmap.bmHeight,
2311  hLocalDC, 0, 0, SRCCOPY);
2312  SelectObject(hLocalDC, hOldBmp);
2313  DeleteDC(hLocalDC);
2314  }
2315  }
2316  };
2386  class radiobutton : public gui
2387  {
2388  public:
2389  radiobutton(gui *parent)
2390  : gui(parent), myValue(false)
2391  {
2392  // Add to current group
2393  group().back().push_back(this);
2394  myGroup = group().size() - 1;
2395 
2396  // set the boolean value when clicked
2397  events().clickWex([this]
2398  {
2399  if (!myfEnabled)
2400  return;
2401  // set all buttons in group false
2402  for (auto b : group()[myGroup])
2403  {
2404  b->myValue = false;
2405  b->update();
2406  }
2407  // set this button true
2408  myValue = true;
2409  update(); });
2410  }
2421  void first()
2422  {
2423  // find button in its group
2424  auto this_it = std::find(
2425  group()[myGroup].begin(),
2426  group()[myGroup].end(),
2427  this);
2428 
2429  if (this_it == group()[myGroup].end())
2430  throw std::runtime_error("wex::radiobutton::first error in group");
2431 
2432  // if button is first in group, nothing is needed
2433  if (this_it == group()[myGroup].begin())
2434  return;
2435 
2436  // construct new group
2437  std::vector<radiobutton *> g;
2438  group().push_back(g);
2439 
2440  // copy button and following buttons in same group to new group
2441  for (
2442  auto it = this_it;
2443  it != group()[myGroup].end();
2444  it++)
2445  {
2446  group().back().push_back(*it);
2447  }
2448 
2449  // erase from old group
2450  group()[myGroup].erase(
2451  this_it,
2452  group()[myGroup].end());
2453 
2454  // tell buttons that were moved about their new group
2455  for (auto b : group().back())
2456  b->myGroup = group().size() - 1;
2457 
2458  // std::cout << "< first\n";
2459  // for( int kg=0; kg< group().size(); kg++ )
2460  // {
2461  // for( auto b : group()[kg] )
2462  // std::cout << b->id() << " , " << b->text() << " ";
2463  // std::cout << "\n";
2464  // }
2465  }
2466 
2468  bool isChecked()
2469  {
2470  return myValue;
2471  }
2472 
2477  {
2478  int off = 0;
2479  for (auto b : group()[myGroup])
2480  {
2481  if (b->isChecked())
2482  break;
2483  off++;
2484  }
2485  if (off < (int)group()[myGroup].size())
2486  return off;
2487  return -1;
2488  }
2489 
2491  void check(bool f = true)
2492  {
2493  if (f)
2494  {
2495  // set all buttons in group false
2496  for (auto b : group()[myGroup])
2497  {
2498  b->myValue = false;
2499  b->update();
2500  }
2501  }
2502  myValue = f;
2503  update();
2504  }
2505 
2506  virtual void draw(PAINTSTRUCT &ps)
2507  {
2508  SelectObject(ps.hdc, myFont);
2509  int color = 0x000000;
2510  if (!myfEnabled)
2511  color = 0xAAAAAA;
2512  SetTextColor(
2513  ps.hdc,
2514  myTextColor);
2515  SetBkColor(
2516  ps.hdc,
2517  myBGColor);
2518  RECT r(ps.rcPaint);
2519  r.left += 20;
2520  shapes S(ps);
2521 
2522  DrawText(
2523  ps.hdc,
2524  myText.c_str(),
2525  -1,
2526  &r,
2527  0);
2528  if (!myValue)
2529  {
2530  S.circle(10, 10, 5);
2531  }
2532  else
2533  {
2534  SelectObject(ps.hdc, GetStockObject(BLACK_BRUSH));
2535  Ellipse(ps.hdc, 5, 5, 15, 15);
2536  }
2537  }
2538 
2539  private:
2540  bool myValue;
2541  int myGroup;
2542 
2544  std::vector<std::vector<radiobutton *>> &group()
2545  {
2546  static std::vector<std::vector<radiobutton *>> theGroups;
2547  static bool fGroupInit = false;
2548  if (!fGroupInit)
2549  {
2550  // create first group
2551  fGroupInit = true;
2552  std::vector<radiobutton *> g;
2553  theGroups.push_back(g);
2554  }
2555  return theGroups;
2556  }
2557  };
2558 
2565  class checkbox : public gui
2566  {
2567  enum class eType
2568  {
2569  check,
2570  plus
2571  } myType;
2572 
2573  public:
2574  checkbox(gui *parent)
2575  : gui(parent), myType(eType::check), myValue(false)
2576  {
2577  // toggle the boolean value when clicked
2578  events().clickWex([this]
2579  {
2580  if (!myfEnabled)
2581  return;
2582  myValue = !myValue;
2583  update(); });
2584  }
2586  void plus(bool f = true)
2587  {
2588  if (f)
2589  myType = eType::plus;
2590  else
2591  myType = eType::check;
2592  }
2593  void check(bool f = true)
2594  {
2595  myValue = f;
2596  }
2597  bool isChecked()
2598  {
2599  return (myValue);
2600  }
2601  virtual void draw(PAINTSTRUCT &ps)
2602  {
2603  SetBkColor(
2604  ps.hdc,
2605  myBGColor);
2606  RECT r(ps.rcPaint);
2607  int cbg = r.bottom - r.top - 10;
2608  r.left += cbg + 5;
2609  r.top -= 2;
2610 
2611  shapes S(ps);
2612  S.textHeight(myLogFont.lfHeight);
2613  S.textFontName(myLogFont.lfFaceName);
2614  S.text(myText, {r.left, r.top, r.right, r.bottom});
2615  S.rectangle({0, 0, cbg, cbg});
2616  S.fill();
2617  S.penThick(2);
2618  S.color(0);
2619  switch (myType)
2620  {
2621  case eType::check:
2622  if (myValue)
2623  {
2624  S.line({2, cbg / 2, cbg / 2 - 1, cbg - 2});
2625  S.line({cbg / 2, cbg - 3, cbg - 4, 3});
2626  }
2627  break;
2628  case eType::plus:
2629  S.line({2, cbg / 2, cbg - 2, cbg / 2});
2630  if (myValue)
2631  S.line({1 + cbg / 2, 2, 1 + cbg / 2, cbg - 2});
2632  break;
2633  }
2634  S.penThick(1);
2635  }
2636  void clickFunction(std::function<void(void)> f)
2637  {
2638  myClickFunction = f;
2639  }
2640 
2641  private:
2642  bool myValue;
2643  std::function<void(void)> myClickFunction;
2644  };
2645 
2647  class msgbox
2648  {
2649  public:
2652  const std::string &msg)
2653  {
2654  myReturn = MessageBox(NULL,
2655  msg.c_str(),
2656  "Message",
2657  MB_OK);
2658  }
2661  gui &parent,
2662  const std::string &msg,
2663  const std::string &title,
2664  unsigned int type)
2665  {
2666  myReturn = MessageBox(parent.handle(),
2667  msg.c_str(),
2668  title.c_str(),
2669  type);
2670  }
2671  int myReturn;
2672  };
2673 
2675  class label : public gui
2676  {
2677  public:
2678  label(gui *parent)
2679  : gui(parent)
2680  {
2681  }
2682  };
2727  class editbox : public gui
2728  {
2729  public:
2730  editbox(gui *parent)
2731  : gui(parent, "Edit",
2732  WS_CHILD | ES_LEFT | WS_BORDER | WS_VISIBLE,
2733  WS_EX_CLIENTEDGE)
2734  {
2735  }
2737  void notification(WORD ntf)
2738  {
2739  std::cout << "editbox notification " << ntf << "\n";
2740  if (ntf == EN_KILLFOCUS)
2741  {
2742  std::cout << "done\n";
2743  }
2744  }
2745  void text(const std::string &t)
2746  {
2747  SetDlgItemText(
2748  myParent->handle(),
2749  myID,
2750  t.c_str());
2751  }
2753  std::string text()
2754  {
2755  char buf[1000];
2756  buf[0] = '\0';
2757  GetWindowText(
2758  handle(),
2759  buf,
2760  999);
2761  return std::string(buf);
2762  }
2764  void readonly(bool f = true)
2765  {
2766  SendMessage(
2767  handle(),
2768  EM_SETREADONLY,
2769  (WPARAM)f, (LPARAM)0);
2770  }
2771  };
2772 
2774  class multiline : public gui
2775  {
2776  public:
2777  multiline(gui *parent)
2778  : gui(parent, "Edit",
2779  WS_CHILD | ES_LEFT | WS_BORDER | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN,
2780  WS_EX_CLIENTEDGE)
2781  {
2782  }
2787  void text(const std::string &t)
2788  {
2789  SetDlgItemText(
2790  myParent->handle(),
2791  myID,
2792  t.c_str());
2793  }
2795  std::string text()
2796  {
2797  char buf[1000];
2798  buf[0] = '\0';
2799  GetWindowText(
2800  handle(),
2801  buf,
2802  999);
2803  return std::string(buf);
2804  }
2805  };
2807  class choice : public gui
2808  {
2809  public:
2810  choice(gui *parent)
2811  : gui(parent, "Combobox",
2812  CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE)
2813  {
2814  }
2816  void move(int x, int y, int w, int h)
2817  {
2818  if (h < 200)
2819  h = 200;
2820  gui::move(x, y, w, h);
2821  }
2823  void itemHeight(int h)
2824  {
2825  SendMessage(
2826  handle(),
2827  CB_SETITEMHEIGHT,
2828  (WPARAM)0, (LPARAM)20);
2829 
2830  /* WPARAM = 1 is supposed to set the "selection box" height
2831  but it appears to do nothing */
2832  // SendMessage(
2833  // handle(),
2834  // CB_SETITEMHEIGHT,
2835  // (WPARAM)1, (LPARAM)40);
2836  }
2837 
2839  void add(const std::string &s)
2840  {
2841  SendMessageA(
2842  handle(),
2843  (UINT)CB_ADDSTRING,
2844  (WPARAM)0,
2845  (LPARAM)s.c_str());
2846  }
2848  void clear()
2849  {
2850  SendMessage(
2851  handle(),
2852  CB_RESETCONTENT,
2853  (WPARAM)0, (LPARAM)0);
2854  }
2858  void select(int i)
2859  {
2860  SendMessage(
2861  handle(),
2862  CB_SETCURSEL,
2863  (WPARAM)i, (LPARAM)0);
2864  }
2868  void select(const std::string &s)
2869  {
2870  SendMessage(
2871  handle(),
2872  CB_SELECTSTRING,
2873  (WPARAM)-1, (LPARAM)s.c_str());
2874  }
2877  {
2878  return SendMessage(
2879  handle(),
2880  (UINT)CB_GETCURSEL,
2881  (WPARAM)0, (LPARAM)0);
2882  }
2884  std::string selectedText()
2885  {
2886  int i = selectedIndex();
2887  if (i < 0)
2888  return std::string("");
2889  return text(i);
2890  }
2892  std::string text(int i)
2893  {
2894  char buf[256];
2895  SendMessage(
2896  handle(),
2897  (UINT)CB_GETLBTEXT,
2898  (WPARAM)i,
2899  (LPARAM)buf);
2900  return std::string(buf);
2901  }
2903  int count()
2904  {
2905  return SendMessage(
2906  handle(),
2907  (UINT)CB_GETCOUNT,
2908  (WPARAM)0, (LPARAM)0);
2909  }
2910  };
2911 
2924  class list : public gui
2925  {
2926  public:
2927  list(gui *parent)
2928  : gui(parent, "listbox",
2929  LBS_NOTIFY | WS_VSCROLL | WS_BORDER |
2930  WS_CHILD | WS_OVERLAPPED | WS_VISIBLE)
2931  {
2932  }
2934  void move(int x, int y, int w, int h)
2935  {
2936  gui::move(x, y, w, h);
2937  SendMessageA(
2938  handle(),
2939  (UINT)LB_SETCOLUMNWIDTH,
2940  (WPARAM)w,
2941  (LPARAM)0);
2942  }
2944  void add(const std::string &s)
2945  {
2946  SendMessageA(
2947  handle(),
2948  (UINT)LB_ADDSTRING,
2949  (WPARAM)0,
2950  (LPARAM)s.c_str());
2951  }
2953  void clear()
2954  {
2955  SendMessage(
2956  handle(),
2957  LB_RESETCONTENT,
2958  (WPARAM)0, (LPARAM)0);
2959  }
2963  void select(int i)
2964  {
2965  SendMessage(
2966  handle(),
2967  LB_SETCURSEL,
2968  (WPARAM)i, (LPARAM)0);
2969  }
2973  void deleteItem(int i)
2974  {
2975  SendMessage(
2976  handle(),
2977  LB_DELETESTRING,
2978  (WPARAM)i, (LPARAM)0);
2979  }
2983  void select(const std::string &s)
2984  {
2985  SendMessage(
2986  handle(),
2987  LB_SELECTSTRING,
2988  (WPARAM)-1, (LPARAM)s.c_str());
2989  }
2994  {
2995  return SendMessage(
2996  handle(),
2997  (UINT)LB_GETCURSEL,
2998  (WPARAM)0, (LPARAM)0);
2999  }
3000 
3002  std::string selectedText()
3003  {
3004  int i = selectedIndex();
3005  if (i < 0)
3006  return std::string("");
3007  char buf[256];
3008  SendMessage(
3009  handle(),
3010  (UINT)LB_GETTEXT,
3011  (WPARAM)i,
3012  (LPARAM)buf);
3013  return std::string(buf);
3014  }
3016  int count()
3017  {
3018  return SendMessage(
3019  handle(),
3020  (UINT)LB_GETCOUNT,
3021  (WPARAM)0, (LPARAM)0);
3022  }
3023  };
3024 
3032  class windex
3033  {
3034  public:
3043  static windex &get()
3044  {
3045  static windex theInstance;
3046  return theInstance;
3047  }
3048 
3049  /* handle window messages
3050 
3051  All messages to windows created by windex come here.
3052  The messages are passed on to be handled by the gui element for the window they are directed to
3053  */
3054  static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3055  {
3056  auto w = get().myGui.find(hwnd);
3057  if (w != get().myGui.end())
3058  {
3059  if (uMsg == WM_GETDLGCODE)
3060  return w->second->WindowMessageHandler(hwnd, uMsg, wParam, lParam);
3061 
3062  if (w->second->WindowMessageHandler(hwnd, uMsg, wParam, lParam))
3063  return 0;
3064  }
3065 
3066  // run default message processing
3067  return DefWindowProc(hwnd, uMsg, wParam, lParam);
3068  }
3069 
3071  gui *Add(gui *g)
3072  {
3073  // delete any destroyed elements
3074  Delete();
3075 
3076  // provide reference to delete list, so new gui element can be removed after destruction
3077  g->delete_list(&myDeleteList);
3078 
3079  // add to existing gui elements
3080  myGui.insert(std::make_pair(g->handle(), g));
3081 
3082  // std::cout << "windexAdd " << myGui.size() <<" in "<< this << "\n";
3083 
3084  return g;
3085  }
3086 
3087  mgui_t myGui;
3088  private:
3089  std::vector<HWND> myDeleteList;
3090 
3091  windex()
3092  {
3093  // register a callback function
3094  // to be invoked every time a windex gui element receives a windows message
3095  WNDCLASS wc = {};
3096  wc.lpfnWndProc = &windex::WindowProc;
3097  wc.hInstance = NULL;
3098  wc.lpszClassName = "windex";
3099  wc.hbrBackground = CreateSolidBrush(0xc8c8c8);
3100  wc.style = CS_DBLCLKS;
3101  RegisterClass(&wc);
3102  }
3103 
3105  void Delete()
3106  {
3107  // std::cout << "windex::Delete " << myDeleteList.size() << "\n";
3108  for (auto h : myDeleteList)
3109  {
3110  auto i = myGui.find(h);
3111  if (i != myGui.end())
3112  myGui.erase(i);
3113  }
3114  myDeleteList.clear();
3115  }
3116  };
3117 
3149  class menu
3150  {
3151  public:
3152  menu(gui &parent)
3153  : myM(CreatePopupMenu()), myParent(parent)
3154  {
3155  }
3156  ~menu()
3157  {
3158  DestroyMenu(myM);
3159  }
3170  void append(
3171  const std::string &title,
3172  const std::function<void(const std::string &)> &f = [](const std::string &title) {})
3173  {
3174  AppendMenu(
3175  myM,
3176  0,
3177  myParent.events().menuCommand(f, title),
3178  title.c_str());
3179  }
3180 
3185  void append(
3186  const std::string &title,
3187  menu &submenu)
3188  {
3189  AppendMenu(
3190  myM,
3191  MF_POPUP,
3192  (UINT_PTR)submenu.handle(),
3193  title.c_str());
3194  }
3195  void appendSeparator()
3196  {
3197  AppendMenu(
3198  myM,
3199  MF_SEPARATOR,
3200  0, "");
3201  }
3202 
3207  void popup(
3208  int x, int y)
3209  {
3210  TrackPopupMenu(
3211  myM,
3212  0,
3213  x, y,
3214  0,
3215  myParent.handle(),
3216  NULL);
3217  }
3218 
3219  HMENU handle()
3220  {
3221  return myM;
3222  }
3228  bool check(int index, bool f = true)
3229  {
3230  unsigned int uCheck;
3231  if (f)
3232  uCheck = MF_BYPOSITION | MF_CHECKED;
3233  else
3234  uCheck = MF_BYPOSITION | MF_UNCHECKED;
3235  return MF_CHECKED == CheckMenuItem(
3236  myM,
3237  index,
3238  uCheck);
3239  }
3241  int size()
3242  {
3243  return GetMenuItemCount(myM);
3244  }
3245 
3246  private:
3247  HMENU myM;
3248  gui &myParent;
3249  };
3250 
3252  class menubar
3253  {
3254  public:
3255  menubar(gui &parent)
3256  : myParent(parent), myM(CreateMenu())
3257  {
3258  // attach menu to window
3259  SetMenu(parent.handle(), myM);
3260  }
3265  void append(
3266  const std::string &title,
3267  menu &m)
3268  {
3269  AppendMenu(
3270  myM,
3271  MF_POPUP,
3272  (UINT_PTR)m.handle(),
3273  title.c_str());
3274  DrawMenuBar(myParent.handle());
3275  }
3276 
3277  private:
3278  gui &myParent;
3279  HMENU myM;
3280  };
3291  class timer
3292  {
3293  public:
3304  timer(gui &g, int intervalmsecs, int id = 1)
3305  : myGUI(g), myID(id)
3306  {
3307  SetTimer(
3308  myGUI.handle(), // handle to window
3309  myID, // timer identifier
3310  intervalmsecs, // interval ms
3311  (TIMERPROC)NULL); // no timer callback
3312  }
3313  ~timer()
3314  {
3315  KillTimer(
3316  myGUI.handle(),
3317  myID);
3318  }
3319 
3320  private:
3321  gui &myGUI;
3322  int myID;
3323  };
3324 }
3325 
3326 #include "widgets.h"
3327 
3328 namespace wex
3329 {
3330 
3337  class maker
3338  {
3339  public:
3344  template <class W, class P>
3345  static W &make(P &parent)
3346  {
3347  return *((W *)windex::get().Add(new W((gui *)&parent)));
3348  }
3349 
3353  static gui &make()
3354  {
3355  datebox::init();
3356 
3357  return *windex::get().Add(new gui());
3358  }
3359  };
3360 
3400  class tabbed : public panel
3401  {
3402  public:
3403  tabbed(gui *parent)
3404  : panel(parent), myTabWidth(50)
3405  {
3406  tabChanging([](int tabIndex) {});
3407  tabChanged([](int tabIndex) {});
3408  }
3415  void add(
3416  const std::string &tabname,
3417  gui &panel)
3418  {
3419  // resize the child panel so it fits neatly under the tab buttons
3420  RECT rect;
3421  GetClientRect(myHandle, &rect);
3422  panel.move(0, 31, rect.right - rect.left, rect.bottom - rect.top - 30);
3423 
3424  button &btn = maker::make<button>(*this);
3425  btn.text(tabname);
3426  btn.move(myButton.size() * myTabWidth,
3427  0, myTabWidth, 30);
3428  myButton.push_back(&btn);
3429  myPanel.push_back(&panel);
3430  int tabIndex = myButton.size() - 1;
3431 
3432  btn.events().click([this, tabIndex]()
3433  {
3434  myTabChangingFn(tabIndex);
3435  select(tabIndex);
3436  myTabChangeFn(tabIndex); });
3437  }
3439  void select(int i)
3440  {
3441  std::cout << "select " << i << "\n";
3442 
3443  if (0 > i || i >= (int)myButton.size())
3444  return;
3445 
3446  for (auto b : myButton)
3447  {
3448  b->bgcolor(0xC8C8C8);
3449  b->update();
3450  }
3451  for (auto p : myPanel)
3452  p->show(false);
3453 
3454  myButton[i]->bgcolor(0xFFFFFF);
3455  myPanel[i]->show();
3456  update();
3457  mySelect = i;
3458  }
3460  int select() const
3461  {
3462  return mySelect;
3463  }
3465  void tabWidth(int w)
3466  {
3467  myTabWidth = w;
3468  }
3473  void tabChanging(std::function<void(int tabIndex)> f)
3474  {
3475  myTabChangingFn = f;
3476  }
3481  void tabChanged(std::function<void(int tabIndex)> f)
3482  {
3483  myTabChangeFn = f;
3484  }
3485 
3486  private:
3487  std::vector<button *> myButton;
3488  std::vector<gui *> myPanel;
3489  int myTabWidth;
3490  int mySelect;
3491  std::function<void(int tabIndex)> myTabChangingFn;
3492  std::function<void(int tabIndex)> myTabChangeFn;
3493  };
3494 
3525  class radiobuttonLayout : public layout
3526  {
3527  public:
3528  radiobuttonLayout(gui *parent)
3529  : layout(parent), myFirst(true)
3530  {
3531  }
3536  {
3537  wex::radiobutton &rb = wex::maker::make<wex::radiobutton>(*this);
3538  if (myFirst)
3539  {
3540  myFirst = false;
3541  rb.first();
3542 
3543  rb.text("AAA");
3544  }
3545  return rb;
3546  }
3548  int checked()
3549  {
3550  if (myFirst)
3551  return -1;
3552  return ((radiobutton *)children()[0])->checkedOffset();
3553  }
3558  void check(int i, bool f = true)
3559  {
3560  if (0 > i || i >= (int)children().size())
3561  return;
3562  ((radiobutton *)children()[i])->check();
3563  }
3567  void enable(bool f = true)
3568  {
3569  for (auto rb : children())
3570  ((radiobutton *)rb)->enable(f);
3571  }
3572 
3573  private:
3574  bool myFirst;
3575  };
3577  class printDoc
3578  {
3579  public:
3583  printDoc(const std::string &title = "printDoc")
3584  {
3585  // https://www.equestionanswers.com/vcpp/screen-dc-printer-dc.php
3586  PRINTDLG pdlg;
3587 
3588  /* Initialize the PRINTDLG structure. */
3589  memset(&pdlg, 0, sizeof(PRINTDLG));
3590  pdlg.lStructSize = sizeof(PRINTDLG);
3591  /* Set the flag to return printer DC. */
3592  pdlg.Flags = PD_RETURNDC;
3593 
3594  /* Invoke the printer dialog box. */
3595  PrintDlg(&pdlg);
3596 
3597  /* hDC member of the PRINTDLG structure contains the printer DC. */
3598  dc = pdlg.hDC;
3599  if (!dc)
3600  return;
3601 
3602  DOCINFO di;
3603  memset(&di, 0, sizeof(DOCINFO));
3604  /* Fill in the required members. */
3605  di.cbSize = sizeof(DOCINFO);
3606  di.lpszDocName = title.c_str();
3607 
3608  StartDoc(dc, &di);
3609  }
3612  {
3613  EndDoc(dc);
3614  DeleteDC(dc);
3615  }
3617  bool isOpen()
3618  {
3619  return (bool)dc;
3620  }
3621  void pageStart()
3622  {
3623  StartPage(dc);
3624  }
3625  void pageEnd()
3626  {
3627  EndPage(dc);
3628  }
3635  void text(
3636  int x, int y,
3637  const std::string &s)
3638  {
3639  TextOut(
3640  dc,
3641  x, y,
3642  s.c_str(), s.length());
3643  }
3644 
3645  private:
3646  HDC dc;
3647  };
3648 
3649  struct free
3650  {
3657  static int startProcess(
3658  const std::string &command,
3659  std::string &error,
3660  bool fBlock = false)
3661  {
3662  STARTUPINFO si;
3663  PROCESS_INFORMATION pi;
3664 
3665  ZeroMemory(&si, sizeof(si));
3666  si.cb = sizeof(si);
3667  ZeroMemory(&pi, sizeof(pi));
3668 
3669  // Retain keyboard focus, minimize module2 window
3670  si.wShowWindow = SW_SHOWNOACTIVATE | SW_MINIMIZE;
3671  si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION;
3672  si.dwX = 600;
3673  si.dwY = 200;
3674 
3675  if (!CreateProcessA(
3676  NULL, // No module name (use command line)
3677  (LPSTR)command.c_str(), // Command line
3678  NULL, // Process handle not inheritable
3679  NULL, // Thread handle not inheritable
3680  FALSE, // Set handle inheritance to FALSE
3681  CREATE_NEW_CONSOLE, // creation flags
3682  NULL, // Use parent's environment block
3683  NULL, // Use parent's starting directory
3684  &si, // Pointer to STARTUPINFO structure
3685  &pi) // Pointer to PROCESS_INFORMATION structure
3686  )
3687  {
3688  int syserrno = GetLastError();
3689  if (syserrno == 2)
3690  {
3691  error = "Cannot find executable file";
3692  return 2;
3693  }
3694  char *lpMsgBuf;
3695  FormatMessageA(
3696  FORMAT_MESSAGE_ALLOCATE_BUFFER |
3697  FORMAT_MESSAGE_FROM_SYSTEM |
3698  FORMAT_MESSAGE_IGNORE_INSERTS,
3699  NULL,
3700  (DWORD)syserrno,
3701  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
3702  (LPSTR)&lpMsgBuf,
3703  0, NULL);
3704  error = lpMsgBuf;
3705  LocalFree(lpMsgBuf);
3706  return 1;
3707  }
3708 
3709  WaitForSingleObject(pi.hProcess, 10000);
3710 
3711  // Close process and thread handles.
3712  CloseHandle(pi.hProcess);
3713  CloseHandle(pi.hThread);
3714 
3715  error = "";
3716  return 0;
3717  }
3718  };
3719 }
2D point or vector
Definition: cxy.h:10
A widget that user can click to start an action.
Definition: wex.h:2206
void imageFile(const std::string &name)
Specify bitmap image to be used for button, read from file.
Definition: wex.h:2217
virtual void draw(PAINTSTRUCT &ps)
draw
Definition: wex.h:2259
int imageResource(const std::string &name)
Specify bitmap image to be used for button, read from resource.
Definition: wex.h:2240
A widget that user can click to toggle a true/false value.
Definition: wex.h:2566
void plus(bool f=true)
set type to plus, useful to indicate expanded or collapsed property categories
Definition: wex.h:2586
A widget where user can choose from a dropdown list of strings.
Definition: wex.h:2808
void clear()
Clear all options.
Definition: wex.h:2848
std::string text(int i)
get text by index
Definition: wex.h:2892
int selectedIndex()
get index of selected item
Definition: wex.h:2876
void itemHeight(int h)
set item height in drop doown list
Definition: wex.h:2823
void add(const std::string &s)
Add an option.
Definition: wex.h:2839
void select(const std::string &s)
Select by string.
Definition: wex.h:2868
void select(int i)
Select by index.
Definition: wex.h:2858
void move(int x, int y, int w, int h)
Override move to ensure height is sufficient to allow dropdown to apprear.
Definition: wex.h:2816
int count()
get count of items
Definition: wex.h:2903
std::string selectedText()
get text of selected item
Definition: wex.h:2884
A widget where users can drop files dragged from windows explorer.
Definition: wex.h:1991
A widget where user can enter a single line string.
Definition: wex.h:2728
void readonly(bool f=true)
disable ( or enable ) user editing
Definition: wex.h:2764
void notification(WORD ntf)
editbox generated a notification - nop
Definition: wex.h:2737
std::string text()
get text in textbox
Definition: wex.h:2753
A class where application code can register functions to be called when an event occurs.
Definition: wex.h:117
void drop(std::function< void(const std::vector< std::string > &files)> f)
register function to call when files dropped by user have been extracted. App code use this!
Definition: wex.h:401
void clickWex(std::function< void(void)> f)
register a function to do some housekeeping when clicked, before calling handler registered by applic...
Definition: wex.h:295
void datePick(std::function< void(int, LPNMDATETIMECHANGE)> f)
Register function to call when a date is picked.
Definition: wex.h:454
void quitApp(std::function< bool(void)> f)
register function to call when application is about to quit The function should return true to allow ...
Definition: wex.h:424
void asyncReadComplete(std::function< void(int id)> f)
register function to call when an asynchronous read completes.
Definition: wex.h:408
void keydown(std::function< void(int keydown)> f)
register function to call when key pressed. Function is passed key code.
Definition: wex.h:363
void clickPropogate(bool f=true)
specify that click event should propogate to parent window after currently registered click event han...
Definition: wex.h:300
void dropStart(std::function< void(HDROP hDrop)> f)
register function to call when user drops files. App code should NOT call this!
Definition: wex.h:396
int menuCommand(std::function< void(const std::string &title)> f, const std::string &title)
Register function to run when menu item clicked.
Definition: wex.h:336
void tcpRead(std::function< void(void)> f)
register function to call when tcp read accurs
Definition: wex.h:417
void change(int id, std::function< void(void)> f)
register function to call when control changes
Definition: wex.h:355
void click(std::function< void(void)> f, bool propogate=false)
register click event handler
Definition: wex.h:280
Displaying a title and a box.
Definition: wex.h:2024
void move(const std::vector< int > &r)
Set size and location of group box.
Definition: wex.h:2033
The base class for all windex gui elements.
Definition: wex.h:900
void update()
force widget to redraw completely
Definition: wex.h:1668
void child(gui *w)
register child on this window
Definition: wex.h:991
unsigned int myAsyncReadCompleteMsgID
handle to tooltip control for this gui element
Definition: wex.h:1794
std::vector< int > size()
Size of window client area.
Definition: wex.h:1719
void tooltip(const std::string &text, int width=0)
Add tooltip that pops up helpfully when mouse cursor hovers over widget.
Definition: wex.h:1298
void showModal(gui &appWindow)
Show this window and suspend all other windows interactions until this is closed.
Definition: wex.h:1599
void scrollRange(int width, int height)
Set the scrolling range.
Definition: wex.h:1211
void Create(HWND parent, const char *window_class, DWORD style, DWORD exstyle=0, int id=0)
Create the managed window.
Definition: wex.h:1805
void endModal()
Stop modal interaction and close window.
Definition: wex.h:1651
eventhandler & events()
Get event handler.
Definition: wex.h:1742
void delete_list(std::vector< HWND > *list)
set delete list for when gui is detroyed
Definition: wex.h:1754
void move(const std::vector< int > &r)
Move the window.
Definition: wex.h:1680
void setfont(LOGFONT &logfont, HFONT &font)
change font for this and all child windows
Definition: wex.h:1760
void size(int w, int h)
Change size without moving top left corner.
Definition: wex.h:1691
void icon(const std::string &iconfilename)
Change icon.
Definition: wex.h:1069
void fontHeight(int h)
Change font height for this and all child windows.
Definition: wex.h:1051
gui * find(int id)
find child window with specified id
Definition: wex.h:1008
bool myfEnabled
true if not disabled
Definition: wex.h:1791
void createNewFont()
Replace font used by this and child windows from logfont.
Definition: wex.h:1845
gui(gui *parent, const char *window_class="windex", unsigned long style=WS_CHILD, unsigned long exstyle=WS_EX_CONTROLPARENT)
Construct child of a parent.
Definition: wex.h:947
std::vector< gui * > myChild
gui elements to be displayed in this window
Definition: wex.h:1789
HWND handle()
get window handle
Definition: wex.h:1748
void textColor(int c)
Set text color.
Definition: wex.h:1101
children_t & children()
get vector of children
Definition: wex.h:997
void enable(bool f=true)
Enable/Disable, default enable.
Definition: wex.h:1039
bool myfModal
true if element is being shown as modal
Definition: wex.h:1790
void move(int x, int y)
Change position without changing size.
Definition: wex.h:1703
sMouse getMouseStatus()
Get mouse status.
Definition: wex.h:1247
void bgcolor(int color)
Change background color.
Definition: wex.h:1025
virtual void show(bool f=true)
Show window and all children.
Definition: wex.h:1579
int NewID()
Create new, unique ID for gui element.
Definition: wex.h:1890
void run()
Run the windows message loop.
Definition: wex.h:1274
gui()
Construct top level window with no parent.
Definition: wex.h:907
void scroll(bool fHoriz=true)
Add scrollbars.
Definition: wex.h:1120
void font(LOGFONT &logfont, HFONT &font)
get font details
Definition: wex.h:1838
A widget that displays a string.
Definition: wex.h:2676
A panel which arranges the widgets it contains in a grid.
Definition: wex.h:2078
void grid(int cols)
Specify number of cols to use for layout.
Definition: wex.h:2095
void colWidths(const std::vector< int > &vw)
Specify column widths.
Definition: wex.h:2102
void colfirst(bool f=true)
Specify that widgets should be added to fill columns first.
Definition: wex.h:2126
A widget where user can choose from a list of strings.
Definition: wex.h:2925
std::string selectedText()
get text of selected item
Definition: wex.h:3002
void clear()
Clear all options.
Definition: wex.h:2953
void deleteItem(int i)
Delete by index.
Definition: wex.h:2973
void move(int x, int y, int w, int h)
Override move to ensure column width is sufficient.
Definition: wex.h:2934
int selectedIndex()
get index of selected item
Definition: wex.h:2993
void select(int i)
Select by index.
Definition: wex.h:2963
void add(const std::string &s)
Add an option.
Definition: wex.h:2944
void select(const std::string &s)
Select by string.
Definition: wex.h:2983
int count()
get count of items
Definition: wex.h:3016
A class for making windex objects.
Definition: wex.h:3338
static W & make(P &parent)
Construct widget.
Definition: wex.h:3345
static gui & make()
Construct a top level window ( first call constructs application window )
Definition: wex.h:3353
A drop down list of options that user can click to start an action.
Definition: wex.h:3150
void append(const std::string &title, menu &submenu)
Append submenu.
Definition: wex.h:3185
void popup(int x, int y)
Popup menu and run user selection.
Definition: wex.h:3207
int size()
Number of items in menu.
Definition: wex.h:3241
bool check(int index, bool f=true)
Set or unset check mark beside menu item.
Definition: wex.h:3228
void append(const std::string &title, const std::function< void(const std::string &)> &f=[](const std::string &title) {})
Append menu item.
Definition: wex.h:3170
A widget that displays across top of a window and contains a number of dropdown menues.
Definition: wex.h:3253
void append(const std::string &title, menu &m)
Append menu to menubar.
Definition: wex.h:3265
Definition: wex.h:45
bool set(int id, HWND h)
Set modal running.
Definition: wex.h:61
bool canClose(int id)
Can a window be closed.
Definition: wex.h:87
static modalMgr & get()
get reference to singleton modal manager
Definition: wex.h:48
A popup with a message.
Definition: wex.h:2648
msgbox(const std::string &msg)
CTOR for simple message box with OK button.
Definition: wex.h:2651
int myReturn
Button id clicked by user.
Definition: wex.h:2671
msgbox(gui &parent, const std::string &msg, const std::string &title, unsigned int type)
CTOR for message box with title and configurable buttons.
Definition: wex.h:2660
A mutiline editbox.
Definition: wex.h:2775
std::string text()
get text in textbox
Definition: wex.h:2795
void text(const std::string &t)
Set text.
Definition: wex.h:2787
A child window that can contain widgets.
Definition: wex.h:1952
Print a text document.
Definition: wex.h:3578
~printDoc()
Finalize and send to printer.
Definition: wex.h:3611
printDoc(const std::string &title="printDoc")
CTOR.
Definition: wex.h:3583
void text(int x, int y, const std::string &s)
Add some text.
Definition: wex.h:3635
bool isOpen()
True if CTOR was successful.
Definition: wex.h:3617
A widget that user can click to select one of an exclusive set of options.
Definition: wex.h:2387
void first()
Make this button first of a new group.
Definition: wex.h:2421
int checkedOffset()
Which button in group is checked.
Definition: wex.h:2476
void check(bool f=true)
set value true( default ) or false
Definition: wex.h:2491
bool isChecked()
true if checked
Definition: wex.h:2468
Widget to layout a group of radio buttons.
Definition: wex.h:3526
void enable(bool f=true)
Enable/disable all radio buttons.
Definition: wex.h:3567
void check(int i, bool f=true)
set status of radio button
Definition: wex.h:3558
radiobutton & add()
add a radio button
Definition: wex.h:3535
int checked()
0-based index of checked radio button
Definition: wex.h:3548
A class that offers application code methods to draw on a window.
Definition: wex.h:529
void penThick(int t)
Set pen thickness in pixels.
Definition: wex.h:601
void textFontName(const std::string &fn)
set text font name
Definition: wex.h:870
void arc(int x, int y, double r, double sa, double ea)
Draw Arc of circle.
Definition: wex.h:719
void textHeight(int h)
Set text height.
Definition: wex.h:862
void text(const std::string &t, const std::vector< int > &v)
Draw text.
Definition: wex.h:763
void textVertical(bool f=true)
Enable / disable drawing text in vertical orientation Note: rotated text will NOT be clipped.
Definition: wex.h:847
void bgcolor(int c)
set background color
Definition: wex.h:582
void transparent(bool f=true)
enable/disable transparent background
Definition: wex.h:593
void line(const std::vector< int > &v)
Draw line between two points.
Definition: wex.h:620
void polygon(const std::vector< int > &v)
Draw Polygon.
Definition: wex.h:687
void circle(int x0, int y0, double r)
Draw circle.
Definition: wex.h:740
void fill(bool f=true)
Set filling option.
Definition: wex.h:608
shapes(PAINTSTRUCT &ps)
Constructor.
Definition: wex.h:534
void rectangle(const std::vector< int > &v)
Draw rectangle.
Definition: wex.h:648
void pixel(int x, int y)
Color a pixel.
Definition: wex.h:613
void color(int r, int g, int b)
Set color for drawings.
Definition: wex.h:563
A widget where user can select which panel to display by clicking a tab button.
Definition: wex.h:3401
void select(int i)
select panel to displayed
Definition: wex.h:3439
void tabChanging(std::function< void(int tabIndex)> f)
register function to call when tab is about to change This is only called when user changes the tab,...
Definition: wex.h:3473
void add(const std::string &tabname, gui &panel)
add panel that can be displayed
Definition: wex.h:3415
int select() const
zero-based index of panel currently selected
Definition: wex.h:3460
void tabChanged(std::function< void(int tabIndex)> f)
register function to call when tab has changed This is only called when user changes the tab,...
Definition: wex.h:3481
void tabWidth(int w)
set width of tab buttons
Definition: wex.h:3465
Generate events at regularly timed intervals.
Definition: wex.h:3292
timer(gui &g, int intervalmsecs, int id=1)
CTOR.
Definition: wex.h:3304
A class containing a database of the current gui elements.
Definition: wex.h:3033
gui * Add(gui *g)
Add new gui element.
Definition: wex.h:3071
static windex & get()
get reference to windex gui framework ( singleton )
Definition: wex.h:3043
mgui_t myGui
map of existing gui elements
Definition: wex.h:3087
Definition: wex.h:3650
static int startProcess(const std::string &command, std::string &error, bool fBlock=false)
Start a command in its own process.
Definition: wex.h:3657
A structure containing the mouse status for event handlers.
Definition: wex.h:29