From 658ab3fb7fc572231763aa3440beb67d5c600270 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Aug 2015 04:09:35 +0800 Subject: [PATCH] new docker-place branch --- include/nana/gui/element.hpp | 101 +- include/nana/gui/place.hpp | 16 +- include/nana/gui/programming_interface.hpp | 2 +- source/gui/dragger.cpp | 2 +- source/gui/element.cpp | 250 +++- source/gui/place.cpp | 1326 +++++++++++++------- source/gui/programming_interface.cpp | 4 +- source/gui/widgets/widget.cpp | 2 +- 8 files changed, 1210 insertions(+), 493 deletions(-) diff --git a/include/nana/gui/element.hpp b/include/nana/gui/element.hpp index ee63ecf0..9ffa721d 100644 --- a/include/nana/gui/element.hpp +++ b/include/nana/gui/element.hpp @@ -98,25 +98,31 @@ namespace nana } }; - void add_crook(const std::string& name, const pat::cloneable>&); - crook_interface* const * keeper_crook(const std::string& name); + void add_arrow(const std::string&, const pat::cloneable>&); + arrow_interface* const * cite_arrow(const std::string&); void add_border(const std::string&, const pat::cloneable>&); - border_interface* const * keeper_border(const std::string&); - - void add_arrow(const std::string&, const pat::cloneable>&); - arrow_interface* const * keeper_arrow(const std::string&); + border_interface* const * cite_border(const std::string&); void add_button(const std::string&, const pat::cloneable>&); - element_interface* const* keeper_button(const std::string&); + element_interface* const* cite_button(const std::string&); + + void add_x_icon(const std::string& name, const pat::cloneable>&); + element_interface* const* cite_x_icon(const std::string&); + + void add_crook(const std::string& name, const pat::cloneable>&); + crook_interface* const * cite_crook(const std::string& name); + + void add_cross(const std::string& name, const pat::cloneable>&); + element_interface* const* cite_cross(const std::string&); }; - class crook; - template - void add_crook(const std::string& name) + class arrow; + template + void add_arrow(const std::string& name) { - using factory_t = provider::factory; - provider().add_crook(name, pat::cloneable(factory_t())); + using factory_t = provider::factory; + provider().add_arrow(name, pat::cloneable(factory_t())); } class border; @@ -127,14 +133,6 @@ namespace nana provider().add_border(name, pat::cloneable(factory_t())); } - class arrow; - template - void add_arrow(const std::string& name) - { - using factory_t = provider::factory; - provider().add_arrow(name, pat::cloneable(factory_t())); - } - class button; template void add_button(const std::string& name) @@ -142,6 +140,30 @@ namespace nana using factory_t = provider::factory; provider().add_button(name, pat::cloneable(factory_t())); } + + class x_icon; + template + void add_x_icon(const std::string& name) + { + using factory_t = provider::factory; + provider().add_x_icon(name, pat::cloneable(factory_t())); + } + + class crook; + template + void add_crook(const std::string& name) + { + using factory_t = provider::factory; + provider().add_crook(name, pat::cloneable(factory_t())); + } + + class cross; + template + void add_cross(const std::string& name) + { + using factory_t = provider::factory; + provider().add_cross(name, pat::cloneable(factory_t())); + } }//end namespace element template class facade; @@ -169,9 +191,27 @@ namespace nana bool draw(graph_reference, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state) override; private: element::crook_interface::data data_; - element::crook_interface* const * keeper_; + element::crook_interface* const * cite_; }; //end class facade + template<> class facade + : public element::element_interface + { + public: + facade(const char* name = nullptr); + void switch_to(const char*); + + void thickness(unsigned thk); + void size(unsigned size_pixels); + public: + //Implement element_interface + bool draw(graph_reference, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle&, element_state) override; + private: + unsigned thickness_{6}; + unsigned size_{ 14 }; + element::element_interface* const * cite_; + }; + template<> class facade : public element::element_interface @@ -185,7 +225,7 @@ namespace nana //Implement element_interface bool draw(graph_reference, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle&, element_state) override; private: - element::border_interface* const * keeper_; + element::border_interface* const * cite_; };//end class facade template<> @@ -207,7 +247,7 @@ namespace nana //Implement element_interface bool draw(graph_reference, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle&, element_state) override; private: - element::arrow_interface* const * keeper_; + element::arrow_interface* const * cite_; ::nana::direction dir_{::nana::direction::north}; };//end class facade @@ -222,9 +262,22 @@ namespace nana //Implement element_interface bool draw(graph_reference, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle&, element_state) override; private: - element::element_interface* const * keeper_; + element::element_interface* const * cite_; };//end class facade + template<> + class facade + : public element::element_interface + { + public: + facade(const char* name = nullptr); + void switch_to(const char*); + public: + //Implement element_interface + bool draw(graph_reference, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle&, element_state) override; + private: + element::element_interface* const * cite_; + };//end class facade namespace element { diff --git a/include/nana/gui/place.hpp b/include/nana/gui/place.hpp index a7e7e0ea..8faedf91 100644 --- a/include/nana/gui/place.hpp +++ b/include/nana/gui/place.hpp @@ -109,9 +109,11 @@ namespace nana window window_handle() const; void div(const char* s); ///< Divides the attached widget into fields. + void modify(const char* field_name, const char* div_text); ///< Modifies a specified field. + field_reference field(const char* name);///< Returns a field with the specified name. - void field_visible(const char* filed_name, bool visible); ///< + void dock(const std::string& dockname, Args&& ... args) + { + dock(dockname, std::bind([](window parent, Args && ... args) + { + return std::unique_ptr(new Panel(parent, std::forward(args)...)); + }, std::placeholders::_1, std::forward(args)...)); + } + + void dock(const std::string& dockname, std::function(window)> factory); private: implement * impl_; }; diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 167fd8d8..bfbcd138 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -191,7 +191,7 @@ namespace API } point window_position(window); - void move_window(window, int x, int y); + void move_window(window, const point&); void move_window(window wd, const rectangle&); void bring_top(window, bool activated); diff --git a/source/gui/dragger.cpp b/source/gui/dragger.cpp index dac2c6c8..5bb82baf 100644 --- a/source/gui/dragger.cpp +++ b/source/gui/dragger.cpp @@ -164,7 +164,7 @@ namespace nana if (!t.restrict_area.empty()) _m_check_restrict_area(wdps, API::window_size(t.wd), t.restrict_area); - API::move_window(t.wd, wdps.x, wdps.y); + API::move_window(t.wd, wdps); } } } diff --git a/source/gui/element.cpp b/source/gui/element.cpp index 1852a554..cbccebed 100644 --- a/source/gui/element.cpp +++ b/source/gui/element.cpp @@ -141,7 +141,7 @@ namespace nana } else { - ::nana::color highlighted(static_cast(0x5eb6f7)); + ::nana::color highlighted(0x5e, 0xb6, 0xf7); auto bld_bgcolor = bgcolor; auto bld_fgcolor = fgcolor; switch(es) @@ -156,7 +156,7 @@ namespace nana bld_fgcolor = fgcolor.blend(highlighted, 0.4); break; case element_state::disabled: - bld_bgcolor = bld_fgcolor.from_rgb(0xb2, 0xb7, 0xbc); + bld_bgcolor = bld_fgcolor = nana::color(0xb2, 0xb7, 0xbc); break; default: //Leave things as they are @@ -264,7 +264,7 @@ namespace nana bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, unsigned weight) { graph.rectangle(r, false, static_cast((element_state::focus_hovered == estate || element_state::focus_normal == estate) ? 0x0595E2 : 0x999A9E)); - graph.rectangle(::nana::rectangle(r).pare_off(1), false, bgcolor); + graph.rectangle(::nana::rectangle{r}.pare_off(1), false, bgcolor); return true; } }; @@ -529,6 +529,63 @@ namespace nana return true; } };//end class annex_button + + class x_icon + : public element_interface + { + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const rectangle& r, element_state estate) override + { + auto clr = fgcolor; + + switch (estate) + { + case element_state::hovered: + case element_state::pressed: + clr = clr.blend(colors::black, 0.8); + break; + case element_state::disabled: + clr = colors::dark_gray; + default: + break; + } + + graph.set_color(clr); + + const int x = r.x + 4; + const int y = r.y + 4; + + point p1{ x, y }, p2{ x + 7, y + 7 }; + + graph.line(p1, p2); + + ++p1.x; + --p2.y; + graph.line(p1, p2); + + p1.x = x; + ++p1.y; + p2.x = x + 6; + p2.y = y + 7; + graph.line(p1, p2); + + p1.x += 7; + p1.y = y; + p2.x = x; + graph.line(p1, p2); + + p1.x = x + 6; + p2.y = y + 6; + graph.line(p1, p2); + + ++p1.x; + ++p1.y; + ++p2.x; + ++p2.y; + graph.line(p1, p2); + + return true; + } + }; }//end namespace element template @@ -567,7 +624,7 @@ namespace nana spare_.emplace_back(keep_e, keep_f); } - element_t * const * keeper() const + element_t * const * cite() const { return &element_ptr_; } @@ -580,6 +637,9 @@ namespace nana class element_manager : nana::noncopyable, nana::nonmovable { + //VC2012 does not support alias declaration. + //template using factory_interface = element::provider::factory_interface; + template struct item { @@ -613,6 +673,8 @@ namespace nana element::add_arrow("hollow_triangle"); element::add_button(""); //"annex" in default + + element::add_x_icon(""); } return obj; } @@ -624,7 +686,17 @@ namespace nana element::crook_interface * const * crook(const std::string& name) const { - return _m_get(name, crook_).keeper(); + return _m_get(name, crook_).cite(); + } + + void cross(const std::string& name, const pat::cloneable>& factory) + { + _m_add(name, cross_, factory); + } + + element::element_interface* const * cross(const std::string& name) const + { + return _m_get(name, cross_).cite(); } void border(const std::string& name, const pat::cloneable>& factory) @@ -634,7 +706,7 @@ namespace nana element::border_interface * const * border(const std::string& name) const { - return _m_get(name, border_).keeper(); + return _m_get(name, border_).cite(); } void arrow(const std::string& name, const pat::cloneable>& factory) @@ -644,7 +716,7 @@ namespace nana element::arrow_interface * const * arrow(const std::string& name) const { - return _m_get((name.empty() ? "arrowhead" : name), arrow_).keeper(); + return _m_get((name.empty() ? "arrowhead" : name), arrow_).cite(); } void button(const std::string& name, const pat::cloneable>& factory) @@ -654,7 +726,17 @@ namespace nana element::element_interface * const * button(const std::string& name) const { - return _m_get((name.empty() ? "annex" : name), button_).keeper(); + return _m_get((name.empty() ? "annex" : name), button_).cite(); + } + + void x_icon(const std::string& name, const pat::cloneable>& factory) + { + _m_add(name, x_icon_, factory); + } + + element::element_interface * const * x_icon(const std::string& name) const + { + return _m_get(name, x_icon_).cite(); } private: using lock_guard = std::lock_guard; @@ -689,9 +771,11 @@ namespace nana private: mutable std::recursive_mutex mutex_; item crook_; + item cross_; item border_; item arrow_; item button_; + item x_icon_; }; namespace element @@ -702,17 +786,27 @@ namespace nana element_manager::instance().crook(name, factory); } - crook_interface* const * provider::keeper_crook(const std::string& name) + crook_interface* const * provider::cite_crook(const std::string& name) { return element_manager::instance().crook(name); } + void provider::add_cross(const std::string& name, const pat::cloneable>& factory) + { + element_manager::instance().cross(name, factory); + } + + element_interface* const* provider::cite_cross(const std::string& name) + { + return element_manager::instance().cross(name); + } + void provider::add_border(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().border(name, factory); } - border_interface* const * provider::keeper_border(const std::string& name) + border_interface* const * provider::cite_border(const std::string& name) { return element_manager::instance().border(name); } @@ -722,7 +816,7 @@ namespace nana element_manager::instance().arrow(name, factory); } - arrow_interface* const * provider::keeper_arrow(const std::string& name) + arrow_interface* const * provider::cite_arrow(const std::string& name) { return element_manager::instance().arrow(name); } @@ -732,16 +826,26 @@ namespace nana element_manager::instance().button(name, factory); } - element_interface* const* provider::keeper_button(const std::string& name) + element_interface* const* provider::cite_button(const std::string& name) { return element_manager::instance().button(name); } + + void provider::add_x_icon(const std::string& name, const pat::cloneable>& factory) + { + element_manager::instance().x_icon(name, factory); + } + + element_interface* const* provider::cite_x_icon(const std::string& name) + { + return element_manager::instance().x_icon(name); + } }//end namespace element //facades //template<> class facade facade::facade(const char* name) - : keeper_(element::provider().keeper_crook(name ? name : "")) + : cite_(element::provider().cite_crook(name ? name : "")) { data_.check_state = state::unchecked; data_.radio = false; @@ -777,40 +881,120 @@ namespace nana void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_crook(name ? name : ""); + cite_ = element::provider().cite_crook(name ? name : ""); } bool facade::draw(graph_reference graph, const ::nana::color& bgcol, const ::nana::color& fgcol, const nana::rectangle& r, element_state es) { - return (*keeper_)->draw(graph, bgcol, fgcol, r, es, data_); + return (*cite_)->draw(graph, bgcol, fgcol, r, es, data_); } //end class facade + //class facade + facade::facade(const char* name) + : cite_(element::provider().cite_cross(name ? name : "")) + { + + } + + void facade::switch_to(const char* name) + { + cite_ = element::provider().cite_cross(name ? name : ""); + } + + void facade::thickness(unsigned thk) + { + thickness_ = thk; + } + + void facade::size(unsigned size_px) + { + size_ = size_px; + } + + //Implement element_interface + bool facade::draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state) + { + if (thickness_ + 2 <= size_) + { + int gap = (static_cast(size_) - static_cast(thickness_)) / 2; + + nana::point ps[12]; + ps[0].x = r.x + gap; + ps[1].x = ps[0].x + static_cast(thickness_) - 1; + ps[1].y = ps[0].y = r.y; + + ps[2].x = ps[1].x; + ps[2].y = r.y + gap; + + ps[3].x = ps[2].x + gap; + ps[3].y = ps[2].y; + + ps[4].x = ps[3].x; + ps[4].y = ps[3].y + static_cast(thickness_)-1; + + ps[5].x = ps[1].x; + ps[5].y = ps[4].y; + + ps[6].x = ps[5].x; + ps[6].y = ps[5].y + gap; + + ps[7].x = r.x + gap; + ps[7].y = ps[6].y; + + ps[8].x = ps[7].x; + ps[8].y = ps[4].y; + + ps[9].x = r.x; + ps[9].y = ps[4].y; + + ps[10].x = r.x; + ps[10].y = r.y + gap; + + ps[11].x = r.x + gap; + ps[11].y = r.y + gap; + + graph.set_color(fgcolor.blend(colors::black, true)); + + for (int i = 0; i < 11; ++i) + graph.line(ps[i], ps[i + 1]); + graph.line(ps[11], ps[0]); + + graph.set_color(fgcolor); + + unsigned thk_minus_2 = thickness_ - 2; + graph.rectangle(rectangle{ ps[10].x + 1, ps[10].y + 1, (gap << 1) + thk_minus_2, thk_minus_2 }, true); + graph.rectangle(rectangle{ ps[0].x + 1, ps[0].y + 1, thk_minus_2, (gap << 1) + thk_minus_2 }, true); + } + return true; + } + //end class facade + //class facade facade::facade(const char* name) - : keeper_(element::provider().keeper_border(name ? name : "")) + : cite_(element::provider().cite_border(name ? name : "")) {} void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_border(name ? name : ""); + cite_ = element::provider().cite_border(name ? name : ""); } bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state es) { - return (*keeper_)->draw(graph, bgcolor, fgcolor, r, es, 2); + return (*cite_)->draw(graph, bgcolor, fgcolor, r, es, 2); } //end class facade //class facade facade::facade(const char* name) - : keeper_(element::provider().keeper_arrow(name ? name : "")) + : cite_(element::provider().cite_arrow(name ? name : "")) { } void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_arrow(name ? name : ""); + cite_ = element::provider().cite_arrow(name ? name : ""); } void facade::direction(::nana::direction dir) @@ -822,27 +1006,45 @@ namespace nana bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state estate) { graph.set_color(fgcolor); - return (*keeper_)->draw(graph, bgcolor, fgcolor, r, estate, dir_); + return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate, dir_); } //end class facade //class facade:: facade::facade(const char* name) - : keeper_(element::provider().keeper_button(name ? name : "")) + : cite_(element::provider().cite_button(name ? name : "")) {} void facade::switch_to(const char* name) { - keeper_ = element::provider().keeper_button(name ? name : ""); + cite_ = element::provider().cite_button(name ? name : ""); } //Implement element_interface bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate) { - return (*keeper_)->draw(graph, bgcolor, fgcolor, r, estate); + return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate); } //end class facade + + //class facade + facade::facade(const char* name) + : cite_(element::provider().cite_x_icon(name ? name : "")) + {} + + void facade::switch_to(const char* name) + { + cite_ = element::provider().cite_x_icon(name ? name : ""); + } + + //Implement element_interface + bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate) + { + return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate); + } + //end class facade + namespace element { void set_bground(const char* name, const pat::cloneable& obj) diff --git a/source/gui/place.cpp b/source/gui/place.cpp index f57df5e5..7a6325ef 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,4 +1,4 @@ -/** +/* * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) @@ -8,333 +8,35 @@ * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/place.cpp + * @contributors: qPCR4vir */ -#include + #include #include -#include -#include +#include +#include #include #include #include +#include #include +#include #include -#include -#include +#include //numeric_limits + +#include "place_parts.hpp" namespace nana { namespace place_parts { + //check the name void check_field_name(const char* name) { - //check the name if (*name && (*name != '_' && !(('a' <= *name && *name <= 'z') || ('A' <= *name && *name <= 'Z')))) - throw std::invalid_argument("place.field: bad field name"); + throw std::invalid_argument("nana.place: bad field name"); } - - class splitter_interface - { - public: - virtual ~splitter_interface(){} - }; - - class splitter_dtrigger - : public drawer_trigger - { - }; - - template - class splitter - : public widget_object ::type, splitter_dtrigger>, - public splitter_interface - { - }; - - //number_t is used for storing a number type variable - //such as integer, real and percent. Essentially, percent is a typo of real. - class number_t - { - public: - enum class kind{ none, integer, real, percent }; - - number_t() - : kind_(kind::none) - { - value_.integer = 0; - } - - void reset() - { - kind_ = kind::none; - value_.integer = 0; - } - - bool is_negative() const - { - switch (kind_) - { - case kind::integer: - return (value_.integer < 0); - case kind::real: - case kind::percent: - return (value_.real < 0); - default: - break; - } - return false; - } - - bool is_none() const - { - return (kind::none == kind_); - } - - bool is_not_none() const - { - return (kind::none != kind_); - } - - kind kind_of() const - { - return kind_; - } - - double get_value(int ref_percent) const - { - switch (kind_) - { - case kind::integer: - return value_.integer; - case kind::real: - return value_.real; - case kind::percent: - return value_.real * ref_percent; - default: - break; - } - return 0; - } - - int integer() const - { - if (kind::integer == kind_) - return value_.integer; - return static_cast(value_.real); - } - - double real() const - { - if (kind::integer == kind_) - return value_.integer; - return value_.real; - } - - void assign(int i) - { - kind_ = kind::integer; - value_.integer = i; - } - - void assign(double d) - { - kind_ = kind::real; - value_.real = d; - } - - void assign_percent(double d) - { - kind_ = kind::percent; - value_.real = d / 100; - } - private: - kind kind_; - union valueset - { - int integer; - double real; - }value_; - };//end class number_t - - class margin - { - public: - margin& operator=(margin&& rhs) - { - if (this != &rhs) - { - all_edges_ = rhs.all_edges_; - margins_ = std::move(rhs.margins_); - } - return *this; - } - - void clear() - { - all_edges_ = true; - margins_.clear(); - } - - void push(const number_t& v) - { - margins_.emplace_back(v); - } - - void set_value(const number_t& v) - { - clear(); - margins_.emplace_back(v); - } - - void set_array(const std::vector& v) - { - all_edges_ = false; - margins_ = v; - } - - nana::rectangle area(const ::nana::rectangle& field_area) const - { - if (margins_.empty()) - return field_area; - - auto r = field_area; - if (all_edges_) - { - auto px = static_cast(margins_.back().get_value(static_cast(r.width))); - const auto dbl_px = static_cast(px << 1); - r.x += px; - r.width = (r.width < dbl_px ? 0 : r.width - dbl_px); - - r.y += px; - r.height = (r.height < dbl_px ? 0 : r.height - dbl_px); - } - else - { - int il{ -1 }, ir{ -1 }, it{ -1 }, ib{ -1 }; //index of four corners in margin - switch (margins_.size()) - { - case 0: break; - case 1: //top - it = 0; - break; - case 2://top,bottom and left,right - it = ib = 0; - il = ir = 1; - break; - default: - il = 3; //left - case 3: //top, right, bottom - it = 0; - ir = 1; - ib = 2; - } - - typedef decltype(r.height) px_type; - auto calc = [](px_type a, px_type b) - { - return (a > b ? a - b : 0); - }; - - if (0 == it) //top - { - auto px = static_cast(margins_[it].get_value(static_cast(field_area.height))); - r.y += px; - r.height = calc(r.height, static_cast(px)); - } - - if (-1 != ib) //bottom - { - auto px = static_cast(margins_[ib].get_value(static_cast(field_area.height))); - r.height = calc(r.height, static_cast(px)); - } - - if (-1 != il) //left - { - auto px = static_cast(margins_[il].get_value(static_cast(field_area.width))); - r.x += px; - r.width = calc(r.width, static_cast(px)); - } - - if (-1 != ir) //right - { - auto px = static_cast(margins_[ir].get_value(static_cast(field_area.width))); - r.width = calc(r.width, static_cast(px)); - } - } - return r; - } - private: - bool all_edges_ = true; - std::vector margins_; - };//end class margin - - class repeated_array - { - public: - - //A workaround for VC2013, becuase it does not generated an implicit declared move-constructor as defaulted. - repeated_array() = default; - - repeated_array(repeated_array && other) - : repeated_{other.repeated_}, - values_(std::move(other.values_)) - { - } - - repeated_array& operator=(repeated_array&& other) - { - if(this != &other) - { - repeated_ = other.repeated_; - other.repeated_ = false; - values_ = std::move(other.values_); - } - return *this; - } - - void assign(std::vector&& c) - { - values_ = std::move(c); - } - - bool empty() const - { - return values_.empty(); - } - - void reset() - { - repeated_ = false; - values_.clear(); - } - - void repeated() - { - repeated_ = true; - } - - void push(const number_t& n) - { - values_.emplace_back(n); - } - - number_t at(std::size_t pos) const - { - if (values_.empty()) - return{}; - - if (repeated_) - pos %= values_.size(); - else if (pos >= values_.size()) - return{}; - - return values_[pos]; - } - private: - bool repeated_ = false; - std::vector values_; - }; }//end namespace place_parts typedef place_parts::number_t number_t; @@ -348,8 +50,8 @@ namespace nana enum class token { div_start, div_end, splitter, - identifier, vert, grid, number, array, reparray, - weight, gap, margin, arrange, variable, repeated, min_px, max_px, + identifier, dock, vert, grid, number, array, reparray, + weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, collapse, parameters, equal, eof, error @@ -384,6 +86,16 @@ namespace nana return parameters_; } + std::size_t pos() const + { + return (sp_ - divstr_); + } + + std::string pos_str() const + { + return std::to_string(pos()); + } + token read() { sp_ = _m_eat_whitespace(sp_); @@ -415,46 +127,46 @@ namespace nana ++sp_; return token::array; } - - { - //When search the repeated. - bool repeated = false; - - while (true) + else { - sp_ = _m_eat_whitespace(sp_); - auto tk = read(); - if (token::number != tk && token::variable != tk && token::repeated != tk) - _m_throw_error("invalid array element"); + //When search the repeated. + bool repeated = false; - if (!repeated) + while (true) { - switch (tk) + sp_ = _m_eat_whitespace(sp_); + auto tk = read(); + if (token::number != tk && token::variable != tk && token::repeated != tk) + _m_throw_error("invalid array element"); + + if (!repeated) { - case token::number: - array_.push_back(number_); - break; - case token::variable: - array_.push_back({}); - break; - default: - repeated = true; - reparray_.repeated(); - reparray_.assign(std::move(array_)); + switch (tk) + { + case token::number: + array_.push_back(number_); + break; + case token::variable: + array_.push_back({}); + break; + default: + repeated = true; + reparray_.repeated(); + reparray_.assign(std::move(array_)); + } } + + sp_ = _m_eat_whitespace(sp_); + char ch = *sp_++; + + if (ch == ']') + return (repeated ? token::reparray : token::array); + + if (ch != ',') + _m_throw_error("invalid array"); } - - sp_ = _m_eat_whitespace(sp_); - char ch = *sp_++; - - if (ch == ']') - return (repeated ? token::reparray : token::array); - - if (ch != ',') - _m_throw_error("invalid array"); } - } - break; + break; case '(': parameters_.clear(); sp_ = _m_eat_whitespace(sp_ + 1); @@ -497,7 +209,7 @@ namespace nana return token::number; } else - _m_throw_error(*sp_); + _m_throw_error("invalid character '" + std::string(1, *sp_) + "'"); break; default: if ('0' <= *sp_ && *sp_ <= '9') @@ -532,6 +244,8 @@ namespace nana case 'a': return token::max_px; } } + else if ("dock" == idstr_) + return token::dock; else if ("vertical" == idstr_ || "vert" == idstr_) return token::vert; else if ("variable" == idstr_ || "repeated" == idstr_) @@ -556,10 +270,24 @@ namespace nana _m_throw_error("a parameter list is required after 'collapse'"); return token::collapse; } + else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_) + { + switch (idstr_.front()) + { + case 'l': return token::left; + case 'r': return token::right; + case 't': return token::top; + case 'b': return token::bottom; + } + } return token::identifier; } - _m_throw_error(*sp_); + std::string err = "an invalid character '"; + err += *sp_; + err += "'"; + + _m_throw_error(err); return token::error; //Useless, just for syntax correction. } private: @@ -570,7 +298,7 @@ namespace nana str += '\''; _m_throw_error(str); } - + void _m_throw_error(const std::string& err) { std::stringstream ss; @@ -583,7 +311,7 @@ namespace nana if (token::equal != read()) _m_throw_error("an equal sign is required after '" + idstr_ + "'"); - const char* p = _m_eat_whitespace(sp_); + auto p = _m_eat_whitespace(sp_); auto neg_ptr = p; if ('-' == *p) @@ -696,29 +424,44 @@ namespace nana }; //end class tokenizer } - //struct implement struct place::implement { - class field_impl; + class field_gather; + class field_dock; + class division; class div_arrange; class div_grid; class div_splitter; + class div_dock; + class div_dockpane; window window_handle{nullptr}; event_handle event_size_handle{nullptr}; std::unique_ptr root_division; - std::map fields; + std::map fields; + std::map docks; + + //A temporary pointer used to refer to a specified div object which + //will be deleted in modification process. + std::unique_ptr tmp_replaced; //The following functions are defined behind the definition of class division. //because the class division here is an incomplete type. ~implement(); + + void collocate(); + static division * search_div_name(division* start, const std::string&); std::unique_ptr scan_div(place_parts::tokenizer&); + void check_unique(const division*) const; + + //connect the field/dock with div object + void connect(division* start); }; //end struct implement - class place::implement::field_impl + class place::implement::field_gather : public place::field_interface { public: @@ -732,11 +475,11 @@ namespace nana {} }; - field_impl(place * p) + field_gather(place * p) : place_ptr_(p) {} - ~field_impl() + ~field_gather() { for (auto & e : elements) API::umake_event(e.evt_destroy); @@ -778,7 +521,7 @@ namespace nana { return API::events(wd).destroy.connect([this, wd](const arg_destroy& arg) { - if (erase_element(elements, arg.window_handle)) + for (auto i = elements.begin(), end = elements.end(); i != end; ++i) { if (!API::is_destroying(API::get_parent_window(wd))) place_ptr_->collocate(); @@ -840,12 +583,22 @@ namespace nana std::vector fastened; private: place * place_ptr_; - };//end class field_impl + };//end class field_gather + + class place::implement::field_dock + { + + public: + div_dockpane * attached{ nullptr }; //attached div object + place_parts::dockarea dockarea; //the dockable widget + };//end class field_dock + + class place::implement::division { public: - enum class kind{ arrange, vertical_arrange, grid, splitter }; + enum class kind{ arrange, vertical_arrange, grid, splitter, dock, dockpane}; division(kind k, std::string&& n) : kind_of_division(k), @@ -879,6 +632,50 @@ namespace nana _m_visible_for_child(this, dsp); visible = dsp; display = dsp; + + if (kind::splitter != kind_of_division) + { + //Don't display the previous one, if it is a splitter + auto div = previous(); + if (div && (kind::splitter == div->kind_of_division)) + { + if (dsp) + { + auto leaf = div->previous(); + if (leaf && leaf->display) + div->set_display(true); + } + else + div->set_display(false); + } + + //Don't display the next one, if it is a splitter + if (div_next && (kind::splitter == div_next->kind_of_division)) + { + if (dsp) + { + auto leaf = div_next->div_next; + if (leaf && leaf->display) + div_next->set_display(true); + } + else + div_next->set_display(false); + } + } + else + { + //This is a splitter, it only checks when it is being displayed + if (dsp) + { + auto left = this->previous(); + if (left && !left->display) + left->set_display(true); + + auto right = div_next; + if (right && !right->display) + right->set_display(true); + } + } } bool is_back(const division* div) const @@ -924,6 +721,18 @@ namespace nana { return margin.area(field_area); } + + division * previous() const + { + if (div_owner) + { + for (auto & child : div_owner->children) + if (child->div_next == this) + return child.get(); + } + return nullptr; + + } public: void _m_visible_for_child(division * div, bool vsb) { @@ -943,21 +752,22 @@ namespace nana kind kind_of_division; bool display{ true }; bool visible{ true }; - const std::string name; + ::nana::direction dir{::nana::direction::west}; + std::string name; std::vector> children; - nana::rectangle field_area; + ::nana::rectangle field_area; number_t weight; number_t min_px, max_px; place_parts::margin margin; place_parts::repeated_array gap; - field_impl * field; + field_gather * field; division * div_next, *div_owner; };//end class division template - void place::implement::field_impl::_m_for_each(division * div, Function fn) + void place::implement::field_gather::_m_for_each(division * div, Function fn) { for (auto & up : div->children) //The element of children is unique_ptr fn(up.get()); @@ -1503,23 +1313,28 @@ namespace nana class place::implement::div_splitter : public division { + struct div_block + { + division * div; + int pixels; + double scale; + + div_block(division* d, int px) + : div(d), pixels(px) + {} + }; + enum{splitter_px = 4}; public: div_splitter(place_parts::number_t init_weight) : division(kind::splitter, std::string()), splitter_cursor_(cursor::arrow), - leaf_left_(nullptr), leaf_right_(nullptr), pause_move_collocate_(false), init_weight_(init_weight) { this->weight.assign(splitter_px); } - void set_leaf(bool is_left, division * d) - { - (is_left ? leaf_left_ : leaf_right_) = d; - } - void direction(bool horizontal) { splitter_cursor_ = (horizontal ? cursor::size_we : cursor::size_ns); @@ -1546,8 +1361,8 @@ namespace nana auto px_ptr = &nana::rectangle::width; //Use field_area of leaf, not margin_area. Otherwise splitter would be at wrong position - auto area_left = leaf_left_->field_area; - auto area_right = leaf_right_->field_area; + auto area_left = _m_leaf_left()->field_area; + auto area_right = _m_leaf_right()->field_area; if (nana::cursor::size_we != splitter_cursor_) { @@ -1582,9 +1397,10 @@ namespace nana else if (left_px < 0) left_px = 0; + auto leaf_left = _m_leaf_left(); double imd_rate = 100.0 / area_px; - left_px = static_cast(limit_px(leaf_left_, left_px, area_px)); - leaf_left_->weight.assign_percent(imd_rate * left_px); + left_px = static_cast(limit_px(_m_leaf_left(), left_px, area_px)); + _m_leaf_left()->weight.assign_percent(imd_rate * left_px); auto right_px = static_cast(right_pixels_)-delta; if (right_px > total_pixels) @@ -1592,15 +1408,15 @@ namespace nana else if (right_px < 0) right_px = 0; - right_px = static_cast(limit_px(leaf_right_, right_px, area_px)); - leaf_right_->weight.assign_percent(imd_rate * right_px); + right_px = static_cast(limit_px(_m_leaf_right(), right_px, area_px)); + _m_leaf_right()->weight.assign_percent(imd_rate * right_px); pause_move_collocate_ = true; div_owner->collocate(splitter_.parent()); //After the collocating, the splitter keeps the calculated weight of left division, //and clear the weight of right division. - leaf_right_->weight.reset(); + _m_leaf_right()->weight.reset(); pause_move_collocate_ = false; }); @@ -1612,10 +1428,12 @@ namespace nana { const bool vert = (::nana::cursor::size_we != splitter_cursor_); - rectangle_rotator left(vert, leaf_left_->field_area); - rectangle_rotator right(vert, leaf_right_->field_area); + auto leaf_left = _m_leaf_left(); + auto leaf_right = _m_leaf_right(); + rectangle_rotator left(vert, leaf_left->field_area); + rectangle_rotator right(vert, leaf_right->field_area); auto area_px = right.right() - left.x(); - auto right_px = static_cast(limit_px(leaf_right_, init_weight_.get_value(area_px), static_cast(area_px))); + auto right_px = static_cast(limit_px(leaf_right, init_weight_.get_value(area_px), static_cast(area_px))); auto pos = area_px - right_px - splitter_px; //New position of splitter if (pos < limited_range.x()) @@ -1623,7 +1441,7 @@ namespace nana else if (pos > limited_range.right()) pos = limited_range.right(); - nana::rectangle_rotator sp_r(vert, field_area); + rectangle_rotator sp_r(vert, field_area); sp_r.x_ref() = pos; left.w_ref() = static_cast(pos - left.x()); @@ -1633,17 +1451,17 @@ namespace nana right.w_ref() = static_cast(right_pos - pos - splitter_px); field_area = sp_r.result(); - leaf_left_->field_area = left.result(); - leaf_right_->field_area = right.result(); - leaf_left_->collocate(wd); - leaf_right_->collocate(wd); + leaf_left->field_area = left.result(); + leaf_right->field_area = right.result(); + leaf_left->collocate(wd); + leaf_right->collocate(wd); //Set the leafs' weight rectangle_rotator area(vert, div_owner->field_area); double imd_rate = 100.0 / static_cast(area.w()); - leaf_left_->weight.assign_percent(imd_rate * static_cast(left.w())); - leaf_right_->weight.assign_percent(imd_rate * static_cast(right.w())); + leaf_left->weight.assign_percent(imd_rate * static_cast(left.w())); + leaf_right->weight.assign_percent(imd_rate * static_cast(right.w())); splitter_.move(this->field_area); @@ -1654,40 +1472,53 @@ namespace nana splitter_.move(this->field_area); } private: + division * _m_leaf_left() const + { + return previous(); + } + + division * _m_leaf_right() const + { + return div_next; + } + rectangle_rotator _m_update_splitter_range() { const bool vert = (cursor::size_ns == splitter_cursor_); rectangle_rotator area(vert, div_owner->margin_area()); - rectangle_rotator left(vert, leaf_left_->field_area); - rectangle_rotator right(vert, leaf_right_->field_area); + auto leaf_left = _m_leaf_left(); + auto leaf_right = _m_leaf_right(); + + rectangle_rotator left(vert, leaf_left->field_area); + rectangle_rotator right(vert, leaf_right->field_area); const int left_base = left.x(), right_base = right.right(); int pos = left_base; int endpos = right_base; - if (leaf_left_->min_px.is_not_none()) + if (leaf_left->min_px.is_not_none()) { - auto v = leaf_left_->min_px.get_value(area.w()); + auto v = leaf_left->min_px.get_value(area.w()); pos += static_cast(v); } - if (leaf_left_->max_px.is_not_none()) + if (leaf_left->max_px.is_not_none()) { - auto v = leaf_left_->max_px.get_value(area.w()); + auto v = leaf_left->max_px.get_value(area.w()); endpos = left_base + static_cast(v); } - if (leaf_right_->min_px.is_not_none()) + if (leaf_right->min_px.is_not_none()) { - auto v = leaf_right_->min_px.get_value(area.w()); + auto v = leaf_right->min_px.get_value(area.w()); auto x = right_base - static_cast(v); if (x < endpos) endpos = x; } - if (leaf_right_->max_px.is_not_none()) + if (leaf_right->max_px.is_not_none()) { - auto v = leaf_right_->max_px.get_value(area.w()); + auto v = leaf_right->max_px.get_value(area.w()); auto x = right_base - static_cast(v); if (x > pos) pos = x; @@ -1702,10 +1533,9 @@ namespace nana } private: nana::cursor splitter_cursor_; + bool created_{ false }; place_parts::splitter splitter_; nana::point begin_point_; - division * leaf_left_; - division * leaf_right_; int left_pos_, right_pos_; unsigned left_pixels_, right_pixels_; dragger dragger_; @@ -1713,12 +1543,372 @@ namespace nana place_parts::number_t init_weight_; }; + class place::implement::div_dockpane + : public division, public place_parts::dock_notifier_interface + { + public: + div_dockpane(std::string && name, implement* impl, direction pane_dir) + : division(kind::dockpane, std::move(name)), + impl_ptr_{impl} + { + dir = pane_dir; + } + + ~div_dockpane() + { + if (dockable_field) + { + dockable_field->dockarea.close(); + dockable_field->attached = nullptr; + } + } + + void collocate(window wd) override + { + if (!dockable_field) + { + // + if (name.empty()) + return; + + auto &dock_ptr = impl_ptr_->docks[name]; + if (!dock_ptr) + dock_ptr = new field_dock; + + dock_ptr->attached = this; + dockable_field = dock_ptr; + } + + auto & dockarea = dockable_field->dockarea; + if (!created_) + { + created_ = true; + dockarea.create(wd, this); + } + + + if (!dockarea.empty() && !dockarea.floating()) + { + dockarea.move(this->field_area); + indicator_.r = this->field_area; + } + } + private: + //Implement dock_notifier_interface + void notify_float() override + { + set_display(false); + + impl_ptr_->collocate(); + } + + void notify_dock() override + { + indicator_.docker.reset(); + set_display(true); + impl_ptr_->collocate(); + } + + void notify_move() override + { + if (!_m_indicator()) + { + indicator_.docker.reset(); + return; + } + + if (!indicator_.docker) + { + auto host_size = API::window_size(impl_ptr_->window_handle); + indicator_.docker.reset(new form(impl_ptr_->window_handle, { static_cast(host_size.width) / 2 - 16, static_cast(host_size.height) / 2 - 16, 32, 32 }, form::appear::bald<>())); + drawing dw(indicator_.docker->handle()); + dw.draw([](paint::graphics& graph) + { + graph.rectangle(false, colors::midnight_blue); + graph.rectangle({ 1, 1, 30, 30 }, true, colors::light_sky_blue); + + facade arrow; + arrow.direction(::nana::direction::south); + arrow.draw(graph, colors::light_sky_blue, colors::midnight_blue, { 12, 0, 16, 16 }, element_state::normal); + + rectangle r{ 4, 16, 24, 11 }; + graph.rectangle(r, true, colors::midnight_blue); + + r.x = 5; + r.y = 19; + r.width = 22; + r.height = 7; + graph.rectangle(r, true, colors::button_face); + }); + + indicator_.docker->z_order(nullptr, ::nana::z_order_action::topmost); + indicator_.docker->show(); + + indicator_.docker->events().destroy([this] + { + if (indicator_.dock_area) + { + indicator_.dock_area.reset(); + indicator_.graph.release(); + } + }); + } + + if (_m_dockable()) + { + if (!indicator_.dock_area) + { + indicator_.graph.make(API::window_size(impl_ptr_->window_handle)); + API::window_graphics(impl_ptr_->window_handle, indicator_.graph); + + indicator_.dock_area.reset(new panel(impl_ptr_->window_handle, false)); + indicator_.dock_area->move(indicator_.r); + + ::nana::drawing dw(indicator_.dock_area->handle()); + dw.draw([this](paint::graphics& graph) + { + indicator_.graph.paste(indicator_.r, graph, 0, 0); + + const int border_px = 4; + rectangle r{ graph.size() }; + int right = r.right(); + int bottom = r.bottom(); + + graph.blend(r.pare_off(border_px), colors::blue, 0.3); + + ::nana::color clr = colors::deep_sky_blue; + r.y = 0; + r.height = border_px; + graph.blend(r, clr, 0.5); + r.y = bottom - border_px; + graph.blend(r, clr, 0.5); + + r.x = r.y = 0; + r = graph.size(); + r.width = border_px; + graph.blend(r, clr, 0.5); + r.x = right - border_px; + graph.blend(r, clr, 0.5); + + }); + + //indicator_.dock_area->z_order(nullptr, ::nana::z_order_action::top); //deprecated + API::bring_top(indicator_.dock_area->handle(), false); + indicator_.dock_area->show(); + } + } + else + { + if (indicator_.dock_area) + { + indicator_.dock_area.reset(); + indicator_.graph.release(); + } + } + + } + + void notify_move_stopped() + { + if (_m_dockable() && dockable_field) + dockable_field->dockarea.dock(); + + indicator_.docker.reset(); + } + private: + bool _m_indicator() const + { + ::nana::point pos; + API::calc_screen_point(impl_ptr_->window_handle, pos); + + rectangle r{ pos, API::window_size(impl_ptr_->window_handle) }; + return r.is_hit(API::cursor_position()); + } + + bool _m_dockable() const + { + if (!indicator_.docker) + return false; + + ::nana::point pos; + API::calc_screen_point(indicator_.docker->handle(), pos); + + rectangle r{ pos, API::window_size(indicator_.docker->handle()) }; + return r.is_hit(API::cursor_position()); + } + public: + field_dock * dockable_field{ nullptr }; + private: + implement * impl_ptr_; + bool created_{ false }; + + struct indicator_tag + { + paint::graphics graph; + //panel widget; + rectangle r; + std::unique_ptr> dock_area; + std::unique_ptr
docker; + }indicator_; + }; + + class place::implement::div_dock + : public division + { + public: + div_dock(std::string && name, implement* impl) + : division(kind::dock, std::move(name)) + {} + + void collocate(window wd) override + { + auto area = this->margin_area(); + + unsigned vert_count = 0, horz_count = 0; + unsigned vert_weight = 0, horz_weight = 0; + + bool prev_attr = _m_is_vert(children.front()->dir); + (prev_attr ? horz_count : vert_count) = 1; + + for (auto & child : children) + { + auto is_vert = _m_is_vert(child->dir); + + if (child->weight.is_not_none()) + { + if (is_vert) + vert_weight += static_cast(child->weight.get_value(area.height)); + else + horz_weight += static_cast(child->weight.get_value(area.width)); + } + + if (is_vert == prev_attr) + { + if (is_vert) + ++vert_count; + else + ++horz_count; + } + else + { + if (is_vert) + ++horz_count; + else + ++vert_count; + } + prev_attr = is_vert; + } + if (0 == vert_count) + ++vert_count; + if (0 == horz_count) + ++horz_count; + + double auto_horz_w = double(area.width - horz_weight) / horz_count; + double auto_vert_w = double(area.height - vert_weight) / vert_count; + + double left = area.x; + double right = area.right(); + double top = area.y; + double bottom = area.bottom(); + + for (auto & child : children) + { + double weight; + if (child->weight.is_not_none()) + weight = child->weight.get_value(_m_is_vert(child->dir) ? area.height : area.height); + else + weight = (_m_is_vert(child->dir) ? auto_vert_w : auto_horz_w); + + ::nana::rectangle child_r; + switch (child->dir) + { + default: + case ::nana::direction::west: + child_r.x = static_cast(left); + child_r.y = static_cast(top); + child_r.width = static_cast(weight); + child_r.height = static_cast(bottom - top); + left += weight; + break; + case ::nana::direction::east: + right -= weight; + child_r.x = static_cast(right); + child_r.y = static_cast(top); + child_r.width = static_cast(weight); + child_r.height = static_cast(bottom - top); + break; + case ::nana::direction::north: + child_r.x = static_cast(left); + child_r.y = static_cast(top); + child_r.width = static_cast(right - left); + child_r.height = static_cast(weight); + top += weight; + break; + case ::nana::direction::south: + child_r.x = static_cast(left); + bottom -= weight; + child_r.y = static_cast(bottom); + child_r.width = static_cast(right - left); + child_r.height = static_cast(weight); + break; + } + + child->field_area = child_r; + child->collocate(wd); + } + } + private: + static bool _m_is_vert(::nana::direction dir) + { + return (dir == ::nana::direction::north || dir == ::nana::direction::south); + } + }; + place::implement::~implement() { API::umake_event(event_size_handle); root_division.reset(); + for (auto & pair : fields) delete pair.second; + + for (auto & dock : docks) + delete dock.second; + } + + void place::implement::collocate() + { + if (root_division && window_handle) + { + root_division->field_area = API::window_size(window_handle); + + if (root_division->field_area.empty()) + return; + + root_division->collocate(window_handle); + + for (auto & field : fields) + { + bool is_show = false; + if (field.second->attached && field.second->attached->visible && field.second->attached->display) + { + is_show = true; + auto div = field.second->attached->div_owner; + while (div) + { + if (!div->visible || !div->display) + { + is_show = false; + break; + } + div = div->div_owner; + } + } + + for (auto & el : field.second->elements) + API::show_window(el.handle, is_show); + } + } } //search_div_name @@ -1755,18 +1945,24 @@ namespace nana std::vector array; std::vector collapses; std::vector> children; + ::nana::direction div_dir = ::nana::direction::west; for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read()) { bool exit_for = false; switch (tk) { + case token::dock: + if (token::eof != div_type && token::dock != div_type) + throw std::invalid_argument("nana.place: conflict of div type at " + tknizer.pos_str()); + + div_type = token::dock; + break; case token::splitter: //Ignore the splitter when there is not a division. if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division)) { auto splitter = new div_splitter(tknizer.number()); - splitter->set_leaf(true, children.back().get()); children.back()->div_next = splitter; children.emplace_back(splitter); } @@ -1774,12 +1970,9 @@ namespace nana case token::div_start: { auto div = scan_div(tknizer); - if (children.size()) - { + if (!children.empty()) children.back()->div_next = div.get(); - if (division::kind::splitter == children.back()->kind_of_division) - dynamic_cast(*children.back()).set_leaf(false, div.get()); - } + children.emplace_back(div.release()); } break; @@ -1815,7 +2008,7 @@ namespace nana else if (arg.kind_of() == number_t::kind::real) return static_cast(arg.real()); - throw std::runtime_error("place: the type of the "+ nth +" parameter for collapse should be integer."); + throw std::invalid_argument("nana.place: the type of the "+ nth +" parameter for collapse should be integer."); }; ::nana::rectangle col; @@ -1851,7 +2044,7 @@ namespace nana } } else - throw std::runtime_error("place: collapse requires 4 parameters."); + throw std::invalid_argument("nana.place: collapse requires 4 parameters."); break; case token::weight: case token::min_px: case token::max_px: { @@ -1904,13 +2097,22 @@ namespace nana case token::identifier: name = tknizer.idstr(); break; + case token::left: + div_dir = ::nana::direction::west; break; + case token::right: + div_dir = ::nana::direction::east; break; + case token::top: + div_dir = ::nana::direction::north; break; + case token::bottom: + div_dir = ::nana::direction::south; break; default: break; } if (exit_for) break; } - field_impl * attached_field = nullptr; + field_gather * attached_field = nullptr; + field_dock* attached_dock = nullptr; if (name.size()) { //find the field with specified name. @@ -1951,8 +2153,16 @@ namespace nana div = std::move(p); } break; + case token::dock: + //deprecated + // + //if (name.empty()) + // throw std::invalid_argument("nana.place: dock must have a name."); + + div.reset(new div_dock(std::move(name), this)); + break; default: - throw std::runtime_error("nana.place: invalid division type."); + throw std::invalid_argument("nana.place: invalid division type."); } //Requirements for min/max @@ -1999,13 +2209,123 @@ namespace nana if (division::kind::splitter == child->kind_of_division) dynamic_cast(*child).direction(div_type != token::vert); } + + if (token::dock == div_type) + { + //adjust these children for dock division + std::vector> adjusted_children; + for (auto & child : children) + { + adjusted_children.emplace_back(new div_dockpane(std::move(child->name), this, child->dir)); + } + + children.swap(adjusted_children); + } } div->children.swap(children); div->margin = std::move(margin); + div->dir = div_dir; return div; } + void place::implement::check_unique(const division* div) const + { + //The second field_impl is useless. Reuse the map type in order to + //reduce the size of the generated code, becuase std::set + //will create a new template class. + std::map unique; + field_gather tmp(nullptr); + + std::function check_fn; + check_fn = [&check_fn, &unique, &tmp](const implement::division* div) + { + if (!div->name.empty()) + { + auto & ptr = unique[div->name]; + if (ptr) + throw std::invalid_argument("place, the name '" + div->name + "' is redefined."); + ptr = &tmp; + } + + for (auto & child : div->children) + { + check_fn(child.get()); + } + }; + + if (div) + check_fn(div); + } + + //connect the field/dock with div object, + void place::implement::connect(division* start) + { + if (!start) + return; + + //disconnect + for (auto & fd : fields) + { + if (fd.second->attached) + { + fd.second->attached->field = nullptr; + fd.second->attached = nullptr; + } + } + + std::map docks_to_be_closed; + //disconnect + for (auto & dk : docks) + { + if (dk.second->attached) + docks_to_be_closed[dk.first] = dk.second; + } + + std::function check_fn; + check_fn = [&check_fn, this, &docks_to_be_closed](division* div) + { + if (div->name.size()) + { + if (division::kind::dock == div->kind_of_division) + { + auto i = docks.find(div->name); + if (i != docks.end()) + { + docks_to_be_closed.erase(div->name); + auto pane = dynamic_cast(div); + pane->dockable_field = i->second; + pane->dockable_field->attached = pane; + } + } + else + { + auto i = fields.find(div->name); + if (i != fields.end()) + { + div->field = i->second; + div->field->attached = div; + } + } + + } + + for (auto & child : div->children) + { + check_fn(child.get()); + } + }; + + check_fn(start); + + for (auto& e : docks_to_be_closed) + { + e.second->dockarea.close(); + e.second->attached->dockable_field = nullptr; + e.second->attached = nullptr; + } + } + //class place place::place() : impl_(new implement) @@ -2046,8 +2366,141 @@ namespace nana void place::div(const char* s) { place_parts::tokenizer tknizer(s); - impl_->root_division.reset(); //clear atachments div-fields - impl_->scan_div(tknizer).swap(impl_->root_division); + auto div = impl_->scan_div(tknizer); + try + { + impl_->check_unique(div.get()); //may throw if there is a redefined name of field. + impl_->connect(div.get()); + impl_->root_division.reset(); //clear atachments div-fields + impl_->root_division.swap(div); + } + catch (...) + { + //redefined a name of field + throw; + } + } + + void place::modify(const char* name, const char* div_text) + { + if (nullptr == div_text) + throw std::invalid_argument("nana.place: invalid div-text"); + + if (nullptr == name) name = ""; + + //check the name, it throws std::invalid_argument + //if name violate the naming convention. + place_parts::check_field_name(name); + + /* //deprecated + implement::division * div_ptr = nullptr; + implement::field_gather * field_ptr = nullptr; + { + auto i = impl_->fields.find(name); + if (i != impl_->fields.end()) + field_ptr = i->second; + } + + if (field_ptr) + { + //remove the existing div object + div_ptr = field_ptr->attached; + } + else + div_ptr = impl_->search_div_name(impl_->root_division.get(), name); + + if (nullptr == div_ptr) + { + std::string what = "nana::place: field '"; + what += name; + what += "' is not found."; + + throw std::invalid_argument(what); + } + */ + + auto div_ptr = impl_->search_div_name(impl_->root_division.get(), name); + if (!div_ptr) + { + std::string what = "nana::place: field '"; + what += name; + what += "' is not found."; + + throw std::invalid_argument(what); + } + + + std::unique_ptr* replaced = nullptr; + + implement::division * div_owner = div_ptr->div_owner; + implement::division * div_next = div_ptr->div_next; + implement::division * div_bro = nullptr; + if (div_owner) + { + for (auto i = div_owner->children.begin(); i != div_owner->children.end(); ++i) + { + if (i->get() == div_ptr) + { + replaced = &(*i); + break; + } + div_bro = i->get(); + } + } + else + replaced = &(impl_->root_division); + + replaced->swap(impl_->tmp_replaced); + + try + { + place_parts::tokenizer tknizer(div_text); + auto modified = impl_->scan_div(tknizer); + auto modified_ptr = modified.get(); + modified_ptr->name = name; + //modified_ptr->field = field_ptr; + + replaced->swap(modified); + + impl_->check_unique(impl_->root_division.get()); + impl_->connect(impl_->root_division.get()); + impl_->tmp_replaced.reset(); + + modified_ptr->div_owner = div_owner; + modified_ptr->div_next = div_next; + //if (field_ptr) + // field_ptr->attached = modified_ptr; + /* + std::function attach; + attach = [this, &attach](implement::division* div) + { + if (!div->name.empty()) + { + auto i = impl_->fields.find(div->name); + if (impl_->fields.end() != i) + { + if (i->second->attached != div) + { + i->second->attached = div; + div->field = i->second; + } + } + } + + for (auto& child : div->children) + { + attach(child.get()); + } + }; + + attach(impl_->root_division.get()); + */ + } + catch (...) + { + replaced->swap(impl_->tmp_replaced); + throw; + } } place::field_reference place::field(const char* name) @@ -2055,14 +2508,15 @@ namespace nana if (nullptr == name) name = ""; - //May throw std::invalid_argument + //check the name, it throws std::invalid_argument + //if name violate the naming convention. place_parts::check_field_name(name); //get the field with specified name, if no such field with specified name //then create one. auto & p = impl_->fields[name]; if (nullptr == p) - p = new implement::field_impl(this); + p = new implement::field_gather(this); if ((!p->attached) && impl_->root_division) { @@ -2129,33 +2583,7 @@ namespace nana void place::collocate() { - if (impl_->root_division && impl_->window_handle) - { - impl_->root_division->field_area = API::window_size(impl_->window_handle); - impl_->root_division->collocate(impl_->window_handle); - - for (auto & field : impl_->fields) - { - bool is_show = false; - if (field.second->attached && field.second->attached->visible && field.second->attached->display) - { - is_show = true; - auto div = field.second->attached->div_owner; - while (div) - { - if (!div->visible || !div->display) - { - is_show = false; - break; - } - div = div->div_owner; - } - } - - for (auto & el : field.second->elements) - API::show_window(el.handle, is_show); - } - } + impl_->collocate(); } void place::erase(window handle) @@ -2170,7 +2598,7 @@ namespace nana recollocate |= (nullptr != fld.second->attached); } - API::umake_event( fld.second->erase_element(fld.second->fastened, handle)); + API::umake_event(fld.second->erase_element(fld.second->fastened, handle)); } if (recollocate) @@ -2181,5 +2609,25 @@ namespace nana { return field(name); } + + void place::dock(const std::string& name, std::function(window)> factory) + { + //check the name, it throws std::invalid_argument + //if name violate the naming convention. + place_parts::check_field_name(name.data()); + + auto & dock_ptr = impl_->docks[name]; + if (!dock_ptr) + dock_ptr = new implement::field_dock; + + dock_ptr->dockarea.add_factory(std::move(factory)); + + auto div = dynamic_cast(impl_->search_div_name(impl_->root_division.get(), name)); + if (div) + { + dock_ptr->attached = div; + div->dockable_field = dock_ptr; + } + } //end class place }//end namespace nana diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 6a64dc2d..94ca4fe4 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -538,11 +538,11 @@ namespace API return nana::point{}; } - void move_window(window wd, int x, int y) + void move_window(window wd, const point& pos) { auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::window_manager.move(iwd, x, y, false)) + if(restrict::window_manager.move(iwd, pos.x, pos.y, false)) { restrict::core_window_t* update_wd = nullptr; if (iwd->displayed() && iwd->effect.bground) diff --git a/source/gui/widgets/widget.cpp b/source/gui/widgets/widget.cpp index af853b38..c7ecc667 100644 --- a/source/gui/widgets/widget.cpp +++ b/source/gui/widgets/widget.cpp @@ -305,7 +305,7 @@ namespace nana void widget::_m_move(int x, int y) { - API::move_window(handle(), x, y); + API::move_window(handle(), { x, y }); } void widget::_m_move(const rectangle& r)