diff --git a/source/paint/text_renderer.cpp b/source/paint/text_renderer.cpp index 6f5654dd..db5ecfc5 100644 --- a/source/paint/text_renderer.cpp +++ b/source/paint/text_renderer.cpp @@ -16,7 +16,7 @@ namespace nana auto const end = str + len; for(auto i = str; i != end; ++i) { - if(*i == '\n') + if('\n' == *i) { top += static_cast(f(top, str, i - str)); str = i + 1; @@ -26,166 +26,125 @@ namespace nana f(top, str, end - str); } - struct draw_string - { - drawable_type dw; - const int x, endpos; - align text_align; - draw_string(drawable_type dw, int x, int endpos, align ta) - : dw(dw), x(x), endpos(endpos), text_align(ta) - {} + class string_drawer + { + public: + string_drawer(graphics& graph, int left, int right, align ta, bool use_ellipsis): + graph_(graph), + left_(left), + right_(right), + text_align_(ta) + { + if (use_ellipsis) + { +#ifdef _nana_std_has_string_view + ellipsis_px_ = graph.text_extent_size(std::string_view{ "...", 3 }).width; +#else + ellipsis_px_ = graph.text_extent_size("...", 3).width; +#endif + } + } unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { + auto const drawable = graph_.handle(); auto const reordered = unicode_reorder(buf, bufsize); - if (reordered.empty()) - return 0; + + unsigned return_max_height = 0; + unsigned string_px = 0; + std::vector word_metrics; + for (auto & ent : reordered) + { + auto word_sz = detail::text_extent_size(drawable, ent.begin, ent.end - ent.begin); + word_metrics.push_back(word_sz); - nana::point pos{ x, top }; - unsigned pixels = 0; + string_px += word_sz.width; + if (word_sz.height > return_max_height) + return_max_height = word_sz.height; + } - switch(text_align) + auto text_align = text_align_; + // Checks if ellipsis is enabled and the total pixels of string is larger than the space. + if (ellipsis_px_ && (string_px > static_cast(right_ - left_))) + { + //The string should be drawn from left most point no matter what text align is. + text_align = align::left; + } + + nana::point pos{ left_, top }; + + auto wdm = word_metrics.data(); + switch (text_align) { case align::left: - for(auto & ent : reordered) + for (auto & ent : reordered) { - std::size_t len = ent.end - ent.begin; - nana::size ts = detail::text_extent_size(dw, ent.begin, len); + if (pos.x + static_cast(wdm->width) > 0) + { + if (pos.x + static_cast(wdm->width) <= right_ - static_cast(ellipsis_px_)) + { + //This word can be fully painted. + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); + } + else + { + //This word is painted partially. Firstly, paints the word on a dummy graphics buffer. - if(ts.height > pixels) pixels = ts.height; - - if(pos.x + static_cast(ts.width) > 0) - detail::draw_string(dw, pos, ent.begin, len); + nana::rectangle r{ nana::size{ static_cast(right_ - ellipsis_px_) - pos.x, wdm->height } }; - pos.x += static_cast(ts.width); - if(pos.x >= endpos) + nana::paint::graphics dummy({ r.width, r.height }); + dummy.typeface(graph_.typeface()); + + dummy.bitblt(r, graph_, pos); + +#ifdef _nana_std_has_string_view + dummy.string({}, { ent.begin, ent.end - ent.begin }, graph_.palette(true)); +#else + dummy.palette(true, graph_.palette(true)); + dummy.string({}, ent.begin, ent.end - ent.begin); +#endif + r.x = pos.x; + r.y = top; + graph_.bitblt(r, dummy); + if (ellipsis_px_) + detail::draw_string(drawable, point{ right_ - static_cast(ellipsis_px_), top }, L"...", 3); + break; + } + } + + pos.x += static_cast(wdm->width); + if (pos.x > right_ - static_cast(ellipsis_px_)) break; + + ++wdm; } break; case align::center: + pos.x = (right_ - left_ - string_px) / 2; + for (auto & ent : reordered) { - unsigned lenpx = 0; - std::unique_ptr entity_pxs(new unsigned[reordered.size()]); - - auto ent_px = entity_pxs.get(); - - for(auto & ent : reordered) - { - auto ts = detail::text_extent_size(dw, ent.begin, ent.end - ent.begin); - if(ts.height > pixels) pixels = ts.height; - lenpx += ts.width; - *ent_px++ = ts.width; - } - - pos.x += (endpos - pos.x - static_cast(lenpx))/2; - ent_px = entity_pxs.get(); - - for(auto & ent : reordered) - { - if (pos.x + static_cast(*ent_px) > 0) - detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin); - - pos.x += static_cast(*ent_px++); - - if(pos.x >= endpos) - break; - } + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); + pos.x += (wdm++)->width; } break; case align::right: + wdm = word_metrics.data() + word_metrics.size() - 1; + pos.x = right_; + for (auto i = reordered.crbegin(); i != reordered.crend(); ++i) { - int xend = endpos; - std::swap(pos.x, xend); - for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) - { - auto & ent = *i; - std::size_t len = ent.end - ent.begin; - nana::size ts = detail::text_extent_size(dw, ent.begin, len); - if(ts.height > pixels) pixels = ts.height; - - if(pos.x > xend) - { - pos.x -= static_cast(ts.width); - detail::draw_string(dw, pos, i->begin, len); - } - - if(pos.x <= xend || pos.x <= 0) - break; - } + pos.x -= (wdm--)->width; + detail::draw_string(drawable, pos, i->begin, i->end - i->begin); } break; } - return pixels; - } - }; - - struct draw_string_omitted - { - graphics & graph; - int x, endpos; - unsigned omitted_pixels; - - draw_string_omitted(graphics& graph, int x, int endpos, bool omitted) - : graph(graph), x(x), endpos(endpos) - { -#ifdef _nana_std_has_string_view - omitted_pixels = (omitted ? graph.text_extent_size(std::string_view{ "...", 3 }).width : 0); -#else - omitted_pixels = (omitted ? graph.text_extent_size("...", 3).width : 0); -#endif - if (endpos - x > static_cast(omitted_pixels)) - this->endpos -= omitted_pixels; - else - this->endpos = x; - } - - unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) - { - drawable_type dw = graph.handle(); - ::nana::point pos{ x, top }; - unsigned pixels = 0; - - auto const reordered = unicode_reorder(buf, bufsize); - - for(auto & i : reordered) - { - std::size_t len = i.end - i.begin; - nana::size ts = detail::text_extent_size(dw, i.begin, len); - if(ts.height > pixels) pixels = ts.height; - - if(pos.x + static_cast(ts.width) <= endpos) - { - detail::draw_string(dw, pos, i.begin, len); - pos.x += static_cast(ts.width); - } - else - { - nana::rectangle r; - r.width = endpos - pos.x; - r.height = ts.height; - - nana::paint::graphics dum_graph({ r.width, r.height }); - - dum_graph.bitblt(r, graph, pos); - -#ifdef _nana_std_has_string_view - dum_graph.string({}, { i.begin, len }, graph.palette(true)); -#else - dum_graph.palette(true, graph.palette(true)); - dum_graph.string({}, i.begin, len); -#endif - - r.x = pos.x; - r.y = top; - graph.bitblt(r, dum_graph); - if(omitted_pixels) - detail::draw_string(dw, point{ endpos, top }, L"...", 3); - break; - } - } - return pixels; + return return_max_height; } + private: + graphics& graph_; + const int left_, right_; //the range of rendering area in x-axis + const align text_align_; + unsigned ellipsis_px_{ 0 }; }; @@ -380,16 +339,10 @@ namespace nana static std::size_t find_splitted(std::size_t begin, std::size_t end, int x, int endpos, unsigned * pxbuf) { - unsigned acc_width = 0; - for(std::size_t i = begin; i < end; ++i) + for (std::size_t i = begin; i < end; ++i) { - if(x + static_cast(acc_width + pxbuf[i]) > endpos) - { - if(i == begin) - ++i; - return i; - } - acc_width += pxbuf[i]; + if ((x += static_cast(pxbuf[i])) > endpos) + return (begin == i ? i + 1 : i); } return end; } @@ -399,12 +352,11 @@ namespace nana wchar_t ch = str[index]; if(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { - wchar_t prch; if(index) { - prch = str[index - 1]; + auto prch = str[index - 1]; if('0' <= ch && ch <= '9') - return !(('0' <= prch && prch <= '9') || (str[index - 1] == '-')); + return !(('0' <= prch && prch <= '9') || (prch == '-')); return (('z' < prch || prch < 'a') && ('Z' < prch || prch < 'A')); } @@ -481,6 +433,7 @@ namespace nana { for(std::size_t i = idx_head; i < len; ++i) xpos += static_cast(pxbuf[i]); + break; } //Check the word whether it is splittable. @@ -585,8 +538,8 @@ namespace nana { if (graph_) { - helper::draw_string ds(graph_.handle(), pos.x, static_cast(graph_.width()), text_align_); - helper::for_each_line(str, len, pos.y, ds); + helper::string_drawer sd{ graph_, pos.x, static_cast(graph_.width()), text_align_, false }; + helper::for_each_line(str, len, pos.y, sd); } } @@ -594,8 +547,8 @@ namespace nana { if (graph_) { - helper::draw_string_omitted dso(graph_, pos.x, pos.x + static_cast(restricted_pixels), omitted); - helper::for_each_line(str, len, pos.y, dso); + helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast(restricted_pixels), text_align_, omitted }; + helper::for_each_line(str, len, pos.y, sd); } }