aj3423

nana gui toy designer

Sep 13th, 2014
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 24.63 KB | None | 0 0
  1. #pragma warning(disable: 4018 4996)
  2. #include <nana/gui/wvl.hpp>
  3. #include <nana/gui/widgets/panel.hpp>
  4. #include <nana/gui/widgets/button.hpp>
  5. #include <nana/gui/widgets/label.hpp>
  6. #include <nana/gui/widgets/textbox.hpp>
  7. #include <nana/gui/widgets/menu.hpp>
  8.  
  9. #if defined(NANA_WINDOWS)
  10. #include <nana/detail/win32/platform_spec.hpp>
  11. #include <windows.h>
  12. // #elif defined(NANA_X11)
  13. // #include <nana/detail/linux_X11/platform_spec.hpp>
  14. #else
  15. #endif
  16.  
  17. #include <nana/paint/text_renderer.hpp>
  18. #include <nana/gui/detail/basic_window.hpp>
  19. #include <nana/gui/place.hpp>
  20. using namespace nana::gui;
  21.  
  22. typedef widget& widget_reference;
  23.  
  24. #include <iostream>
  25. #include <functional>
  26. #include <unordered_map>
  27. #include <sstream>
  28. #include <numeric>
  29. #include <memory>
  30. using namespace std;
  31. using namespace std::placeholders;
  32.  
  33.  
  34. template<typename T>
  35. T str_to(const std::string& str) {
  36.     T t;
  37.     std::stringstream ss(str);
  38.     ss >> t;
  39.     return t;
  40. }
  41. template <class T>
  42. std::string to_str(T value) {
  43.     std::stringstream ss;
  44.     ss << value;
  45.     return ss.str();
  46. }
  47.  
  48. template <class T>
  49. struct singleton {
  50.     static inline T& inst() {
  51.         static T instance;
  52.         return instance;
  53.     }
  54. };
  55.  
  56. struct widget_mgr : singleton<widget_mgr> { // keep tracking of widget and its children
  57.     typedef widget* child_t;
  58.     typedef widget* owner_t;
  59.     typedef vector<child_t> children_t;
  60.  
  61.     map<owner_t, children_t> children_map;
  62.     map<child_t, owner_t> owner_map;
  63.  
  64.     static int id() {
  65.         static int _id = 0;
  66.         return _id++;
  67.     }
  68.     children_t& children(owner_t owner) { // get the children
  69.         return children_map[owner];
  70.     }
  71.     owner_t owner(child_t w) { // get the owner
  72.         return owner_map[w];
  73.     }
  74.     void register_(owner_t owner, child_t ch) {
  75.         children_map[owner].push_back(ch);
  76.         owner_map[ch] = owner;
  77.     }
  78.     void unregister(widget* w) {
  79.         auto ch = children_map.find(w);
  80.         if(ch != children_map.end()) { // remove it from children_map
  81.             children_map.erase(ch);
  82.         }
  83.         auto owner = owner_map.find(w);
  84.         if(owner != owner_map.end()) {
  85.             auto& children = children_map[owner->second]; // remove it from its owner's children
  86.             children.erase(std::find(children.begin(), children.end(), w));
  87.  
  88.             owner_map.erase(owner); // remove it from owner_map
  89.         }
  90.     }
  91.     // apply 'callback' to the 'root' and its children recursively
  92.     void cascade(widget* root, function<void(widget*)> callback) {
  93.         callback(root);
  94.         children_t& ch = children_map[root];
  95.         for_each(ch.begin(), ch.end(), bind(&widget_mgr::cascade, this, _1, callback));
  96.     }
  97. };
  98.  
  99. struct layout_mgr : singleton<layout_mgr> {// keep tracking of created 'place layout'
  100.     vector<place*> v_place;
  101.     place* new_(widget* owner) {
  102.         place* p = new place(*owner);
  103.         register_(p);
  104.         return p;
  105.     }
  106.     void register_(place* p) {
  107.         v_place.push_back(p);
  108.     }
  109.     void clear() {
  110.         v_place.clear();
  111.     }
  112. };
  113.  
  114.  
  115. typedef map<string, string> property_map;
  116.  
  117. struct property_form : public panel<true> { // the left side property widget
  118.     typedef property_form self_t;
  119.     typedef shared_ptr<widget> widget_ptr;
  120.     typedef pair<widget_ptr, widget_ptr> widget_pair;
  121.  
  122.     shared_ptr<place> plc;
  123.     shared_ptr<label> tip;
  124.     unordered_map<string, widget_pair> widgets;
  125.  
  126.     function<void(property_map&)> committor;
  127.  
  128.     property_form(window wd, const nana::rectangle& rc = nana::rectangle())
  129.         : panel(wd, rc)
  130.     {
  131.         background(0xffffff);
  132.         plc.reset(new place(*this));
  133.         tip.reset(new label(*this));
  134.         tip->transparent(true);
  135.         tip->format(true);
  136.     }
  137.  
  138.     void append(const string& prop_name, const string& tip_str = "") {
  139.         label* lbl = new label(*this);
  140.         lbl->transparent(true);
  141.         lbl->text_align(nana::align::center, nana::align_v::center);
  142.        
  143.         lbl->caption(nana::charset(prop_name));
  144.         textbox* tbx = new textbox(*this);
  145.         tbx->multi_lines(false);
  146.         tbx->make_event<events::key_char>(bind(&self_t::on_char_event, this, _1));
  147.         tbx->make_event<events::focus>(bind(&self_t::on_focus, this, tip_str));
  148.         widgets[prop_name] = widget_pair(widget_ptr(lbl), widget_ptr(tbx));
  149.     }
  150.  
  151.     void collocate() {
  152.         // prepare the div string
  153.         {
  154.             int i = 0;
  155.             stringstream ss;
  156.             ss << "<vertical margin=[5,2] gap=2 ";
  157.             for_each(widgets.begin(),
  158.                      widgets.end(),
  159.                      [&](const pair<string, widget_pair>& p)
  160.             {
  161.                 char bf[128];
  162.                 sprintf(bf, "<weight=26 <label%d weight=60> <edit%d>>", i, i);
  163.                 ss << bf;
  164.  
  165.                 i++;
  166.             });
  167.             ss << "<margin=5 tip>>";
  168.             plc->div(ss.str().c_str());
  169.         }
  170.         // fill fields with label-edit-pair
  171.         {
  172.             int i = 0;
  173.             for_each(widgets.begin(),
  174.                      widgets.end(),
  175.                      [&](const pair<string, widget_pair>& p)
  176.             {
  177.                 stringstream ss_lbl;
  178.                 ss_lbl << "label" << i;
  179.                 stringstream ss_edt;
  180.                 ss_edt << "edit" << i;
  181.  
  182.                 plc->field(ss_lbl.str().c_str()) << *p.second.first;
  183.                 plc->field(ss_edt.str().c_str()) << *p.second.second;
  184.  
  185.                 i++;
  186.             });
  187.         }
  188.         // fill tip field
  189.         {
  190.             plc->field("tip") << *tip;
  191.         }
  192.         plc->collocate();
  193.     }
  194.     void on_char_event(const eventinfo& e) {
  195.         if(e.keyboard.key == nana::gui::keyboard::enter) {
  196.             if(committor) {
  197.                 property_map pmap = get();
  198.                 committor(pmap);
  199.             }
  200.         }
  201.     }
  202.     void on_focus(const string& tip_str) {
  203.         tip->caption(nana::charset(tip_str));
  204.     }
  205.     // apply 'callback' to all properties
  206.     void walk_through(const function<void(pair<string, widget_pair>)>& callback) {
  207.         for_each(widgets.begin(), widgets.end(), callback);
  208.     }
  209.  
  210.     void clear() {
  211.         walk_through([](pair<string, widget_pair>& pr) {
  212.             pr.second.second.get()->caption(L"");
  213.         });
  214.     }
  215.     void set(const property_map& pmap) {
  216.         clear();
  217.         walk_through([&](pair<string, widget_pair>& pr) {
  218.             string name = pr.first;
  219.             if(pmap.find(name) != pmap.end() && pmap.at(name).length() > 0) {
  220.                 pr.second.second->caption(nana::charset(pmap.at(name)));
  221.             }
  222.         });
  223.     }
  224.     property_map get() {
  225.         property_map pmap;
  226.         walk_through([&](pair<string, widget_pair>& pr) {
  227.             string name = pr.first,
  228.                    text = nana::charset(pr.second.second->caption());
  229.             if(text.length() > 0) {
  230.                 pmap[name] = text;
  231.             }
  232.         });
  233.         return pmap;
  234.     }
  235.     void set_committor(function<void(property_map&)> cmt) {
  236.         committor = cmt;
  237.     }
  238. };
  239.  
  240.  
  241.  
  242.  
  243. namespace util {
  244. #define RESIZE_RADIUS 5
  245.     bool is_tl(const nana::rectangle& rc, const nana::point& p) {
  246.         return p.x <= RESIZE_RADIUS && p.y <= RESIZE_RADIUS;
  247.     }
  248.     bool is_top(const nana::rectangle& rc, const nana::point& p) {
  249.         return p.x > RESIZE_RADIUS && p.x < (rc.width - RESIZE_RADIUS) && p.y <= RESIZE_RADIUS;
  250.     }
  251.     bool is_tr(const nana::rectangle& rc, const nana::point& p) {
  252.         return p.x >= (rc.width - RESIZE_RADIUS) && p.y <= RESIZE_RADIUS;
  253.     }
  254.     bool is_left(const nana::rectangle& rc, const nana::point& p) {
  255.         return p.x <= RESIZE_RADIUS && p.y > RESIZE_RADIUS && p.y < (rc.height - RESIZE_RADIUS);
  256.     }
  257.     bool is_right(const nana::rectangle& rc, const nana::point& p) {
  258.         return p.x >= (rc.width - RESIZE_RADIUS) && p.y > RESIZE_RADIUS && p.y < (rc.height - RESIZE_RADIUS);
  259.     }
  260.     bool is_bl(const nana::rectangle& rc, const nana::point& p) {
  261.         return p.x <= RESIZE_RADIUS && p.y >= (rc.height - RESIZE_RADIUS);
  262.     }
  263.     bool is_bottom(const nana::rectangle& rc, const nana::point& p) {
  264.         return p.x > RESIZE_RADIUS && p.x < (rc.width - RESIZE_RADIUS) && p.y >= (rc.height - RESIZE_RADIUS);
  265.     }
  266.     bool is_br(const nana::rectangle& rc, const nana::point& p) {
  267.         return p.x >= (rc.width - RESIZE_RADIUS) && p.y >= (rc.height - RESIZE_RADIUS);
  268.     }
  269.     // near the border of widget
  270.     bool is_in_resize_rect(const nana::rectangle& rc, const nana::point& p) {
  271.         return p.x <= RESIZE_RADIUS || p.x >= (rc.width-RESIZE_RADIUS)
  272.             || p.y <= RESIZE_RADIUS || p.y >= (rc.height-RESIZE_RADIUS);
  273.     }
  274. }
  275.  
  276. struct mouse_event_handler { // base class for 'dragger' and 'resizer'
  277.     typedef mouse_event_handler self_t;
  278.  
  279.     widget* wd_ptr;
  280.  
  281.     void bind(widget_reference widget) {
  282.         using namespace std::placeholders;
  283.         wd_ptr = &widget;
  284.  
  285.         wd_ptr->make_event<events::mouse_down>(std::bind(&self_t::mouse_down, this, _1));
  286.         wd_ptr->make_event<events::mouse_up>(std::bind(&self_t::mouse_up, this, _1));
  287.         wd_ptr->make_event<events::mouse_move>(std::bind(&self_t::mouse_move, this, _1));
  288.     }
  289.     virtual void mouse_down(const eventinfo&) = 0;
  290.     virtual void mouse_up(const eventinfo&) = 0;
  291.     virtual void mouse_move(const eventinfo&) = 0;
  292. };
  293.  
  294. struct dragger : mouse_event_handler {
  295.     bool is_dragging;
  296.  
  297.     nana::point start_cursor_pos;
  298.     nana::point start_wd_pos;
  299.    
  300.     dragger() : is_dragging(false) {}
  301.  
  302.     void mouse_down(const eventinfo& e) {
  303.         int x = e.mouse.x,
  304.             y = e.mouse.y;
  305.         nana::rectangle rc(wd_ptr->size());
  306.  
  307.         // dragging, not resizing
  308.         if(x > RESIZE_RADIUS && x < rc.width-RESIZE_RADIUS
  309.            && y > RESIZE_RADIUS && y < rc.height-RESIZE_RADIUS)
  310.         {
  311.             // start dragging
  312.             start_cursor_pos = API::cursor_position();
  313.             start_wd_pos = wd_ptr->pos();
  314.             API::capture_window(*wd_ptr, true);
  315.             is_dragging = true;
  316.         }
  317.     }
  318.     void mouse_up(const eventinfo& e) {
  319.         if(is_dragging) {
  320.             nana::rectangle rc(wd_ptr->size());
  321.  
  322.             API::capture_window(*wd_ptr, false);
  323.             is_dragging = false;
  324.         }
  325.     }
  326.     void mouse_move(const eventinfo& e) {
  327.         if(is_dragging) {
  328.             nana::point ccp = API::cursor_position();
  329.             auto diff_x = ccp.x - start_cursor_pos.x,
  330.                  diff_y = ccp.y - start_cursor_pos.y;
  331.  
  332.             wd_ptr->move(start_wd_pos.x + diff_x, start_wd_pos.y + diff_y);
  333.         }
  334.     }
  335. };
  336.  
  337. struct resizer : mouse_event_handler {
  338.     bool is_resizing;
  339.     nana::point start_cursor_pos;
  340.     nana::point start_wd_pos;
  341.     nana::size start_size_wd;
  342.    
  343.     enum resize_direction {
  344.         tl,   top,    tr,
  345.         left, none,   right,
  346.         bl,   bottom, br
  347.     } dir;
  348.  
  349.     resizer() : is_resizing(false) {}
  350.  
  351.     void mouse_down(const eventinfo& e) {
  352.         using namespace util;
  353.  
  354.         nana::rectangle rc(wd_ptr->size());
  355.         nana::point pt(e.mouse.x, e.mouse.y);
  356.  
  357.         if(is_in_resize_rect(rc, pt)) {
  358.             dir = is_tl(rc, pt) ? tl
  359.                 : is_top(rc, pt) ? top
  360.                 : is_tr(rc, pt) ? tr
  361.                 : is_left(rc, pt) ? left
  362.                 : is_right(rc, pt) ? right
  363.                 : is_bl(rc, pt) ? bl
  364.                 : is_bottom(rc, pt) ? bottom
  365.                 : is_br(rc, pt) ? br : none;
  366.        
  367.             start_cursor_pos = API::cursor_position();
  368.             start_wd_pos = wd_ptr->pos();
  369.             start_size_wd = wd_ptr->size();
  370.             API::capture_window(*wd_ptr, true);
  371.             is_resizing = true;
  372.         }
  373.     }
  374.     void mouse_up(const eventinfo& e) {
  375.         if(is_resizing) {
  376.             API::capture_window(*wd_ptr, false);
  377.             is_resizing = false;
  378.         }
  379.     }
  380.     void mouse_move(const eventinfo& e) {
  381.         if(is_resizing) {
  382.             nana::point cp = API::cursor_position();
  383.             int diff_x = cp.x - start_cursor_pos.x,
  384.                 diff_y = cp.y - start_cursor_pos.y;
  385.  
  386.             int x = start_wd_pos.x,
  387.                 y = start_wd_pos.y,
  388.                 w = start_size_wd.width,
  389.                 h = start_size_wd.height;
  390.  
  391.             if(dir == tl) {
  392.                 x += diff_x; y += diff_y;
  393.                 w -= diff_x; h -= diff_y;
  394.             } else if(dir == top) {
  395.                 y += diff_y; h -= diff_y;
  396.             } else if(dir == tr) {
  397.                 y += diff_y;
  398.                 w += diff_x; h -= diff_y;
  399.             } else if(dir == left) {
  400.                 x += diff_x;
  401.                 w -= diff_x;
  402.             } else if(dir == right) {
  403.                 w += diff_x;
  404.             } else if(dir == bl) {
  405.                 x += diff_x;
  406.                 w -= diff_x; h += diff_y;
  407.             } else if(dir == bottom) {
  408.                 h += diff_y;
  409.             } else if(dir == br) {
  410.                 w += diff_x;h  += diff_y;
  411.             }
  412.  
  413.             wd_ptr->move(x, y);
  414.             wd_ptr->size(w, h);
  415.         }
  416.     }
  417. };
  418.  
  419. struct resizer_drawer { // the drawer_trigger for resizer
  420.     typedef resizer_drawer self_t;
  421.     typedef nana::paint::graphics& graph_reference;
  422.  
  423.     widget* wd_ptr;
  424.     nana::gui::cursor cursor_shape;
  425.  
  426.     resizer_drawer() {
  427.         cursor_shape = cursor::arrow;
  428.     }
  429.     void bind(widget_reference widget) {
  430.         wd_ptr = &widget;
  431.     }
  432.  
  433.     void mouse_move(graph_reference graph, const eventinfo& e) {
  434.         using namespace util;
  435.  
  436.         if(wd_ptr->focused()) {
  437.             nana::rectangle rc(graph.size());
  438.             nana::point pt(e.mouse.x, e.mouse.y);
  439.  
  440.             if(is_tl(rc, pt) || is_br(rc, pt)) {
  441.                 cursor_shape = cursor::size_top_left;
  442.             } else if(is_tr(rc, pt) || is_bl(rc, pt)) {
  443.                 cursor_shape = cursor::size_bottom_left;
  444.             } else if(is_left(rc, pt) || is_right(rc, pt)) {
  445.                 cursor_shape = cursor::size_we;
  446.             } else if(is_top(rc, pt) || is_bottom(rc, pt)) {
  447.                 cursor_shape = cursor::size_ns;
  448.             } else {
  449.                 cursor_shape = cursor::arrow;
  450.             }
  451.         } else {
  452.             cursor_shape = cursor::arrow;
  453.         }
  454.  
  455.         update_mouse_shape();
  456.     }
  457.  
  458.     void update_mouse_shape() {
  459.         wd_ptr->cursor(cursor_shape);
  460.     }
  461.  
  462.     void draw(graph_reference graph) {
  463.         if(wd_ptr->focused() && !graph.empty()) {
  464.             draw_dash_line(graph);
  465.             draw_8_dots(graph);
  466.         }
  467.         update_mouse_shape();
  468.     }
  469.     void draw_dash_line(graph_reference graph) {
  470.         window wd = wd_ptr->handle();
  471.  
  472.         nana::rectangle rc(graph.size());
  473.  
  474. #if defined(NANA_WINDOWS)
  475.         HDC hdc = graph.handle()->context;
  476.         RECT r = {rc.x, rc.y, rc.right(), rc.bottom()};
  477.         DrawFocusRect(hdc, &r);
  478. #else
  479.         graph.rectangle(rc, 0xff, false);
  480. #endif
  481.     }
  482.  
  483.     void draw_8_dots(graph_reference graph) {
  484.         nana::rectangle rc(graph.size());
  485. #define CLR 0xff // color blue
  486. #define W 5 // width
  487.         graph.rectangle(rc.x, rc.y, W, W, CLR, false); // top left
  488.         graph.rectangle((rc.width-W)/2, rc.y, W, W, CLR, false); // top center
  489.         graph.rectangle(rc.width-W, rc.y, W, W, CLR, false); // top right
  490.         graph.rectangle(0, (rc.height-W)/2, W, W, CLR, false); // left center
  491.         graph.rectangle(rc.width-W, (rc.height-W)/2, W, W, CLR, false); // right center
  492.         graph.rectangle(rc.x, rc.height-W, W, W, CLR, false); // bottom left
  493.         graph.rectangle((rc.width-W)/2, rc.height-W, W, W, CLR, false); // bottom center
  494.         graph.rectangle(rc.width-W, rc.height-W, W, W, CLR, false); // bottom right
  495.     }
  496.  
  497. };
  498.  
  499. // base class for 'div_panel' and 'my_widget' which provides 'property' functionality
  500. struct my_widget_base {
  501.     enum type_t {
  502.         wdgt, div
  503.     };
  504.     property_map prop;
  505.  
  506.     virtual void apply_property(property_map& pmap) {
  507.         widget* w = dynamic_cast<widget*>(this);
  508.         w->caption(nana::charset(pmap["name"]));
  509.         w->move(str_to<int>(pmap["x"]), str_to<int>(pmap["y"]));
  510.         w->size(str_to<int>(pmap["width"]), str_to<int>(pmap["height"]));
  511.  
  512.         this->prop = pmap;
  513.     }
  514. #define DIV_BEG "<"
  515. #define DIV_END ">"
  516.     std::string to_div_str_beg(bool self_property, bool children_property) {
  517.  
  518.         stringstream ss;
  519.  
  520.         widget* w = dynamic_cast<widget*>(this);
  521.         ss << DIV_BEG;
  522.         if(self_property) { // attributes for aligning itself in its parent
  523.             ss << " " << (string)nana::charset(w->caption());
  524.             if(prop.find("weight") != prop.end())
  525.                 ss << " weight=" << prop.at("weight");
  526.         }
  527.         if(children_property) { // attributes for aligning its children
  528.             if(prop.find("align") != prop.end())
  529.                 ss << " " << prop.at("align");
  530.             if(prop.find("gap") != prop.end())
  531.                 ss << " gap=" << prop.at("gap");
  532.             if(prop.find("margin") != prop.end())
  533.                 ss << " margin=" << prop.at("margin");
  534.             if(prop.find("grid") != prop.end())
  535.                 ss << " grid=" << prop.at("grid");
  536.         }
  537.  
  538.         return ss.str();
  539.     }
  540.     std::string to_div_str_end() {
  541.         return DIV_END;
  542.     }
  543.  
  544.     std::string children_div_str(bool recursive, bool self_property, bool children_property) {
  545.         vector<widget*>& ch = widget_mgr::inst().children(dynamic_cast<widget*>(this));
  546.         stringstream ss;
  547.         for_each(ch.begin(), ch.end(), [&](widget* c) {
  548.             my_widget_base* wb = dynamic_cast<my_widget_base*>(c);
  549.             ss << " " << wb->to_div_str_beg(self_property, children_property);
  550.             if(recursive)
  551.                 ss << wb->children_div_str(recursive, self_property, children_property);
  552.  
  553.             ss << wb->to_div_str_end();
  554.         });
  555.         return ss.str();
  556.     }
  557.  
  558.     void do_layout() {
  559.         widget* _this = dynamic_cast<widget*>(this);
  560.         vector<widget*>& children = widget_mgr::inst().children(_this);
  561.  
  562.         if(children.size() == 0)
  563.             return;
  564.  
  565.         place* plc = layout_mgr::inst().new_(_this);
  566.  
  567.         string div_str = to_div_str_beg(false, true)
  568.                         + children_div_str(false, true, false)
  569.                         + to_div_str_end();
  570.         plc->div(div_str.c_str());
  571.         cout << "apply_div for <" << (string)nana::charset(_this->caption())
  572.             << ">: " << div_str << endl;
  573.  
  574.  
  575.         // fill fields
  576.         for_each(children.begin(), children.end(), [&](widget* wgt) {
  577.             string name = (string)nana::charset(wgt->caption());
  578.             plc->field(name.c_str()) << *wgt;
  579.         });
  580.  
  581.         plc->collocate();
  582.     }
  583.    
  584.  
  585.     virtual type_t type() = 0;
  586. };
  587.  
  588. template <typename super_t>
  589. struct my_widget_trigger: public super_t {
  590.     widget* wd_ptr;
  591.  
  592.     typedef nana::paint::graphics& graph_reference;
  593.  
  594.     resizer_drawer rsz;
  595.    
  596.     struct attr_tag {
  597.         bool focused;
  598.     } attr_;
  599.  
  600.     my_widget_trigger() : super_t() {
  601.         attr_.focused = false;
  602.     }
  603.    
  604.     void attached(widget_reference widget, graph_reference graph) {
  605.         wd_ptr = &widget;
  606.         window wd = widget.handle();
  607.  
  608.         rsz.bind(widget);
  609.  
  610.         using namespace API::dev;
  611.         make_drawer_event<events::key_down>(wd);
  612.         make_drawer_event<events::focus>(wd);
  613.         make_drawer_event<events::mouse_move>(wd);
  614.     }
  615.  
  616.     void refresh(graph_reference graph) {
  617.         if(!graph.empty()) {
  618.             window wd = wd_ptr->handle();
  619.             draw_background(graph);
  620.                        
  621.             draw_text(graph);
  622.             rsz.draw(graph);
  623.             API::lazy_refresh();
  624.         }
  625.     }
  626.  
  627.     virtual void draw_background(graph_reference graph) {
  628.         nana::rectangle r(graph.size());
  629.         if(attr_.focused) {
  630.             r.pare_off(1); // for the dash line
  631.         }
  632.         nana::color_t color_start = nana::paint::graphics::mix(0x0000cc, 0xFFFFFF, 0.2);
  633.         nana::color_t color_end = nana::paint::graphics::mix(0x0000dc, 0x0, 0.95);
  634.  
  635.         graph.shadow_rectangle(r, color_start, color_end, true);
  636.     }
  637.    
  638.  
  639.     virtual void draw_text(graph_reference graph) {// code from button.cpp
  640.         nana::string text = wd_ptr->caption();
  641.  
  642.         nana::size ts = graph.text_extent_size(text);
  643.  
  644.         int x = (graph.width() - ts.width) >> 1;
  645.         int y = (graph.height() - ts.height) >> 1;
  646.  
  647.         if(ts.width) {
  648. #define fgcolor 0xFF
  649.             graph.string(x, y, fgcolor, text);
  650.         }
  651.     }
  652.    
  653.     // handle UP/DOWN/LEFT/RIGHT Key
  654.     void key_down(graph_reference graph, const eventinfo& e) {
  655. #define STEP 1
  656.         auto pt = wd_ptr->pos();
  657.  
  658.         switch(e.keyboard.key) {
  659.             case keyboard::os_arrow_up:
  660.                 wd_ptr->move(pt.x, pt.y - STEP);
  661.                 break;
  662.             case keyboard::os_arrow_down:
  663.                 wd_ptr->move(pt.x, pt.y + STEP);
  664.                 break;
  665.             case keyboard::os_arrow_left:
  666.                 wd_ptr->move(pt.x - STEP, pt.y);
  667.                 break;
  668.             case keyboard::os_arrow_right:
  669.                 wd_ptr->move(pt.x + STEP, pt.y);
  670.                 break;
  671.         }
  672.     }
  673.  
  674.     void focus(graph_reference graph, const eventinfo& e) {
  675.         attr_.focused = e.focus.getting;
  676.         refresh(graph);
  677.     }
  678.  
  679.     void mouse_move(graph_reference graph, const eventinfo& e) {
  680.         rsz.mouse_move(graph, e);
  681.     }
  682.  
  683. };
  684.  
  685. struct my_widget
  686.     : my_widget_base
  687.     , public widget_object<category::widget_tag, my_widget_trigger<drawer_trigger> >
  688. {
  689.     resizer rsz;
  690.     dragger dg;
  691.  
  692.     my_widget(widget& w, const nana::rectangle& rc = nana::rectangle(), const nana::string& text = L"") {
  693.         create(w, rc);
  694.         caption(text);
  695.  
  696.         dg.bind(*this);
  697.         rsz.bind(*this);
  698.     }
  699.     my_widget_base::type_t type() { return my_widget_base::wdgt; }
  700. };
  701.  
  702. class div_panel_trigger : public my_widget_trigger<drawer_trigger> {
  703.     void draw_background(graph_reference graph) {
  704.         nana::rectangle r(graph.size());
  705.         if(attr_.focused) {
  706.             r.pare_off(1); // for the dash line
  707.         }
  708.         nana::color_t color_start = nana::paint::graphics::mix(0xaacccc, 0xFFFFFF, 0.2);
  709.         nana::color_t color_end = nana::paint::graphics::mix(0xbbdddd, 0x0, 0.95);
  710.  
  711.         graph.shadow_rectangle(r, color_start, color_end, true);
  712.     }
  713.     virtual void draw_text(graph_reference graph) {
  714.         // hide the name for div_panel
  715.     }
  716. };
  717.  
  718. struct div_panel
  719.     : my_widget_base
  720.     , public widget_object<category::widget_tag, div_panel_trigger >
  721. {
  722.     typedef div_panel self_t;
  723.  
  724.     resizer rsz;
  725.     dragger dg;
  726.  
  727.     div_panel(widget& w, const nana::rectangle& rc = nana::rectangle(), const nana::string& text = L"") {
  728.         create(w, rc);
  729.         caption(text);
  730.  
  731.         dg.bind(*this);
  732.         rsz.bind(*this);
  733.     }
  734.     my_widget_base::type_t type() { return my_widget_base::div; }
  735. };
  736.  
  737. // the main class
  738. struct design_app : public form {
  739.     typedef design_app self_t;
  740.  
  741.     shared_ptr<div_panel> designer; // the design area
  742.  
  743.     shared_ptr<textbox> console; // the console
  744.     shared_ptr<property_form> prop_form; // the left side property_form
  745.  
  746.     shared_ptr<place> plc;
  747.  
  748.     shared_ptr<menu> mnu;
  749.     nana::point menu_point; // the right clicking point, create widget there
  750.  
  751.     widget* focused_widget;
  752.  
  753.     design_app(const nana::rectangle& rc) : form(rc), focused_widget(NULL)
  754.     {
  755.         background(0xffffff);
  756.  
  757.         mnu.reset(new menu);
  758.         mnu->append(L"new div", bind(&self_t::on_new_div_click, this, placeholders::_1));
  759.         mnu->append(L"new element", bind(&self_t::on_new_widget_click, this, placeholders::_1));
  760.         mnu->append_splitter();
  761.         mnu->append(L"delete", bind(&self_t::on_delete_click, this, placeholders::_1));
  762.  
  763.  
  764.         // designer panel
  765.         designer.reset(new div_panel(*this, nana::rectangle(), L"designer"));
  766.         bind_events_to_newly_created(designer.get());
  767.    
  768.         console.reset(new textbox(*this));
  769.         console->line_wrapped(true);
  770.  
  771.         prop_form.reset(new property_form(*this));
  772.         prop_form->set_committor(bind(&self_t::on_commit, this, placeholders::_1));
  773.         prop_form->append("name");
  774.         prop_form->append("x");
  775.         prop_form->append("y");
  776.         prop_form->append("width");
  777.         prop_form->append("height");
  778.         prop_form->append("weight", "eg:\nweight=200\nweight=30%");
  779.         prop_form->append("gap", "eg:\ngap=5");
  780.         prop_form->append("margin", "eg, margin =\n"
  781.                           "\n10 (all sides)"
  782.                           "\n[top]"
  783.                           "\n[vertcal,horizontal]"
  784.                           "\n[top,right,bottom]"
  785.                           "\n[top,right,bottom,right]"
  786.                           "\n<color=0x888888>margin can be percent:</>"
  787.                           "\n[10%,20%] or 15%"
  788.                           );
  789.         prop_form->append("align", "can be \"vertical\"");
  790.         prop_form->append("grid", "eg:\ngrid=[columns, rows]\n<color=0x888888>plc.room(x, columns, rows)</>\n");
  791.         prop_form->collocate();
  792.        
  793.         plc.reset(new place(*this));
  794.  
  795.         plc->div("< <vertical margin=5 weight=180 <prop_form> > <vertical <design> <weight=150 console>> >");
  796.  
  797.         plc->field("prop_form") << *prop_form;
  798.  
  799.         plc->field("design") << *designer;
  800.         plc->field("console") << *console;
  801.  
  802.         plc->collocate();
  803.     }
  804.  
  805.     void set_focused_widget(widget* w) {
  806.         focused_widget = w;
  807.     }
  808.     void set_menu_point(const eventinfo& e) {
  809.         menu_point = nana::point(e.mouse.x, e.mouse.y);
  810.     }
  811.  
  812.     void bind_events_to_newly_created(widget* widget) {
  813.         // save the focused widget
  814.         widget->make_event<events::focus>(std::bind(&self_t::set_focused_widget, this, widget));
  815.         // menu
  816.         widget->make_event<events::click>(std::bind(&self_t::set_menu_point, this, placeholders::_1));
  817.         widget->make_event<events::click>(menu_popuper(*mnu));
  818.         // display properties on the left panel
  819.         widget->make_event<events::click>(std::bind(&self_t::display_property, this, widget)); // for click/dragging
  820.         widget->make_event<events::focus>(std::bind(&self_t::display_property, this, widget)); // for manually call widget->focus()
  821.     }
  822.  
  823.     void display_property(widget* w) {
  824.         property_map pmap = dynamic_cast<my_widget_base*>(w)->prop;
  825.         pmap["x"] = to_str(w->pos().x);
  826.         pmap["y"] = to_str(w->pos().y);
  827.         pmap["width"] = to_str(w->size().width);
  828.         pmap["height"] = to_str(w->size().height);
  829.         pmap["name"] = nana::charset(w->caption());
  830.  
  831.         prop_form->set(pmap);
  832.     }
  833.  
  834.     void on_commit(property_map& pmap) {
  835.         // save the modified property to widget*
  836.         my_widget_base* w = dynamic_cast<my_widget_base*>(focused_widget);
  837.         if(focused_widget) {
  838.             w->apply_property(pmap);
  839.         }
  840.         // display the div str in console
  841.         string div_str = designer->to_div_str_beg(true, true)
  842.                         + designer->children_div_str(true, true, true)
  843.                         + designer->to_div_str_end();
  844.         console->caption(nana::charset(div_str));
  845.  
  846.         try {
  847.             do_layout();
  848.         } catch(const runtime_error& e) {
  849.             console->caption(nana::charset(e.what()));
  850.         }
  851.     }
  852.     void do_layout() {
  853.         layout_mgr::inst().clear(); // delete all the previous places
  854.  
  855.         widget_mgr::inst().cascade(designer.get(), [](widget* w) {
  856.             my_widget_base* wb = dynamic_cast<my_widget_base*>(w);
  857.             wb->do_layout();
  858.         });
  859.     }
  860.  
  861.     void on_new_div_click(const menu::item_proxy& ip) { // create a new div
  862.         nana::string div_name = nana::charset(string("div_") + to_str(widget_mgr::id()));
  863.         widget* w = new div_panel(*focused_widget, nana::rectangle(menu_point, nana::size(100, 40)), div_name);
  864.         bind_events_to_newly_created(w);
  865.         widget_mgr::inst().register_(focused_widget, w);
  866.         w->focus();
  867.     }
  868.     void on_new_widget_click(const menu::item_proxy& ip) { // create a new widget
  869.         nana::string widget_name = nana::charset(string("widget_") + to_str(widget_mgr::id());
  870.         widget* w = new my_widget(*focused_widget, nana::rectangle(menu_point, nana::size(100, 40)), widget_name);
  871.         bind_events_to_newly_created(w);
  872.         widget_mgr::inst().register_(focused_widget, w);
  873.         w->focus();
  874.     }
  875.     void on_delete_click(const menu::item_proxy& ip) { // delete a div/widget
  876.         if(focused_widget == designer.get())
  877.             return;
  878.         widget_mgr::inst().unregister(focused_widget);
  879.         delete focused_widget;
  880.         focused_widget = NULL;
  881.     }
  882. };
  883.  
  884. int main() {
  885.     design_app app(API::make_center(600, 480));
  886.     app.show();
  887.     exec();
  888. }
Advertisement
Add Comment
Please, Sign In to add comment