diff --git a/include/nana/gui/widgets/toolbar.hpp b/include/nana/gui/widgets/toolbar.hpp index 02e88c6c..85cf0466 100644 --- a/include/nana/gui/widgets/toolbar.hpp +++ b/include/nana/gui/widgets/toolbar.hpp @@ -33,14 +33,51 @@ namespace nana { namespace toolbar { - struct item_proxy - { - nana::toolbar& widget; - std::size_t button; + enum class tool_type + { + button, + toggle + }; - void enable(bool enable_state); + class item_proxy + { + public: + item_proxy(::nana::toolbar*, std::size_t pos); + + bool enable() const; + item_proxy& enable(bool enable_state); + + item_proxy& tooltype(tool_type type); ///< Sets the tool style. + + bool istoggle() const; ///< Returns true if the tool style is toggle. + bool toggle() const; ///< Gets the tool toggle state (only if tool style is toggle). + item_proxy& toggle(bool toggle_state); ///< Sets the tool toggle state (only if tool style is toggle). + std::string toggle_group() const; ///< Returns the toggle group associated with the tool (only if tool style is toggle). + item_proxy& toggle_group(const ::std::string& group); ///< Adds the tool to a toggle group (only if tool style is toggle). + + private: + nana::toolbar* const tb_; + std::size_t const pos_; }; + struct item_type + { + std::string text; + nana::paint::image image; + unsigned pixels{ 0 }; + unsigned position{ 0 }; // last item position. + nana::size textsize; + bool enable{ true }; + + tool_type type{ tool_type::button }; + bool toggle{ false }; + std::string group; + + item_type(const std::string& text, const nana::paint::image& img, tool_type type) + :text(text), image(img), type(type) + {} + }; + struct toolbar_events : public general_events { @@ -49,7 +86,6 @@ namespace nana basic_event leave; ///< The mouse leaves a control button. }; - struct item_type; class item_container; class drawer @@ -90,6 +126,7 @@ namespace nana { public: using size_type = std::size_t; ///< A type to count the number of elements. + using tool_type = drawerbase::toolbar::tool_type; toolbar() = default; toolbar(window, bool visible, bool detached=false); @@ -102,6 +139,15 @@ namespace nana bool enable(size_type index) const; void enable(size_type index, bool enable_state); + + void tooltype(size_type index, tool_type type); ///< Sets the tool style. + + bool istoggle(size_type index) const; ///< Returns true if the tool style is toggle. + bool toggle(size_type index) const; ///< Gets the tool toggle state (only if tool style is toggle). + void toggle(size_type index, bool toggle_state); ///< Sets the tool toggle state (only if tool style is toggle). + std::string toggle_group(size_type index) const; ///< Returns the toggle group associated with the tool (only if tool style is toggle). + void toggle_group(size_type index, const ::std::string& group); ///< Adds the tool to a toggle group (only if tool style is toggle). + void scale(unsigned s); ///< Sets the scale of control button. /// Enable to place buttons at right part. After calling it, every new button is right aligned. diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 963b99cf..3172e966 100644 --- a/source/gui/widgets/toolbar.cpp +++ b/source/gui/widgets/toolbar.cpp @@ -27,26 +27,6 @@ namespace nana { namespace toolbar { - struct item_type - { - enum kind{ button, container}; - - typedef std::size_t size_type; - - std::string text; - nana::paint::image image; - unsigned pixels{0}; - unsigned position{ 0 }; // last item position. - nana::size textsize; - bool enable{true}; - - kind type; - - item_type(const std::string& text, const nana::paint::image& img, kind type) - :text(text), image(img), type(type) - {} - }; - class item_container { public: @@ -58,7 +38,7 @@ namespace nana clear(); } - void insert(size_type pos, std::string text, const nana::paint::image& img, item_type::kind type) + void insert(size_type pos, std::string text, const nana::paint::image& img, tool_type type) { item_type* m = new item_type(std::move(text), img, type); @@ -70,12 +50,12 @@ namespace nana void push_back(const std::string& text, const nana::paint::image& img) { - insert(cont_.size(), text, img, item_type::kind::button); + insert(cont_.size(), text, img, tool_type::button); } void push_back(const std::string& text) { - insert(cont_.size(), text, nana::paint::image(), item_type::kind::button); + insert(cont_.size(), text, nana::paint::image(), tool_type::button); } //Contributed by kmribti(pr#105) @@ -126,6 +106,45 @@ namespace nana cont_.clear(); } + + void update_toggle_group(item_type* item, bool toggle_state, bool clicked = true) + { + if(!item) + return; + + if(item->group.empty()) + { + item->toggle = toggle_state; + return; + } + + // group rules: + // 1. inside a group only one item at the time is selected + // 2. inside a group one item must always be selected + // 3. a group with only one item IS NOT a group + + bool is_group = false; + + // look for other items inside the group + for(auto i : cont_) + { + if(i == item) + continue; + + if(i && i->group == item->group) + { + if(toggle_state == false && clicked == false) // needs to avoid to break rule no. 2 + return; + + is_group = true; + i->toggle = false; + } + } + + item->toggle = is_group ? true : toggle_state; + } + + private: container_type cont_; size_t right_{ npos }; @@ -152,6 +171,13 @@ namespace nana if (state_t::highlighted == state || state_t::selected == state) graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(state_t::selected == state ? 0x99CCFF : 0xC0DDFC), true); } + else if (item.type == tool_type::toggle && item.toggle) + { + nana::rectangle background_r(x, y, width, height); + graph.rectangle(background_r, false, static_cast(item.enable ? 0x3399FF : 0x999999)); + + graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(item.enable ? 0xC0DDFC : 0x969696), true); + } if(!item.image.empty()) { @@ -378,6 +404,10 @@ namespace nana size_type which = _m_which(arg.pos, false); if(impl_->which == which) { + // update toggle state + auto m = impl_->items.at(impl_->which); + impl_->items.update_toggle_group(m, !m->toggle); + ::nana::arg_toolbar arg{ *widget_, which }; widget_->events().selected.emit(arg, widget_->handle()); @@ -429,9 +459,52 @@ namespace nana //class drawer // Item Proxy - void item_proxy::enable(bool enable_state) + item_proxy::item_proxy(::nana::toolbar* t, std::size_t pos) + : tb_{ t }, pos_{ pos } + {} + + bool item_proxy::enable() const { - widget.enable(button, enable_state); + return tb_->enable(pos_); + } + + item_proxy& item_proxy::enable(bool enable_state) + { + tb_->enable(pos_, enable_state); + return *this; + } + + item_proxy& item_proxy::tooltype(tool_type type) + { + tb_->tooltype(pos_, type); + return *this; + } + + bool item_proxy::istoggle() const + { + return tb_->istoggle(pos_); + } + + bool item_proxy::toggle() const + { + return tb_->toggle(pos_); + } + + item_proxy& item_proxy::toggle(bool toggle_state) + { + tb_->toggle(pos_, toggle_state); + return *this; + } + + std::string item_proxy::toggle_group() const + { + return tb_->toggle_group(pos_); + } + + item_proxy& item_proxy::toggle_group(const ::std::string& group) + { + tb_->toggle_group(pos_, group); + return *this; } }//end namespace toolbar }//end namespace drawerbase @@ -465,14 +538,14 @@ namespace nana { get_drawer_trigger().items().push_back(text, img); API::refresh_window(handle()); - return {*this, get_drawer_trigger().items().size() - 1u}; + return {this, get_drawer_trigger().items().size() - 1u}; } drawerbase::toolbar::item_proxy toolbar::append(const std::string& text) { get_drawer_trigger().items().push_back(text, {}); API::refresh_window(this->handle()); - return {*this, get_drawer_trigger().items().size() - 1u}; + return {this, get_drawer_trigger().items().size() - 1u}; } void toolbar::clear() @@ -507,6 +580,85 @@ namespace nana } } + void toolbar::tooltype(size_type index, tool_type type) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m && m->type != type) + { + m->type = type; + API::refresh_window(this->handle()); + } + } + } + + bool toolbar::istoggle(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return false; + + auto m = items.at(index); + return (m && m->type == tool_type::toggle); + } + + bool toolbar::toggle(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return false; + + auto m = items.at(index); + return (m && m->toggle); + } + + void toolbar::toggle(size_type index, bool toggle_state) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m) + { + items.update_toggle_group(m, toggle_state, false); + + API::refresh_window(this->handle()); + } + } + } + + std::string toolbar::toggle_group(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return ""; + + auto m = items.at(index); + return m ? m->group : ""; + } + + void toolbar::toggle_group(size_type index, const ::std::string& group) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m && (m->group != group)) + { + m->group = group; + API::refresh_window(this->handle()); //XXX + } + } + } + void toolbar::scale(unsigned s) { get_drawer_trigger().scale(s);