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 
14 #ifndef M_PI
15 #define M_PI 3.14159265358979323846
16 #endif
17 
18 namespace wex
19 {
20 
21  class gui;
22 
23  typedef std::map<HWND, gui *> mgui_t;
24  typedef std::vector<gui *> children_t;
25 
27  struct sMouse
28  {
29  int x;
30  int y;
31  bool left;
32  bool right;
33  bool shift;
34  };
35 
36  enum eventMsgID
37  {
38  asyncReadComplete = WM_APP + 1,
39  tcpServerAccept,
40  tcpServerReadComplete,
41  };
42 
43  class modalMgr
44  {
45  public:
47  static modalMgr &get()
48  {
49  static modalMgr theModalMgr;
50  return theModalMgr;
51  }
52 
60  bool set(int id, HWND h)
61  {
62  if (!id)
63  {
64  myModalID = 0;
65  return true;
66  }
67  if (myModalID)
68  {
69  std::cout << "App tried to show two modal windows\n";
70  SetFocus(myModalHandle);
71  return false;
72  }
73 
74  myModalID = id;
75  myModalHandle = h;
76  return true;
77  }
78 
86  bool canClose(int id)
87  {
88  if (!myModalID)
89  return true; // no modal window is running
90 
91  if (myModalID == id)
92  {
93  // the modal window is being closed
94  myModalID = 0;
95  return true;
96  }
97 
98  // attempt to close other window while modal window is running
99  // give the modal window focus
100  SetFocus(myModalHandle);
101  return false;
102  }
103 
104  private:
105  int myModalID;
106  HWND myModalHandle;
107 
108  modalMgr()
109  : myModalID(0)
110  {
111  }
112  };
113 
116  {
117  public:
118  eventhandler()
119  : myfClickPropogate(false), myfMouseTracking(false)
120  {
121  // initialize functions with no-ops
122  click([] {});
123  clickDouble([] {});
124  clickWex([] {});
125  clickRight([] {});
126  draw([](PAINTSTRUCT &ps) {});
127  resize([](int w, int h) {});
128  scrollH([](int c) {});
129  scrollV([](int c) {});
130  keydown([](int c) {});
131  mouseEnter([] {});
132  mouseLeave([] {});
133  mouseMove([](sMouse &m) {});
134  mouseWheel([](int dist) {});
135  mouseUp([] {});
136  timer([](int id) {});
137  slid([](int pos) {});
138  dropStart([](HDROP hDrop) {});
139  drop([](const std::vector<std::string> &files) {});
140  asyncReadComplete([](int id) {});
141  tcpServerAccept([] {});
142  tcpRead([] {});
143  quitApp([]
144  { return true; });
145  datePick([](int id, LPNMDATETIMECHANGE date) {});
146  }
147  bool onLeftdown()
148  {
149  myClickFunWex();
150  myClickFunctionApp();
151  return !myfClickPropogate;
152  }
153  void onRightDown()
154  {
155  myClickRightFunction();
156  }
157  void onMouseUp()
158  {
159  myMouseUpFunction();
160  }
161  void onDoubleClick()
162  {
163  myClickDoubleFunction();
164  }
165  void onDraw(PAINTSTRUCT &ps)
166  {
167  myDrawFunction(ps);
168  }
169  void onResize(int w, int h)
170  {
171  myResizeFunction(w, h);
172  }
173  void onScrollH(int code)
174  {
175  myScrollHFunction(code);
176  }
177  void onScrollV(int code)
178  {
179  myScrollVFunction(code);
180  }
181  void onMenuCommand(int id)
182  {
183  if (0 > id || id >= (int)myVectorMenuFunction.size())
184  return;
185  myVectorMenuFunction[id](myVectorMenuTitle[id]);
186  }
187  void onKeydown(int keycode)
188  {
189  myKeydownFunction(keycode);
190  }
191  void onMouseMove(WPARAM wParam, LPARAM lParam)
192  {
193  sMouse m;
194  m.x = LOWORD(lParam);
195  m.y = HIWORD(lParam);
196  m.left = (wParam == MK_LBUTTON);
197 
198  myMouseMoveFunction(m);
199  }
200  void onMouseEnter()
201  {
202  myMouseEnterFunction();
203  }
204  void onMouseWheel(int dist)
205  {
206  myMouseWheelFunction(dist);
207  }
208  void onMouseLeave()
209  {
210  myMouseLeaveFunction();
211  }
212  void onTimer(int id)
213  {
214  myTimerFunction(id);
215  }
216  bool onSelect(
217  unsigned short id)
218  {
219  auto it = mapControlFunction().find(std::make_pair(id, CBN_SELCHANGE));
220  if (it == mapControlFunction().end())
221  return true;
222  it->second();
223  return true;
224  }
225  bool onChange(
226  unsigned short id)
227  {
228  auto it = mapControlFunction().find(std::make_pair(id, EN_CHANGE));
229  if (it == mapControlFunction().end())
230  return true;
231  it->second();
232  return true;
233  }
234  void onSlid(unsigned short id)
235  {
236  mySlidFunction((int)id);
237  }
238  void onDropStart(HDROP hDrop)
239  {
240  myDropStartFunction(hDrop);
241  }
242  void onDrop(const std::vector<std::string> &files)
243  {
244  myDropFunction(files);
245  }
246  void onAsyncReadComplete(int id)
247  {
248  myAsyncReadCompleteFunction(id);
249  }
250  void onTcpServerAccept()
251  {
252  myTcpServerAcceptFunction();
253  }
254  void onTcpServerReadComplete()
255  {
256  myTcpServerReadCompleteFunction();
257  }
258  bool onQuitApp()
259  {
260  return myQuitAppFunction();
261  }
262  void onDatePicked(
263  int idFrom,
264  LPNMDATETIMECHANGE date)
265  {
266  myDatePickFunction(idFrom, date);
267  }
269 
276  void click(
277  std::function<void(void)> f,
278  bool propogate = false)
279  {
280  myClickFunctionApp = f;
281  myfClickPropogate = propogate;
282  }
291  void clickWex(std::function<void(void)> f)
292  {
293  myClickFunWex = f;
294  }
296  void clickPropogate(bool f = true)
297  {
298  myfClickPropogate = f;
299  }
300 
301  void clickRight(std::function<void(void)> f)
302  {
303  myClickRightFunction = f;
304  }
305 
306  void clickDouble(std::function<void(void)> f)
307  {
308  myClickDoubleFunction = f;
309  }
310 
311  void draw(std::function<void(PAINTSTRUCT &ps)> f)
312  {
313  myDrawFunction = f;
314  }
315  void resize(std::function<void(int w, int h)> f)
316  {
317  myResizeFunction = f;
318  }
319  void scrollH(std::function<void(int code)> f)
320  {
321  myScrollHFunction = f;
322  }
323  void scrollV(std::function<void(int code)> f)
324  {
325  myScrollVFunction = f;
326  }
333  std::function<void(const std::string &title)> f,
334  const std::string &title)
335  {
336  int id = (int)myVectorMenuFunction.size();
337  myVectorMenuFunction.push_back(f);
338  myVectorMenuTitle.push_back(title);
339  return id;
340  }
341  void select(
342  int id,
343  std::function<void(void)> f)
344  {
345  mapControlFunction().insert(
346  std::make_pair(std::make_pair(id, CBN_SELCHANGE), f));
347  }
351  void change(
352  int id,
353  std::function<void(void)> f)
354  {
355  mapControlFunction().insert(
356  std::make_pair(std::make_pair(id, EN_CHANGE), f));
357  }
359  void keydown(std::function<void(int keydown)> f)
360  {
361  myKeydownFunction = f;
362  }
363  void mouseEnter(std::function<void(void)> f)
364  {
365  myMouseEnterFunction = f;
366  }
367  void mouseMove(std::function<void(sMouse &m)> f)
368  {
369  myMouseMoveFunction = f;
370  }
371  void mouseWheel(std::function<void(int dist)> f)
372  {
373  myMouseWheelFunction = f;
374  }
375  void mouseUp(std::function<void(void)> f)
376  {
377  myMouseUpFunction = f;
378  }
379  void mouseLeave(std::function<void(void)> f)
380  {
381  myMouseLeaveFunction = f;
382  }
383  void timer(std::function<void(int id)> f)
384  {
385  myTimerFunction = f;
386  }
387  void slid(std::function<void(int pos)> f)
388  {
389  mySlidFunction = f;
390  }
392  void dropStart(std::function<void(HDROP hDrop)> f)
393  {
394  myDropStartFunction = f;
395  }
397  void drop(std::function<void(const std::vector<std::string> &files)> f)
398  {
399  myDropFunction = f;
400  }
404  void asyncReadComplete(std::function<void(int id)> f)
405  {
406  myAsyncReadCompleteFunction = f;
407  }
408  void tcpServerAccept(std::function<void(void)> f)
409  {
410  myTcpServerAcceptFunction = f;
411  }
413  void tcpRead(std::function<void(void)> f)
414  {
415  myTcpServerReadCompleteFunction = f;
416  }
420  void quitApp(std::function<bool(void)> f)
421  {
422  myQuitAppFunction = f;
423  }
450  void datePick(std::function<void(int, LPNMDATETIMECHANGE)> f)
451  {
452  myDatePickFunction = f;
453  }
454 
455  private:
456  bool myfClickPropogate;
457  bool myfMouseTracking;
458 
459  // event handlers registered by application code
460  std::function<void(void)> myClickFunctionApp;
461  std::function<void(void)> myClickRightFunction;
462  std::function<void(void)> myClickDoubleFunction;
463  std::function<void(PAINTSTRUCT &ps)> myDrawFunction;
464  std::function<void(int w, int h)> myResizeFunction;
465  std::function<void(int code)> myScrollHFunction;
466  std::function<void(int code)> myScrollVFunction;
467  std::vector<std::function<void(const std::string &title)>> myVectorMenuFunction;
468  std::vector<std::string> myVectorMenuTitle;
469  std::function<void(int keycode)> myKeydownFunction;
470  std::function<void(sMouse &m)> myMouseMoveFunction;
471  std::function<void(void)> myMouseEnterFunction;
472  std::function<void(int dist)> myMouseWheelFunction;
473  std::function<void(int id)> myTimerFunction;
474  std::function<void(void)> myMouseUpFunction;
475  std::function<void(void)> myMouseLeaveFunction;
476  std::function<void(int pos)> mySlidFunction;
477  std::function<void(HDROP hDrop)> myDropStartFunction;
478  std::function<void(const std::vector<std::string> &files)> myDropFunction;
479  std::function<void(int id)> myAsyncReadCompleteFunction;
480  std::function<void(void)> myTcpServerAcceptFunction;
481  std::function<void(void)> myTcpServerReadCompleteFunction;
482  std::function<bool(void)> myQuitAppFunction;
483  std::function<void(int, LPNMDATETIMECHANGE)> myDatePickFunction;
484 
485  // event handlers registered by windex class
486  std::function<void(void)> myClickFunWex;
487 
490  std::map<std::pair<int, unsigned short>, std::function<void(void)>> &
491  mapControlFunction()
492  {
493  static std::map<std::pair<int, unsigned short>, std::function<void(void)>>
494  myMapControlFunction;
495  return myMapControlFunction;
496  }
497  };
498 
524  class shapes
525  {
526  public:
530  shapes(PAINTSTRUCT &ps)
531  : myHDC(ps.hdc), myPenThick(1), myFill(false)
532  {
533  hPen = CreatePen(
534  PS_SOLID,
535  myPenThick,
536  RGB(0, 0, 0));
537  hPenOld = SelectObject(myHDC, hPen);
538 
539  myLogfont = {0};
540  HANDLE hFont;
541  ZeroMemory(&myLogfont, sizeof(LOGFONT));
542  myLogfont.lfWeight = FW_NORMAL;
543  strcpy(myLogfont.lfFaceName, "Tahoma");
544  myLogfont.lfHeight = 20;
545  hFont = CreateFontIndirect(&myLogfont);
546  hFont = (HFONT)SelectObject(myHDC, hFont);
547  DeleteObject(hFont);
548  }
549  ~shapes()
550  {
551  HGDIOBJ pen = SelectObject(myHDC, hPenOld);
552  DeleteObject(pen);
553  }
559  void color(int r, int g, int b)
560  {
561  color(RGB(r, g, b));
562  }
563  void color(int c)
564  {
565  myColor = c;
566  hPen = CreatePen(
567  PS_SOLID,
568  myPenThick,
569  c);
570  HGDIOBJ old = SelectObject(myHDC, hPen);
571  DeleteObject(old);
572  SetTextColor(myHDC, c);
573  HBRUSH brush = CreateSolidBrush(c);
574  old = SelectObject(myHDC, brush);
575  DeleteObject(old);
576  }
578  void bgcolor(int c)
579  {
580  SetBkColor(
581  myHDC,
582  c);
583  }
584  void bgcolor(int r, int g, int b)
585  {
586  bgcolor(RGB(r, g, b));
587  }
589  void transparent(bool f = true)
590  {
591  SetBkMode(
592  myHDC,
593  TRANSPARENT);
594  }
595 
597  void penThick(int t)
598  {
599  myPenThick = t;
600  color(myColor);
601  }
602 
604  void fill(bool f = true)
605  {
606  myFill = f;
607  }
609  void pixel(int x, int y)
610  {
611  SetPixel(myHDC, x, y, myColor);
612  }
616  void line(const std::vector<int> &v)
617  {
618  MoveToEx(
619  myHDC,
620  v[0],
621  v[1],
622  NULL);
623  LineTo(
624  myHDC,
625  v[2],
626  v[3]);
627  }
628 
629  void polyLine(POINT *pp, int n)
630  {
631  Polyline(
632  myHDC,
633  pp,
634  n);
635  }
639  void rectangle(const std::vector<int> &v)
640  {
641  if (!myFill)
642  {
643  MoveToEx(
644  myHDC,
645  v[0],
646  v[1],
647  NULL);
648  LineTo(
649  myHDC,
650  v[0] + v[2],
651  v[1]);
652  LineTo(
653  myHDC,
654  v[0] + v[2],
655  v[1] + v[3]);
656  LineTo(
657  myHDC,
658  v[0],
659  v[1] + v[3]);
660  LineTo(
661  myHDC,
662  v[0],
663  v[1]);
664  }
665  else
666  {
667  // std::cout << "wex rectangle fill " << v[0]<<" "<< v[1]<<" "<< v[0]+v[2]<<" "<< v[1]+v[3] << "\n";
668  Rectangle(
669  myHDC,
670  v[0], v[1], v[0] + v[2], v[1] + v[3]);
671  }
672  }
673 
678  void polygon(const std::vector<int> &v)
679  {
680  Polygon(myHDC, (const POINT *)&(v[0]), v.size() / 2);
681  }
682 
693  void arc(
694  int x, int y, double r,
695  double sa, double ea)
696  {
697  int xl = round(x - r);
698  int yt = round(y - r);
699  int xr = round(x + r);
700  int yb = round(y + r);
701  int xs = round(x + r * cos(sa * M_PI / 180));
702  int ys = round(y - r * sin(sa * M_PI / 180));
703  int xe = round(x + r * cos(ea * M_PI / 180));
704  int ye = round(y - r * sin(ea * M_PI / 180));
705  Arc(
706  myHDC,
707  xl, yt, xr, yb, xs, ys, xe, ye);
708  }
714  void circle(int x0, int y0, double r)
715  {
716  int ir = r;
717  Ellipse(
718  myHDC,
719  x0 - ir, y0 - ir,
720  x0 + ir, y0 + ir);
721  }
727  void text(
728  const std::string &t,
729  const std::vector<int> &v)
730  {
731  RECT rect;
732  switch ((int)v.size())
733  {
734  case 2:
735  TextOut(
736  myHDC,
737  v[0],
738  v[1],
739  t.c_str(),
740  t.length());
741  break;
742  case 4:
743  rect.left = v[0];
744  rect.top = v[1];
745  rect.right = v[0] + v[2];
746  rect.bottom = v[1] + v[3];
747  DrawText(
748  myHDC,
749  t.c_str(),
750  -1,
751  &rect,
752  0);
753  break;
754  }
755  return;
756  }
757 
758  void textCenterHz(
759  const std::string &t,
760  const std::vector<int> &v)
761  {
762  int ws = textWidthPixels(t);
763  int pad = (v[2] - ws) / 2;
764  if (pad < 0)
765  pad = 0;
766  std::vector<int> vc = v;
767  vc[0] += pad;
768  text(t, vc);
769  }
771  void textVertical(bool f = true)
772  {
773  if (f)
774  myLogfont.lfEscapement = 2700;
775  else
776  myLogfont.lfEscapement = 0;
777  myLogfont.lfOrientation = myLogfont.lfEscapement;
778  HANDLE hFont = CreateFontIndirect(&myLogfont);
779  hFont = (HFONT)SelectObject(myHDC, hFont);
780  DeleteObject(hFont);
781  }
782 
786  void textHeight(int h)
787  {
788  myLogfont.lfHeight = h;
789  HANDLE hFont = CreateFontIndirect(&myLogfont);
790  hFont = (HFONT)SelectObject(myHDC, hFont);
791  DeleteObject(hFont);
792  }
794  void textFontName(const std::string &fn)
795  {
796  strcpy(myLogfont.lfFaceName, fn.c_str());
797  HANDLE hFont = CreateFontIndirect(&myLogfont);
798  hFont = (HFONT)SelectObject(myHDC, hFont);
799  DeleteObject(hFont);
800  }
801  int textWidthPixels(const std::string &t)
802  {
803  SIZE sz;
804  GetTextExtentPoint32A(
805  myHDC,
806  t.c_str(),
807  t.length(),
808  &sz);
809  return sz.cx;
810  }
811 
812  private:
813  HDC myHDC;
814  int myPenThick;
815  HGDIOBJ hPen;
816  HGDIOBJ hPenOld;
817  bool myFill;
818  LOGFONT myLogfont;
819  int myColor;
820  };
821 
823  class gui
824  {
825  public:
831  gui()
832  : myParent(NULL), myBGColor(0xC8C8C8), myBGBrush(CreateSolidBrush(myBGColor)), myTextColor(0),
833  myDeleteList(0), myfModal(false), myfEnabled(true), myfnobgerase(false), myToolTip(NULL), myAsyncReadCompleteMsgID(0), myCursorID(0)
834  {
835  myID = NewID();
836  Create(
837  NULL,
838  "windex",
839  WS_OVERLAPPEDWINDOW, WS_EX_CONTROLPARENT,
840  0);
841 
842  /* default resize event handler
843  simply forces a refresh so partially visible widgets are correctly drawn
844  Application code, if it needs to move child windows around,
845  should overwrite this event handler. Remember to call update() at end of event handler.
846  */
847  events().resize([this](int w, int h)
848  { update(); });
849 
850  /* Construct font, initialized with default GUI font
851 
852  Each top level window keeps a font and associated logfont
853  so that the font can be changed and inherited by all child windows
854  */
855  myLogFont = {0};
856  GetObject(
857  GetStockObject(DEFAULT_GUI_FONT),
858  sizeof(myLogFont), &myLogFont);
859 
860  // default font clips descenders ( p, q, y) so increase height
861  myLogFont.lfHeight = 18;
862 
863  myFont = CreateFontIndirectA(&myLogFont);
864  }
872  gui *parent,
873  const char *window_class = "windex",
874  unsigned long style = WS_CHILD,
875  unsigned long exstyle = WS_EX_CONTROLPARENT) : myParent(parent),
876  myDeleteList(0),
877  myfEnabled(true),
878  myToolTip(NULL),
879  myCursorID(IDC_ARROW)
880  {
881  // get a new unique ID
882  myID = NewID();
883 
884  // create the window as requested
885  Create(parent->handle(), window_class, style, exstyle, myID);
886 
887  // tell the parent that it has a new child
888  parent->child(this);
889 
890  // by default show text with ID ( helps debugging )
891  text("???" + std::to_string(myID));
892 
893  // inherit background color from parent
894  bgcolor(parent->bgcolor());
895 
896  // inherit font from parent
897  parent->font(myLogFont, myFont);
898  SendMessage(
899  myHandle,
900  WM_SETFONT,
901  (WPARAM)myFont,
902  0);
903 
904  myTextColor = 0;
905  }
906  virtual ~gui()
907  {
908  // std::cout << "deleting " << myText << "\n";
909  DestroyWindow(myHandle);
910  if (myDeleteList)
911  myDeleteList->push_back(myHandle);
912  }
913 
915  void child(gui *w)
916  {
917  myChild.push_back(w);
918  }
919 
921  children_t &children()
922  {
923  return myChild;
924  }
925 
926  gui *parent()
927  {
928  return myParent;
929  }
930 
932  gui *find(int id)
933  {
934  for (auto w : myChild)
935  {
936  if (w->id() == id)
937  return (gui *)w;
938  }
939  return nullptr;
940  }
941 
942  void focus()
943  {
944  SetFocus(myHandle);
945  }
949  void bgcolor(int color)
950  {
951  myBGColor = color;
952  DeleteObject(myBGBrush);
953  myBGBrush = CreateSolidBrush(color);
954  for (auto w : myChild)
955  w->bgcolor(color);
956  }
957 
958  void nobgerase()
959  {
960  myfnobgerase = true;
961  }
963  void enable(bool f = true)
964  {
965  myfEnabled = f;
966  EnableWindow( myHandle, myfEnabled);
967  update();
968  }
969  bool isEnabled() const
970  {
971  return myfEnabled;
972  }
973 
975  void fontHeight(int h)
976  {
977  myLogFont.lfHeight = h;
978  createNewFont();
979  setfont(myLogFont, myFont);
980  }
981  void fontName(const std::string &name)
982  {
983  strcpy(myLogFont.lfFaceName, name.c_str());
984  createNewFont();
985  setfont(myLogFont, myFont);
986  }
987 
993  void icon(const std::string &iconfilename)
994  {
995  HICON hIcon = ExtractIconA(
996  NULL,
997  iconfilename.c_str(),
998  0);
999  SetClassLongPtr(
1000  myHandle,
1001  GCLP_HICON,
1002  (LONG_PTR)hIcon);
1003 
1004  SendMessage(myHandle, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
1005  SendMessage(myHandle, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
1006  SendMessage(GetWindow(myHandle, GW_OWNER), WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
1007  SendMessage(GetWindow(myHandle, GW_OWNER), WM_SETICON, ICON_BIG, (LPARAM)hIcon);
1008  }
1009  void cursor(char *cursorID)
1010  {
1011  myCursorID = cursorID;
1012  }
1013  int id()
1014  {
1015  return myID;
1016  }
1017  int bgcolor() const
1018  {
1019  return myBGColor;
1020  }
1025  void textColor( int c )
1026  {
1027  myTextColor = c;
1028  }
1029  void text(const std::string &text)
1030  {
1031  myText = text;
1032  SetWindowText(myHandle, myText.c_str());
1033  }
1034 
1035  std::string text() const
1036  {
1037  return myText;
1038  }
1044  void scroll(bool fHoriz = true)
1045  {
1046  myfScrollHoriz = fHoriz;
1047 
1048  // Add scrollbars to window style
1049  LONG_PTR extra;
1050  if (fHoriz)
1051  extra = WS_HSCROLL | WS_VSCROLL;
1052  else
1053  extra = WS_VSCROLL;
1054  SetWindowLongPtr(
1055  myHandle,
1056  GWL_STYLE,
1057  GetWindowLongPtr(myHandle, GWL_STYLE) | extra);
1058 
1059  // Set the scrolling range and page size to defaaults
1060  scrollRange(100, 100);
1061 
1062  // horizontal scroll handler
1063  events().scrollH([this](int code)
1064  {
1065  SCROLLINFO si;
1066  si.cbSize = sizeof(si);
1067  si.fMask = SIF_POS | SIF_TRACKPOS | SIF_PAGE;
1068  if (!GetScrollInfo(myHandle, SB_HORZ, &si))
1069  return;
1070 
1071  int oldPos = scrollMove(si, code);
1072 
1073  si.fMask = SIF_POS;
1074  SetScrollInfo(myHandle, SB_HORZ, &si, TRUE);
1075  GetScrollInfo(myHandle, SB_CTL, &si);
1076 
1077  RECT rect;
1078  GetClientRect(myHandle, &rect);
1079  int xs = oldPos - si.nPos;
1080  //std::cout << "scrollH " << xs <<" "<< oldPos <<" "<< si.nPos << "\n";
1081  ScrollWindow(
1082  myHandle,
1083  xs,
1084  0, NULL, NULL);
1085  UpdateWindow( myHandle );
1086 
1087  for (auto &w : myChild)
1088  w->update(); });
1089 
1090  // vertical scroll handler
1091  events().scrollV([this](int code)
1092  {
1093  SCROLLINFO si;
1094  si.cbSize = sizeof(si);
1095  si.fMask = SIF_POS | SIF_TRACKPOS | SIF_PAGE;
1096  if (!GetScrollInfo(myHandle, SB_VERT, &si))
1097  return;
1098 
1099  int oldPos = scrollMove(si, code);
1100 
1101  si.fMask = SIF_POS;
1102  SetScrollInfo(myHandle, SB_VERT, &si, TRUE);
1103  GetScrollInfo(myHandle, SB_VERT, &si);
1104  RECT rect;
1105  GetClientRect(myHandle, &rect);
1106  int ys = oldPos - si.nPos;
1107  ScrollWindow(
1108  myHandle,
1109  0,
1110  ys, // amount to scroll
1111  NULL, NULL);
1112 
1113  // update entire window and all children
1114  // this prevents visual artefacts on fast scrolling
1115  // but creates an unpleasant flicker
1116  // so it is commented out
1117  //update();
1118 
1119  // update any child windows
1120  // this has a fast and smooth appearance
1121  // but sometimes leaves fragments littering the window
1122  for (auto &w : myChild)
1123  w->update(); });
1124  }
1135  void scrollRange(int width, int height)
1136  {
1137 
1138  /* maximum scroll position
1139 
1140  We want the max scroll position
1141  which is the top of the visible portion
1142  to be placed where the bottom if the underlying window is just visible
1143  */
1144 
1145  RECT r;
1146  GetClientRect(myHandle, &r);
1147  int xmax = width - r.right;
1148  if (xmax < 0)
1149  xmax = 0;
1150  int ymax = height - (r.bottom - r.top) + 60;
1151  if (ymax < 0)
1152  ymax = 0;
1153  SCROLLINFO si;
1154  si.cbSize = sizeof(si);
1155  si.fMask = SIF_RANGE | SIF_PAGE;
1156  si.nMin = 0;
1157  si.nMax = ymax;
1158  si.nPage = ymax / 10;
1159  SetScrollInfo(myHandle, SB_VERT, &si, TRUE);
1160  if (myfScrollHoriz)
1161  {
1162  si.nMax = xmax;
1163  si.nPage = xmax / 10;
1164  SetScrollInfo(myHandle, SB_HORZ, &si, TRUE);
1165  }
1166  }
1167 
1172  {
1173  sMouse m;
1174  POINT p;
1175  GetCursorPos(&p);
1176  if (!ScreenToClient(myHandle, &p))
1177  {
1178  m.x = -1;
1179  m.y = -1;
1180  }
1181  m.x = p.x;
1182  m.y = p.y;
1183  m.left = (GetKeyState(VK_LBUTTON) < 0);
1184  m.right = (GetKeyState(VK_RBUTTON) < 0);
1185  m.shift = (GetKeyState(VK_SHIFT) < 0);
1186  return m;
1187  }
1198  void run()
1199  {
1200  MSG msg = {};
1201  while (GetMessage(&msg, NULL, 0, 0))
1202  {
1203  // std::cout << "gui::run " << msg.message << "\n";
1204  // if( msg.message == 256 )
1205  // {
1206  // std::cout << "widget text: " << myText << "\n";
1207  // int dbg = 0;
1208  // continue;
1209  // }
1210  if (!IsDialogMessage(myHandle, &msg))
1211  {
1212  TranslateMessage(&msg);
1213  DispatchMessage(&msg);
1214  }
1215  }
1216  }
1217 
1222  void tooltip(const std::string &text, int width = 0)
1223  {
1224  TOOLINFO toolInfo = {0};
1225  toolInfo.cbSize = sizeof(toolInfo);
1226  toolInfo.hwnd = myHandle;
1227  toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
1228  toolInfo.uId = (UINT_PTR)myHandle;
1229  toolInfo.lpszText = (char *)text.c_str();
1230 
1231  // check for existing tooltip
1232  if (!myToolTip)
1233  {
1234  // Create the tooltip.
1235  myToolTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL,
1236  WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
1237  CW_USEDEFAULT, CW_USEDEFAULT,
1238  CW_USEDEFAULT, CW_USEDEFAULT,
1239  myHandle, NULL, NULL, NULL);
1240  SendMessage(myToolTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
1241  }
1242 
1243  else
1244 
1245  // change tooltip
1246  SendMessage(myToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&toolInfo);
1247 
1248  // std::cout << "tooltip: " << width << "\n" << text << "\n";
1249 
1250  if (width > 0)
1251  SendMessage(myToolTip, TTM_SETMAXTIPWIDTH, 0, width);
1252  }
1253 
1254  virtual LRESULT WindowMessageHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1255  {
1256  // if( uMsg != 132 && uMsg != 275 )
1257  // std::cout << " widget " << myText << " WindowMessageHandler " << uMsg << "\n";
1258  if (hwnd == myHandle)
1259  {
1260  switch (uMsg)
1261  {
1262  case WM_CLOSE:
1263 
1264  // Premission to close window requested
1265 
1266  if (!modalMgr::get().canClose(myID))
1267  {
1268  // Cannot close when modal window running
1269  return true;
1270  }
1271  if (myID == 1)
1272  {
1273  // check with registered QuitApp function
1274  if (!myEvents.onQuitApp())
1275  {
1276  return true;
1277  }
1278  }
1279  // close permission granted
1280  DestroyWindow(myHandle);
1281 
1282  return true;
1283 
1284  case WM_DESTROY:
1285 
1286  // if this is the appliction window
1287  // then quit application when destroyed
1288  if (myID == 1)
1289  {
1290  PostQuitMessage(0);
1291  }
1292  return true;
1293 
1294  case WM_NCDESTROY:
1295 
1296  // all the children are gone
1297  // so a modal display can close
1298  myfModal = false;
1299  return false;
1300 
1301  // case WM_SETFOCUS:
1302  // std::cout << myText << " got focus\n";
1303  // return true;
1304 
1305  case WM_ERASEBKGND:
1306  {
1307  if (myfnobgerase)
1308  return true;
1309  RECT rc;
1310  GetWindowRect(hwnd, &rc);
1311  FillRect((HDC)wParam, &rc, myBGBrush);
1312  return true;
1313  }
1314 
1315  case WM_PAINT:
1316  {
1317  PAINTSTRUCT ps;
1318  BeginPaint(myHandle, &ps);
1319  if (!myfnobgerase)
1320  FillRect(ps.hdc, &ps.rcPaint, myBGBrush);
1321  draw(ps);
1322 
1323  EndPaint(myHandle, &ps);
1324  }
1325  return true;
1326 
1327  case WM_CTLCOLORSTATIC:
1328  {
1329  // SetBkColor((HDC)wParam, myBGColor);
1330  // return (INT_PTR)myBGBrush;
1331  RECT r;
1332  GetBoundsRect(GetDC(myHandle), &r, 0);
1333  FillRect(GetDC(myHandle), &r, myBGBrush);
1334  SetBkMode((HDC)wParam, TRANSPARENT);
1335 
1336  return (INT_PTR)GetStockObject(NULL_BRUSH);
1337  }
1338 
1339  case WM_NOTIFY:
1340  {
1341  NMHDR *pnmhdr = reinterpret_cast<NMHDR *>(lParam);
1342  if (pnmhdr->code == DTN_DATETIMECHANGE)
1343  {
1344  myEvents.onDatePicked(
1345  pnmhdr->idFrom,
1346  (LPNMDATETIMECHANGE)(lParam));
1347  }
1348  }
1349  break;
1350 
1351  case WM_LBUTTONDOWN:
1352  // std::cout << "click on " << myText << "\n";
1353  if (!myfEnabled)
1354  return true;
1355  if (myEvents.onLeftdown())
1356  return true;
1357  // the event was not completely handled, maybe the parent can look after it
1358  if (myParent)
1359  {
1360  if (myParent->WindowMessageHandler(
1361  myParent->handle(),
1362  uMsg, wParam, lParam))
1363  return true;
1364  }
1365  break;
1366 
1367  case WM_RBUTTONDOWN:
1368  myEvents.onRightDown();
1369  break;
1370 
1371  case WM_LBUTTONUP:
1372  case WM_RBUTTONUP:
1373  myEvents.onMouseUp();
1374  return true;
1375 
1376  case WM_LBUTTONDBLCLK:
1377  std::cout << "WM_LBUTTONDBLCLK\n";
1378  myEvents.onDoubleClick();
1379  return true;
1380 
1381  case WM_MOUSEMOVE:
1382  myEvents.onMouseMove(wParam, lParam);
1383  break;
1384 
1385  case WM_MOUSEWHEEL:
1386  {
1387  int d = HIWORD(wParam);
1388  if (d > 0xEFFF)
1389  d = -120;
1390  myEvents.onMouseWheel(d);
1391  }
1392  break;
1393 
1394  case WM_MOUSELEAVE:
1395  myEvents.onMouseLeave();
1396  break;
1397 
1398  case WM_SIZE:
1399  myEvents.onResize(LOWORD(lParam), HIWORD(lParam));
1400  return true;
1401 
1402  case WM_HSCROLL:
1403  if (lParam)
1404  trackbarMessageHandler((HWND)lParam);
1405  else
1406  myEvents.onScrollH(LOWORD(wParam));
1407  return true;
1408 
1409  case WM_VSCROLL:
1410  std::cout << "VSCROLL\n";
1411  if (lParam)
1412  trackbarMessageHandler((HWND)lParam);
1413  else
1414  myEvents.onScrollV(LOWORD(wParam));
1415  return true;
1416 
1417  case WM_COMMAND:
1418  {
1419  // https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command
1420 
1421  auto wp_hi = HIWORD(wParam);
1422  if (!wp_hi)
1423  {
1424  events().onMenuCommand(wParam);
1425  return true;
1426  }
1427 
1428  if (wp_hi == CBN_SELCHANGE || wp_hi == LBN_SELCHANGE)
1429  {
1430  return events().onSelect(LOWORD(wParam));
1431  }
1432 
1433  if (wp_hi == EN_CHANGE)
1434  {
1435  return events().onChange(LOWORD(wParam));
1436  }
1437  return true;
1438  }
1439 
1440  case WM_TIMER:
1441  events().onTimer((int)wParam);
1442  return true;
1443 
1444  case WM_DROPFILES:
1445  events().onDropStart((HDROP)wParam);
1446  return true;
1447 
1448  case WM_GETDLGCODE:
1449  events().onKeydown((int)wParam);
1450  return DLGC_WANTARROWS;
1451 
1452  case WM_KEYDOWN:
1453  events().onKeydown((int)wParam);
1454  return true;
1455 
1456  case WM_SETCURSOR:
1457  if (myCursorID)
1458  {
1459  SetCursor(LoadCursor(NULL, (LPCSTR)myCursorID));
1460  return true;
1461  }
1462  return false;
1463 
1464  case eventMsgID::asyncReadComplete:
1465  events().onAsyncReadComplete(wParam);
1466  return true;
1467 
1468  case eventMsgID::tcpServerAccept:
1469  events().onTcpServerAccept();
1470  return true;
1471 
1472  case eventMsgID::tcpServerReadComplete:
1473  events().onTcpServerReadComplete();
1474  return true;
1475  }
1476  }
1477  else
1478  {
1479  for (auto w : myChild)
1480  {
1481  if (w->WindowMessageHandler(hwnd, uMsg, wParam, lParam))
1482  return true;
1483  }
1484  }
1485 
1486  return false;
1487  }
1488 
1490  virtual void show(bool f = true)
1491  {
1492  int cmd = SW_SHOWDEFAULT;
1493  if (!f)
1494  cmd = SW_HIDE;
1495  // std::cout << "show " << myText <<" "<< myHandle <<" "<< myChild.size() << "\n"; ;
1496  ShowWindow(myHandle, cmd);
1497  // display any children
1498  for (auto w : myChild)
1499  w->show(f);
1500  }
1501 
1504 
1505  void showModal( gui& appWindow )
1506  {
1507  myfModal = true;
1508  if (!modalMgr::get().set(myID, myHandle))
1509  {
1510  // problem, abandon
1511  myfModal = false;
1512  return;
1513  }
1514 
1515  appWindow.enable( false );
1516 
1517  show();
1518 
1519  // prevent other windows from interaction
1520  // by running our own message loop
1521  //std::cout << "-> modal msg loop\n";
1522  MSG msg = {};
1523  while (GetMessage(&msg, NULL, 0, 0))
1524  {
1525  if (!IsDialogMessage(myHandle, &msg))
1526  {
1527  TranslateMessage(&msg);
1528  DispatchMessage(&msg);
1529  }
1530  else
1531  {
1532  switch (msg.message)
1533  {
1534  case WM_CLOSE:
1535  std::cout << myText << " WM_CLOSE";
1536  break;
1537 
1538  case WM_DESTROY:
1539  std::cout << myText << " WM_DESTROY";
1540  myfModal = false;
1541  break;
1542  }
1543  }
1544  // window no longer modal
1545  // so break out of our own message loop
1546  if (!myfModal)
1547  break;
1548  }
1549  //std::cout << "<- modal msg loop\n";
1550 
1551  // restore rest of application
1552  appWindow.enable( true );
1553  appWindow.focus();
1554  appWindow.update();
1555 
1556  }
1558  void endModal()
1559  {
1560  myfModal = false;
1561  modalMgr::get().set(0, 0);
1562  DestroyWindow(myHandle);
1563  if (myDeleteList)
1564  myDeleteList->push_back(myHandle);
1565  }
1566 
1575  void update()
1576  {
1577  InvalidateRect(myHandle, NULL, true);
1578  UpdateWindow(myHandle);
1579  for (auto g : myChild)
1580  g->update();
1581  }
1582 
1587  void move(const std::vector<int> &r)
1588  {
1589  if (r.size() != 4)
1590  return;
1591  MoveWindow(myHandle,
1592  r[0], r[1], r[2], r[3], false);
1593  }
1598  void size(int w, int h)
1599  {
1600  RECT rect;
1601  GetWindowRect(myHandle, &rect);
1602  MoveWindow(myHandle,
1603  rect.left, rect.top, w, h,
1604  false);
1605  }
1610  void move(int x, int y)
1611  {
1612  RECT rect;
1613  GetClientRect(myHandle, &rect);
1614  MoveWindow(myHandle,
1615  x, y, rect.right - rect.left, rect.bottom - rect.top,
1616  false);
1617  }
1618  void move(int x, int y, int w, int h)
1619  {
1620  MoveWindow(myHandle,
1621  x, y, w, h, false);
1622  }
1626  std::vector<int> size()
1627  {
1628  RECT r;
1629  GetClientRect(myHandle, &r);
1630  std::vector<int> ret{
1631  r.right - r.left, r.bottom - r.top};
1632  return ret;
1633  }
1634  std::vector<int> lefttop()
1635  {
1636  RECT rp;
1637  GetWindowRect(myParent->handle(), &rp);
1638  RECT r;
1639  GetWindowRect(myHandle, &r);
1640  // std::cout << "parent " << rp.left <<" "<< rp.top
1641  // << " child " << r.left <<" "<< r.top << "\n";
1642  static std::vector<int> ret(2);
1643  ret[0] = r.left - rp.left;
1644  ret[1] = r.top - rp.top;
1645  return ret;
1646  }
1647 
1650  {
1651  return myEvents;
1652  }
1653 
1655  HWND handle()
1656  {
1657  return myHandle;
1658  }
1659 
1661  void delete_list(std::vector<HWND> *list)
1662  {
1663  myDeleteList = list;
1664  }
1665 
1667  void setfont(LOGFONT &logfont, HFONT &font)
1668  {
1669  myLogFont = logfont;
1670  myFont = font;
1671  SendMessage(
1672  myHandle,
1673  WM_SETFONT,
1674  (WPARAM)myFont,
1675  0);
1676  for (auto w : myChild)
1677  w->setfont(myLogFont, myFont);
1678  }
1679  void setAsyncReadCompleteMsgID(int id)
1680  {
1682  }
1683 
1684  protected:
1685  HWND myHandle;
1686  gui *myParent;
1687  eventhandler myEvents;
1688  int myBGColor;
1689  int myTextColor;
1690  HBRUSH myBGBrush;
1691  LOGFONT myLogFont;
1692  HFONT myFont;
1693  std::vector<HWND> *myDeleteList;
1694  std::string myText;
1695  int myID;
1696  std::vector<gui *> myChild;
1697  bool myfModal;
1698  bool myfEnabled;
1699  bool myfnobgerase;
1700  HWND myToolTip;
1702  char *myCursorID;
1703  bool myfScrollHoriz;
1704 
1712  void Create(
1713  HWND parent,
1714  const char *window_class,
1715  DWORD style, DWORD exstyle = 0,
1716  int id = 0)
1717  {
1718  myHandle = CreateWindowEx(
1719  exstyle, // Optional window styles.
1720  window_class, // Window class
1721  "widget", // Window text
1722  style, // Window style
1723 
1724  // Size and position
1725  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1726 
1727  parent, // Parent window
1728  reinterpret_cast<HMENU>(id), // Menu or control id
1729  NULL, // Instance handle
1730  NULL // Additional application data
1731  );
1732 
1733  if (!myHandle)
1734  throw std::runtime_error(
1735  "Create Window failed");
1736  }
1737 
1745  void font(LOGFONT &logfont, HFONT &font)
1746  {
1747  logfont = myLogFont;
1748  font = myFont;
1749  }
1750 
1753  {
1754  DeleteObject(myFont);
1755  myFont = CreateFontIndirectA(&myLogFont);
1756  }
1757 
1758  virtual void draw(PAINTSTRUCT &ps)
1759  {
1760  SetBkColor(
1761  ps.hdc,
1762  myBGColor);
1763  int color = 0x000000;
1764  if (!myfEnabled)
1765  color = 0xAAAAAA;
1766  SetTextColor(
1767  ps.hdc,
1768  myTextColor);
1769  if (myParent)
1770  {
1771  SelectObject(ps.hdc, myFont);
1772 
1773  RECT r(ps.rcPaint);
1774  auto hbrBkgnd = CreateSolidBrush(myBGColor);
1775  FillRect(
1776  ps.hdc,
1777  &r,
1778  hbrBkgnd);
1779  DeleteObject(hbrBkgnd);
1780  r.left += 1;
1781  r.top += 1;
1782  DrawText(
1783  ps.hdc,
1784  myText.c_str(),
1785  -1,
1786  &r,
1787  0);
1788  }
1789  myEvents.onDraw(ps);
1790  }
1791 
1797  int NewID()
1798  {
1799  static int lastID = 0;
1800  lastID++;
1801  return lastID;
1802  }
1803  int scrollMove(SCROLLINFO &si, int code)
1804  {
1805  int oldPos = si.nPos;
1806  switch (code)
1807  {
1808  // User clicked the left arrow.
1809  case SB_LINELEFT:
1810  si.nPos -= 1;
1811  break;
1812 
1813  // User clicked the right arrow.
1814  case SB_LINERIGHT:
1815  si.nPos += 1;
1816  break;
1817 
1818  // User clicked the scroll bar shaft left of the scroll box.
1819  case SB_PAGELEFT:
1820  si.nPos -= si.nPage;
1821  break;
1822 
1823  // User clicked the scroll bar shaft right of the scroll box.
1824  case SB_PAGERIGHT:
1825  si.nPos += si.nPage;
1826  break;
1827 
1828  // User dragged the scroll box.
1829  case SB_THUMBTRACK:
1830  si.nPos = si.nTrackPos;
1831  break;
1832  }
1833  return oldPos;
1834  }
1835 
1836  private:
1837  void trackbarMessageHandler(HWND hwnd)
1838  {
1839  std::cout << "trackbarMessageHandler\n";
1840  // trackbar notifications are sent to trackbar's parent window
1841  // find the child that generated this notification
1842  // get the tackbar position and call the slid event handler
1843  for (auto c : myChild)
1844  {
1845  if (c->handle() == hwnd)
1846  {
1847  c->events().onSlid(
1848  SendMessage(
1849  hwnd,
1850  TBM_GETPOS,
1851  (WPARAM)0, (LPARAM)0));
1852  }
1853  }
1854  }
1855  };
1856 
1858  class panel : public gui
1859  {
1860  public:
1861  panel(gui *parent)
1862  : gui(parent)
1863  {
1864  text("");
1865  }
1866  };
1897  class drop : public gui
1898  {
1899  public:
1900  drop(gui *parent)
1901  : gui(parent)
1902  {
1903  text("");
1904 
1905  // register as drop recipient
1906  DragAcceptFiles(myHandle, true);
1907 
1908  // handle drop event
1909  myEvents.dropStart([this](HDROP hDrop)
1910  {
1911  int count = DragQueryFileA(hDrop, 0xFFFFFFFF, NULL, 0);
1912  if (count)
1913  {
1914  // extract files from drop structure
1915  std::vector<std::string> files;
1916  char fname[MAX_PATH];
1917  for (int k = 0; k < count; k++)
1918  {
1919  DragQueryFileA(hDrop, k, fname, MAX_PATH);
1920  files.push_back(fname);
1921  }
1922  // call app code's event handler
1923  myEvents.onDrop(files);
1924  }
1925  DragFinish(hDrop); });
1926  }
1927  };
1928 
1930  class groupbox : public panel
1931  {
1932  public:
1933  groupbox(gui *parent)
1934  : panel(parent)
1935  {
1936  }
1940  void move(const std::vector<int> &r)
1941  {
1942  if (r.size() != 4)
1943  return;
1944 
1945  // store location and size of groupbox
1946  myRect.left = r[0];
1947  myRect.top = r[1];
1948  myRect.right = r[0] + r[2];
1949  myRect.bottom = r[1] + r[3];
1950 
1951  // set location and size of groupbox label
1952  MoveWindow(myHandle,
1953  r[0] + 5, r[1] + 2, 60, 25, false);
1954  }
1955  virtual void draw(PAINTSTRUCT &ps)
1956  {
1957  // Draw group box on parent window
1958  HDC hdc = GetDC(myParent->handle());
1959  DrawEdge(
1960  hdc,
1961  &myRect,
1962  EDGE_BUMP,
1963  BF_RECT);
1964  ReleaseDC(myParent->handle(), hdc);
1965 
1966  // Draw label
1967  SetBkColor(
1968  ps.hdc,
1969  myBGColor);
1970  SelectObject(ps.hdc, myFont);
1971  DrawText(
1972  ps.hdc,
1973  myText.c_str(),
1974  myText.length(),
1975  &ps.rcPaint,
1976  0);
1977  }
1978 
1979  private:
1980  RECT myRect;
1981  };
1982 
1984  class layout : public panel
1985  {
1986  public:
1987  layout(gui *parent)
1988  : panel(parent), myColCount(2), myfWidthsSpecified(false), myfColFirst(false)
1989  {
1990  }
2002  void grid(int cols)
2003  {
2004  myColCount = cols;
2005  }
2009  void colWidths(const std::vector<int> &vw)
2010  {
2011  myWidths = vw;
2012  myfWidthsSpecified = true;
2013  }
2033  void colfirst(bool f = true)
2034  {
2035  myfColFirst = f;
2036  }
2037  void draw(PAINTSTRUCT &ps)
2038  {
2039  if (!myChild.size())
2040  return;
2041  RECT r;
2042  GetClientRect(myHandle, &r);
2043  if (!myfWidthsSpecified)
2044  {
2045  // col widths not specified, default to all the same width to fill panel
2046  int colwidth = (r.right - r.left) / myColCount;
2047  myWidths.clear();
2048  for (int k = 0; k < myColCount; k++)
2049  {
2050  myWidths.push_back(colwidth);
2051  }
2052  }
2053  int rowheight;
2054  if (!myfColFirst)
2055  {
2056  // rowheight = (r.bottom - r.top) / ((myChild.size() + 1) / myColCount);
2057  int rowCount = (myChild.size() + 1) / myColCount;
2058  if (!rowCount)
2059  rowCount = 1;
2060  rowheight = (r.bottom - r.top) / rowCount;
2061  }
2062  else
2063  rowheight = 50;
2064 
2065  // display the children laid out in a grid
2066  int colcount = 0;
2067  int rowcount = 0;
2068  int x = 0;
2069 
2070  if (!myfColFirst)
2071  {
2072  for (auto w : myChild)
2073  {
2074  w->move(x, rowcount * rowheight);
2075  w->update();
2076 
2077  x += myWidths[colcount];
2078  colcount++;
2079  if (colcount >= myColCount)
2080  {
2081  colcount = 0;
2082  x = 0;
2083  rowcount++;
2084  }
2085  }
2086  }
2087  else
2088  {
2089  for (auto w : myChild)
2090  {
2091  w->move(x, rowcount * rowheight);
2092  w->update();
2093  rowcount++;
2094  if (rowcount >= (int)myChild.size() / myColCount)
2095  {
2096  rowcount = 0;
2097  x += myWidths[colcount];
2098  colcount++;
2099  }
2100  }
2101  }
2102  }
2103 
2104  private:
2105  int myColCount;
2106  std::vector<int> myWidths;
2107  bool myfWidthsSpecified; // true if app code specified column widths
2108  bool myfColFirst; // true if columns should be filled first
2109  };
2110 
2112  class button : public gui
2113  {
2114  public:
2115  button(gui *parent)
2116  : gui(parent), myBitmap(NULL)
2117  {
2118  myBGColor = 0xC8C8C8;
2119  }
2120 
2124  void imageFile(const std::string &name)
2125  {
2126  myBitmap = (HBITMAP)LoadImage(
2127  NULL, name.c_str(), IMAGE_BITMAP,
2128  0, 0, LR_LOADFROMFILE);
2129  }
2147  int imageResource(const std::string &name)
2148  {
2149  int ret = 0;
2150  auto h = GetModuleHandleA(NULL);
2151  if (!h)
2152  ret = 1;
2153  myBitmap = LoadBitmap(
2154  h, name.c_str());
2155  if (!myBitmap)
2156  ret = 2;
2157  if (ret)
2158  text("");
2159  return ret;
2160  }
2161 
2162  protected:
2163  HBITMAP myBitmap;
2164 
2166  virtual void draw(PAINTSTRUCT &ps)
2167  {
2168  if (!myBitmap)
2169  {
2170  // button with text
2171 
2172  int color = 0x000000;
2173  if (!myfEnabled)
2174  color = 0xAAAAAA;
2175  SetTextColor(
2176  ps.hdc,
2177  myTextColor);
2178  SetBkColor(
2179  ps.hdc,
2180  myBGColor);
2181 
2182  SelectObject(ps.hdc, myFont);
2183 
2184  RECT r(ps.rcPaint);
2185  auto hbrBkgnd = CreateSolidBrush(myBGColor);
2186  FillRect(
2187  ps.hdc,
2188  &r,
2189  hbrBkgnd);
2190  DeleteObject(hbrBkgnd);
2191 
2192  r.left += 1;
2193  r.top += 1;
2194  DrawText(
2195  ps.hdc,
2196  myText.c_str(),
2197  -1,
2198  &r,
2199  DT_SINGLELINE | DT_CENTER | DT_VCENTER);
2200 
2201  DrawEdge(
2202  ps.hdc,
2203  &ps.rcPaint,
2204  EDGE_RAISED,
2205  BF_RECT);
2206  }
2207  else
2208  {
2209  // button with bitmap
2210 
2211  HDC hLocalDC = CreateCompatibleDC(ps.hdc);
2212  BITMAP qBitmap;
2213  GetObject(reinterpret_cast<HGDIOBJ>(myBitmap), sizeof(BITMAP),
2214  reinterpret_cast<LPVOID>(&qBitmap));
2215  HBITMAP hOldBmp = (HBITMAP)SelectObject(hLocalDC, myBitmap);
2216  BitBlt(
2217  ps.hdc, 0, 0, qBitmap.bmWidth, qBitmap.bmHeight,
2218  hLocalDC, 0, 0, SRCCOPY);
2219  SelectObject(hLocalDC, hOldBmp);
2220  DeleteDC(hLocalDC);
2221  }
2222  }
2223  };
2293  class radiobutton : public gui
2294  {
2295  public:
2296  radiobutton(gui *parent)
2297  : gui(parent), myValue(false)
2298  {
2299  // Add to current group
2300  group().back().push_back(this);
2301  myGroup = group().size() - 1;
2302 
2303  // set the boolean value when clicked
2304  events().clickWex([this]
2305  {
2306  if (!myfEnabled)
2307  return;
2308  // set all buttons in group false
2309  for (auto b : group()[myGroup])
2310  {
2311  b->myValue = false;
2312  b->update();
2313  }
2314  // set this button true
2315  myValue = true;
2316  update(); });
2317  }
2328  void first()
2329  {
2330  // find button in its group
2331  auto this_it = std::find(
2332  group()[myGroup].begin(),
2333  group()[myGroup].end(),
2334  this);
2335 
2336  if (this_it == group()[myGroup].end())
2337  throw std::runtime_error("wex::radiobutton::first error in group");
2338 
2339  // if button is first in group, nothing is needed
2340  if (this_it == group()[myGroup].begin())
2341  return;
2342 
2343  // construct new group
2344  std::vector<radiobutton *> g;
2345  group().push_back(g);
2346 
2347  // copy button and following buttons in same group to new group
2348  for (
2349  auto it = this_it;
2350  it != group()[myGroup].end();
2351  it++)
2352  {
2353  group().back().push_back(*it);
2354  }
2355 
2356  // erase from old group
2357  group()[myGroup].erase(
2358  this_it,
2359  group()[myGroup].end());
2360 
2361  // tell buttons that were moved about their new group
2362  for (auto b : group().back())
2363  b->myGroup = group().size() - 1;
2364 
2365  // std::cout << "< first\n";
2366  // for( int kg=0; kg< group().size(); kg++ )
2367  // {
2368  // for( auto b : group()[kg] )
2369  // std::cout << b->id() << " , " << b->text() << " ";
2370  // std::cout << "\n";
2371  // }
2372  }
2373 
2375  bool isChecked()
2376  {
2377  return myValue;
2378  }
2379 
2384  {
2385  int off = 0;
2386  for (auto b : group()[myGroup])
2387  {
2388  if (b->isChecked())
2389  break;
2390  off++;
2391  }
2392  if (off < (int)group()[myGroup].size())
2393  return off;
2394  return -1;
2395  }
2396 
2398  void check(bool f = true)
2399  {
2400  if (f)
2401  {
2402  // set all buttons in group false
2403  for (auto b : group()[myGroup])
2404  {
2405  b->myValue = false;
2406  b->update();
2407  }
2408  }
2409  myValue = f;
2410  update();
2411  }
2412 
2413  virtual void draw(PAINTSTRUCT &ps)
2414  {
2415  SelectObject(ps.hdc, myFont);
2416  int color = 0x000000;
2417  if (!myfEnabled)
2418  color = 0xAAAAAA;
2419  SetTextColor(
2420  ps.hdc,
2421  myTextColor);
2422  SetBkColor(
2423  ps.hdc,
2424  myBGColor);
2425  RECT r(ps.rcPaint);
2426  r.left += 20;
2427  shapes S(ps);
2428 
2429  DrawText(
2430  ps.hdc,
2431  myText.c_str(),
2432  -1,
2433  &r,
2434  0);
2435  if (!myValue)
2436  {
2437  S.circle(10, 10, 5);
2438  }
2439  else
2440  {
2441  SelectObject(ps.hdc, GetStockObject(BLACK_BRUSH));
2442  Ellipse(ps.hdc, 5, 5, 15, 15);
2443  }
2444  }
2445 
2446  private:
2447  bool myValue;
2448  int myGroup;
2449 
2451  std::vector<std::vector<radiobutton *>> &group()
2452  {
2453  static std::vector<std::vector<radiobutton *>> theGroups;
2454  static bool fGroupInit = false;
2455  if (!fGroupInit)
2456  {
2457  // create first group
2458  fGroupInit = true;
2459  std::vector<radiobutton *> g;
2460  theGroups.push_back(g);
2461  }
2462  return theGroups;
2463  }
2464  };
2465 
2472  class checkbox : public gui
2473  {
2474  enum class eType
2475  {
2476  check,
2477  plus
2478  } myType;
2479 
2480  public:
2481  checkbox(gui *parent)
2482  : gui(parent), myType(eType::check), myValue(false)
2483  {
2484  // toggle the boolean value when clicked
2485  events().clickWex([this]
2486  {
2487  if (!myfEnabled)
2488  return;
2489  myValue = !myValue;
2490  update(); });
2491  }
2493  void plus(bool f = true)
2494  {
2495  if (f)
2496  myType = eType::plus;
2497  else
2498  myType = eType::check;
2499  }
2500  void check(bool f = true)
2501  {
2502  myValue = f;
2503  }
2504  bool isChecked()
2505  {
2506  return (myValue);
2507  }
2508  virtual void draw(PAINTSTRUCT &ps)
2509  {
2510  SetBkColor(
2511  ps.hdc,
2512  myBGColor);
2513  RECT r(ps.rcPaint);
2514  int cbg = r.bottom - r.top - 10;
2515  r.left += cbg + 5;
2516  r.top -= 2;
2517 
2518  shapes S(ps);
2519  S.textHeight(myLogFont.lfHeight);
2520  S.textFontName(myLogFont.lfFaceName);
2521  S.text(myText, {r.left, r.top, r.right, r.bottom});
2522  S.rectangle({0, 0, cbg, cbg});
2523  S.fill();
2524  S.penThick(2);
2525  S.color(0);
2526  switch (myType)
2527  {
2528  case eType::check:
2529  if (myValue)
2530  {
2531  S.line({2, cbg / 2, cbg / 2 - 1, cbg - 2});
2532  S.line({cbg / 2, cbg - 3, cbg - 4, 3});
2533  }
2534  break;
2535  case eType::plus:
2536  S.line({2, cbg / 2, cbg - 2, cbg / 2});
2537  if (myValue)
2538  S.line({1 + cbg / 2, 2, 1 + cbg / 2, cbg - 2});
2539  break;
2540  }
2541  S.penThick(1);
2542  }
2543  void clickFunction(std::function<void(void)> f)
2544  {
2545  myClickFunction = f;
2546  }
2547 
2548  private:
2549  bool myValue;
2550  std::function<void(void)> myClickFunction;
2551  };
2552 
2554  class msgbox
2555  {
2556  public:
2559  const std::string &msg)
2560  {
2561  myReturn = MessageBox(NULL,
2562  msg.c_str(),
2563  "Message",
2564  MB_OK);
2565  }
2568  gui &parent,
2569  const std::string &msg,
2570  const std::string &title,
2571  unsigned int type)
2572  {
2573  myReturn = MessageBox(parent.handle(),
2574  msg.c_str(),
2575  title.c_str(),
2576  type);
2577  }
2578  int myReturn;
2579  };
2580 
2582  class label : public gui
2583  {
2584  public:
2585  label(gui *parent)
2586  : gui(parent)
2587  {
2588  }
2589  };
2634  class editbox : public gui
2635  {
2636  public:
2637  editbox(gui *parent)
2638  : gui(parent, "Edit",
2639  WS_CHILD | ES_LEFT | WS_BORDER | WS_VISIBLE,
2640  WS_EX_CLIENTEDGE)
2641  {
2642  }
2644  void notification(WORD ntf)
2645  {
2646  std::cout << "editbox notification " << ntf << "\n";
2647  if (ntf == EN_KILLFOCUS)
2648  {
2649  std::cout << "done\n";
2650  }
2651  }
2652  void text(const std::string &t)
2653  {
2654  SetDlgItemText(
2655  myParent->handle(),
2656  myID,
2657  t.c_str());
2658  }
2660  std::string text()
2661  {
2662  char buf[1000];
2663  buf[0] = '\0';
2664  GetWindowText(
2665  handle(),
2666  buf,
2667  999);
2668  return std::string(buf);
2669  }
2671  void readonly(bool f = true)
2672  {
2673  SendMessage(
2674  handle(),
2675  EM_SETREADONLY,
2676  (WPARAM)f, (LPARAM)0);
2677  }
2678  };
2679 
2681  class multiline : public gui
2682  {
2683  public:
2684  multiline(gui *parent)
2685  : gui(parent, "Edit",
2686  WS_CHILD | ES_LEFT | WS_BORDER | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN,
2687  WS_EX_CLIENTEDGE)
2688  {
2689  }
2694  void text(const std::string &t)
2695  {
2696  SetDlgItemText(
2697  myParent->handle(),
2698  myID,
2699  t.c_str());
2700  }
2702  std::string text()
2703  {
2704  char buf[1000];
2705  buf[0] = '\0';
2706  GetWindowText(
2707  handle(),
2708  buf,
2709  999);
2710  return std::string(buf);
2711  }
2712  };
2714  class choice : public gui
2715  {
2716  public:
2717  choice(gui *parent)
2718  : gui(parent, "Combobox",
2719  CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE)
2720  {
2721  }
2723  void move(int x, int y, int w, int h)
2724  {
2725  if (h < 200)
2726  h = 200;
2727  gui::move(x, y, w, h);
2728  }
2730  void itemHeight(int h)
2731  {
2732  SendMessage(
2733  handle(),
2734  CB_SETITEMHEIGHT,
2735  (WPARAM)0, (LPARAM)20);
2736 
2737  /* WPARAM = 1 is supposed to set the "selection box" height
2738  but it appears to do nothing */
2739  // SendMessage(
2740  // handle(),
2741  // CB_SETITEMHEIGHT,
2742  // (WPARAM)1, (LPARAM)40);
2743  }
2744 
2746  void add(const std::string &s)
2747  {
2748  SendMessageA(
2749  handle(),
2750  (UINT)CB_ADDSTRING,
2751  (WPARAM)0,
2752  (LPARAM)s.c_str());
2753  }
2755  void clear()
2756  {
2757  SendMessage(
2758  handle(),
2759  CB_RESETCONTENT,
2760  (WPARAM)0, (LPARAM)0);
2761  }
2765  void select(int i)
2766  {
2767  SendMessage(
2768  handle(),
2769  CB_SETCURSEL,
2770  (WPARAM)i, (LPARAM)0);
2771  }
2775  void select(const std::string &s)
2776  {
2777  SendMessage(
2778  handle(),
2779  CB_SELECTSTRING,
2780  (WPARAM)-1, (LPARAM)s.c_str());
2781  }
2784  {
2785  return SendMessage(
2786  handle(),
2787  (UINT)CB_GETCURSEL,
2788  (WPARAM)0, (LPARAM)0);
2789  }
2791  std::string selectedText()
2792  {
2793  int i = selectedIndex();
2794  if (i < 0)
2795  return std::string("");
2796  return text(i);
2797  }
2799  std::string text(int i)
2800  {
2801  char buf[256];
2802  SendMessage(
2803  handle(),
2804  (UINT)CB_GETLBTEXT,
2805  (WPARAM)i,
2806  (LPARAM)buf);
2807  return std::string(buf);
2808  }
2810  int count()
2811  {
2812  return SendMessage(
2813  handle(),
2814  (UINT)CB_GETCOUNT,
2815  (WPARAM)0, (LPARAM)0);
2816  }
2817  };
2818 
2831  class list : public gui
2832  {
2833  public:
2834  list(gui *parent)
2835  : gui(parent, "listbox",
2836  LBS_NOTIFY | WS_VSCROLL | WS_BORDER |
2837  WS_CHILD | WS_OVERLAPPED | WS_VISIBLE)
2838  {
2839  }
2841  void move(int x, int y, int w, int h)
2842  {
2843  gui::move(x, y, w, h);
2844  SendMessageA(
2845  handle(),
2846  (UINT)LB_SETCOLUMNWIDTH,
2847  (WPARAM)w,
2848  (LPARAM)0);
2849  }
2851  void add(const std::string &s)
2852  {
2853  SendMessageA(
2854  handle(),
2855  (UINT)LB_ADDSTRING,
2856  (WPARAM)0,
2857  (LPARAM)s.c_str());
2858  }
2860  void clear()
2861  {
2862  SendMessage(
2863  handle(),
2864  LB_RESETCONTENT,
2865  (WPARAM)0, (LPARAM)0);
2866  }
2870  void select(int i)
2871  {
2872  SendMessage(
2873  handle(),
2874  LB_SETCURSEL,
2875  (WPARAM)i, (LPARAM)0);
2876  }
2880  void deleteItem(int i)
2881  {
2882  SendMessage(
2883  handle(),
2884  LB_DELETESTRING,
2885  (WPARAM)i, (LPARAM)0);
2886  }
2890  void select(const std::string &s)
2891  {
2892  SendMessage(
2893  handle(),
2894  LB_SELECTSTRING,
2895  (WPARAM)-1, (LPARAM)s.c_str());
2896  }
2901  {
2902  return SendMessage(
2903  handle(),
2904  (UINT)LB_GETCURSEL,
2905  (WPARAM)0, (LPARAM)0);
2906  }
2907 
2909  std::string selectedText()
2910  {
2911  int i = selectedIndex();
2912  if (i < 0)
2913  return std::string("");
2914  char buf[256];
2915  SendMessage(
2916  handle(),
2917  (UINT)LB_GETTEXT,
2918  (WPARAM)i,
2919  (LPARAM)buf);
2920  return std::string(buf);
2921  }
2923  int count()
2924  {
2925  return SendMessage(
2926  handle(),
2927  (UINT)LB_GETCOUNT,
2928  (WPARAM)0, (LPARAM)0);
2929  }
2930  };
2931 
2939  class windex
2940  {
2941  public:
2950  static windex &get()
2951  {
2952  static windex theInstance;
2953  return theInstance;
2954  }
2955 
2956  /* handle window messages
2957 
2958  All messages to windows created by windex come here.
2959  The messages are passed on to be handled by the gui element for the window they are directed to
2960  */
2961  static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2962  {
2963  auto w = get().myGui.find(hwnd);
2964  if (w != get().myGui.end())
2965  {
2966  if (uMsg == WM_GETDLGCODE)
2967  return w->second->WindowMessageHandler(hwnd, uMsg, wParam, lParam);
2968 
2969  if (w->second->WindowMessageHandler(hwnd, uMsg, wParam, lParam))
2970  return 0;
2971  }
2972 
2973  // run default message processing
2974  return DefWindowProc(hwnd, uMsg, wParam, lParam);
2975  }
2976 
2978  gui *Add(gui *g)
2979  {
2980  // delete any destroyed elements
2981  Delete();
2982 
2983  // provide reference to delete list, so new gui element can be removed after destruction
2984  g->delete_list(&myDeleteList);
2985 
2986  // add to existing gui elements
2987  myGui.insert(std::make_pair(g->handle(), g));
2988 
2989  // std::cout << "windexAdd " << myGui.size() <<" in "<< this << "\n";
2990 
2991  return g;
2992  }
2993 
2994  mgui_t myGui;
2995  private:
2996  std::vector<HWND> myDeleteList;
2997 
2998  windex()
2999  {
3000  // register a callback function
3001  // to be invoked every time a windex gui element receives a windows message
3002  WNDCLASS wc = {};
3003  wc.lpfnWndProc = &windex::WindowProc;
3004  wc.hInstance = NULL;
3005  wc.lpszClassName = "windex";
3006  wc.hbrBackground = CreateSolidBrush(0xc8c8c8);
3007  wc.style = CS_DBLCLKS;
3008  RegisterClass(&wc);
3009  }
3010 
3012  void Delete()
3013  {
3014  // std::cout << "windex::Delete " << myDeleteList.size() << "\n";
3015  for (auto h : myDeleteList)
3016  {
3017  auto i = myGui.find(h);
3018  if (i != myGui.end())
3019  myGui.erase(i);
3020  }
3021  myDeleteList.clear();
3022  }
3023  };
3024 
3056  class menu
3057  {
3058  public:
3059  menu(gui &parent)
3060  : myM(CreatePopupMenu()), myParent(parent)
3061  {
3062  }
3063  ~menu()
3064  {
3065  DestroyMenu(myM);
3066  }
3077  void append(
3078  const std::string &title,
3079  const std::function<void(const std::string &)> &f = [](const std::string &title) {})
3080  {
3081  AppendMenu(
3082  myM,
3083  0,
3084  myParent.events().menuCommand(f, title),
3085  title.c_str());
3086  }
3087 
3092  void append(
3093  const std::string &title,
3094  menu &submenu)
3095  {
3096  AppendMenu(
3097  myM,
3098  MF_POPUP,
3099  (UINT_PTR)submenu.handle(),
3100  title.c_str());
3101  }
3106  void popup(
3107  int x, int y)
3108  {
3109  TrackPopupMenu(
3110  myM,
3111  0,
3112  x, y,
3113  0,
3114  myParent.handle(),
3115  NULL);
3116  }
3117 
3118  HMENU handle()
3119  {
3120  return myM;
3121  }
3127  bool check(int index, bool f = true)
3128  {
3129  unsigned int uCheck;
3130  if (f)
3131  uCheck = MF_BYPOSITION | MF_CHECKED;
3132  else
3133  uCheck = MF_BYPOSITION | MF_UNCHECKED;
3134  return MF_CHECKED == CheckMenuItem(
3135  myM,
3136  index,
3137  uCheck);
3138  }
3140  int size()
3141  {
3142  return GetMenuItemCount(myM);
3143  }
3144 
3145  private:
3146  HMENU myM;
3147  gui &myParent;
3148  };
3149 
3151  class menubar
3152  {
3153  public:
3154  menubar(gui &parent)
3155  : myParent(parent), myM(CreateMenu())
3156  {
3157  // attach menu to window
3158  SetMenu(parent.handle(), myM);
3159  }
3164  void append(
3165  const std::string &title,
3166  menu &m)
3167  {
3168  AppendMenu(
3169  myM,
3170  MF_POPUP,
3171  (UINT_PTR)m.handle(),
3172  title.c_str());
3173  DrawMenuBar(myParent.handle());
3174  }
3175 
3176  private:
3177  gui &myParent;
3178  HMENU myM;
3179  };
3190  class timer
3191  {
3192  public:
3203  timer(gui &g, int intervalmsecs, int id = 1)
3204  : myGUI(g), myID(id)
3205  {
3206  SetTimer(
3207  myGUI.handle(), // handle to window
3208  myID, // timer identifier
3209  intervalmsecs, // interval ms
3210  (TIMERPROC)NULL); // no timer callback
3211  }
3212  ~timer()
3213  {
3214  KillTimer(
3215  myGUI.handle(),
3216  myID);
3217  }
3218 
3219  private:
3220  gui &myGUI;
3221  int myID;
3222  };
3223 }
3224 
3225 #include "widgets.h"
3226 
3227 namespace wex
3228 {
3229 
3236  class maker
3237  {
3238  public:
3243  template <class W, class P>
3244  static W &make(P &parent)
3245  {
3246  return *((W *)windex::get().Add(new W((gui *)&parent)));
3247  }
3248 
3252  static gui &make()
3253  {
3254  datebox::init();
3255 
3256  return *windex::get().Add(new gui());
3257  }
3258  };
3259 
3299  class tabbed : public panel
3300  {
3301  public:
3302  tabbed(gui *parent)
3303  : panel(parent), myTabWidth(50)
3304  {
3305  tabChanging([](int tabIndex) {});
3306  tabChanged([](int tabIndex) {});
3307  }
3314  void add(
3315  const std::string &tabname,
3316  gui &panel)
3317  {
3318  // resize the child panel so it fits neatly under the tab buttons
3319  RECT rect;
3320  GetClientRect(myHandle, &rect);
3321  panel.move(0, 31, rect.right - rect.left, rect.bottom - rect.top - 30);
3322 
3323  button &btn = maker::make<button>(*this);
3324  btn.text(tabname);
3325  btn.move(myButton.size() * myTabWidth,
3326  0, myTabWidth, 30);
3327  myButton.push_back(&btn);
3328  myPanel.push_back(&panel);
3329  int tabIndex = myButton.size() - 1;
3330 
3331  btn.events().click([this, tabIndex]()
3332  {
3333  myTabChangingFn(tabIndex);
3334  select(tabIndex);
3335  myTabChangeFn(tabIndex); });
3336  }
3338  void select(int i)
3339  {
3340  std::cout << "select " << i << "\n";
3341 
3342  if (0 > i || i >= (int)myButton.size())
3343  return;
3344 
3345  for (auto b : myButton)
3346  {
3347  b->bgcolor(0xC8C8C8);
3348  b->update();
3349  }
3350  for (auto p : myPanel)
3351  p->show(false);
3352 
3353  myButton[i]->bgcolor(0xFFFFFF);
3354  myPanel[i]->show();
3355  update();
3356  mySelect = i;
3357  }
3359  int select() const
3360  {
3361  return mySelect;
3362  }
3364  void tabWidth(int w)
3365  {
3366  myTabWidth = w;
3367  }
3372  void tabChanging(std::function<void(int tabIndex)> f)
3373  {
3374  myTabChangingFn = f;
3375  }
3380  void tabChanged(std::function<void(int tabIndex)> f)
3381  {
3382  myTabChangeFn = f;
3383  }
3384 
3385  private:
3386  std::vector<button *> myButton;
3387  std::vector<gui *> myPanel;
3388  int myTabWidth;
3389  int mySelect;
3390  std::function<void(int tabIndex)> myTabChangingFn;
3391  std::function<void(int tabIndex)> myTabChangeFn;
3392  };
3393 
3424  class radiobuttonLayout : public layout
3425  {
3426  public:
3427  radiobuttonLayout(gui *parent)
3428  : layout(parent), myFirst(true)
3429  {
3430  }
3435  {
3436  wex::radiobutton &rb = wex::maker::make<wex::radiobutton>(*this);
3437  if (myFirst)
3438  {
3439  myFirst = false;
3440  rb.first();
3441 
3442  rb.text("AAA");
3443  }
3444  return rb;
3445  }
3447  int checked()
3448  {
3449  if (myFirst)
3450  return -1;
3451  return ((radiobutton *)children()[0])->checkedOffset();
3452  }
3457  void check(int i, bool f = true)
3458  {
3459  if (0 > i || i >= (int)children().size())
3460  return;
3461  ((radiobutton *)children()[i])->check();
3462  }
3466  void enable(bool f = true)
3467  {
3468  for (auto rb : children())
3469  ((radiobutton *)rb)->enable(f);
3470  }
3471 
3472  private:
3473  bool myFirst;
3474  };
3476  class printDoc
3477  {
3478  public:
3482  printDoc(const std::string &title = "printDoc")
3483  {
3484  // https://www.equestionanswers.com/vcpp/screen-dc-printer-dc.php
3485  PRINTDLG pdlg;
3486 
3487  /* Initialize the PRINTDLG structure. */
3488  memset(&pdlg, 0, sizeof(PRINTDLG));
3489  pdlg.lStructSize = sizeof(PRINTDLG);
3490  /* Set the flag to return printer DC. */
3491  pdlg.Flags = PD_RETURNDC;
3492 
3493  /* Invoke the printer dialog box. */
3494  PrintDlg(&pdlg);
3495 
3496  /* hDC member of the PRINTDLG structure contains the printer DC. */
3497  dc = pdlg.hDC;
3498  if (!dc)
3499  return;
3500 
3501  DOCINFO di;
3502  memset(&di, 0, sizeof(DOCINFO));
3503  /* Fill in the required members. */
3504  di.cbSize = sizeof(DOCINFO);
3505  di.lpszDocName = title.c_str();
3506 
3507  StartDoc(dc, &di);
3508  }
3511  {
3512  EndDoc(dc);
3513  DeleteDC(dc);
3514  }
3516  bool isOpen()
3517  {
3518  return (bool)dc;
3519  }
3520  void pageStart()
3521  {
3522  StartPage(dc);
3523  }
3524  void pageEnd()
3525  {
3526  EndPage(dc);
3527  }
3534  void text(
3535  int x, int y,
3536  const std::string &s)
3537  {
3538  TextOut(
3539  dc,
3540  x, y,
3541  s.c_str(), s.length());
3542  }
3543 
3544  private:
3545  HDC dc;
3546  };
3547 
3548  struct free
3549  {
3555  static int startProcess(
3556  const std::string &command,
3557  std::string &error)
3558  {
3559  STARTUPINFO si;
3560  PROCESS_INFORMATION pi;
3561 
3562  ZeroMemory(&si, sizeof(si));
3563  si.cb = sizeof(si);
3564  ZeroMemory(&pi, sizeof(pi));
3565 
3566  // Retain keyboard focus, minimize module2 window
3567  si.wShowWindow = SW_SHOWNOACTIVATE | SW_MINIMIZE;
3568  si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION;
3569  si.dwX = 600;
3570  si.dwY = 200;
3571 
3572  if (!CreateProcessA(
3573  NULL, // No module name (use command line)
3574  (LPSTR)command.c_str(), // Command line
3575  NULL, // Process handle not inheritable
3576  NULL, // Thread handle not inheritable
3577  FALSE, // Set handle inheritance to FALSE
3578  CREATE_NEW_CONSOLE, // creation flags
3579  NULL, // Use parent's environment block
3580  NULL, // Use parent's starting directory
3581  &si, // Pointer to STARTUPINFO structure
3582  &pi) // Pointer to PROCESS_INFORMATION structure
3583  )
3584  {
3585  int syserrno = GetLastError();
3586  if (syserrno == 2)
3587  {
3588  error = "Cannot find executable file";
3589  return 2;
3590  }
3591  char *lpMsgBuf;
3592  FormatMessageA(
3593  FORMAT_MESSAGE_ALLOCATE_BUFFER |
3594  FORMAT_MESSAGE_FROM_SYSTEM |
3595  FORMAT_MESSAGE_IGNORE_INSERTS,
3596  NULL,
3597  (DWORD)syserrno,
3598  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
3599  (LPSTR)&lpMsgBuf,
3600  0, NULL);
3601  error = lpMsgBuf;
3602  LocalFree(lpMsgBuf);
3603  return 1;
3604  }
3605 
3606  // Close process and thread handles.
3607  CloseHandle(pi.hProcess);
3608  CloseHandle(pi.hThread);
3609 
3610  error = "";
3611  return 0;
3612  }
3613  };
3614 }
A widget that user can click to start an action.
Definition: wex.h:2113
void imageFile(const std::string &name)
Specify bitmap image to be used for button, read from file.
Definition: wex.h:2124
virtual void draw(PAINTSTRUCT &ps)
draw
Definition: wex.h:2166
int imageResource(const std::string &name)
Specify bitmap image to be used for button, read from resource.
Definition: wex.h:2147
A widget that user can click to toggle a true/false value.
Definition: wex.h:2473
void plus(bool f=true)
set type to plus, useful to indicate expanded or collapsed property categories
Definition: wex.h:2493
A widget where user can choose from a dropdown list of strings.
Definition: wex.h:2715
void clear()
Clear all options.
Definition: wex.h:2755
std::string text(int i)
get text by index
Definition: wex.h:2799
int selectedIndex()
get index of selected item
Definition: wex.h:2783
void itemHeight(int h)
set item height in drop doown list
Definition: wex.h:2730
void add(const std::string &s)
Add an option.
Definition: wex.h:2746
void select(const std::string &s)
Select by string.
Definition: wex.h:2775
void select(int i)
Select by index.
Definition: wex.h:2765
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:2723
int count()
get count of items
Definition: wex.h:2810
std::string selectedText()
get text of selected item
Definition: wex.h:2791
A widget where users can drop files dragged from windows explorer.
Definition: wex.h:1898
A widget where user can enter a single line string.
Definition: wex.h:2635
void readonly(bool f=true)
disable ( or enable ) user editing
Definition: wex.h:2671
void notification(WORD ntf)
editbox generated a notification - nop
Definition: wex.h:2644
std::string text()
get text in textbox
Definition: wex.h:2660
A class where application code can register functions to be called when an event occurs.
Definition: wex.h:116
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:397
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:291
void datePick(std::function< void(int, LPNMDATETIMECHANGE)> f)
Register function to call when a date is picked.
Definition: wex.h:450
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:420
void asyncReadComplete(std::function< void(int id)> f)
register function to call when an asynchronous read completes.
Definition: wex.h:404
void keydown(std::function< void(int keydown)> f)
register function to call when key pressed. Function is passed key code.
Definition: wex.h:359
void clickPropogate(bool f=true)
specify that click event should propogate to parent window after currently registered click event han...
Definition: wex.h:296
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:392
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:332
void tcpRead(std::function< void(void)> f)
register function to call when tcp read accurs
Definition: wex.h:413
void change(int id, std::function< void(void)> f)
register function to call when control changes
Definition: wex.h:351
void click(std::function< void(void)> f, bool propogate=false)
register click event handler
Definition: wex.h:276
Displaying a title and a box.
Definition: wex.h:1931
void move(const std::vector< int > &r)
Set size and location of group box.
Definition: wex.h:1940
The base class for all windex gui elements.
Definition: wex.h:824
void update()
force widget to redraw completely
Definition: wex.h:1575
void child(gui *w)
register child on this window
Definition: wex.h:915
unsigned int myAsyncReadCompleteMsgID
handle to tooltip control for this gui element
Definition: wex.h:1701
std::vector< int > size()
Size of window client area.
Definition: wex.h:1626
void tooltip(const std::string &text, int width=0)
Add tooltip that pops up helpfully when mouse cursor hovers over widget.
Definition: wex.h:1222
void showModal(gui &appWindow)
Show this window and suspend all other windows interactions until this is closed.
Definition: wex.h:1505
void scrollRange(int width, int height)
Set the scrolling range.
Definition: wex.h:1135
void Create(HWND parent, const char *window_class, DWORD style, DWORD exstyle=0, int id=0)
Create the managed window.
Definition: wex.h:1712
void endModal()
Stop modal interaction and close window.
Definition: wex.h:1558
eventhandler & events()
Get event handler.
Definition: wex.h:1649
void delete_list(std::vector< HWND > *list)
set delete list for when gui is detroyed
Definition: wex.h:1661
void move(const std::vector< int > &r)
Move the window.
Definition: wex.h:1587
void setfont(LOGFONT &logfont, HFONT &font)
change font for this and all child windows
Definition: wex.h:1667
void size(int w, int h)
Change size without moving top left corner.
Definition: wex.h:1598
void icon(const std::string &iconfilename)
Change icon.
Definition: wex.h:993
void fontHeight(int h)
Change font height for this and all child windows.
Definition: wex.h:975
gui * find(int id)
find child window with specified id
Definition: wex.h:932
bool myfEnabled
true if not disabled
Definition: wex.h:1698
void createNewFont()
Replace font used by this and child windows from logfont.
Definition: wex.h:1752
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:871
std::vector< gui * > myChild
gui elements to be displayed in this window
Definition: wex.h:1696
HWND handle()
get window handle
Definition: wex.h:1655
void textColor(int c)
Set text color.
Definition: wex.h:1025
children_t & children()
get vector of children
Definition: wex.h:921
void enable(bool f=true)
Enable/Disable, default enable.
Definition: wex.h:963
bool myfModal
true if element is being shown as modal
Definition: wex.h:1697
void move(int x, int y)
Change position without changing size.
Definition: wex.h:1610
sMouse getMouseStatus()
Get mouse status.
Definition: wex.h:1171
void bgcolor(int color)
Change background color.
Definition: wex.h:949
virtual void show(bool f=true)
Show window and all children.
Definition: wex.h:1490
int NewID()
Create new, unique ID for gui element.
Definition: wex.h:1797
void run()
Run the windows message loop.
Definition: wex.h:1198
gui()
Construct top level window with no parent.
Definition: wex.h:831
void scroll(bool fHoriz=true)
Add scrollbars.
Definition: wex.h:1044
void font(LOGFONT &logfont, HFONT &font)
get font details
Definition: wex.h:1745
A widget that displays a string.
Definition: wex.h:2583
A panel which arranges the widgets it contains in a grid.
Definition: wex.h:1985
void grid(int cols)
Specify number of cols to use for layout.
Definition: wex.h:2002
void colWidths(const std::vector< int > &vw)
Specify column widths.
Definition: wex.h:2009
void colfirst(bool f=true)
Specify that widgets should be added to fill columns first.
Definition: wex.h:2033
A widget where user can choose from a list of strings.
Definition: wex.h:2832
std::string selectedText()
get text of selected item
Definition: wex.h:2909
void clear()
Clear all options.
Definition: wex.h:2860
void deleteItem(int i)
Delete by index.
Definition: wex.h:2880
void move(int x, int y, int w, int h)
Override move to ensure column width is sufficient.
Definition: wex.h:2841
int selectedIndex()
get index of selected item
Definition: wex.h:2900
void select(int i)
Select by index.
Definition: wex.h:2870
void add(const std::string &s)
Add an option.
Definition: wex.h:2851
void select(const std::string &s)
Select by string.
Definition: wex.h:2890
int count()
get count of items
Definition: wex.h:2923
A class for making windex objects.
Definition: wex.h:3237
static W & make(P &parent)
Construct widget.
Definition: wex.h:3244
static gui & make()
Construct a top level window ( first call constructs application window )
Definition: wex.h:3252
A drop down list of options that user can click to start an action.
Definition: wex.h:3057
void append(const std::string &title, menu &submenu)
Append submenu.
Definition: wex.h:3092
void popup(int x, int y)
Popup menu and run user selection.
Definition: wex.h:3106
int size()
Number of items in menu.
Definition: wex.h:3140
bool check(int index, bool f=true)
Set or unset check mark beside menu item.
Definition: wex.h:3127
void append(const std::string &title, const std::function< void(const std::string &)> &f=[](const std::string &title) {})
Append menu item.
Definition: wex.h:3077
A widget that displays across top of a window and contains a number of dropdown menues.
Definition: wex.h:3152
void append(const std::string &title, menu &m)
Append menu to menubar.
Definition: wex.h:3164
Definition: wex.h:44
bool set(int id, HWND h)
Set modal running.
Definition: wex.h:60
bool canClose(int id)
Can a window be closed.
Definition: wex.h:86
static modalMgr & get()
get reference to singleton modal manager
Definition: wex.h:47
A popup with a message.
Definition: wex.h:2555
msgbox(const std::string &msg)
CTOR for simple message box with OK button.
Definition: wex.h:2558
int myReturn
Button id clicked by user.
Definition: wex.h:2578
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:2567
A mutiline editbox.
Definition: wex.h:2682
std::string text()
get text in textbox
Definition: wex.h:2702
void text(const std::string &t)
Set text.
Definition: wex.h:2694
A child window that can contain widgets.
Definition: wex.h:1859
Print a text document.
Definition: wex.h:3477
~printDoc()
Finalize and send to printer.
Definition: wex.h:3510
printDoc(const std::string &title="printDoc")
CTOR.
Definition: wex.h:3482
void text(int x, int y, const std::string &s)
Add some text.
Definition: wex.h:3534
bool isOpen()
True if CTOR was successful.
Definition: wex.h:3516
A widget that user can click to select one of an exclusive set of options.
Definition: wex.h:2294
void first()
Make this button first of a new group.
Definition: wex.h:2328
int checkedOffset()
Which button in group is checked.
Definition: wex.h:2383
void check(bool f=true)
set value true( default ) or false
Definition: wex.h:2398
bool isChecked()
true if checked
Definition: wex.h:2375
Widget to layout a group of radio buttons.
Definition: wex.h:3425
void enable(bool f=true)
Enable/disable all radio buttons.
Definition: wex.h:3466
void check(int i, bool f=true)
set status of radio button
Definition: wex.h:3457
radiobutton & add()
add a radio button
Definition: wex.h:3434
int checked()
0-based index of checked radio button
Definition: wex.h:3447
A class that offers application code methods to draw on a window.
Definition: wex.h:525
void penThick(int t)
Set pen thickness in pixels.
Definition: wex.h:597
void textFontName(const std::string &fn)
set text font name
Definition: wex.h:794
void arc(int x, int y, double r, double sa, double ea)
Draw Arc of circle.
Definition: wex.h:693
void textHeight(int h)
Set text height.
Definition: wex.h:786
void text(const std::string &t, const std::vector< int > &v)
Draw text.
Definition: wex.h:727
void textVertical(bool f=true)
Enable / disable drawing text in vertical orientation.
Definition: wex.h:771
void bgcolor(int c)
set background color
Definition: wex.h:578
void transparent(bool f=true)
enable/disable transparent background
Definition: wex.h:589
void line(const std::vector< int > &v)
Draw line between two points.
Definition: wex.h:616
void polygon(const std::vector< int > &v)
Draw Polygon.
Definition: wex.h:678
void circle(int x0, int y0, double r)
Draw circle.
Definition: wex.h:714
void fill(bool f=true)
Set filling option.
Definition: wex.h:604
shapes(PAINTSTRUCT &ps)
Constructor.
Definition: wex.h:530
void rectangle(const std::vector< int > &v)
Draw rectangle.
Definition: wex.h:639
void pixel(int x, int y)
Color a pixel.
Definition: wex.h:609
void color(int r, int g, int b)
Set color for drawings.
Definition: wex.h:559
A widget where user can select which panel to display by clicking a tab button.
Definition: wex.h:3300
void select(int i)
select panel to displayed
Definition: wex.h:3338
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:3372
void add(const std::string &tabname, gui &panel)
add panel that can be displayed
Definition: wex.h:3314
int select() const
zero-based index of panel currently selected
Definition: wex.h:3359
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:3380
void tabWidth(int w)
set width of tab buttons
Definition: wex.h:3364
Generate events at regularly timed intervals.
Definition: wex.h:3191
timer(gui &g, int intervalmsecs, int id=1)
CTOR.
Definition: wex.h:3203
A class containing a database of the current gui elements.
Definition: wex.h:2940
gui * Add(gui *g)
Add new gui element.
Definition: wex.h:2978
static windex & get()
get reference to windex gui framework ( singleton )
Definition: wex.h:2950
mgui_t myGui
map of existing gui elements
Definition: wex.h:2994
Definition: wex.h:3549
static int startProcess(const std::string &command, std::string &error)
Start a command in its own process.
Definition: wex.h:3555
A structure containing the mouse status for event handlers.
Definition: wex.h:28