From 42dd27b68f8fc5a5f367ca184ce49c245af16191 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Aug 2015 01:38:23 +0800 Subject: [PATCH] add a new tabbar_lite widget --- include/nana/gui/widgets/tabbar.hpp | 47 +++++ include/nana/paint/graphics.hpp | 4 +- source/gui/widgets/tabbar.cpp | 293 ++++++++++++++++++++++++++++ source/paint/graphics.cpp | 13 +- 4 files changed, 355 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index 94cf2407..1160c724 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -334,4 +334,51 @@ namespace nana std::unique_ptr > evt_agent_; }; }//end namespace nana + + +namespace nana +{ + namespace ng + { + namespace drawerbase + { + namespace tabbar_lite + { + class model; + + class driver + : public drawer_trigger + { + public: + driver(); + ~driver(); + + model* get_model(); + 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& = {}); + + + void push_back(std::string text, ::nana::any par = {}); + void push_front(std::string text, ::nana::any par = {}); + }; + } +} + #endif diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index e790fc89..17a31491 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -96,7 +96,8 @@ namespace nana ::nana::size glyph_extent_size(const char_t*, std::size_t length, std::size_t begin, std::size_t end) const; ::nana::size glyph_extent_size(const string&, std::size_t length, std::size_t begin, std::size_t end) const; bool glyph_pixels(const char_t *, std::size_t length, unsigned* pxbuf) const; - ::nana::size bidi_extent_size(const string&) const; + ::nana::size bidi_extent_size(const std::wstring&) const; + ::nana::size bidi_extent_size(const std::string&) const; bool text_metrics(unsigned & ascent, unsigned& descent, unsigned& internal_leading) const; @@ -136,6 +137,7 @@ namespace nana void set_text_color(const ::nana::color&); unsigned bidi_string(const nana::point&, const char_t *, std::size_t len); + unsigned bidi_string(const point& pos, const char*, std::size_t len); void blend(const ::nana::rectangle& r, const ::nana::color&, double fade_rate); diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 7a4aaf02..6532bf6e 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -1270,3 +1270,296 @@ namespace nana }//end namespace tabbar }//end namespace drawerbase }//end namespace nana + +#include +namespace nana +{ + namespace ng + { + namespace drawerbase + { + namespace tabbar_lite + { + struct item + { + ::std::string text; + ::nana::any value; + ::std::pair pos_ends; + + item(std::string t, ::nana::any v) + : text(std::move(t)), value(std::move(v)) + {} + }; + + void calc_px(std::vector& values, unsigned limit) + { + unsigned base = 0; + auto count = values.size(); + + while (count) + { + unsigned minv = static_cast(-1); + + unsigned minv_count = 0; + for (auto u : values) + { + if (u >= base && u < minv) + { + minv_count = 1; + minv = u; + } + else if (minv == u) + minv_count++; + } + + count -= minv_count; + if (minv * count >= limit) + { + auto piece = limit / double(count); + double deviation = 0; + for (auto & u : values) + { + if (minv >= u) + { + auto px = piece + deviation; + u = static_cast(px); + deviation = px - u; + } + } + return; + } + + base = minv; + limit -= minv_count * minv; + } + } + + class model + { + struct indexes + { + std::size_t hovered_pos{ npos }; + std::size_t active_pos{ 0 }; + }; + public: + using graph_reference = ::nana::paint::graphics&; + static const std::size_t npos = static_cast(-1); + + void set_widget(widget& wdg) + { + widget_ = &wdg; + } + + ::nana::dev::widget_traits::scheme_type & scheme() + { + return API::scheme(*widget_); + } + + std::forward_list& items() + { + return items_; + } + + bool track_pointer(const point& pos) + { + std::size_t item_pos = 0; + bool matched = false; + for (auto & m : items_) + { + if (m.pos_ends.first <= pos.x && pos.x < m.pos_ends.second) + { + matched = true; + break; + } + + ++item_pos; + } + + if (!matched) + item_pos = npos; + + if (indexes_.hovered_pos == item_pos) + return false; + + indexes_.hovered_pos = item_pos; + return true; + } + + indexes& get_indexes() + { + return indexes_; + } + private: + widget * widget_{ nullptr }; + std::forward_list items_; + indexes indexes_; + }; + + class renderer + { + public: + using graph_reference = ::nana::paint::graphics&; + + void render(graph_reference graph, model& model) + { + _m_calc_metrics(graph, model.items()); + + auto & scheme = model.scheme(); + + //draw background + graph.rectangle(true, scheme.background); + + auto & indexes = model.get_indexes(); + std::size_t pos = 0; + for (auto & m : model.items()) + { + rectangle r{ m.pos_ends.first, 0, static_cast(m.pos_ends.second - m.pos_ends.first), graph.height() }; + if (indexes.active_pos == pos) + { + graph.set_color(colors::white); + graph.set_text_color(colors::black); + } + else + { + ::nana::color bgcolor(colors::coral); + + if (pos == indexes.hovered_pos) + bgcolor = bgcolor.blend(colors::white, 0.5); + + graph.set_color(bgcolor); + graph.set_text_color(colors::white); + } + + graph.rectangle(r, true); + graph.bidi_string({ m.pos_ends.first + 5, 0 }, m.text.data(), m.text.size()); + + ++pos; + } + + } + private: + void _m_calc_metrics(graph_reference graph, std::forward_list& items) + { + const auto height_px = graph.height(); + std::vector pxs; + + unsigned pixels = 0; + for (auto & m : items) + { + auto ts = graph.bidi_extent_size(m.text); + pxs.push_back(ts.width + 12); + pixels += ts.width + 12; + } + + if (pixels > graph.width()) + calc_px(pxs, graph.width()); + + auto i = pxs.cbegin(); + int pos = 0; + for (auto & m : items) + { + m.pos_ends.first = pos; + m.pos_ends.second = (pos += static_cast(*i++)); + } + } + }; + + + //class driver + driver::driver() + : model_(new model) + { + } + + driver::~driver() + { + delete model_; + } + + model* driver::get_model() + { + return model_; + } + + void driver::attached(widget_reference wdg, graph_reference) + { + model_->set_widget(wdg); + } + + //Overrides drawer_trigger's method + void driver::refresh(graph_reference graph) + { + renderer rd; + rd.render(graph, *model_); + } + + void driver::mouse_move(graph_reference graph, const arg_mouse& arg) + { + if (!model_->track_pointer(arg.pos)) + return; + + refresh(graph); + API::lazy_refresh(); + } + + void driver::mouse_leave(graph_reference graph, const arg_mouse&) + { + if (model_->get_indexes().hovered_pos == model_->npos) + return; + + refresh(graph); + API::lazy_refresh(); + } + + void driver::mouse_down(graph_reference graph, const arg_mouse&) + { + auto & indexes = model_->get_indexes(); + if (indexes.hovered_pos == model_->npos) + return; + + indexes.active_pos = indexes.hovered_pos; + refresh(graph); + API::lazy_refresh(); + } + //end class driver + } + } + + //class tabbar + + tabbar_lite::tabbar_lite(window parent_wd, bool visible, const ::nana::rectangle& r) + { + this->create(parent_wd, r, visible); + } + + void tabbar_lite::push_back(std::string text, ::nana::any any) + { + auto & items = get_drawer_trigger().get_model()->items(); + internal_scope_guard lock; + + auto i = items.cbefore_begin(); + while (true) + { + auto next = i++; + if (i == items.cend()) + { + items.emplace_after(next, std::move(text), std::move(any)); + break; + } + + } + + API::refresh_window(handle()); + } + + void tabbar_lite::push_front(std::string text, ::nana::any any) + { + auto & items = get_drawer_trigger().get_model()->items(); + internal_scope_guard lock; + + items.emplace_front(std::move(text), std::move(any)); + API::refresh_window(handle()); + } + //end class tabbar + } +}//end namespace nana diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 70bba1ef..2a997679 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -451,7 +451,7 @@ namespace paint return true; } - nana::size graphics::bidi_extent_size(const nana::string& str) const + nana::size graphics::bidi_extent_size(const std::wstring& str) const { nana::size sz; #if defined NANA_UNICODE @@ -472,6 +472,11 @@ namespace paint return sz; } + ::nana::size graphics::bidi_extent_size(const std::string& str) const + { + return bidi_extent_size(std::wstring{ ::nana::charset(str, ::nana::unicode::utf8) }); + } + bool graphics::text_metrics(unsigned & ascent, unsigned& descent, unsigned& internal_leading) const { if(handle_) @@ -880,6 +885,12 @@ namespace paint return static_cast(moved_pos.x - pos.x); } + unsigned graphics::bidi_string(const point& pos, const char* str, std::size_t len) + { + std::wstring wstr = ::nana::charset(std::string(str, str + len), ::nana::unicode::utf8); + return bidi_string(pos, wstr.data(), wstr.size()); + } + void graphics::blend(const nana::rectangle& r, const ::nana::color& clr, double fade_rate) { if (handle_)