diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index 39637097..e7860b80 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -220,12 +220,14 @@ #endif #undef _nana_std_has_string_view +#undef _nana_std_has_returnable_emplace_back #if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && defined(_MSVC_LANG) && _MSVC_LANG >= 201703)) || \ ((__cplusplus >= 201703L) && \ (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__ >= 400) || \ (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 701))) \ ) # define _nana_std_has_string_view +# define _nana_std_has_returnable_emplace_back #endif diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index c9d12f88..71ad399d 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -65,11 +65,12 @@ namespace nana bool review_utf8(std::string& text); const std::string& to_utf8(const std::string&); - std::string to_utf8(const std::wstring&); #ifdef _nana_std_has_string_view + std::string to_utf8(std::wstring_view sv); std::wstring to_wstring(std::string_view utf8_str); #else + std::string to_utf8(const std::wstring&); std::wstring to_wstring(const std::string& utf8_str); #endif diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index 551c65d3..f6ba12df 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -1,7 +1,7 @@ /** * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -77,6 +77,7 @@ namespace nana public: item_proxy(drawer_impl*, std::size_t pos); item_proxy& text(const ::std::string&); + ::std::string text() const; item_proxy& select(); bool selected() const; @@ -103,22 +104,19 @@ namespace nana } template - item_proxy& value(const T& t) + item_proxy& value(T&& val) { - *_m_anyobj(true) = t; - return *this; - } - - template - item_proxy& value(T&& t) - { - *_m_anyobj(true) = ::std::move(t); + *_m_anyobj(true) = ::std::forward(val); return *this; } public: /// Behavior of Iterator's value_type +#ifdef _nana_std_has_string_view + bool operator==(::std::string_view) const; +#else bool operator==(const ::std::string&) const; bool operator==(const char*) const; +#endif /// Behavior of Iterator item_proxy & operator=(const item_proxy&); @@ -192,19 +190,11 @@ namespace nana return _m_at_key(std::move(p)); } - template - void erase_key(const Key& kv) - { - typedef typename nana::detail::type_escape::type key_t; - std::unique_ptr p(new nana::key >(kv)); - _m_erase(p.get()); - } - template void erase_key(Key&& kv) { typedef typename nana::detail::type_escape::type key_t; - std::unique_ptr p(new nana::key >(std::move(kv))); + std::unique_ptr p(new nana::key >(std::forward(kv))); _m_erase(p.get()); } diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index d3bb5bd8..6e518fe9 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -933,15 +933,20 @@ namespace nana template item_proxy & value(T&& t) { - *_m_value(true) = std::forward(t); + *_m_value(true) = ::std::forward(t); return *this; } /// Behavior of Iterator's value_type +#ifdef _nana_std_has_string_view + bool operator==(::std::string_view sv) const; + bool operator==(::std::wstring_view sv) const; +#else bool operator==(const char * s) const; bool operator==(const wchar_t * s) const; bool operator==(const ::std::string& s) const; bool operator==(const ::std::wstring& s) const; +#endif /// Behavior of Iterator item_proxy & operator=(const item_proxy&); diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index ea61349e..52c33b70 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -1,7 +1,7 @@ /* * Text Token Stream * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -22,6 +22,7 @@ #include #include +#include namespace nana{ namespace widgets{ namespace skeletons { @@ -95,10 +96,19 @@ namespace nana{ namespace widgets{ namespace skeletons return std::stoi(idstr_, nullptr, 0); } private: - static bool _m_unicode_word_breakable(wchar_t ch) + /* + static bool _m_unicode_word_breakable(wchar_t ch) //deprecated { return ((0x4E00 <= ch) && (ch <= 0x9FFF)); } + */ + + static bool _m_unicode_word_breakable(const wchar_t* ch) noexcept + { + if (*ch) + return unicode_wordbreak(*ch, ch[1]); + return true; + } //Read the data token token _m_token() @@ -112,14 +122,14 @@ namespace nana{ namespace widgets{ namespace skeletons idstr_.clear(); idstr_.append(1, ch); - if(_m_unicode_word_breakable(ch)) + if (_m_unicode_word_breakable(iptr_)) { ++iptr_; return token::data; } ch = *++iptr_; - while((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(ch))) + while((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(iptr_))) { idstr_.append(1, ch); diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index c46aa1ca..01c9a9d1 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -17,6 +17,8 @@ #include "skeletons/textbase_export_interface.hpp" #include "skeletons/text_editor_part.hpp" +#include + namespace nana { class textbox; @@ -173,6 +175,13 @@ namespace nana /// Read the text from a specified line with a set offset. It returns true for success. bool getline(std::size_t line_index,std::size_t offset,std::string& text) const; + /// Read the text from a specified line; returns an empty optional on failure + std::optional getline(std::size_t pos) const; + + ///Read the text from a specified line with a set offset. Returns an empty optional for + /// failure. + std::optional getline(std::size_t line_index, std::size_t offset) const; + /// Gets the caret position /// Returns true if the caret is in the area of display, false otherwise. bool caret_pos(point& pos, bool text_coordinate) const; diff --git a/include/nana/unicode_bidi.hpp b/include/nana/unicode_bidi.hpp index 72d65860..5efd7003 100644 --- a/include/nana/unicode_bidi.hpp +++ b/include/nana/unicode_bidi.hpp @@ -71,6 +71,8 @@ namespace nana std::vector unicode_reorder(const wchar_t* text, std::size_t length); + bool unicode_wordbreak(wchar_t left, wchar_t right); + } #include diff --git a/source/deploy.cpp b/source/deploy.cpp index 06bbda02..1ae165ef 100644 --- a/source/deploy.cpp +++ b/source/deploy.cpp @@ -175,12 +175,12 @@ namespace nana return str; } - std::string to_utf8(const std::wstring& text) +#ifdef _nana_std_has_string_view + std::string to_utf8(std::wstring_view text) { - return ::nana::charset(text).to_bytes(::nana::unicode::utf8); + return ::nana::charset(std::wstring{text}).to_bytes(::nana::unicode::utf8); } -#ifdef _nana_std_has_string_view std::wstring to_wstring(std::string_view utf8_str) { if (utf8_str.empty()) @@ -189,6 +189,11 @@ namespace nana return ::nana::charset(std::string{ utf8_str.data(), utf8_str.size() }, unicode::utf8); } #else + std::string to_utf8(const std::wstring& text) + { + return ::nana::charset(text).to_bytes(::nana::unicode::utf8); + } + std::wstring to_wstring(const std::string& utf8_str) { return ::nana::charset(utf8_str, ::nana::unicode::utf8); diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index 3fc6fcaf..d6f4b99b 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -984,6 +984,15 @@ namespace nana{ nana::detail::platform_scope_guard lock; + if(point{x, y} == window_position(wd)) + { + //Returns if the requested position is same with the current position. + //In some X-Server versions/implementations, XMoveWindow() doesn't generate + //a ConfigureNotify if the requested position is same with the current position. + //It causes that x11_wait_for always waiting for the ConfigureNotify. + return; + } + XWindowAttributes attr; ::XGetWindowAttributes(disp, reinterpret_cast(wd), &attr); if(attr.map_state == IsUnmapped) diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 56ce8b55..619446a2 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -821,6 +821,14 @@ namespace nana } /// Behavior of Iterator's value_type +#ifdef _nana_std_has_string_view + bool item_proxy::operator == (::std::string_view s) const + { + if (pos_ == nana::npos) + return false; + return (impl_->at(pos_).item_text == s); + } +#else bool item_proxy::operator == (const ::std::string& s) const { if (pos_ == nana::npos) @@ -834,6 +842,7 @@ namespace nana return false; return (impl_->at(pos_).item_text == s); } +#endif /// Behavior of Iterator diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 790ae3b8..65b72c81 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -20,6 +20,8 @@ #include #include +#define VISUAL_LINES + namespace nana { namespace drawerbase @@ -28,6 +30,7 @@ namespace nana { class renderer { +#ifndef VISUAL_LINES typedef widgets::skeletons::dstream::linecontainer::iterator iterator; struct pixel_tag @@ -37,6 +40,32 @@ namespace nana std::size_t baseline; //The baseline for drawing text. std::vector values; //line values }; +#else + //Iterator of content element in a line. + using content_element_iterator = widgets::skeletons::dstream::linecontainer::const_iterator; //subsitute for member type iterator + + struct visual_line //subsitute of pixel_tag + { + struct element + { + content_element_iterator content_element; + std::pair range; //A part of text in a text element. first: text begin, second: text length + + element(const content_element_iterator& iterator, std::size_t range_begin, std::size_t range_end): + content_element(iterator), + range(range_begin, range_end) + { + } + }; + + int x_base; //The x position where this line starts. + + std::size_t extent_height_px; + std::size_t baseline; //The baseline for rendering text. + std::vector elements; //description of text element in this rendering line. + }; + +#endif //this is a helper variable, it just keeps the status while drawing. struct render_status @@ -46,8 +75,12 @@ namespace nana align_v text_align_v; nana::point pos; +#ifndef VISUAL_LINES std::vector pixels; - std::size_t index; +#else + std::vector vslines; //The lines description of a line of text. substitute of member pixels. +#endif + std::size_t index; //indicates the current rendering visual line. }; struct traceable @@ -102,18 +135,32 @@ namespace nana rs.text_align = th; rs.text_align_v = tv; +#ifndef VISUAL_LINES std::deque > pixel_lines; +#else + //All visual lines data of whole text. + std::deque> content_lines; +#endif std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted. for (auto & line : dstream_) { +#ifndef VISUAL_LINES _m_line_pixels(line, def_line_pixels, rs); for (auto & m : rs.pixels) extent_v_pixels += m.pixels; pixel_lines.emplace_back(std::move(rs.pixels)); +#else + _m_prepare_visual_lines(graph, line, def_line_pixels, rs); + + for (auto & vsline : rs.vslines) + extent_v_pixels += vsline.extent_height_px; + + content_lines.emplace_back(std::move(rs.vslines)); +#endif if(extent_v_pixels >= graph.height()) break; @@ -129,13 +176,17 @@ namespace nana else rs.pos.y = 0; +#ifndef VISUAL_LINES auto pixels_iterator = pixel_lines.begin(); - +#else + auto vsline_iterator = content_lines.begin(); +#endif for (auto & line : dstream_) { if (rs.pos.y >= static_cast(graph.height())) break; +#ifndef VISUAL_LINES rs.index = 0; rs.pixels.clear(); @@ -148,6 +199,17 @@ namespace nana break; rs.pos.y += static_cast(rs.pixels.back().pixels); +#else + rs.index = 0; + rs.vslines.clear(); + rs.vslines.swap(*vsline_iterator++); + rs.pos.x = rs.vslines.front().x_base; + + if (!_m_foreach_visual_line(graph, rs)) + break; + + rs.pos.y += static_cast(rs.vslines.back().extent_height_px); +#endif } graph.typeface(pre_font); @@ -194,8 +256,13 @@ namespace nana for(auto & line: dstream_) { +#ifndef VISUAL_LINES rs.pixels.clear(); unsigned w = _m_line_pixels(line, def_line_pixels, rs); +#else + rs.vslines.clear(); + auto w = _m_prepare_visual_lines(graph, line, def_line_pixels, rs); +#endif if(limited && (w > limited)) w = limited; @@ -203,8 +270,13 @@ namespace nana if(retsize.width < w) retsize.width = w; +#ifndef VISUAL_LINES for (auto & px : rs.pixels) retsize.height += static_cast(px.pixels); +#else + for (auto& vsline : rs.vslines) + retsize.height += static_cast(vsline.extent_height_px); +#endif } return retsize; @@ -315,6 +387,7 @@ namespace nana } } +#ifndef VISUAL_LINES void _m_align_x_base(const render_status& rs, pixel_tag & px, unsigned w) noexcept { switch(rs.text_align) @@ -330,7 +403,209 @@ namespace nana break; } } +#else + void _m_prepare_x(const render_status& rs, visual_line & vsline, unsigned w) noexcept + { + switch (rs.text_align) + { + case align::left: + vsline.x_base = 0; + break; + case align::center: + vsline.x_base = (static_cast(rs.allowed_width - w) >> 1); + break; + case align::right: + vsline.x_base = static_cast(rs.allowed_width - w); + break; + } + } +#endif +#ifdef VISUAL_LINES + + /** + * prepare data for rendering a line of text. + */ + unsigned _m_prepare_visual_lines(graph_reference graph, dstream::linecontainer& line, unsigned def_line_px, render_status& rs) + { + unsigned abs_text_px = 0; + unsigned max_ascent = 0; + unsigned max_descent = 0; + unsigned max_content_height = 0; + + int text_pos = 0; + + std::vector vsline_elements; + + for (auto i = line.cbegin(); i != line.cend(); ++i) + { + auto const data = i->data_ptr; + auto fblock = i->fblock_ptr; + + abs_text_px += data->size().width; + + unsigned ascent = 0; + unsigned descent = 0; + + + auto extent_size = data->size(); + + //Check if the content is displayed in current line. + if ((0 == rs.allowed_width) || (text_pos + extent_size.width <= rs.allowed_width)) + { + text_pos += static_cast(extent_size.width); + + //Adjust height of extent_size for special text alignement. + if (fblock::aligns::baseline == fblock->text_align) + { + ascent = static_cast(data->ascent()); + descent = static_cast(extent_size.height - ascent); + + if (max_descent < descent) + max_descent = descent; + + if ((false == data->is_text()) && (extent_size.height < max_ascent + max_descent)) + extent_size.height = max_ascent + max_descent; + } + + if (max_ascent < ascent) max_ascent = ascent; + if (max_descent < descent) max_descent = descent; + if (max_content_height < extent_size.height) max_content_height = extent_size.height; + vsline_elements.emplace_back(i, 0, data->text().size()); + + continue; + } + + //make a visual line for existing vsline elements + if (text_pos) + { +#ifdef _nana_std_has_returnable_emplace_back + auto & vsline = rs.vslines.emplace_back(); +#else + rs.vslines.emplace_back(); + auto & vsline = rs.vslines.back(); +#endif + _m_prepare_x(rs, vsline, static_cast(text_pos)); + + if (max_ascent + max_descent > max_content_height) + max_content_height = max_descent + max_ascent; + else + max_ascent = max_content_height - max_descent; + + vsline.extent_height_px = max_content_height; + vsline.baseline = max_ascent; + vsline.elements.swap(vsline_elements); + } + + text_pos = 0; + max_content_height = max_ascent = max_descent = 0; + //Adjust height of extent_size for special text alignement. + if (fblock::aligns::baseline == fblock->text_align) + { + ascent = static_cast(data->ascent()); + descent = static_cast(extent_size.height - ascent); + + if (max_descent < descent) + max_descent = descent; + + if ((false == data->is_text()) && (extent_size.height < max_ascent + max_descent)) + extent_size.height = max_ascent + max_descent; + } + + if (max_ascent < ascent) max_ascent = ascent; + if (max_descent < descent) max_descent = descent; + if (max_content_height < extent_size.height) max_content_height = extent_size.height; + + if (data->is_text()) + { + _m_change_font(graph, fblock); + //Split a text into multiple lines + auto rest_extent_size = extent_size.width; + std::size_t text_begin = 0; + while (text_begin < data->text().size()) + { + unsigned sub_text_px = 0; + auto sub_text_len = _m_fit_text(graph, data->text().substr(text_begin), rs.allowed_width, sub_text_px); + + if (text_begin + sub_text_len < data->text().size()) + { + //make a new visual line +#ifdef _nana_std_has_returnable_emplace_back + auto & vsline = rs.vslines.emplace_back(); +#else + rs.vslines.emplace_back(); + auto & vsline = rs.vslines.back(); +#endif + _m_prepare_x(rs, vsline, sub_text_px); + + vsline.extent_height_px = max_content_height; + vsline.baseline = max_ascent; + vsline.elements.emplace_back(i, text_begin, sub_text_len); + } + else + { + //the last part, write it to vsline_elements to keep the status for next line element(next i) + vsline_elements.emplace_back(i, text_begin, sub_text_len); + + text_pos = sub_text_px; + } + + text_begin += sub_text_len; + } + } + else + { + //the last part, write it to vsline_elements to keep the status for next line element(next i) + vsline_elements.emplace_back(i, 0, 0); + + text_pos = static_cast(i->data_ptr->size().width); + } + } + + if (!vsline_elements.empty()) + { +#ifdef _nana_std_has_returnable_emplace_back + auto & vsline = rs.vslines.emplace_back(); +#else + rs.vslines.emplace_back(); + auto & vsline = rs.vslines.back(); +#endif + _m_prepare_x(rs, vsline, static_cast(text_pos)); + + if (max_ascent + max_descent > max_content_height) + max_content_height = max_descent + max_ascent; + else + max_ascent = max_content_height - max_descent; + + vsline.extent_height_px = max_content_height; + vsline.baseline = max_ascent; + vsline.elements.swap(vsline_elements); + } + + return abs_text_px; + } + + //Get the length of characters in a text whose length in pixels doesn't beyond the limited width. + static unsigned _m_fit_text(graph_reference graph, const std::wstring& text, unsigned limited_width_px, unsigned& text_px) noexcept + { +#ifdef _nana_std_has_string_view + auto pxbuf = graph.glyph_pixels(text); +#else + std::unique_ptr pxbuf(new unsigned[text.size()]); + graph.glyph_pixels(text.c_str(), text.size(), pxbuf.get()); +#endif + + text_px = 0; + for (std::size_t i = 0; i < text.size(); ++i) + { + if (text_px + pxbuf[i] > limited_width_px) + return i; + + text_px += pxbuf[i]; + } + return text.size(); + } +#else unsigned _m_line_pixels(dstream::linecontainer& line, unsigned def_line_pixels, render_status & rs) { if (line.empty()) @@ -441,7 +716,9 @@ namespace nana } return total_w; } +#endif +#ifndef VISUAL_LINES bool _m_each_line(graph_reference graph, dstream::linecontainer&, render_status& rs) { std::wstring text; @@ -501,7 +778,36 @@ namespace nana } return (rs.pos.y <= lastpos); } +#else + bool _m_foreach_visual_line(graph_reference graph, render_status& rs) + { + std::wstring text; + + content_element_iterator block_start; + auto const bottom = static_cast(graph.height()) - 1; + + for (auto & vsline : rs.vslines) + { + rs.pos.x = vsline.x_base; + for (auto& content_elm : vsline.elements) + { + _m_draw_vsline_element(graph, content_elm, rs); + } + + ++rs.index; //next line index + rs.pos.y += vsline.extent_height_px; + + if (rs.pos.y > bottom) + return false; + } + + return (rs.pos.y <= bottom); + } +#endif + + +#if 0 //deprecated static bool _m_overline(const render_status& rs, int right, bool equal_required) noexcept { if(align::left == rs.text_align) @@ -509,7 +815,24 @@ namespace nana return (equal_required ? rs.pixels[rs.index].x_base <= 0 : rs.pixels[rs.index].x_base < 0); } +#endif +#ifdef VISUAL_LINES + static int _m_vsline_element_top(const visual_line& vsline, fblock* fblock_ptr, const data* data_ptr) noexcept + { + switch (fblock_ptr->text_align) + { + case fblock::aligns::center: + return static_cast(vsline.extent_height_px - data_ptr->size().height) / 2; + case fblock::aligns::bottom: + return static_cast(vsline.extent_height_px - data_ptr->size().height); + case fblock::aligns::baseline: + return static_cast(vsline.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height)); + default: break; + } + return 0; + } +#else static int _m_text_top(const pixel_tag& px, fblock* fblock_ptr, const data* data_ptr) { switch(fblock_ptr->text_align) @@ -524,7 +847,52 @@ namespace nana } return 0; } +#endif +#ifdef VISUAL_LINES + void _m_draw_vsline_element(graph_reference graph, const visual_line::element& vsline_elm, render_status& rs) + { + auto data = vsline_elm.content_element->data_ptr; + auto fblock = vsline_elm.content_element->fblock_ptr; + + if (data->is_text()) + { + auto const text = data->text().c_str() + vsline_elm.range.first; + auto const reordered = unicode_reorder(text, vsline_elm.range.second); + + _m_change_font(graph, fblock); + for (auto & bidi : reordered) + { + auto extent_size = data->size(); +#ifdef _nana_std_has_string_view + std::wstring_view text_sv{ bidi.begin, static_cast(bidi.end - bidi.begin) }; + if (data->text().size() != text_sv.size()) + extent_size = graph.text_extent_size(text_sv); + + const int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data); + graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock)); +#else + std::wstring text{ bidi.begin, static_cast(bidi.end - bidi.begin) }; + if (data->text().size() != text.size()) + extent_size = graph.text_extent_size(text); + + const int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data); + graph.string({ rs.pos.x, y }, text, _m_fgcolor(fblock)); +#endif + _m_insert_if_traceable(rs.pos.x, y, extent_size, fblock); + rs.pos.x += static_cast(extent_size.width); + } + } + else + { + int y = rs.pos.y + _m_vsline_element_top(rs.vslines[rs.index], fblock, data); + + data->nontext_render(graph, rs.pos.x, y); + _m_insert_if_traceable(rs.pos.x, y, data->size(), fblock); + rs.pos.x += static_cast(data->size().width); + } + } +#else void _m_draw_block(graph_reference graph, const std::wstring& s, dstream::linecontainer::iterator block_start, render_status& rs) { auto const reordered = unicode_reorder(s.data(), s.length()); @@ -550,9 +918,35 @@ namespace nana fblock * fblock_ptr = i->fblock_ptr; data * data_ptr = i->data_ptr; +#if 1 + const int range_text_area = static_cast(rs.allowed_width) - rs.pos.x; + + _m_change_font(graph, fblock_ptr); + + auto text_extent_size = data_ptr->size(); +#ifndef _nana_std_has_string_view + std::wstring_view text_sv{ data_ptr->text().c_str() + text_range.first, text_range.second }; + if (data_ptr->text().size() != text_sv.size()) + text_extent_size = graph.text_extent_size(text_sv); +#else + auto text_sv = data_ptr->text().substr(text_range.first, text_range.second); + if (data_ptr->text().size() != text_sv.size()) + text_extent_size = graph.text_extent_size(text_sv); +#endif + if ((static_cast(text_extent_size.width) > range_text_area) && (rs.pos.x != px.x_base)) + { + //Change a new line + rs.pos.y += static_cast(px.pixels); + px = rs.pixels[++rs.index]; + rs.pos.x = px.x_base; + } + + const int y = rs.pos.y + _m_text_top(px, fblock_ptr, data_ptr); + graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock_ptr)); +#else const int w = static_cast(rs.allowed_width) - rs.pos.x; - nana::size sz = data_ptr->size(); - if ((static_cast(sz.width) > w) && (rs.pos.x != px.x_base)) + nana::size text_extent_size = data_ptr->size(); + if ((static_cast(text_extent_size.width) > w) && (rs.pos.x != px.x_base)) { //Change a new line rs.pos.y += static_cast(px.pixels); @@ -569,7 +963,7 @@ namespace nana if (text_range.second != text_sv.size()) { text_sv = text_sv.substr(text_range.first, text_range.second); - sz = graph.text_extent_size(text_sv); + text_extent_size = graph.text_extent_size(text_sv); } graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock_ptr)); @@ -581,15 +975,15 @@ namespace nana else { auto str = data_ptr->text().substr(text_range.first, text_range.second); - sz = graph.text_extent_size(str); + text_extent_size = graph.text_extent_size(str); graph.string({ rs.pos.x, y }, str, _m_fgcolor(fblock_ptr)); } #endif +#endif //#if 0 - - _m_insert_if_traceable(rs.pos.x, y, sz, fblock_ptr); - rs.pos.x += static_cast(sz.width); + _m_insert_if_traceable(rs.pos.x, y, text_extent_size, fblock_ptr); + rs.pos.x += static_cast(text_extent_size.width); if(text_range.second < len) { @@ -601,6 +995,7 @@ namespace nana } } } +#endif //VISUAL_LINES static std::pair _m_locate(dstream::linecontainer::iterator& i, std::size_t pos) { diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index f25ac277..7f16fd3d 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -4824,6 +4824,17 @@ namespace nana } //Behavior of Iterator's value_type +#ifdef _nana_std_has_string_view + bool item_proxy::operator==(std::string_view sv) const + { + return (text(0) == sv); + } + + bool item_proxy::operator==(std::wstring_view sv) const + { + return (text(0) == to_utf8(sv)); + } +#else bool item_proxy::operator==(const char * s) const { return this->operator==(std::string(s)); @@ -4843,6 +4854,7 @@ namespace nana { return (text(0) == to_utf8(s)); } +#endif item_proxy & item_proxy::operator=(const item_proxy& rhs) { diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index db57ae82..6836c231 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -377,6 +377,27 @@ namespace drawerbase { return false; } + std::optional textbox::getline(std::size_t pos) const + { + auto result = std::string{}; + if ( getline(pos, result) ) + { + return { std::move(result) }; + } + return {}; + } + + std::optional textbox::getline(std::size_t line_index, std::size_t offset) const + { + auto result = std::string{}; + if ( getline(line_index, offset, result) ) + { + return { std::move(result) }; + } + return {}; + } + + /// Gets the caret position bool textbox::caret_pos(point& pos, bool text_coordinate) const { diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index 15b77d12..3591d6ef 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -946,4 +946,68 @@ namespace nana { return unicode_bidi{}.reorder(text, length); } + + enum class unicode_character_type + { + format, + katakana, + aletter, + midletter, + midnumlet, + midnum, + numeric, + other + }; + + //http://www.unicode.org/reports/tr29/WordBreakTest.html + unicode_character_type unicode_char_type(unsigned long ch) + { + if ((0x0600 <= ch && ch <= 0x0603) || (0x06DD == ch || 0x070F == ch || 0x17B4 == ch || 0x17B5 == ch) || (0x200C <= ch && ch <= 0x200F) || + (0x202A <= ch && ch <= 0x202E) || (0x2060 <= ch && ch <= 0x2063) || (0x206A <= ch && ch <= 0x206F) || (0xFEFF == ch) || (0xFFF9 <= ch && ch <= 0xFFFB) || + (0x1D173 <= ch && ch <= 0x1D17A) || (0xE0001 == ch) || (0xE0020 <= ch && ch <= 0xE007F)) + return unicode_character_type::format; + + if ((0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x31F0 <= ch && ch <= 0x31FF) || (0xFF66 <= ch && ch <= 0xFF9F)) + return unicode_character_type::katakana; + + if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || (0x00AA == ch || 0x00B5 == ch || 0x00BA == ch) || (0x00C0 <= ch && ch <= 0x00D6) || + (0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch || ch <= 0x02C1)) + return unicode_character_type::aletter; + + if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch) + return unicode_character_type::midletter; + + if ('.' == ch || '\\' == ch || ':' == ch) + return unicode_character_type::midnumlet; + + if (0x2024 <= ch && ch <= 0x2026) + return unicode_character_type::midnum; + + if (('0' <= ch && ch <= '9') || (0x0660 <= ch && ch <= 0x0669) || (0x06F0 <= ch && ch <= 0x06F9)) + return unicode_character_type::numeric; + + return unicode_character_type::other; + } + + bool unicode_wordbreak(wchar_t left, wchar_t right) + { + auto l_type = unicode_char_type(left); + auto r_type = unicode_char_type(right); + + switch (l_type) + { + case unicode_character_type::format: + case unicode_character_type::midletter: + case unicode_character_type::midnumlet: + case unicode_character_type::midnum: + case unicode_character_type::other: + return (r_type != unicode_character_type::format); + case unicode_character_type::katakana: + return !(unicode_character_type::format == r_type) || (unicode_character_type::katakana == r_type); + case unicode_character_type::aletter: + case unicode_character_type::numeric: + return !(unicode_character_type::format == r_type) || (unicode_character_type::aletter == r_type) || (unicode_character_type::numeric == r_type); + } + return true; + } }//end namespace nana