diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 717d6d91..4827a227 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1219,7 +1219,6 @@ namespace nana unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width - unsigned suspension_width{ 8 }; ///< def= . the trigger will set this to the width if ("...") unsigned text_margin{ 5 }; ///< def= 5. Additional or extended with added (before) to the text width to determine the cell width. cell_w = text_w + ext_w +1 unsigned item_height_ex{ 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex @@ -1603,6 +1602,8 @@ the nana::detail::basic_window member pointer scheme * It returns true to deselect the selected items. It returns false to cancel to deselect the selected items. */ void set_deselect(std::function predicate); + + unsigned suspension_width() const; 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/include/nana/paint/text_renderer.hpp b/include/nana/paint/text_renderer.hpp index 96fbbfdb..3510bb3d 100644 --- a/include/nana/paint/text_renderer.hpp +++ b/include/nana/paint/text_renderer.hpp @@ -10,14 +10,20 @@ namespace nana { public: using graph_reference = graphics &; + + enum class mode + { + truncate_with_ellipsis, + truncate_letter_with_ellipsis, + word_wrap + }; text_renderer(graph_reference graph, align = align::left); - nana::size extent_size(int x, int y, const wchar_t*, std::size_t len, unsigned restricted_pixels) const; + nana::size extent_size(int x, int y, const wchar_t*, std::size_t len, unsigned space_pixels) const; void render(const point&, const wchar_t*, std::size_t len); - void render(const point&, const wchar_t*, std::size_t len, unsigned restricted_pixels, bool omitted); - void render(const point&, const wchar_t*, std::size_t len, unsigned restricted_pixels); + void render(const point&, const wchar_t*, std::size_t len, unsigned space_pixels, mode); private: graph_reference graph_; align text_align_; diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 97295d60..74dda204 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -140,7 +140,7 @@ namespace nana //draw caption auto text = to_wstring(API::window_caption(window_handle_)); - text_rd_->render({ 3, 1 }, text.data(), text.size(), graph.size().width - 20, true); + text_rd_->render({ 3, 1 }, text.data(), text.size(), graph.size().width - 20, paint::text_renderer::mode::truncate_with_ellipsis); //draw x button auto r = _m_button_area(); diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 96ff7c25..ecc09817 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -261,7 +261,7 @@ namespace nana{ namespace drawerbase graph.palette(true, text_color); if (attr_.omitted) - tr.render(pos, txtptr, txtlen, omitted_pixels, true); + tr.render(pos, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); else #ifdef _nana_std_has_string_view graph.bidi_string(pos, { txtptr, txtlen }); @@ -276,9 +276,9 @@ namespace nana{ namespace drawerbase graph.palette(true, color{ colors::white }); if(attr_.omitted) { - tr.render(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen, omitted_pixels, true); + tr.render(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); graph.palette(true, color{ colors::gray }); - tr.render(pos, txtptr, txtlen, omitted_pixels, true); + tr.render(pos, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); } else { diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index fc08fcac..4741d5b2 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -70,13 +70,13 @@ namespace nana{ namespace drawerbase if (!wdg->enabled()) { graph.palette(true, colors::white); - tr.render({ 17 + interval, 2 }, title.c_str(), title.length(), pixels); + tr.render({ 17 + interval, 2 }, title.c_str(), title.length(), pixels, paint::text_renderer::mode::word_wrap); graph.palette(true, static_cast(0x808080)); } else graph.palette(true, wdg->fgcolor()); - tr.render({ 16 + interval, 1 }, title.c_str(), title.length(), pixels); + tr.render({ 16 + interval, 1 }, title.c_str(), title.length(), pixels, paint::text_renderer::mode::word_wrap); } //draw crook diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 8ef8a453..494daa4c 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -2059,6 +2059,11 @@ namespace nana }; } + unsigned suspension_width() const + { + return (graph ? graph->text_extent_size(L"...").width : 0); + } + bool cs_status(index_pair abs_pos, bool for_selection) const { if (abs_pos.is_category()) @@ -3352,7 +3357,7 @@ namespace nana else { //Default scheme - new_w = (std::max)(new_w, essence_->scheme_ptr->suspension_width + essence_->scheme_ptr->min_column_width); + new_w = (std::max)(new_w, essence_->suspension_width() + essence_->scheme_ptr->min_column_width); } if(col.width_px != new_w) @@ -3997,6 +4002,7 @@ namespace nana if (draw_column) { + //Draw item text paint::aligner text_aligner{*graph, col.alignment}; unsigned text_margin_right = 0; @@ -4142,7 +4148,6 @@ namespace nana if (graph.text_metrics(as, ds, il)) essence_->text_height = as + ds; - essence_->scheme_ptr->suspension_width = graph.text_extent_size("...").width; essence_->calc_content_size(true); } @@ -6114,6 +6119,12 @@ namespace nana _m_ess().pred_msup_deselect = std::move(predicate); } + unsigned listbox::suspension_width() const + { + nana::internal_scope_guard lock; + return _m_ess().suspension_width(); + } + drawerbase::listbox::essence & listbox::_m_ess() const { return get_drawer_trigger().ess(); diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index e3b181e4..389b39b9 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -147,7 +147,7 @@ namespace nana nana::paint::text_renderer tr(graph); auto wstr = to_wstring(text); - tr.render(pos, wstr.c_str(), wstr.length(), text_pixels, true); + tr.render(pos, wstr.c_str(), wstr.length(), text_pixels, paint::text_renderer::mode::truncate_with_ellipsis); } void sub_arrow(graph_reference graph, const nana::point& pos, unsigned pixels, const attr&) diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 4cf17919..6a79c648 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -1050,7 +1050,7 @@ namespace nana std::wstring wtext = to_wstring(item.text); tr.render({ m.r.x + 24, m.r.y + static_cast(m.r.height - ts.height) / 2 }, - wtext.c_str(), wtext.length(), basis_.item_pixels - 24 - 18, true); + wtext.c_str(), wtext.length(), basis_.item_pixels - 24 - 18, paint::text_renderer::mode::truncate_with_ellipsis); } } diff --git a/source/paint/text_renderer.cpp b/source/paint/text_renderer.cpp index db5ecfc5..19b305a9 100644 --- a/source/paint/text_renderer.cpp +++ b/source/paint/text_renderer.cpp @@ -46,6 +46,7 @@ namespace nana } } + unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { auto const drawable = graph_.handle(); @@ -66,7 +67,7 @@ namespace nana 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_))) + if (ellipsis_px_ && (static_cast(string_px) > right_ - left_)) { //The string should be drawn from left most point no matter what text align is. text_align = align::left; @@ -99,7 +100,7 @@ namespace nana dummy.bitblt(r, graph_, pos); #ifdef _nana_std_has_string_view - dummy.string({}, { ent.begin, ent.end - ent.begin }, graph_.palette(true)); + dummy.string({}, { ent.begin, static_cast(ent.end - ent.begin) }, graph_.palette(true)); #else dummy.palette(true, graph_.palette(true)); dummy.string({}, ent.begin, ent.end - ent.begin); @@ -151,46 +152,54 @@ namespace nana struct draw_string_auto_changing_lines { graphics & graph; - int x, endpos; - align text_align; + const int left, right; + const align text_align; - draw_string_auto_changing_lines(graphics& graph, int x, int endpos, align ta) - : graph(graph), x(x), endpos(endpos), text_align(ta) + draw_string_auto_changing_lines(graphics& graph, int left, int right, align ta): + graph(graph), + left(left), + right(right), + text_align(ta) {} unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { - unsigned pixels = 0; + unsigned return_max_height = 0; - auto const dw = graph.handle(); - unsigned str_w = 0; + auto const drawable = graph.handle(); + unsigned string_px = 0; - std::vector ts_keeper; + std::vector word_metrics; auto const reordered = unicode_reorder(buf, bufsize); for(auto & i : reordered) { - nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); - if(ts.height > pixels) pixels = ts.height; - ts_keeper.emplace_back(ts); - str_w += ts.width; + auto word_sz = detail::text_extent_size(drawable, i.begin, i.end - i.begin); + + word_metrics.emplace_back(word_sz); + string_px += word_sz.width; + + if (return_max_height < word_sz.height) + return_max_height = word_sz.height; } + const nana::size* wdm = word_metrics.data(); + //Test whether the text needs the new line. - if(x + static_cast(str_w) > endpos) + if(left + static_cast(string_px) > right) { - pixels = 0; - unsigned line_pixels = 0; - nana::point pos{ x, top }; - int orig_top = top; - auto i_ts_keeper = ts_keeper.cbegin(); + unsigned max_height = 0; + + nana::point pos{ left, top }; + const int orig_top = top; + for(auto & i : reordered) { - if(line_pixels < i_ts_keeper->height) - line_pixels = i_ts_keeper->height; + if(max_height < wdm->height) + max_height = wdm->height; - bool beyond_edge = (pos.x + static_cast(i_ts_keeper->width) > endpos); + bool beyond_edge = (pos.x + static_cast(wdm->width) > right); if(beyond_edge) { const std::size_t len = i.end - i.begin; @@ -210,10 +219,10 @@ namespace nana { auto pxbuf = pixel_buf.get(); - idx_splitted = find_splitted(idx_head, len, pos.x, endpos, pxbuf); + idx_splitted = find_splitted(idx_head, len, pos.x, right, pxbuf); if(idx_splitted == len) { - detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); + detail::draw_string(drawable, pos, i.begin + idx_head, idx_splitted - idx_head); for(std::size_t i = idx_head; i < len; ++i) pos.x += static_cast(pxbuf[i]); @@ -223,17 +232,16 @@ namespace nana //Check the word whether it is splittable. if(splittable(i.begin, idx_splitted)) { - detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); + detail::draw_string(drawable, pos, i.begin + idx_head, idx_splitted - idx_head); idx_head = idx_splitted; - pos.x = x; - pos.y += static_cast(line_pixels); - line_pixels = i_ts_keeper->height; + pos.x = left; + pos.y += static_cast(max_height); } else { //Search the splittable character from idx_head to idx_splitted const wchar_t * u = i.begin + idx_splitted; - const wchar_t * head = i.begin + idx_head; + const wchar_t * const head = i.begin + idx_head; for(; head < u; --u) { @@ -243,98 +251,94 @@ namespace nana if(u != head) { - detail::draw_string(dw, pos, head, u - head); + detail::draw_string(drawable, pos, head, u - head); idx_head += u - head; - pos.x = x; - pos.y += static_cast(line_pixels); - line_pixels = i_ts_keeper->height; + pos.x = left; + pos.y += static_cast(max_height); } else { u = i.begin + idx_splitted; - const wchar_t * end = i.begin + len; - for(; u < end; ++u) + for(; u < i.begin + len; ++u) { if(splittable(head, u - head)) break; } std::size_t splen = u - head; - pos.y += static_cast(line_pixels); - pos.x = x; - detail::draw_string(dw, pos, head, splen); - line_pixels = i_ts_keeper->height; + pos.y += static_cast(max_height); + pos.x = left; + detail::draw_string(drawable, pos, head, splen); for(std::size_t k = idx_head; k < idx_head + splen; ++k) pos.x += static_cast(pxbuf[k]); - if (pos.x >= endpos) + if (pos.x >= right) { - pos.x = x; - pos.y += static_cast(line_pixels); + pos.x = left; + pos.y += static_cast(wdm->height); } idx_head += splen; } } + max_height = wdm->height; }while(idx_head < len); } else { - pos.x = x; - pos.y += static_cast(line_pixels); - detail::draw_string(dw, pos, i.begin, 1); - pos.x += static_cast(i_ts_keeper->width); + pos.x = left; + pos.y += static_cast(max_height); + detail::draw_string(drawable, pos, i.begin, 1); + pos.x += static_cast(wdm->width); } - line_pixels = 0; + max_height = 0; } else { - detail::draw_string(dw, pos, i.begin, i.end - i.begin); - pos.x += static_cast(i_ts_keeper->width); + detail::draw_string(drawable, pos, i.begin, i.end - i.begin); + pos.x += static_cast(wdm->width); } - ++i_ts_keeper; + ++wdm; } - pixels = (top - orig_top) + line_pixels; + return_max_height = (top - orig_top) + max_height; } else { + point pos{ left, top }; //The text could be drawn in a line. if((align::left == text_align) || (align::center == text_align)) { - point pos{ x, top }; if(align::center == text_align) - pos.x += (endpos - x - static_cast(str_w)) / 2; - auto i_ts_keeper = ts_keeper.cbegin(); + pos.x += (right - left - static_cast(string_px)) / 2; + for(auto & ent : reordered) { - const nana::size & ts = *i_ts_keeper; + if (pos.x + static_cast(wdm->width) > 0) + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); - if (pos.x + static_cast(ts.width) > 0) - detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin); - - pos.x += static_cast(ts.width); - ++i_ts_keeper; + pos.x += static_cast(wdm->width); + ++wdm; } } else if(align::right == text_align) { - point pos{ endpos, top }; - auto i_ts_keeper = ts_keeper.crbegin(); + pos.x = right; for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) { + if (pos.x < 0) + break; + auto & ent = *i; std::size_t len = ent.end - ent.begin; - const nana::size & ts = *i_ts_keeper; - pos.x -= static_cast(ts.width); - if (pos.x >= 0) - detail::draw_string(dw, pos, ent.begin, len); - ++i_ts_keeper; + pos.x -= static_cast(wdm->width); + detail::draw_string(drawable, pos, ent.begin, len); + ++wdm; } } } - return pixels; + return return_max_height; } static std::size_t find_splitted(std::size_t begin, std::size_t end, int x, int endpos, unsigned * pxbuf) @@ -370,44 +374,53 @@ namespace nana struct extent_auto_changing_lines { graphics & graph; - int x, endpos; + const int left, right; + unsigned extents; - extent_auto_changing_lines(graphics& graph, int x, int endpos) - : graph(graph), x(x), endpos(endpos), extents(0) + extent_auto_changing_lines(graphics& graph, int left, int right): + graph(graph), + left(left), + right(right), + extents(0) {} unsigned operator()(int top, const wchar_t * buf, std::size_t bufsize) { - unsigned pixels = 0; + unsigned return_max_height = 0; - drawable_type dw = graph.handle(); - unsigned str_w = 0; - std::vector ts_keeper; + auto drawable = graph.handle(); + + std::vector word_metrics; + unsigned string_px = 0; auto const reordered = unicode_reorder(buf, bufsize); for(auto & i : reordered) { - nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); - ts_keeper.emplace_back(ts); - str_w += ts.width; + auto word_sz = detail::text_extent_size(drawable, i.begin, i.end - i.begin); + word_metrics.emplace_back(word_sz); + string_px += word_sz.width; + + if (return_max_height < word_sz.height) + return_max_height = word_sz.height; } - auto i_ts_keeper = ts_keeper.cbegin(); //Test whether the text needs the new line. - if(x + static_cast(str_w) > endpos) + if(left + static_cast(string_px) > right) { - unsigned line_pixels = 0; - int xpos = x; - int orig_top = top; + unsigned max_height = 0; + int xpos = left; + const int orig_top = top; + + auto wdm = word_metrics.data(); for(auto & i : reordered) { - if(line_pixels < i_ts_keeper->height) - line_pixels = i_ts_keeper->height; + if(max_height < wdm->height) + max_height = wdm->height; - bool beyond_edge = (xpos + static_cast(i_ts_keeper->width) > endpos); + bool beyond_edge = (xpos + static_cast(wdm->width) > right); if(beyond_edge) { std::size_t len = i.end - i.begin; @@ -427,7 +440,7 @@ namespace nana do { - idx_splitted = draw_string_auto_changing_lines::find_splitted(idx_head, len, xpos, endpos, pxbuf); + idx_splitted = draw_string_auto_changing_lines::find_splitted(idx_head, len, xpos, right, pxbuf); if(idx_splitted == len) { @@ -440,9 +453,9 @@ namespace nana if(draw_string_auto_changing_lines::splittable(i.begin, idx_splitted)) { idx_head = idx_splitted; - xpos = x; - top += line_pixels; - line_pixels = i_ts_keeper->height; + xpos = left; + top += max_height; + max_height = wdm->height; } else { @@ -456,14 +469,11 @@ namespace nana break; } - if(u != head) - { - idx_head += u - head; - xpos = x; - top += line_pixels; - line_pixels = i_ts_keeper->height; - } - else + xpos = left; + top += max_height; + max_height = wdm->height; + + if(u == head) { u = i.begin + idx_splitted; const wchar_t * end = i.begin + len; @@ -473,52 +483,45 @@ namespace nana break; } std::size_t splen = u - head; - top += line_pixels; - xpos = x; - line_pixels = i_ts_keeper->height; for(std::size_t k = idx_head; k < idx_head + splen; ++k) xpos += static_cast(pxbuf[k]); - if(xpos >= endpos) + if(xpos >= right) { - xpos = x; - top += line_pixels; + xpos = left; + top += max_height; } idx_head += splen; } + else + idx_head += u - head; } }while(idx_head < len); } else - xpos = x + static_cast(i_ts_keeper->width); + xpos = left + static_cast(wdm->width); - line_pixels = 0; + max_height = 0; } else - xpos += static_cast(i_ts_keeper->width); + xpos += static_cast(wdm->width); - ++i_ts_keeper; + ++wdm; } - pixels = (top - orig_top) + line_pixels; + return_max_height = (top - orig_top) + max_height; } - else - { - while(i_ts_keeper != ts_keeper.cend()) - { - const nana::size & ts = *(i_ts_keeper++); - if(ts.height > pixels) pixels = ts.height; - } - } - extents += pixels; - return pixels; + + extents += return_max_height; + return return_max_height; } }; }//end namespace helper //class text_renderer - text_renderer::text_renderer(graph_reference graph, align ta) - : graph_(graph), text_align_(ta) + text_renderer::text_renderer(graph_reference graph, align ta): + graph_(graph), + text_align_(ta) {} nana::size text_renderer::extent_size(int x, int y, const wchar_t* str, std::size_t len, unsigned restricted_pixels) const @@ -538,26 +541,25 @@ namespace nana { if (graph_) { - helper::string_drawer sd{ graph_, pos.x, static_cast(graph_.width()), text_align_, false }; + helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast(graph_.width()), text_align_, false }; helper::for_each_line(str, len, pos.y, sd); } } - void text_renderer::render(const point& pos, const wchar_t* str, std::size_t len, unsigned restricted_pixels, bool omitted) + void text_renderer::render(const point& pos, const wchar_t* str, std::size_t len, unsigned space_pixels, mode rendering_mode) { - if (graph_) + if (graph_ && str && len && space_pixels) { - 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); - } - } - - void text_renderer::render(const point& pos, const wchar_t * str, std::size_t len, unsigned restricted_pixels) - { - if (graph_) - { - helper::draw_string_auto_changing_lines dsacl(graph_, pos.x, pos.x + static_cast(restricted_pixels), text_align_); - helper::for_each_line(str, len, pos.y, dsacl); + if (mode::truncate_letter_with_ellipsis == rendering_mode || mode::truncate_with_ellipsis == rendering_mode) + { + helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast(space_pixels), text_align_, true }; + helper::for_each_line(str, len, pos.y, sd); + } + else if (mode::word_wrap == rendering_mode) + { + helper::draw_string_auto_changing_lines dsacl(graph_, pos.x, pos.x + static_cast(space_pixels), text_align_); + helper::for_each_line(str, len, pos.y, dsacl); + } } } //end class text_renderer