diff --git a/include/nana/gui/widgets/detail/tree_cont.hpp b/include/nana/gui/widgets/detail/tree_cont.hpp index e2373bb5..dfa4c1ad 100644 --- a/include/nana/gui/widgets/detail/tree_cont.hpp +++ b/include/nana/gui/widgets/detail/tree_cont.hpp @@ -1,6 +1,6 @@ /* * A Tree Container class implementation - * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -95,6 +95,13 @@ namespace detail typedef tree_node node_type; typedef typename node_type::value_type value_type; + enum class enum_order + { + stop, //Stop enumeration + proceed_with_children, + proceed + }; + tree_cont() :root_(nullptr) {} @@ -234,59 +241,6 @@ namespace detail return 0; } - template - void for_each(node_type* node, Functor f) - { - if(nullptr == node) node = root_.child; - int state = 0; //0: Sibling, the last is a sibling of node - //1: Owner, the last is the owner of node - //>= 2: Children, the last is is a child of the node that before this node. - while(node) - { - switch(f(*node, state)) - { - case 0: return; - case 1: - { - if(node->child) - { - node = node->child; - state = 1; - } - else - return; - continue; - } - break; - } - - if(node->next) - { - node = node->next; - state = 0; - } - else - { - state = 1; - if(node == &root_) return; - - while(true) - { - ++state; - if(node->owner->next) - { - node = node->owner->next; - break; - } - else - node = node->owner; - - if(node == &root_) return; - } - } - } - } - template void for_each(node_type* node, Functor f) const { @@ -296,24 +250,18 @@ namespace detail //>= 2: Children, the last is is a child of the node that before this node. while(node) { - switch(f(*node, state)) - { - case 0: return; - case 1: - { - if(node->child) - { - node = node->child; - state = 1; - } - else - return; - continue; - } - break; - } + enum_order order = f(*node, state); - if(node->next) + if (enum_order::stop == order) + { + return; + } + else if (node->child && (enum_order::proceed_with_children == order)) + { + node = node->child; + state = 1; + } + else if(node->next) { node = node->next; state = 0; @@ -340,31 +288,37 @@ namespace detail } } - template - unsigned child_size_if(const ::std::string& key, PredAllowChild pac) const + template + unsigned child_size_if(const ::std::string& key, PredAllowChild pac, PredAllowNode pan) const { auto node = _m_locate(key); - return (node ? child_size_if(*node, pac) : 0); + return (node ? child_size_if(*node, pac, pan) : 0); } - template - unsigned child_size_if(const node_type& node, PredAllowChild pac) const + template + unsigned child_size_if(const node_type& node, PredAllowChild pac, PredAllowNode pan) const { unsigned size = 0; const node_type* pnode = node.child; while(pnode) { + if (!pan(*pnode)) + { + pnode = pnode->next; + continue; + } + ++size; if(pnode->child && pac(*pnode)) - size += child_size_if(*pnode, pac); + size += child_size_if(*pnode, pac, pan); pnode = pnode->next; } return size; } - template - std::size_t distance_if(const node_type * node, PredAllowChild pac) const + template + std::size_t distance_if(const node_type * node, PredAllowChild pac, PredAllowNode pan) const { if(nullptr == node) return 0; const node_type * iterator = root_.child; @@ -374,6 +328,12 @@ namespace detail while(iterator && iterator != node) { + if (!pan(*iterator)) + { + iterator = iterator->next; + continue; + } + ++off; if(iterator->child && pac(*iterator)) @@ -393,8 +353,8 @@ namespace detail return off; } - template - node_type* advance_if(node_type* node, std::size_t off, PredAllowChild pac) + template + node_type* advance_if(node_type* node, std::size_t off, PredAllowChild pac, PredAllowNode pan) { if(nullptr == node) node = root_.child; @@ -402,6 +362,12 @@ namespace detail while(node && off) { + if (!pan(*node)) + { + node = node->next; + continue; + } + --off; if(node->child && pac(*node)) { @@ -490,7 +456,7 @@ namespace detail void _m_for_each(const ::std::string& key, Function function) const { //Ignores separaters at the begin of key. - ::std::string::size_type beg = key.find_first_not_of("\\/"); + auto beg = key.find_first_not_of("\\/"); if (key.npos == beg) return; diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index c1edbadf..d6d67cc8 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -1,7 +1,7 @@ /** * A Tree Box Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE or copy at @@ -49,7 +49,7 @@ namespace nana struct node_attribute { - bool has_children; + bool has_children; ///< Determines whether the node has visible children bool expended; checkstate checked; bool selected; @@ -117,6 +117,7 @@ namespace nana ::std::string text; nana::any value; bool expanded; + bool hidden; checkstate checked; ::std::string img_idstr; }; @@ -218,6 +219,12 @@ namespace nana /// Select the node, and returns itself.. item_proxy& select(bool); + /// Return true when the node is hidden. + bool hidden() const; + + /// Hide the node, and returns itself. + item_proxy& hide(bool); + /// Return the icon. const ::std::string& icon() const; @@ -343,6 +350,7 @@ namespace nana basic_event checked; ///< a user checks or unchecks a node basic_event selected; ///< a user selects or unselects a node basic_event hovered; ///< a user moves the cursor over a node + basic_event hidden; ///< a user hides or shows a node }; }//end namespace treebox }//end namespace drawerbase diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 7e4a880e..7e056b1d 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1,7 +1,7 @@ /* * A Treebox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -70,7 +70,7 @@ namespace nana const node_type* find_track_child_node(const node_type* node, const node_type * end, const char* pattern, std::size_t len, bool &finish) { - if(node->value.second.expanded) + if(node->value.second.expanded && !node->value.second.hidden) { node = node->child; while(node) @@ -79,7 +79,7 @@ namespace nana if(node == end) break; - if(node->value.second.expanded) + if(node->value.second.expanded && !node->value.second.hidden) { auto t = find_track_child_node(node, end, pattern, len, finish); if(t || finish) @@ -184,10 +184,11 @@ namespace nana class trigger::item_locator { public: + using enum_order = tree_cont_type::enum_order; using node_type = tree_cont_type::node_type; item_locator(implementation * impl, int item_pos, int x, int y); - int operator()(node_type &node, int affect); + enum_order operator()(node_type &node, int affect); node_type * node() const; component what() const; bool item_body() const; @@ -212,6 +213,14 @@ namespace nana } }; + struct pred_allow_node + { + bool operator()(const trigger::tree_cont_type::node_type& node) + { + return !node.value.second.hidden; + } + }; + //struct implementation //@brief: some data for treebox trigger class trigger::implementation @@ -220,6 +229,7 @@ namespace nana : public compset_interface { public: + using enum_order = tree_cont_type::enum_order; using node_type = tree_cont_type::node_type; item_rendering_director(implementation * impl, const nana::point& pos): @@ -232,9 +242,11 @@ namespace nana //0 = Sibling, the last is a sibling of node //1 = Owner, the last is the owner of node //>=2 = Children, the last is a child of a node that before this node. - int operator()(const node_type& node, int affect) + enum_order operator()(const node_type& node, int affect) { iterated_node_ = &node; + + // Increase/decrease indent switch (affect) { case 1: @@ -245,6 +257,9 @@ namespace nana pos_.x -= impl_->data.scheme_ptr->indent_displacement * (affect - 1); } + if (iterated_node_->value.second.hidden) // Skip drawing if the node is hidden + return enum_order::proceed; + auto & comp_placer = impl_->data.comp_placer; impl_->assign_node_attr(node_attr_, iterated_node_); @@ -263,9 +278,9 @@ namespace nana pos_.y += node_r_.height; if (pos_.y > static_cast(impl_->data.graph->height())) - return 0; + return enum_order::stop; - return (node.child && node.value.second.expanded ? 1 : 2); + return ((node.child && node.value.second.expanded) ? enum_order::proceed_with_children : enum_order::proceed); } private: //Overrides compset_interface @@ -375,7 +390,18 @@ namespace nana void assign_node_attr(node_attribute& ndattr, const node_type* node) const { - ndattr.has_children = (nullptr != node->child); + // Check if there is a visible child that node has. + // This is an improvement based on a new feature that allows treebox node to be hidden(PR#500) + ndattr.has_children = false; + for (auto p = node->child; p; p = p->next) + { + if (!p->value.second.hidden) + { + ndattr.has_children = true; + break; + } + } + ndattr.expended = node->value.second.expanded; ndattr.text = node->value.second.text; ndattr.checked = node->value.second.checked; @@ -552,7 +578,7 @@ namespace nana if (p->child) { - if (p->value.second.expanded || !ignore_folded_children) + if ((p->value.second.expanded || !ignore_folded_children) && !p->value.second.hidden) { p = p->child; continue; @@ -603,8 +629,8 @@ namespace nana } } - auto pos = tree.distance_if(node, pred_allow_child{}); - auto last_pos = tree.distance_if(last(true), pred_allow_child{}); + auto pos = tree.distance_if(node, pred_allow_child{}, pred_allow_node{}); + auto last_pos = tree.distance_if(last(true), pred_allow_child{}, pred_allow_node{}); auto const capacity = screen_capacity(true); @@ -612,7 +638,7 @@ namespace nana //position of the requested item. if (!use_bearing) { - auto first_pos = tree.distance_if(shape.first, pred_allow_child{}); + auto first_pos = tree.distance_if(shape.first, pred_allow_child{}, pred_allow_node{}); if (pos < first_pos) bearing = align_v::top; @@ -655,7 +681,7 @@ namespace nana } auto prv_first = shape.first; - shape.first = attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); + shape.first = attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}, pred_allow_node{}); //Update the position of scroll show_scroll(); @@ -669,8 +695,8 @@ namespace nana auto & tree = attr.tree_cont; - auto const first_pos = tree.distance_if(shape.first, pred_allow_child{}); - auto const node_pos = tree.distance_if(node, pred_allow_child{}); + auto const first_pos = tree.distance_if(shape.first, pred_allow_child{}, pred_allow_node{}); + auto const node_pos = tree.distance_if(node, pred_allow_child{}, pred_allow_node{}); auto const max_allow = max_allowed(); switch(reason) { @@ -680,12 +706,12 @@ namespace nana //adjust if the number of its children are over the max number allowed if (shape.first != node) { - auto child_size = tree.child_size_if(*node, pred_allow_child()); + auto child_size = tree.child_size_if(*node, pred_allow_child(), pred_allow_node{}); if (child_size < max_allow) { auto const size = node_pos - first_pos + child_size + 1; if (size > max_allow) - shape.first = tree.advance_if(shape.first, size - max_allow, pred_allow_child{}); + shape.first = tree.advance_if(shape.first, size - max_allow, pred_allow_child{}, pred_allow_node{}); } else shape.first = node; @@ -698,7 +724,7 @@ namespace nana if (visual_size > max_allow) { if (first_pos + max_allow > visual_size) - shape.first = tree.advance_if(nullptr, visual_size - max_allow, pred_allow_child{}); + shape.first = tree.advance_if(nullptr, visual_size - max_allow, pred_allow_child{}, pred_allow_node{}); } else shape.first = nullptr; @@ -739,7 +765,7 @@ namespace nana } else if (node_pos - first_pos > max_allow) { - shape.first = tree.advance_if(nullptr, node_pos - max_allow + 1, pred_allow_child{}); + shape.first = tree.advance_if(nullptr, node_pos - max_allow + 1, pred_allow_child{}, pred_allow_node{}); return true; } } @@ -814,6 +840,27 @@ namespace nana return false; } + bool set_hidden(node_type* node, bool value) + { + if (node && node->value.second.hidden != value) + { + if (value == false) + { + //if hiding a parent of the selected node or the selected node itself - select nothing. + if (node->is_ancestor_of(node_state.selected) || node_state.selected == node) + set_selected(nullptr); + } + + node->value.second.hidden = value; + data.stop_drawing = true; + item_proxy iprx(data.trigger_ptr, node); + data.widget_ptr->events().hidden.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, value }, data.widget_ptr->handle()); + data.stop_drawing = false; + return true; + } + return false; + } + void show_scroll() { if(nullptr == data.graph) return; @@ -841,7 +888,7 @@ namespace nana adjust.scroll_timestamp = nana::system::timestamp(); adjust.timer.start(); - shape.first = attr.tree_cont.advance_if(nullptr, shape.scroll->value(), pred_allow_child{}); + shape.first = attr.tree_cont.advance_if(nullptr, shape.scroll->value(), pred_allow_child{}, pred_allow_node{}); draw(false, false, true); }); } @@ -850,13 +897,13 @@ namespace nana scroll.range(max_allow); } - auto pos = attr.tree_cont.distance_if(shape.first, pred_allow_child{}); + auto pos = attr.tree_cont.distance_if(shape.first, pred_allow_child{}, pred_allow_node{}); scroll.value(pos); } std::size_t visual_item_size() const { - return attr.tree_cont.child_size_if(std::string(), pred_allow_child{}); + return attr.tree_cont.child_size_if(std::string(), pred_allow_child{}, pred_allow_node{}); } int visible_w_pixels() const @@ -1153,6 +1200,20 @@ namespace nana return *this; } + bool item_proxy::hidden() const + { + return node_->value.second.hidden; + } + + item_proxy& item_proxy::hide(bool h) + { + auto* impl = trigger_->impl(); + if (impl->set_hidden(node_, h)) + impl->draw(true); + + return *this; + } + const std::string& item_proxy::icon() const { return node_->value.second.img_idstr; @@ -1398,7 +1459,7 @@ namespace nana switch(comp) { case component_t::expander: - if(attr.has_children) + if (attr.has_children) { r->width = scheme_.item_offset; return true; @@ -1584,7 +1645,7 @@ namespace nana node_(nullptr) {} - int trigger::item_locator::operator()(node_type &node, int affect) + auto trigger::item_locator::operator()(node_type &node, int affect) -> enum_order { switch(affect) { @@ -1595,6 +1656,9 @@ namespace nana item_pos_.x -= static_cast(impl_->data.scheme_ptr->indent_displacement) * (affect - 1); } + if (node.value.second.hidden) // Do not account for hidden nodes + return enum_order::proceed; + impl_->assign_node_attr(node_attr_, &node); nana::rectangle node_r; auto & comp_placer = impl_->data.comp_placer; @@ -1626,15 +1690,12 @@ namespace nana } } - return 0; //Stop iterating + return enum_order::stop; //Stop iterating } item_pos_.y += node_r.height; - if(node.value.second.expanded && node.child) - return 1; - - return 2; + return ((node.child && node.value.second.expanded) ? enum_order::proceed_with_children : enum_order::proceed); } trigger::item_locator::node_type * trigger::item_locator::node() const @@ -1665,11 +1726,11 @@ namespace nana //class trigger //struct treebox_node_type trigger::treebox_node_type::treebox_node_type() - :expanded(false), checked(checkstate::unchecked) + :expanded(false), checked(checkstate::unchecked), hidden(false) {} trigger::treebox_node_type::treebox_node_type(std::string text) - :text(std::move(text)), expanded(false), checked(checkstate::unchecked) + :text(std::move(text)), expanded(false), checked(checkstate::unchecked), hidden(false) {} trigger::treebox_node_type& trigger::treebox_node_type::operator=(const treebox_node_type& rhs)