diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index ef5d8e9f..9456887f 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -211,10 +211,10 @@ #undef _nana_std_optional -#if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && ((!defined(_MSVC_LANG)) || _MSVC_LANG < 201703))) || \ - ((__cplusplus < 201703L) || \ +#if ((defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || _MSVC_LANG < 201703))) || \ + ((!defined(_MSC_VER)) && ((__cplusplus < 201703L) || \ (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__ < 400)) || \ - (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 701)) \ + (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 701))) \ ) # define _nana_std_optional #endif diff --git a/include/nana/gui/animation.hpp b/include/nana/gui/animation.hpp index 9f0df563..e8338399 100644 --- a/include/nana/gui/animation.hpp +++ b/include/nana/gui/animation.hpp @@ -1,7 +1,7 @@ /* * An Animation Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -49,24 +49,18 @@ namespace nana struct impl; class performance_manager; + + /// Non-copyable + animation(const animation&) = delete; + animation& operator=(const animation&) = delete; public: animation(std::size_t fps = 23); ~animation(); - void push_back(frameset frms); - /* - void branch(const std::string& name, const frameset& frms) - { - impl_->branches[name].frames = frms; - } + animation(animation&&); + animation& operator=(animation&&); - void branch(const std::string& name, const frameset& frms, std::function condition) - { - auto & br = impl_->branches[name]; - br.frames = frms; - br.condition = condition; - } - */ + void push_back(frameset frms); void looped(bool enable); ///< Enables or disables the animation repeating playback. diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index 75158284..c6bbd7c9 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -1,7 +1,7 @@ /* * A Menubar implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2009-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2009-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -34,26 +34,6 @@ namespace nana color_proxy border_highlight{ colors::highlight }; }; - class item_renderer - { - public: - enum class state - { - normal, highlighted, selected - }; - - using graph_reference = paint::graphics&; - using scheme = ::nana::drawerbase::menubar::scheme; - - item_renderer(window, graph_reference); - virtual void background(const point&, const ::nana::size&, state); - virtual void caption(const point&, const native_string_type&); - scheme *scheme_ptr() const { return scheme_ptr_; }; - private: - graph_reference graph_; - scheme *scheme_ptr_; - }; - class trigger : public drawer_trigger { diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 671851c2..ef804ff9 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -87,13 +87,15 @@ namespace nana class trigger :public drawer_trigger { - template - struct basic_implement; + //template + //struct basic_implement; //deprecated - class item_renderer; + class implementation; + + //class item_renderer; //deprecated class item_locator; - typedef basic_implement implement; + //typedef basic_implement implement; //deprecated public: struct treebox_node_type { @@ -116,27 +118,30 @@ namespace nana trigger(); ~trigger(); - implement * impl() const; + implementation * impl() const; void check(node_type*, checkstate); - void renderer(::nana::pat::cloneable&&); - const ::nana::pat::cloneable& renderer() const; + pat::cloneable& renderer() const; + + //void renderer(::nana::pat::cloneable&&); + //const ::nana::pat::cloneable& renderer() const; //deprecated void placer(::nana::pat::cloneable&&); const ::nana::pat::cloneable& placer() const; node_type* insert(node_type*, const std::string& key, std::string&&); node_type* insert(const std::string& path, std::string&&); - node_type * selected() const; - void selected(node_type*); + //node_type * selected() const; //deprecated + //void selected(node_type*); - node_image_tag& icon(const ::std::string&) const; + node_image_tag& icon(const ::std::string&); void icon_erase(const ::std::string&); void node_icon(node_type*, const ::std::string& id); unsigned node_width(const node_type*) const; bool rename(node_type*, const char* key, const char* name); + private: //Overrides drawer_trigger methods void attached(widget_reference, graph_reference) override; @@ -152,7 +157,7 @@ namespace nana void key_press(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override; private: - implement * const impl_; + implementation * const impl_; }; //end class trigger @@ -171,11 +176,11 @@ namespace nana /// Append a child with a specified value (user object.). template - item_proxy append(const ::std::string& key, ::std::string name, const T&t) + item_proxy append(const ::std::string& key, ::std::string name, T&& t) { item_proxy ip = append(key, std::move(name)); if(false == ip.empty()) - ip.value(t); + ip.value(std::forward(t)); return ip; } @@ -291,17 +296,19 @@ namespace nana return *p; } + /* template - item_proxy & value(const T& t) + item_proxy & value(const T& t) //deprecated { _m_value() = t; return *this; } + */ template item_proxy & value(T&& t) { - _m_value() = std::move(t); + _m_value() = std::forward(t); return *this; } @@ -378,7 +385,7 @@ namespace nana template treebox & renderer(const ItemRenderer & rd) ///< set user-defined node renderer { - get_drawer_trigger().renderer(::nana::pat::cloneable(rd)); + get_drawer_trigger().renderer() = ::nana::pat::cloneable{rd}; return *this; } @@ -403,6 +410,23 @@ namespace nana /// @param enable bool whether to enable. void auto_draw(bool enable); + /// Prevents drawing during execution. + template + void avoid_drawing(Function fn) + { + this->auto_draw(false); + try + { + fn(); + } + catch (...) + { + this->auto_draw(true); + throw; + } + this->auto_draw(true); + } + /// \brief Enable the checkboxs for each item of the widget. /// @param enable bool indicates whether to show or hide the checkboxs. treebox & checkable(bool enable); @@ -419,8 +443,9 @@ namespace nana /// These states are 'normal', 'hovered' and 'expanded'. /// If 'hovered' or 'expanded' are not set, it uses 'normal' state image for these 2 states. /// See also in [documentation](http://nanapro.org/en-us/help/widgets/treebox.htm) - node_image_type& icon(const ::std::string& id ///< the name of an icon scheme. If the name is not existing, it creates a new scheme for the name. - ) const; + /// @param id The name of an icon scheme. If the name is not existing, it creates a new scheme for the name. + /// @return The reference of node image scheme correspending with the specified id. + node_image_type& icon(const ::std::string& id); void icon_erase(const ::std::string& id); @@ -445,6 +470,19 @@ namespace nana item_proxy selected() const; ///< returns the selected node + /// Scrolls a specified item into view. + /** + * @param item An item to be requested. + * @param bearing The position where the item to be positioned in the view. + */ + void scroll_into_view(item_proxy item, align_v bearing); + + /// Scrolls a specified item into view. + /** + * @param item An item to be requested. + */ + void scroll_into_view(item_proxy item); + private: std::shared_ptr _m_scroll_operation() override; diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index bf3ab2fb..62cb293a 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -658,21 +658,31 @@ namespace detail platform_scope_guard lock; if(umake_owner(wd)) { + auto & wd_manager = detail::bedrock::instance().wd_manager(); + + std::vector owned_children; + auto i = wincontext_.find(wd); if(i != wincontext_.end()) { if(i->second.owned) { - set_error_handler(); - auto & wd_manager = detail::bedrock::instance().wd_manager(); - for(auto u = i->second.owned->rbegin(); u != i->second.owned->rend(); ++u) - wd_manager.close(wd_manager.root(*u)); - - rev_error_handler(); - - delete i->second.owned; + for(auto child : *i->second.owned) + owned_children.push_back(child); } + } + //Closing a child will erase the wd from the table wincontext_, so the + //iterator i can't be reused after children closed. + set_error_handler(); + for(auto u = owned_children.rbegin(); u != owned_children.rend(); ++u) + wd_manager.close(wd_manager.root(*u)); + rev_error_handler(); + + i = wincontext_.find(wd); + if(i != wincontext_.end()) + { + delete i->second.owned; wincontext_.erase(i); } } diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index c737d334..16e54729 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -1,7 +1,7 @@ /* * An Animation Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -571,6 +571,25 @@ namespace nana delete impl_; } + animation::animation(animation&& rhs) + : impl_(rhs.impl_) + { + rhs.impl_ = new impl(23); + } + + animation& animation::operator=(animation&& rhs) + { + if (this != &rhs) + { + auto imp = new impl{ 23 }; + + delete impl_; + impl_ = rhs.impl_; + rhs.impl_ = imp; + } + return *this; + } + void animation::push_back(frameset frms) { impl_->framesets.emplace_back(std::move(frms)); diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 62738e98..0504cf95 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -973,7 +973,12 @@ namespace detail case Expose: if(msgwnd->visible && (msgwnd->root_graph->empty() == false)) { - nana::detail::platform_scope_guard lock; + nana::internal_scope_guard lock; + //Don't lock this scope using platform-scope-guard. Because it would cause the platform-scope-lock to be locked + //before the internal-scope-guard, and the order of locking would cause dead-lock. + // + //Locks this scope using internal-scope-guard is correct and safe. In the scope, the Xlib functions aren't called + //directly. if(msgwnd->is_draw_through()) { msgwnd->other.attribute.root->draw_through(); diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index ef0b1935..8d3dd9c8 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -371,6 +371,8 @@ namespace nana{ } Window parent = (owner ? reinterpret_cast(owner) : restrict::spec.root_window()); + + //The position passed to XCreateWindow is a screen coordinate. nana::point pos(r.x, r.y); if((false == nested) && owner) { @@ -396,7 +398,9 @@ namespace nana{ { auto origin_owner = (owner ? owner : reinterpret_cast(restrict::spec.root_window())); restrict::spec.make_owner(origin_owner, reinterpret_cast(handle)); - exposed_positions[handle] = pos; + + //The exposed_position is a relative position to its owner/parent. + exposed_positions[handle] = r.position(); } XChangeWindowAttributes(disp, handle, attr_mask, &win_attr); @@ -945,13 +949,14 @@ namespace nana{ auto fm_extents = window_frame_extents(wd); origin.x = -fm_extents.left; origin.y = -fm_extents.top; - +#if 0 //deprecated if(reinterpret_cast(coord_wd) != restrict::spec.root_window()) { fm_extents = window_frame_extents(coord_wd); origin.x += fm_extents.left; origin.y += fm_extents.top; } +#endif } else coord_wd = get_window(wd, window_relationship::parent); @@ -1010,8 +1015,14 @@ namespace nana{ if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { auto origin = window_position(owner); +#if 0 x += origin.x; y += origin.y; +#else + auto owner_extents = window_frame_extents(owner); + x += origin.x + owner_extents.left; + y += origin.y + owner_extents.top; +#endif } ::XMoveWindow(disp, reinterpret_cast(wd), x, y); @@ -1099,8 +1110,14 @@ namespace nana{ if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { auto origin = window_position(owner); +#if 0 x += origin.x; y += origin.y; +#else + auto owner_extents = window_frame_extents(owner); + x += origin.x + owner_extents.left; + y += origin.y + owner_extents.top; +#endif } ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); @@ -1580,14 +1597,21 @@ namespace nana{ pos.y = point.y; return true; } - return false; #elif defined(NANA_X11) nana::detail::platform_scope_guard psg; int x = pos.x, y = pos.y; Window child; - return (True == ::XTranslateCoordinates(restrict::spec.open_display(), - reinterpret_cast(wd), restrict::spec.root_window(), x, y, &pos.x, &pos.y, &child)); + if(True == ::XTranslateCoordinates(restrict::spec.open_display(), + reinterpret_cast(wd), restrict::spec.root_window(), x, y, &pos.x, &pos.y, &child)) + { + //deprecated + //auto fm_extents = window_frame_extents(wd); + //pos.x += fm_extents.left; + //pos.y += fm_extents.top; + return true; + } #endif + return false; } bool native_interface::calc_window_point(native_window_type wd, nana::point& pos) @@ -1600,14 +1624,21 @@ namespace nana{ pos.y = point.y; return true; } - return false; #elif defined(NANA_X11) nana::detail::platform_scope_guard psg; int x = pos.x, y = pos.y; Window child; - return (True == ::XTranslateCoordinates(restrict::spec.open_display(), - restrict::spec.root_window(), reinterpret_cast(wd), x, y, &pos.x, &pos.y, &child)); + if(True == ::XTranslateCoordinates(restrict::spec.open_display(), restrict::spec.root_window(), reinterpret_cast(wd), x, y, &pos.x, &pos.y, &child)) + { + //deprecated + //Now the origin of pos is the left-top corner of the window(including titlebar and border) + //auto fm_extents = window_frame_extents(wd); + //pos.x += fm_extents.left; + //pos.y += fm_extents.top; + return true; + } #endif + return false; } native_window_type native_interface::find_window(int x, int y) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 569271e8..74bedf81 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -303,7 +303,7 @@ namespace nana extent_size.width = width_px; for (auto & vsline : rs.vslines) - extent_size.height += vsline.extent_height_px; + extent_size.height += static_cast(vsline.extent_height_px); content_lines.emplace_back(std::move(rs.vslines)); @@ -311,7 +311,8 @@ namespace nana break; } - if (allowed_width_px < extent_size.width) + //The width is not restricted if the allowed_width_px is zero. + if (allowed_width_px && (allowed_width_px < extent_size.width)) extent_size.width = allowed_width_px; if (transient_.current_font != pre_font) @@ -445,6 +446,10 @@ namespace nana unsigned sub_text_px = 0; auto sub_text_len = _m_fit_text(graph, data->text().substr(text_begin), rs.allowed_width, sub_text_px); + //At least one character must be displayed no matter whether the width is enough or not. + if (0 == sub_text_len) + sub_text_len = 1; + if (text_begin + sub_text_len < data->text().size()) { //make a new visual line @@ -886,7 +891,8 @@ namespace nana if(graph_ptr->empty()) { graph_ptr = &substitute; - graph_ptr->make({ 10, 10 }); + substitute.make({ 10, 10 }); + substitute.typeface(this->typeface()); } return impl->renderer.measure(*graph_ptr, limited, impl->text_align, impl->text_align_v); diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 94d7c6c2..aeb0c405 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -1,7 +1,7 @@ /* * A Menubar implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2009-2017 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2009-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -215,12 +215,32 @@ namespace nana }; //class item_renderer - item_renderer::item_renderer(window wd, graph_reference graph) - :graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) - {} - - void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) + class item_renderer + { + public: + enum class state { + normal, highlighted, selected + }; + + using graph_reference = paint::graphics&; + using scheme = ::nana::drawerbase::menubar::scheme; + + item_renderer(window, graph_reference); + virtual void background(const point&, const ::nana::size&, state); + virtual void caption(const point&, const native_string_type&); + scheme *scheme_ptr() const { return scheme_ptr_; }; + private: + graph_reference graph_; + scheme *scheme_ptr_; + }; + + item_renderer::item_renderer(window wd, graph_reference graph) + :graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) + {} + + void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) + { auto bground = scheme_ptr_->text_fgcolor; ::nana::color border, body; @@ -245,13 +265,12 @@ namespace nana paint::draw{ graph_ }.corner(r, 1); graph_.rectangle(r.pare_off(1), true, body); - } + } - void item_renderer::caption(const point& pos, const native_string_type& text) - { - graph_.string(pos, text, scheme_ptr_->text_fgcolor); - - } + void item_renderer::caption(const point& pos, const native_string_type& text) + { + graph_.string(pos, text, scheme_ptr_->text_fgcolor); + } //end class item_renderer //class trigger diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 3cbf61b7..fa8a9ad0 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -180,13 +180,13 @@ namespace nana } };//end class tooltip_window - //item_locator should be defined before the definition of basic_implement + //item_locator should be defined before the definition of implementation class trigger::item_locator { public: using node_type = tree_cont_type::node_type; - item_locator(implement * impl, int item_pos, int x, int y); + item_locator(implementation * impl, int item_pos, int x, int y); int operator()(node_type &node, int affect); node_type * node() const; component what() const; @@ -194,7 +194,7 @@ namespace nana nana::rectangle text_pos() const; private: - trigger::implement * impl_; + implementation * const impl_; nana::point item_pos_; const nana::point pos_; //Mouse pointer position component what_; @@ -212,11 +212,89 @@ namespace nana } }; - //struct implement + //struct implementation //@brief: some data for treebox trigger - template - struct trigger::basic_implement + class trigger::implementation { + class item_rendering_director + : public compset_interface + { + public: + using node_type = tree_cont_type::node_type; + + item_rendering_director(implementation * impl, const nana::point& pos): + impl_(impl), + pos_(pos) + { + } + + //affect + //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) + { + iterated_node_ = &node; + switch (affect) + { + case 1: + pos_.x += impl_->shape.indent_pixels; + break; + default: + if (affect >= 2) + pos_.x -= impl_->shape.indent_pixels * (affect - 1); + } + + auto & comp_placer = impl_->data.comp_placer; + + impl_->assign_node_attr(node_attr_, iterated_node_); + node_r_.x = node_r_.y = 0; + node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_); + node_r_.height = comp_placer->item_height(*impl_->data.graph); + + auto renderer = impl_->data.renderer; + renderer->begin_paint(*impl_->data.widget_ptr); + renderer->bground(*impl_->data.graph, this); + renderer->expander(*impl_->data.graph, this); + renderer->crook(*impl_->data.graph, this); + renderer->icon(*impl_->data.graph, this); + renderer->text(*impl_->data.graph, this); + + pos_.y += node_r_.height; + + if (pos_.y > static_cast(impl_->data.graph->height())) + return 0; + + return (node.child && node.value.second.expanded ? 1 : 2); + } + private: + //Overrides compset_interface + virtual const item_attribute_t& item_attribute() const override + { + return node_attr_; + } + + virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override + { + attr.area = node_r_; + if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area)) + { + attr.mouse_pointed = node_attr_.mouse_pointed; + attr.area.x += pos_.x; + attr.area.y += pos_.y; + return true; + } + return false; + } + private: + implementation * const impl_; + ::nana::point pos_; + const node_type * iterated_node_; + item_attribute_t node_attr_; + ::nana::rectangle node_r_; + }; + + public: using node_type = trigger::node_type; struct rep_tag @@ -271,7 +349,7 @@ namespace nana nana::timer timer; }adjust; public: - basic_implement() + implementation() { data.graph = nullptr; data.widget_ptr = nullptr; @@ -353,6 +431,11 @@ namespace nana return true; } + static constexpr unsigned margin_top_bottom() + { + return 1; + } + bool draw(bool reset_scroll, bool ignore_update = false, bool ignore_auto_draw = false) { if(data.graph && (false == data.stop_drawing)) @@ -368,7 +451,7 @@ namespace nana data.graph->rectangle(true, data.widget_ptr->bgcolor()); //Draw tree - attr.tree_cont.for_each(shape.first, Renderer(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, 1))); + attr.tree_cont.for_each(shape.first, item_rendering_director(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, margin_top_bottom()))); if (!ignore_update) API::update_window(data.widget_ptr->handle()); @@ -460,6 +543,129 @@ namespace nana return nullptr; } + node_type* last(bool ignore_folded_children) const + { + auto p = attr.tree_cont.get_root(); + + while (true) + { + while (p->next) + p = p->next; + + if (p->child) + { + if (p->value.second.expanded || !ignore_folded_children) + { + p = p->child; + continue; + } + } + + break; + } + + return p; + } + + std::size_t screen_capacity(bool completed) const + { + auto const item_px = data.comp_placer->item_height(*data.graph); + auto screen_px = data.graph->size().height - (margin_top_bottom() << 1); + + if (completed || ((screen_px % item_px) == 0)) + return screen_px / item_px; + + return screen_px / item_px + 1; + } + + bool scroll_into_view(node_type* node, bool use_bearing, align_v bearing) + { + auto & tree = attr.tree_cont; + + auto parent = node->owner; + + std::vector parent_path; + while (parent) + { + parent_path.push_back(parent); + parent = parent->owner; + } + + bool has_expanded = false; + + //Expands the shrinked nodes which are ancestors of node + for (auto i = parent_path.rbegin(); i != parent_path.rend(); ++i) + { + if (!(*i)->value.second.expanded) + { + has_expanded = true; + (*i)->value.second.expanded = true; + item_proxy iprx(data.trigger_ptr, *i); + data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle()); + } + } + + auto pos = tree.distance_if(node, pred_allow_child{}); + auto last_pos = tree.distance_if(last(true), pred_allow_child{}); + + auto const capacity = screen_capacity(true); + auto const item_px = data.comp_placer->item_height(*data.graph); + + //If use_bearing is false, it calculates a bearing depending on the current + //position of the requested item. + if (!use_bearing) + { + auto first_pos = tree.distance_if(shape.first, pred_allow_child{}); + + if (pos < first_pos) + bearing = align_v::top; + else if (pos >= first_pos + capacity) + bearing = align_v::bottom; + else + { + //The item is already in the view. + //Returns true if a draw operation is needed + return has_expanded; + } + } + + if (align_v::top == bearing) + { + if (last_pos - pos + 1 < capacity) + { + if (last_pos + 1 >= capacity) + pos = last_pos + 1 - capacity; + else + pos = 0; + } + } + else if (align_v::center == bearing) + { + auto const short_side = (std::min)(pos, last_pos - pos); + if (short_side >= capacity / 2) + pos -= capacity / 2; + else if (short_side == pos || (last_pos + 1 < capacity)) + pos = 0; + else + pos = last_pos + 1 - capacity; + } + else if (align_v::bottom == bearing) + { + if (pos + 1 >= capacity) + pos = pos + 1 - capacity; + else + pos = 0; + } + + auto prv_first = shape.first; + shape.first = attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); + + //Update the position of scroll + show_scroll(); + + return has_expanded || (prv_first != shape.first); + } + bool make_adjust(node_type * node, int reason) { if(!node) return false; @@ -1356,7 +1562,7 @@ namespace nana //class trigger::item_locator - trigger::item_locator::item_locator(implement * impl, int item_pos, int x, int y) + trigger::item_locator::item_locator(implementation * impl, int item_pos, int x, int y) : impl_(impl), item_pos_(item_pos, 1), pos_(x, y), @@ -1439,86 +1645,6 @@ namespace nana return{node_text_r_.x + item_pos_.x, node_text_r_.y + item_pos_.y, node_text_r_.width, node_text_r_.height}; } //end class item_locator - - class trigger::item_renderer - : public compset_interface - { - public: - typedef tree_cont_type::node_type node_type; - - item_renderer(implement * impl, const nana::point& pos) - : impl_(impl), - pos_(pos) - { - } - - //affect - //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) - { - implement * draw_impl = impl_; - - iterated_node_ = &node; - switch(affect) - { - case 1: - pos_.x += draw_impl->shape.indent_pixels; - break; - default: - if(affect >= 2) - pos_.x -= draw_impl->shape.indent_pixels * (affect - 1); - } - - auto & comp_placer = impl_->data.comp_placer; - - impl_->assign_node_attr(node_attr_, iterated_node_); - node_r_.x = node_r_.y = 0; - node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_); - node_r_.height = comp_placer->item_height(*impl_->data.graph); - - auto renderer = draw_impl->data.renderer; - renderer->begin_paint(*draw_impl->data.widget_ptr); - renderer->bground(*draw_impl->data.graph, this); - renderer->expander(*draw_impl->data.graph, this); - renderer->crook(*draw_impl->data.graph, this); - renderer->icon(*draw_impl->data.graph, this); - renderer->text(*draw_impl->data.graph, this); - - pos_.y += node_r_.height; - - if(pos_.y > static_cast(draw_impl->data.graph->height())) - return 0; - - return (node.child && node.value.second.expanded ? 1 : 2); - } - private: - //Overrides compset_interface - virtual const item_attribute_t& item_attribute() const override - { - return node_attr_; - } - - virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override - { - attr.area = node_r_; - if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area)) - { - attr.mouse_pointed = node_attr_.mouse_pointed; - attr.area.x += pos_.x; - attr.area.y += pos_.y; - return true; - } - return false; - } - private: - trigger::implement * impl_; - ::nana::point pos_; - const node_type * iterated_node_; - item_attribute_t node_attr_; - ::nana::rectangle node_r_; - }; } //Treebox Implementation @@ -1548,7 +1674,7 @@ namespace nana //end struct treebox_node_type trigger::trigger() - : impl_(new implement) + : impl_(new implementation) { impl_->data.trigger_ptr = this; impl_->data.renderer = nana::pat::cloneable(internal_renderer()); @@ -1615,7 +1741,7 @@ namespace nana delete impl_; } - trigger::implement * trigger::impl() const + trigger::implementation * trigger::impl() const { return impl_; } @@ -1687,6 +1813,7 @@ namespace nana } } + /* //deprecated void trigger::renderer(::nana::pat::cloneable&& r) { impl_->data.renderer = std::move(r); @@ -1696,6 +1823,12 @@ namespace nana { return impl_->data.renderer; } + */ + + ::nana::pat::cloneable& trigger::renderer() const + { + return impl_->data.renderer; + } void trigger::placer(::nana::pat::cloneable&& r) { @@ -1729,18 +1862,22 @@ namespace nana return x; } - trigger::node_type* trigger::selected() const + /* + trigger::node_type* trigger::selected() const //deprecated { return impl_->node_state.selected; } + */ - void trigger::selected(node_type* node) + /* + void trigger::selected(node_type* node) //deprecated { if(impl_->attr.tree_cont.verify(node) && impl_->set_selected(node)) impl_->draw(true); } + */ - node_image_tag& trigger::icon(const std::string& id) const + node_image_tag& trigger::icon(const std::string& id) { auto i = impl_->shape.image_table.find(id); if(i != impl_->shape.image_table.end()) @@ -2167,7 +2304,7 @@ namespace nana impl->draw(true); } - treebox::node_image_type& treebox::icon(const std::string& id) const + treebox::node_image_type& treebox::icon(const std::string& id) { return get_drawer_trigger().icon(id); } @@ -2233,7 +2370,24 @@ namespace nana treebox::item_proxy treebox::selected() const { - return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); + //return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); //deprecated + auto dw = &get_drawer_trigger(); + return item_proxy(const_cast(dw), dw->impl()->node_state.selected); + } + + void treebox::scroll_into_view(item_proxy item, align_v bearing) + { + internal_scope_guard lock; + if(get_drawer_trigger().impl()->scroll_into_view(item._m_node(), true, bearing)) + API::refresh_window(*this); + } + + void treebox::scroll_into_view(item_proxy item) + { + internal_scope_guard lock; + //The third argument for scroll_into_view is ignored if the second argument is false. + if(get_drawer_trigger().impl()->scroll_into_view(item._m_node(), false, align_v::center)) + API::refresh_window(*this); } std::shared_ptr treebox::_m_scroll_operation()