diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 6215d276..212be2f7 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1052,6 +1052,19 @@ namespace nana cat_proxy & select(bool); bool selected() const; + /// Enables/disables the number of items in the category to be displayed behind the category title + cat_proxy& display_number(bool display); + + /// Determines whether the category is expanded. + bool expanded() const; + + /// Expands/collapses the category + /** + * @param expand Indicates whether to expand or collapse the category. If this parameter is true, it expands the category. If the parameter is false, it collapses the category. + * @return the reference of *this. + */ + cat_proxy& expanded(bool expand); + /// Behavior of a container void push_back(std::string text_utf8); @@ -1484,6 +1497,22 @@ the nana::detail::basic_window member pointer scheme void enable_single(bool for_selection, bool category_limited); void disable_single(bool for_selection); export_options& def_export_options(); + + + /// Sets a renderer for category icon + /** + * @param icon_renderer The renderer of category icon + * @return the reference of *this. + */ + listbox& category_icon(std::function icon_renderer); + + /// Sets category icons + /** + * @param img_expanded An icon displayed in front of category title when the category is expanded. + * @param img_collapsed An icon displayed in front of category title when the category is collapsed. + * @return the reference of *this. + */ + listbox& category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed); private: drawerbase::listbox::essence & _m_ess() const; nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 3126ce6d..5bb523a9 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -659,7 +659,8 @@ namespace nana std::unique_ptr model_ptr; - bool expand{true}; + bool expand{ true }; + bool display_number{ true }; //A cat may have a key object to identify the category std::shared_ptr key_ptr; @@ -1938,6 +1939,8 @@ namespace nana std::unique_ptr content_view; + std::function ctg_icon_renderer; ///< Renderer for the category icon + struct mouse_selection_part { bool started{ false }; @@ -3544,27 +3547,36 @@ namespace nana color txt_color{ static_cast(0x3399) }; - facade arrow("double"); - arrow.direction(categ.expand ? ::nana::direction::north : ::nana::direction::south); - arrow.draw( *graph, {}, txt_color, - { x + 5, y + static_cast(item_height - 16) / 2, 16, 16 }, - element_state::normal); + //Area of category icon + rectangle rt_ctg_icon{ x + 5, y + static_cast(item_height - 16) / 2, 16, 16 }; + + if (essence_->ctg_icon_renderer) + { + essence_->ctg_icon_renderer(*graph, rt_ctg_icon, categ.expand); + } + else + { + facade arrow("double"); + arrow.direction(categ.expand ? ::nana::direction::south : ::nana::direction::east); + arrow.draw(*graph, {}, txt_color, rt_ctg_icon, element_state::normal); + } graph->string({ x + 20, y + txtoff }, categ.text, txt_color); - native_string_type str = to_nstring('(' + std::to_string(categ.items.size()) + ')'); - - auto text_s = graph->text_extent_size(categ.text).width; - auto extend_text_w = text_s + graph->text_extent_size(str).width; - - graph->string({ x + 25 + static_cast(text_s), y + txtoff }, str); - - if (35 + extend_text_w < width) + auto text_px = graph->text_extent_size(categ.text).width; + if (categ.display_number) { - ::nana::point pos{ x + 30 + static_cast(extend_text_w), y + static_cast(item_height) / 2 }; + //Display the number of items in the category + native_string_type str = to_nstring('(' + std::to_string(categ.items.size()) + ')'); + graph->string({ x + 25 + static_cast(text_px), y + txtoff }, str); + text_px += graph->text_extent_size(str).width; + } - graph->line(pos, { x + static_cast(width) - 5, pos.y }, - txt_color); + if (35 + text_px < width) + { + ::nana::point pos{ x + 30 + static_cast(text_px), y + static_cast(item_height) / 2 }; + + graph->line(pos, { x + static_cast(width) - 5, pos.y }, txt_color); } //Draw selecting inner rectangle @@ -4814,6 +4826,32 @@ namespace nana return true; } + cat_proxy& cat_proxy::display_number(bool display) + { + if (cat_->display_number != display) + { + cat_->display_number = display; + ess_->update(); + } + return *this; + } + + bool cat_proxy::expanded() const + { + return cat_->expand; + } + + cat_proxy& cat_proxy::expanded(bool expand) + { + //The first category isn't allowed to be collapsed + if ((expand != cat_->expand) && pos_) + { + cat_->expand = expand; + ess_->update(); + } + return *this; + } + auto cat_proxy::columns() const -> size_type { return ess_->header.cont().size(); @@ -5585,6 +5623,32 @@ namespace nana return _m_ess().def_exp_options; } + listbox& listbox::category_icon(std::function icon_renderer) + { + _m_ess().ctg_icon_renderer.swap(icon_renderer); + _m_ess().update(); + return *this; + } + + listbox& listbox::category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed) + { + _m_ess().ctg_icon_renderer = [img_expanded, img_collapsed](paint::graphics& graph, const rectangle& rt_icon, bool expanded) + { + if (expanded) + { + img_expanded.stretch(rectangle{ img_expanded.size() }, graph, rt_icon); + } + else + { + img_collapsed.stretch(rectangle{ img_collapsed.size() }, graph, rt_icon); + } + }; + + _m_ess().update(); + return *this; + } + + drawerbase::listbox::essence & listbox::_m_ess() const { return get_drawer_trigger().ess();