/** * A Tabbar implementation * Nana C++ Library(http://www.nanapro.org) * 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 * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/tabbar.hpp * @brief A tabbar contains tab items and toolbox for scrolling, closing, selecting items. * */ #ifndef NANA_GUI_WIDGET_TABBAR_HPP #define NANA_GUI_WIDGET_TABBAR_HPP #include #include "widget.hpp" #include #include namespace nana { template class tabbar; template struct arg_tabbar : public event_arg { tabbar & widget; T & value; arg_tabbar(tabbar& wdg, T& v) : widget(wdg), value{ v } {} }; template struct arg_tabbar_adding : public event_arg { arg_tabbar_adding(tabbar& wdg, std::size_t pos) : widget(wdg), where(pos) {} tabbar & widget; mutable bool add = true; ///< determines whether to add the item std::size_t where; ///< position where to add the item }; template struct arg_tabbar_removed : public arg_tabbar { arg_tabbar_removed(tabbar& wdg, T& v) : arg_tabbar({wdg, v}) {} mutable bool remove = true; ///< determines whether to remove the item mutable bool close_attach_window = true; ///< determines whether to close the attached window. It is ignored if remove is false }; namespace drawerbase { namespace tabbar { template struct tabbar_events : public general_events { using value_type = T; basic_event> adding; basic_event> added; basic_event> activated; basic_event> removed; }; class event_agent_interface { public: virtual ~event_agent_interface() = default; virtual bool adding(std::size_t) = 0; virtual void added(std::size_t) = 0; virtual void activated(std::size_t) = 0; virtual bool removed(std::size_t, bool & close_attached) = 0; }; class item_renderer { public: typedef item_renderer item_renderer_type; typedef ::nana::paint::graphics & graph_reference; enum state_t{disable, normal, highlight, press}; struct item_t { ::nana::rectangle r; ::nana::color bgcolor; ::nana::color fgcolor; }; virtual ~item_renderer() = default; virtual void background(graph_reference, const nana::rectangle& r, const ::nana::color& bgcolor) = 0; virtual void item(graph_reference, const item_t&, bool active, state_t) = 0; virtual void close_fly(graph_reference, const nana::rectangle&, bool active, state_t) = 0; virtual void add(graph_reference, const nana::rectangle&, state_t) = 0; virtual void close(graph_reference, const nana::rectangle&, state_t) = 0; virtual void back(graph_reference, const nana::rectangle&, state_t) = 0; virtual void next(graph_reference, const nana::rectangle&, state_t) = 0; virtual void list(graph_reference, const nana::rectangle&, state_t) = 0; }; template class event_agent : public event_agent_interface { public: using arg_tabbar = ::nana::arg_tabbar; event_agent(::nana::tabbar& tb, DrawerTrigger & dtr) : tabbar_(tb), drawer_trigger_(dtr) {} bool adding(std::size_t pos) override { ::nana::arg_tabbar_adding arg(tabbar_, pos); tabbar_.events().adding.emit(arg, tabbar_); return arg.add; } void added(std::size_t pos) override { if(pos != npos) { drawer_trigger_.at_no_bound_check(pos) = T(); tabbar_.events().added.emit(arg_tabbar({ tabbar_, tabbar_[pos] }), tabbar_); } } void activated(std::size_t pos) override { if(pos != npos) tabbar_.events().activated.emit(arg_tabbar({ tabbar_, tabbar_[pos]}), tabbar_); } bool removed(std::size_t pos, bool & close_attach) override { if (pos != npos) { ::nana::arg_tabbar_removed arg(tabbar_, tabbar_[pos]); tabbar_.events().removed.emit(arg, tabbar_); close_attach = arg.close_attach_window; return arg.remove; } close_attach = true; return true; } private: ::nana::tabbar & tabbar_; DrawerTrigger& drawer_trigger_; }; class layouter; class trigger : public drawer_trigger { public: using native_string_type = ::nana::detail::native_string_type; enum class kits { add, ///< The type identifies the add button of the tabbar's toolbox. scroll, ///< The type identifies the scroll button of the tabbar's toolbox list, ///< The type identifies the list button of the tabbar's toolbox close ///< The type identifies the close button of the tabbar's toolbox }; trigger(); ~trigger(); void activate(std::size_t); std::size_t activated() const; nana::any& at(std::size_t) const; nana::any& at_no_bound_check(std::size_t) const; const pat::cloneable & ext_renderer() const; void ext_renderer(const pat::cloneable&); void set_event_agent(event_agent_interface*); void insert(std::size_t, native_string_type&&, nana::any&&); std::size_t length() const; bool close_fly(bool); window attach(std::size_t, window, bool drop_other); void erase(std::size_t); void tab_color(std::size_t, bool is_bgcolor, const ::nana::color&); void tab_image(size_t, const nana::paint::image&); void text(std::size_t, const native_string_type&); native_string_type text(std::size_t) const; bool toolbox(kits, bool); private: void attached(widget_reference, graph_reference) override; void detached() override; void refresh(graph_reference) override; void mouse_down(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse&) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_leave(graph_reference, const arg_mouse&) override; private: layouter * layouter_; }; } }//end namespace drawerbase /// Analogous to dividers in a notebook or the labels in a file cabinet template class tabbar : public widget_object> { typedef drawerbase::tabbar::trigger drawer_trigger_t; public: using value_type = Type; ///< The type of element data which is stored in the tabbar. using item_renderer = drawerbase::tabbar::item_renderer; ///< A user-defined item renderer should be derived from this interface. using kits = drawer_trigger_t::kits; tabbar() { evt_agent_.reset(new drawerbase::tabbar::event_agent(*this, this->get_drawer_trigger())); this->get_drawer_trigger().set_event_agent(evt_agent_.get()); } tabbar(window wd, bool visible) : tabbar() { this->create(wd, rectangle(), visible); } tabbar(window wd, const rectangle& r = rectangle(), bool visible = true) : tabbar() { this->create(wd, r, visible); } ~tabbar() { this->get_drawer_trigger().set_event_agent(nullptr); } value_type & operator[](std::size_t pos) const { return any_cast(this->get_drawer_trigger().at_no_bound_check(pos)); } void activated(std::size_t pos) /// Activates a tab specified by pos. { this->get_drawer_trigger().activate(pos); } std::size_t activated() const { return this->get_drawer_trigger().activated(); } value_type const & at(std::size_t pos) const /// Returns pos'th element { return any_cast(this->get_drawer_trigger().at(pos)); } void close_fly(bool fly) /// Draw or not a close button in each tab. { if (this->get_drawer_trigger().close_fly(fly)) API::refresh_window(this->handle()); } const pat::cloneable& renderer() const { return this->get_drawer_trigger().ext_renderer(); } void renderer(const pat::cloneable& ir) { this->get_drawer_trigger().ext_renderer(ir); } std::size_t length() const /// Returns the number of items. { return this->get_drawer_trigger().length(); } /// Append a new tab tabbar& append(std::string text, window attach_wd, value_type value = {}) // 2x text convertion. maybe better to duplicate code? { return this->append( static_cast(nana::charset(std::move(text), nana::unicode::utf8)), attach_wd, std::move(value)); } tabbar& append(std::wstring text, window attach_wd, value_type value = {}) { if (attach_wd && API::empty_window(attach_wd)) throw std::invalid_argument("Appening a tab to a tabbar - error: tabbar.attach: invalid window handle"); this->get_drawer_trigger().insert(::nana::npos, to_nstring(std::move(text)), std::move(value)); if (attach_wd) this->attach(this->get_drawer_trigger().length() - 1, attach_wd); API::update_window(*this); return *this; } void push_back(std::string text) /// Append a new item. { this->get_drawer_trigger().insert(::nana::npos, to_nstring(std::move(text)), value_type()); API::update_window(*this); } void insert(std::size_t pos, std::string text, value_type value = {}) { if (pos > length()) throw std::out_of_range("tabbar::insert invalid position"); this->get_drawer_trigger().insert(pos, to_nstring(std::move(text)), std::move(value)); API::update_window(*this); } void insert(std::size_t pos, std::wstring text, value_type value = {}) { if (pos > length()) throw std::out_of_range("tabbar::insert invalid position"); this->get_drawer_trigger().insert(pos, to_nstring(std::move(text)), std::move(value)); API::update_window(*this); } /// Attach a window to a specified tab. When the tab is activated, tabbar shows the attached window. /** * @param pos The position of tab to set the attached window. * @param attach_wd A handle to the window to be set. * @param drop_other Drop the attached windows of other tabs whose attach windows are equal to the parameter attach_wd. If drop_other is true, the other tabs attached windows equal to attach_wd will be dropped. * @return A handle to the last attached window of specified tab. */ window attach(std::size_t pos, window attach_wd, bool drop_other = true) { if (attach_wd && API::empty_window(attach_wd)) throw std::invalid_argument("tabbar.attach: invalid window handle"); return this->get_drawer_trigger().attach(pos, attach_wd, drop_other); } void erase(std::size_t pos) { this->get_drawer_trigger().erase(pos); } void tab_bgcolor(std::size_t pos, const ::nana::color& clr) { this->get_drawer_trigger().tab_color(pos, true, clr); } void tab_fgcolor(std::size_t pos, const ::nana::color& clr) { this->get_drawer_trigger().tab_color(pos, false, clr); } void tab_image(std::size_t pos, const nana::paint::image& img) { this->get_drawer_trigger().tab_image(pos, img); } /// Sets buttons of the tabbar's toolbox, refer to notes for more details. void toolbox(kits kit, bool enable) { if (this->get_drawer_trigger().toolbox(kit, enable)) API::refresh_window(this->handle()); } void text(std::size_t pos, const std::string& str) /// Sets the title of the specified item, If pos is invalid, the method throws an std::out_of_range object. { this->get_drawer_trigger().text(pos, to_nstring(str)); } std::string text(std::size_t pos) const /// Returns a title of a specified item, If pos is invalid, the method trhows a std::out_of_range object. { return to_utf8(this->get_drawer_trigger().text(pos)); } private: std::unique_ptr > evt_agent_; }; }//end namespace nana namespace nana { namespace drawerbase { namespace tabbar_lite { class model; struct events : public general_events { basic_event selected; }; class driver : public drawer_trigger { public: driver(); ~driver(); model* get_model() const noexcept; private: //Overrides drawer_trigger's method void attached(widget_reference, graph_reference) override; void refresh(graph_reference) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_leave(graph_reference, const arg_mouse&) override; void mouse_down(graph_reference, const arg_mouse&) override; private: model* const model_; }; } }//end namespace drawerbase class tabbar_lite : public widget_object { public: tabbar_lite() = default; tabbar_lite(window, bool visible = true, const::nana::rectangle& = {}); public: //capacity std::size_t length() const; public: //modifiers void attach(std::size_t pos, window); window attach(std::size_t pos) const; void push_back(std::string text, ::nana::any par = {}); void push_front(std::string text, ::nana::any par = {}); std::size_t selected() const; void erase(std::size_t pos, bool close_attached = true); }; } #include #endif