WINDEX
propertygrid.h
1 #pragma once
2 #include <memory>
3 #ifdef windex_has_boost
4 #include <boost/property_tree/ptree.hpp>
5 #include <boost/property_tree/json_parser.hpp>
6 #endif
7 #include "wex.h"
8 namespace wex
9 {
10  class propertyGrid;
11 
13  class property
14  {
15  public:
16  property(
17  gui *parent,
18  const std::string &name,
19  const std::string &value)
20  : myName(name), myValue(value), W(windex::get()),
21  myLabel(wex::maker::make<wex::label>(*parent)),
22  myEditbox(wex::maker::make<editbox>(*parent)),
23  myCombobox(wex::maker::make<choice>(*parent)),
24  myCheckbox(wex::maker::make<checkbox>(*parent)),
25  myCategoryExpanded(wex::maker::make<checkbox>(*parent)),
26  myLabelWidth(100),
27  myType(eType::string),
28  myLabelClicked(false)
29  {
30  myLabel.text(myName);
31  myLabel.events().click(
32  [this]
33  {
34  myLabelClicked = true;
35  });
36  myLabel.events().clickPropogate();
37 
38  myEditbox.text(myValue);
39  }
40  property(
41  gui *parent,
42  const std::string &name,
43  bool value)
44  : myName(name), myValue(std::to_string((int)value)), W(windex::get()),
45  myLabel(wex::maker::make<label>(*parent)), myEditbox(wex::maker::make<editbox>(*parent)), myCombobox(wex::maker::make<choice>(*parent)), myCheckbox(wex::maker::make<checkbox>(*parent)), myCategoryExpanded(wex::maker::make<checkbox>(*parent)), myLabelWidth(100), myType(eType::check)
46  {
47  myLabel.text(myName);
48  myCheckbox.check(value);
49  myCheckbox.text("");
50  myCheckbox.events().clickPropogate();
51  }
52 
53  // construct a choice property
54  property(
55  gui *parent,
56  const std::string &name,
57  const std::vector<std::string> &value)
58  : myName(name), myValue(""), W(windex::get()), myLabel(wex::maker::make<label>(*parent)), myEditbox(wex::maker::make<editbox>(*parent)), myCombobox(wex::maker::make<choice>(*parent)), myCheckbox(wex::maker::make<checkbox>(*parent)), myCategoryExpanded(wex::maker::make<checkbox>(*parent)), myLabelWidth(100), myType(eType::choice)
59  {
60  myLabel.text(myName);
61  myCombobox.itemHeight(30);
62  for (auto &t : value)
63  {
64  myCombobox.add(t);
65  }
66  }
69  gui *parent,
70  const std::string &name)
71  : myName(name), W(windex::get()), myLabel(wex::maker::make<label>(*parent)), myEditbox(wex::maker::make<editbox>(*parent)), myCombobox(wex::maker::make<choice>(*parent)), myCheckbox(wex::maker::make<checkbox>(*parent)), myCategoryExpanded(wex::maker::make<checkbox>(*parent)), myType(eType::category)
72  {
73  myCategoryExpanded.text(myName);
74  myCategoryExpanded.plus();
75  myCategoryExpanded.check();
76  myCategoryExpanded.events().clickPropogate();
77  }
78 
81  {
82  myLabel.~label();
83  myEditbox.~editbox();
84  myCategoryExpanded.~checkbox();
85  myCombobox.~choice();
86  }
87  void move(const std::vector<int> &r)
88  {
89  std::vector<int> rl(r);
90  rl[2] = myLabelWidth;
91  myLabel.move(rl);
92 
93  // size of edit box
94  std::vector<int> re(r);
95  // right side of label
96  re[0] += myLabelWidth;
97  // window width minus label with minus scroll control
98  re[2] -= myLabelWidth + 25;
99 
100  switch (myType)
101  {
102  case eType::string:
103  myCategoryExpanded.move({0, 0, 0, 0});
104  myEditbox.move(re);
105  break;
106  case eType::choice:
107  re[3] *= myCombobox.count();
108  myCombobox.move(re[0], re[1], re[2], re[3]);
109  break;
110  case eType::check:
111  myCheckbox.move(re);
112  break;
113  case eType::category:
114  re = r;
115  re[1] += 0.5 * re[3];
116  re[3] /= 2;
117  myCategoryExpanded.move(re);
118  myLabel.move({0, 0, 0, 0}); // keep unused label out of the way
119  break;
120  }
121  }
122  void labelWidth(int w)
123  {
124  myLabelWidth = w;
125  }
126  void bgcolor(int color)
127  {
128  myLabel.bgcolor(color);
129  }
130 
131  bool labelClicked() const
132  {
133  return myLabelClicked;
134  }
135  void labelClicked(bool f)
136  {
137  myLabelClicked = f;
138  }
139  std::string labeltext() const
140  {
141  return myLabel.text();
142  }
143 
144  void tabList(bool f = true)
145  {
146  if (myType != eType::string)
147  return;
148  auto s = GetWindowLongPtr(myEditbox.handle(), GWL_STYLE);
149  if (f)
150  s |= WS_TABSTOP;
151  else
152  s &= ~WS_TABSTOP;
153  SetWindowLongPtr(
154  myEditbox.handle(),
155  GWL_STYLE,
156  s);
157  }
158 
164  property &tooltip(const std::string &tip, int width = 0)
165  {
166  myLabel.tooltip(tip, width);
167  return *this;
168  }
175  property &readonly(bool f = true)
176  {
177  myCheckbox.enable(!f);
178  switch (myType)
179  {
180  case eType::string:
181  myEditbox.readonly(f);
182  break;
183  default:
184  break;
185  }
186  return *this;
187  }
188 
189  void show(bool f = true)
190  {
191  myLabel.show(f);
192  switch (myType)
193  {
194  case eType::string:
195  myEditbox.show(f);
196  break;
197  case eType::choice:
198  myCombobox.show(f);
199  break;
200  case eType::check:
201  myCheckbox.show(f);
202  break;
203  case eType::category:
204  myLabel.show(false); // keep unused label out of the way
205  myCategoryExpanded.show(f);
206  break;
207  }
208  }
216  void update()
217  {
218  myLabel.update();
219  myCheckbox.update();
220  myCategoryExpanded.update();
221  }
222 
223  const std::string &name() const
224  {
225  return myName;
226  }
227  const std::string value() const
228  {
229  switch (myType)
230  {
231  case eType::string:
232  return myEditbox.text();
233 
234  case eType::choice:
235  return myCombobox.selectedText();
236 
237  case eType::check:
238  return std::to_string((int)myCheckbox.isChecked());
239  break;
240 
241  case eType::category:
242  break;
243  }
244  return std::string("");
245  }
246 
247  bool isChecked()
248  {
249  if (myType == eType::check)
250  return myCheckbox.isChecked();
251  return false;
252  }
254  property &value(const std::string v)
255  {
256  switch (myType)
257  {
258  case eType::string:
259  myValue = v;
260  myEditbox.text(v);
261  myEditbox.update();
262  break;
263  case eType::choice:
264  if (v.empty())
265  {
266  myCombobox.select(-1);
267  break;
268  }
269  myValue = v;
270  myCombobox.select(v);
271  break;
272  default:
273  break;
274  }
275  return *this;
276  }
277  void value_bool(bool v)
278  {
279  switch (myType)
280  {
281  case eType::check:
282  myValue = std::to_string((int)v);
283  myCheckbox.check(v);
284  break;
285  default:
286  // other property types ignore requests to change boolean vaue
287  break;
288  }
289  }
291  void saveValue()
292  {
293  switch (myType)
294  {
295  case eType::string:
296  myValue = myEditbox.text();
297  break;
298  case eType::choice:
299  myValue = myCombobox.selectedText();
300  break;
301  case eType::check:
302  myValue = std::to_string((int)myCheckbox.isChecked());
303  break;
304  case eType::category:
305  break;
306  }
307  }
308 
309  // get myValue attribute
310  const std::string savedValue() const
311  {
312  return myValue;
313  }
314 
315  bool isCategory() const
316  {
317  return myType == eType::category;
318  }
319  bool isExpanded() const
320  {
321  if (!isCategory())
322  return false;
323  return myCategoryExpanded.isChecked();
324  }
325  void expand(bool f)
326  {
327  myCategoryExpanded.check(f);
328  }
330  void change(std::function<void()> f)
331  {
332  switch (myType)
333  {
334  case eType::string:
335  myEditbox.events().change(myEditbox.id(), f);
336  break;
337  case eType::check:
338  myCheckbox.events().click(f);
339  break;
340  default:
341  break;
342  }
343  }
344 
345  #ifdef windex_has_boost
346  void BoostPropertyTree(
347  boost::property_tree::ptree &tree,
348  const std::string &catname)
349  const
350  {
351  if (myType == eType::choice)
352  tree.put(
353  catname + myName + ".value",
354  myCombobox.selectedText());
355  else
356  tree.put(
357  catname + myName + ".value",
358  myValue);
359  tree.put(
360  catname + myName + ".type",
361  std::to_string((int)myType));
362  if (myType == eType::choice)
363  {
364  for (int k = 0; k < myCombobox.count(); k++)
365  tree.put(
366  catname + myName + ".choice" + std::to_string(k),
367  myCombobox.text(k));
368  }
369  }
370  #endif
371 
372  private:
373  std::string myName;
374  std::string myValue;
375  wex::windex &W;
376  wex::label &myLabel;
377  wex::editbox &myEditbox;
378  wex::choice &myCombobox;
379  wex::checkbox &myCheckbox;
380  wex::checkbox &myCategoryExpanded;
381  int myLabelWidth;
382  enum class eType
383  {
384  string,
385  choice,
386  check,
387  category
388  } myType;
389  bool myLabelClicked;
390  };
395  class propertyGrid : public gui
396  {
397  public:
398  typedef std::shared_ptr<property> prop_t;
399 
400  propertyGrid(gui *parent)
401  : gui(parent, "windex", WS_CHILD, WS_EX_CONTROLPARENT), myHeight(25), myHeightCategory(2), myWidth(300), myLabelWidth(100), myBGColor(0xc8c8c8), myfScroll(false), myftabstop(false)
402  {
403  text("PG");
404 
405  // handle click event
406  events().click(
407  [this]
408  {
409  labelClick();
410  visible();
411  });
412 
413  // regsiter NOP event handlers
414  change([] {});
415  nameClick([](const std::string &) {});
416  }
417  void clear()
418  {
419  myProperty.clear();
420  }
426  property &string(
427  const std::string &name,
428  const std::string &value)
429  {
430  myProperty.push_back(prop_t(new property(this, name, value)));
431  CommonConstruction();
432  return *myProperty.back().get();
433  }
439  property &choice(
440  const std::string &name,
441  const std::vector<std::string> &choice)
442  {
443  myProperty.push_back(prop_t(new property(this, name, choice)));
444  CommonConstruction();
445  return *myProperty.back().get();
446  }
452  property &check(
453  const std::string &name,
454  bool f)
455  {
456  myProperty.push_back(prop_t(new property(this, name, f)));
457  CommonConstruction();
458  return *myProperty.back().get();
459  }
461  void category(
462  const std::string &name)
463  {
464  myProperty.push_back(prop_t(new property(this, name)));
465  CommonConstruction();
466  }
467 
468 #ifdef windex_has_boost
475  void add(boost::property_tree::ptree &pt)
476  {
477  // loop over categories
478  for (auto cat : pt)
479  {
480  category(cat.first);
481 
482  // loop over properties in category
483  for (auto prop : cat.second)
484  {
485  int type = pt.get<int>(cat.first + "." + prop.first + "." + "type", 0);
486 
487  switch (type)
488  {
489  case 1:
490  {
491  // choice property
492 
493  std::vector<std::string> vc;
494  for (auto ch : prop.second)
495  {
496  if (ch.first.find("choice") == 0)
497  {
498  vc.push_back(ch.second.data());
499  }
500  }
501  choice(prop.first, vc).value(pt.get<std::string>(cat.first + "." + prop.first + "." + "value"));
502  }
503  break;
504 
505  default:
506 
507  // string property
508 
509  string(
510  prop.first,
511  pt.get<std::string>(cat.first + "." + prop.first + "." + "value", "missing"));
512  break;
513  }
514  }
515  }
516  }
517 
519  boost::property_tree::ptree
520  BoostPropertyTree()
521  {
522  // ensure that values user sees are stored in value attributes
523  saveValues();
524 
525  boost::property_tree::ptree tree;
526  std::string catname;
527  for (auto p : myProperty)
528  {
529  if (p->isCategory())
530  {
531  catname = p->name() + ".";
532  }
533  else
534  {
535  p->BoostPropertyTree(tree, catname);
536  }
537  }
538  return tree;
539  }
540 
542  std::string json()
543  {
544  std::stringstream ss;
545  write_json(ss, BoostPropertyTree());
546  return ss.str();
547  }
548 
554  void addjson(const std::string &json)
555  {
556  std::cout << "->addjson\n";
557  std::stringstream ss(json);
558  boost::property_tree::ptree tree;
559  read_json(ss, tree);
560  std::cout << "<-read\n";
561  add(tree);
562  std::cout << "<-addjson\n";
563  }
564 
565 #endif
566 
568  void scroll()
569  {
570  myfScroll = true;
571  gui::scroll(false);
572  }
574  void expand(
575  const std::string name,
576  bool fexpand = true)
577  {
578  for (auto &p : myProperty)
579  {
580  if (p->name() == name &&
581  p->isCategory())
582  {
583  p->expand(fexpand);
584  visible();
585  return;
586  }
587  }
588  }
589  void expandAll(
590  bool fexpand = true)
591  {
592  for (auto &p : myProperty)
593  {
594  if (p->isCategory())
595  {
596  p->expand(fexpand);
597  }
598  }
599  visible();
600  }
601  void move(const std::vector<int> &r)
602  {
603  gui::move(r);
604  myWidth = r[2];
605  }
606  void labelWidth(int w)
607  {
608  myLabelWidth = w;
609  }
610 
612  void update()
613  {
614  for (auto &p : myProperty)
615  p->update();
616  gui::update();
617  }
618 
620  property *find(const std::string &name)
621  {
622  for (auto &p : myProperty)
623  {
624  //std::cout << "property::find " << name <<" "<< p.name() << "\n";
625  if (p->name() == name)
626  return p.get();
627  }
628  return nullptr;
629  }
630 
632  property *find(
633  const std::string &category,
634  const std::string &name)
635  {
636  for (auto p = myProperty.begin();
637  p != myProperty.end();
638  p++)
639  {
640  if ((*p)->isCategory())
641  {
642  if ((*p)->name() == category)
643  {
644  for (p++;
645  p != myProperty.end();
646  p++)
647  {
648  if ((*p)->isCategory())
649  return nullptr;
650  if ((*p)->name() == name)
651  return p->get();
652  }
653  }
654  }
655  }
656  return nullptr;
657  }
658 
660  const std::string value(const std::string &name)
661  {
662  //std::cout << "PG value " << name << "\n";
663  property *p = find(name);
664  if (!p)
665  {
666  static std::string null;
667  return null;
668  }
669  std::string v = p->value();
670  return p->value();
671  }
672 
673  bool isChecked(const std::string &name)
674  {
675  property *p = find(name);
676  if (!p)
677  return false;
678  return p->isChecked();
679  }
680 
682  void saveValues()
683  {
684  for (auto &p : myProperty)
685  {
686  p->saveValue();
687  }
688  }
689 
690  int propHeight() const
691  {
692  return myHeight;
693  }
694 
696  void propHeight(int h)
697  {
698  myHeight = h;
699  }
700 
702  void categoryHeightLow(bool f = true)
703  {
704  if (f)
705  myHeightCategory = 1;
706  else
707  myHeightCategory = 2;
708  }
709  int width() const
710  {
711  return myWidth;
712  }
713  int propCount() const
714  {
715  return (int)myProperty.size();
716  }
718  void change(std::function<void()> f)
719  {
720  onChange = f;
721  }
723  void nameClick(std::function<void(const std::string &)> f)
724  {
725  onName = f;
726  }
727 
733  void tabList(bool f = true)
734  {
735  // set flag so that any properties added later will have the tabstop style
736  myftabstop = f;
737 
738  // add tabstop style to existing properties
739  for (auto p : myProperty)
740  p->tabList(f);
741  }
742 
743  private:
744  std::vector<prop_t> myProperty; // the properties in the grid
745  int myHeight; // height of a single property
746  int myHeightCategory;
747  int myWidth; // width of grid
748  int myLabelWidth; // width of property labels
749  int myBGColor; // grid background color
750  bool myfScroll; // true if scrollbars used
751  bool myftabstop;
752  std::function<void()> onChange; // funtion to call when property has changed
753  std::function<void(const std::string &)> onName; // functio to call when property name is clicked
754 
755  void CommonConstruction()
756  {
757  prop_t P = myProperty.back();
758  P->labelWidth(myLabelWidth);
759  P->bgcolor(myBGColor);
760  if (myfScroll)
761  {
762  //std::cout << "pg scroll range " << ((int)myProperty.size()+1) * myHeight << "\n";
763  scrollRange(
764  myWidth,
765  ((int)myProperty.size() + 1) * myHeight);
766  }
767 
768  visible();
769 
770  P->change([this]
771  { onChange(); });
772 
773  if (myftabstop)
774  P->tabList();
775  }
776  void labelClick()
777  {
778  // search properties for the one whose label was clicked
779  for (auto P : myProperty)
780  {
781  if (P->labelClicked())
782  {
783 
784  // run regsitered event handler
785  onName(P->labeltext());
786 
787  // clear the clicked flag
788  P->labelClicked(false);
789  }
790  }
791  }
794  void visible()
795  {
796  // hide all the properties
797  for (auto P : myProperty)
798  P->show(false);
799 
800  // show properties in expanded categories
801  bool expanded = true;
802  int index = 0;
803  for (auto P : myProperty)
804  {
805  if (P->isCategory())
806  {
807  //std::cout << "cat " << P.name() << " at " << index << "\n";
808  P->move({0, index * myHeight, myWidth, myHeightCategory * myHeight}); // category always visible
809  P->show();
810  index += myHeightCategory; // display takes two rows
811  expanded = P->isExpanded(); // control visibility of contained properties
812  }
813  else if (expanded)
814  {
815  //std::cout << "show " << P.name() <<" at "<< index << "\n";
816  P->move({0, index * myHeight, myWidth, myHeight}); // property is visible
817  P->show();
818  index++; // displays in one row
819  }
820  else
821  {
822  //std::cout << "hide " << P.name() << "\n";
823  P->move({0, 0, 0, 0}); // invisible property
824  P->show(false);
825  }
826  }
827  if (myfScroll)
828  scrollRange(
829  myWidth,
830  index * myHeight);
831  update();
832  }
833  };
834 }
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
std::string text(int i)
get text by index
Definition: wex.h:2799
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(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 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 clickPropogate(bool f=true)
specify that click event should propogate to parent window after currently registered click event han...
Definition: wex.h:296
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
The base class for all windex gui elements.
Definition: wex.h:824
void update()
force widget to redraw completely
Definition: wex.h:1575
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 scrollRange(int width, int height)
Set the scrolling range.
Definition: wex.h:1135
eventhandler & events()
Get event handler.
Definition: wex.h:1649
void move(const std::vector< int > &r)
Move the window.
Definition: wex.h:1587
HWND handle()
get window handle
Definition: wex.h:1655
void enable(bool f=true)
Enable/Disable, default enable.
Definition: wex.h:963
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
gui()
Construct top level window with no parent.
Definition: wex.h:831
void scroll(bool fHoriz=true)
Add scrollbars.
Definition: wex.h:1044
A widget that displays a string.
Definition: wex.h:2583
A class for making windex objects.
Definition: wex.h:3237
A grid of properties.
Definition: propertygrid.h:396
void change(std::function< void()> f)
Register function to call when property value has changed.
Definition: propertygrid.h:718
void expand(const std::string name, bool fexpand=true)
Expand, or contract, category of properties.
Definition: propertygrid.h:574
property & check(const std::string &name, bool f)
Add boolean property.
Definition: propertygrid.h:452
void update()
force every property to redraw its label
Definition: propertygrid.h:612
property & string(const std::string &name, const std::string &value)
Add string property.
Definition: propertygrid.h:426
void saveValues()
save values in all property textboxes in the property's myValue attribute
Definition: propertygrid.h:682
property & choice(const std::string &name, const std::vector< std::string > &choice)
Add choice property.
Definition: propertygrid.h:439
void tabList(bool f=true)
Enable tab stepping through the properties.
Definition: propertygrid.h:733
void scroll()
Add vertical scrollbar.
Definition: propertygrid.h:568
const std::string value(const std::string &name)
get value in editbox of property with name
Definition: propertygrid.h:660
property * find(const std::string &category, const std::string &name)
get pointer to first property with name in a category
Definition: propertygrid.h:632
void category(const std::string &name)
Add categoty.
Definition: propertygrid.h:461
void propHeight(int h)
set property display height in pixels. Default 25
Definition: propertygrid.h:696
property * find(const std::string &name)
get pointer to first property with name, ignoring categories
Definition: propertygrid.h:620
void nameClick(std::function< void(const std::string &)> f)
Register function to call when property name is clicked.
Definition: propertygrid.h:723
void categoryHeightLow(bool f=true)
set category height low i.e. just one property height, rather than 2x prop height
Definition: propertygrid.h:702
A name value pair.
Definition: propertygrid.h:14
property(gui *parent, const std::string &name)
construct a category
Definition: propertygrid.h:68
void update()
Update ( redraw ) property child widgets.
Definition: propertygrid.h:216
void change(std::function< void()> f)
register function to call when property value changes
Definition: propertygrid.h:330
property & value(const std::string v)
set property value
Definition: propertygrid.h:254
property & tooltip(const std::string &tip, int width=0)
Add pop help message when mouse hovers over property label.
Definition: propertygrid.h:164
void saveValue()
copy value from gui into myValue attribute
Definition: propertygrid.h:291
~property()
destructor
Definition: propertygrid.h:80
property & readonly(bool f=true)
Set property to be readonly.
Definition: propertygrid.h:175
A class containing a database of the current gui elements.
Definition: wex.h:2940
static windex & get()
get reference to windex gui framework ( singleton )
Definition: wex.h:2950