diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d023469..80f0e8c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # Robert Hauck - Enable support for PNG/Freetype # Qiangqiang Wu - Add biicode support # Ariel Vina-Rodriguez (qPCR4vir) -# Pavel O. - fix compilation with boost::filesystem (#281) +# Pavel O. - fix compilation with boost::filesystem (#281) # Frostbane - Add option for compiling a shared library (#263,#265) # # Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized. @@ -156,25 +156,25 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if(NANA_CMAKE_SHARED_LIB) list(APPEND NANA_LINKS -lgcc -lstdc++ -pthread) else() - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") - # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") + if(MINGW) + set(CMAKE_EXE_LINKER_FLAGS "-static -pthread") + else() + set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") + endif() endif(NANA_CMAKE_SHARED_LIB) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - # GCC 4.9 + # GCC 4.9 list(APPEND NANA_LINKS "-lboost_system -lboost_thread") - elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - # IS_GNUCXX < 5.3 + # IS_GNUCXX < 5.3 else() list(APPEND NANA_LINKS -lstdc++fs) - endif() - -endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW + endif() +endif() if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++") list(APPEND NANA_LINKS -stdlib=libstdc++) endif () diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index 39637097..ef5d8e9f 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_emplace_return_type #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_emplace_return_type #endif diff --git a/include/nana/gui/tooltip.hpp b/include/nana/gui/tooltip.hpp index e724d19b..89ec2c1a 100644 --- a/include/nana/gui/tooltip.hpp +++ b/include/nana/gui/tooltip.hpp @@ -1,6 +1,6 @@ /* * A Tooltip Implementation - * 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 diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index d3bb5bd8..473ba9b9 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -809,7 +809,6 @@ namespace nana /// operate with absolute positions and contain only the position but montain pointers to parts of the real items /// item_proxy self, it references and iterators are not invalidated by sort() class item_proxy - //: public std::iterator //deprecated : public ::nana::widgets::detail::widget_iterator { public: @@ -984,7 +983,6 @@ namespace nana }; class cat_proxy - //: public std::iterator //deprecated : public ::nana::widgets::detail::widget_iterator { public: diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index d3536182..dd24d365 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -191,14 +191,14 @@ namespace nana{ namespace widgets void draw_corner(); void render(bool focused); public: - void put(std::wstring); + void put(std::wstring, bool perform_event); void put(wchar_t); void copy() const; void cut(); void paste(); - void enter(bool record_undo = true); + void enter(bool record_undo, bool perform_event); void del(); - void backspace(bool record_undo = true); + void backspace(bool record_undo, bool perform_event); void undo(bool reverse); void set_undo_queue_length(std::size_t len); void move_ns(bool to_north); //Moves up and down @@ -243,9 +243,9 @@ namespace nana{ namespace widgets void _m_reset(); //Inserts text at position where the caret is - ::nana::upoint _m_put(::std::wstring); + ::nana::upoint _m_put(::std::wstring, bool perform_event); - ::nana::upoint _m_erase_select(); + ::nana::upoint _m_erase_select(bool perform_event); ::std::wstring _m_make_select_string() const; static bool _m_resolve_text(const ::std::wstring&, std::vector> & lines); diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index ea61349e..f94dfea8 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,9 +96,11 @@ 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(const wchar_t* ch) noexcept { - return ((0x4E00 <= ch) && (ch <= 0x9FFF)); + if (*ch) + return unicode_wordbreak(*ch, ch[1]); + return true; } //Read the data token @@ -112,14 +115,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/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index 751d9888..d11bc872 100644 --- a/include/nana/gui/widgets/skeletons/textbase.hpp +++ b/include/nana/gui/widgets/skeletons/textbase.hpp @@ -1,7 +1,7 @@ /* * A textbase class 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 @@ -301,6 +301,29 @@ namespace skeletons } } + //Triggers the text_changed event. + //It is exposed for outter classes. For a outter class(eg. text_editor), a changing text content operation + //may contains multiple textbase operations, therefore, the outter class determines when an event should be triggered. + // + //Addtional, using text_changed() method, it is possible to allow a outter class performing some updating operations + //before triggering text_changed event. + void text_changed() + { + if (!changed_) + { + _m_first_change(); + changed_ = true; + } + + if (edited_) + { + if (evt_agent_) + evt_agent_->text_changed(); + + edited_ = false; + } + } + size_type lines() const { return text_cont_.size(); @@ -330,7 +353,7 @@ namespace skeletons _m_at(pos).swap(text); _m_make_max(pos); - _m_edited(); + edited_ = true; } void insert(upoint pos, string_type && str) @@ -351,7 +374,7 @@ namespace skeletons } _m_make_max(pos.y); - _m_edited(); + edited_ = true; } void insertln(size_type pos, string_type&& str) @@ -362,7 +385,7 @@ namespace skeletons text_cont_.emplace_back(new string_type(std::move(str))); _m_make_max(pos); - _m_edited(); + edited_ = true; } void erase(size_type line, size_type pos, size_type count) @@ -378,7 +401,7 @@ namespace skeletons if (attr_max_.line == line) _m_scan_for_max(); - _m_edited(); + edited_ = true; } } @@ -398,7 +421,7 @@ namespace skeletons else if (pos < attr_max_.line) attr_max_.line -= n; - _m_edited(); + edited_ = true; return true; } @@ -426,7 +449,7 @@ namespace skeletons if(pos < attr_max_.line) --attr_max_.line; - _m_edited(); + edited_ = true; } } @@ -514,23 +537,12 @@ namespace skeletons changed_ = false; } - - void _m_edited() - { - if(!changed_) - { - _m_first_change(); - changed_ = true; - } - - if (evt_agent_) - evt_agent_->text_changed(); - } private: std::deque> text_cont_; textbase_event_agent_interface* evt_agent_{ nullptr }; - mutable bool changed_{ false }; + mutable bool changed_{ false }; + mutable bool edited_{ false }; mutable std::string filename_; //A string for the saved filename. const string_type nullstr_; diff --git a/include/nana/threads/pool.hpp b/include/nana/threads/pool.hpp index 38d9952e..1234bcb2 100644 --- a/include/nana/threads/pool.hpp +++ b/include/nana/threads/pool.hpp @@ -1,6 +1,6 @@ /* * A Thread Pool Implementation - * 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 @@ -13,10 +13,14 @@ #ifndef NANA_THREADS_POOL_HPP #define NANA_THREADS_POOL_HPP +#include #include #include #include +#ifndef STD_THREAD_NOT_SUPPORTED +# include +#endif namespace nana{ /// Some mutex classes for synchronizing. @@ -58,9 +62,12 @@ namespace threads pool(const pool&) = delete; pool& operator=(const pool&) = delete; public: - pool(); ///< Creates a group of threads. +#ifndef STD_THREAD_NOT_SUPPORTED + pool(unsigned thread_number = std::thread::hardware_concurrency()); ///< Creates a group of threads. +#else + pool(unsigned thread_number = 0); +#endif pool(pool&&); - pool(std::size_t thread_number); ///< Creates a number of threads specifed by thread_number. ~pool(); ///< waits for the all running tasks till they are finished and skips all the queued tasks. pool& operator=(pool&&); 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/basic_types.cpp b/source/basic_types.cpp index 69c6fa78..dafe8574 100644 --- a/source/basic_types.cpp +++ b/source/basic_types.cpp @@ -186,10 +186,12 @@ namespace nana throw std::invalid_argument(excpt_what); std::vector rgb; - +#ifdef _nana_std_has_emplace_return_type + auto const is_real = (rgb.emplace_back(i->str()).back() == '%'); +#else rgb.emplace_back(i->str()); - const bool is_real = (rgb.back().back() == '%'); +#endif pat.assign(is_real ? "(\\d*\\.)?\\d+\\%" : "\\d+"); for (++i; i != end; ++i) @@ -275,9 +277,13 @@ namespace nana { std::vector rgb; +#ifdef _nana_std_has_emplace_return_type + auto const is_real = (rgb.emplace_back(std::move(str)).back() == '%'); +#else rgb.emplace_back(std::move(str)); const bool is_real = (rgb.back().back() == '%'); +#endif for (int i = 0; i < 2; ++i) { diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 1a58ec6c..bf3ab2fb 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -656,38 +656,6 @@ namespace detail msg_dispatcher_->erase(reinterpret_cast(wd)); platform_scope_guard lock; -#if 0 - auto i = wincontext_.find(wd); - if(i == wincontext_.end()) return; - - if(i->second.owner) - { - auto u = wincontext_.find(i->second.owner); - if(u != wincontext_.end()) - { - auto * vec = u->second.owned; - if(vec) - { - auto j = std::find(vec->begin(), vec->end(), i->first); - if(j != vec->end()) - vec->erase(j); - } - } - } - - auto * vec = i->second.owned; - if(vec) - { - set_error_handler(); - auto & wd_manager = detail::bedrock::instance().wd_manager(); - for(auto u = vec->rbegin(); u != vec->rend(); ++u) - wd_manager.close(wd_manager.root(*u)); - - rev_error_handler(); - } - delete vec; - wincontext_.erase(i); -#else if(umake_owner(wd)) { auto i = wincontext_.find(wd); @@ -708,7 +676,6 @@ namespace detail wincontext_.erase(i); } } -#endif iconbase_.erase(wd); } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index debbbfee..62738e98 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -1133,62 +1133,11 @@ namespace detail wd_manager.do_lazy_refresh(msgwnd, false); break; } -#if 0 - //Fall through - case XLookupChars: - if (msgwnd->flags.enabled) - { - const wchar_t* charbuf; - - nana::detail::charset_conv charset(NANA_UNICODE, "UTF-8"); - const std::string& str = charset.charset(std::string(keybuf, keybuf + len)); - charbuf = reinterpret_cast(str.c_str()); - len = str.size() / sizeof(wchar_t); - - for(int i = 0; i < len; ++i) - { - arg_keyboard arg = modifiers_status; - arg.ignore = false; - arg.key = charbuf[i]; - - // ignore Unicode BOM (it may or may not appear) - if (arg.key == 0xFEFF) continue; - - //Only accept tab when it is not ignored. - if ((keyboard::tab == arg.key) && root_runtime->condition.ignore_tab) - continue; - - if(context.is_alt_pressed) - { - arg.ctrl = arg.shift = false; - arg.evt_code = event_code::shortkey; - brock.shortkey_occurred(true); - auto shr_wd = wd_manager.find_shortkey(native_window, arg.key); - if(shr_wd) - { - arg.window_handle = reinterpret_cast(shr_wd); - brock.emit(event_code::shortkey, shr_wd, arg, true, &context); - } - continue; - } - arg.evt_code = event_code::key_char; - arg.window_handle = reinterpret_cast(msgwnd); - msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); - if(arg.ignore == false && wd_manager.available(msgwnd)) - draw_invoker(&drawer::key_char, msgwnd, arg, &context); - } - - if(brock.shortkey_occurred(false)) - context.is_alt_pressed = false; - } - break; -#else x_lookup_chars(root_runtime, msgwnd, keybuf, len, modifiers_status); break; case XLookupChars: x_lookup_chars(root_runtime, msgwnd, keybuf, len, modifiers_status); break; -#endif } wd_manager.do_lazy_refresh(msgwnd, false); diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index 3fc6fcaf..ef0b1935 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -745,6 +745,14 @@ namespace nana{ { nana::detail::platform_scope_guard psg; Display* disp = restrict::spec.open_display(); + + //Returns if the requested visibility is same with the current status. + //In some X-Server versions/implementations, XMapWindow() doesn't generate + //a ConfigureNotify if the requested visibility is same with the current status. + //It causes that x11_wait_for always waiting for the ConfigureNotify. + if(show == is_window_visible(wd)) + return; + if(show) { ::XMapWindow(disp, reinterpret_cast(wd)); @@ -984,6 +992,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) @@ -1041,6 +1058,16 @@ namespace nana{ XSizeHints hints; nana::detail::platform_scope_guard psg; + + //Returns if the requested rectangle is same with the current rectangle. + //In some X-Server versions/implementations, XMapWindow() doesn't generate + //a ConfigureNotify if the requested rectangle is same with the current rectangle. + //It causes that x11_wait_for always waiting for the ConfigureNotify. + rectangle current_r; + get_window_rect(wd, current_r); + if(r == current_r) + return true; + ::XGetWMNormalHints(disp, reinterpret_cast(wd), &hints, &supplied); if((hints.flags & (PMinSize | PMaxSize)) && (hints.min_width == hints.max_width) && (hints.min_height == hints.max_height)) { @@ -1230,6 +1257,15 @@ namespace nana{ auto disp = restrict::spec.open_display(); nana::detail::platform_scope_guard psg; + //Returns if the requested size is same with the current size. + //In some X-Server versions/implementations, XMapWindow() doesn't generate + //a ConfigureNotify if the requested size is same with the current size. + //It causes that x11_wait_for always waiting for the ConfigureNotify. + rectangle current_r; + get_window_rect(wd, current_r); + if(current_r.dimension() == sz) + return true; + //Check the XSizeHints for testing whether the window is sizable. XSizeHints hints; long supplied; @@ -1265,6 +1301,9 @@ namespace nana{ unsigned border, depth; nana::detail::platform_scope_guard psg; ::XGetGeometry(restrict::spec.open_display(), reinterpret_cast(wd), &root, &x, &y, &r.width, &r.height, &border, &depth); + + auto pos = window_position(wd); + r.position(pos); #endif } diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 785f45be..9d8ebd65 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -85,8 +85,12 @@ namespace nana } } +#ifdef _nana_std_has_emplace_return_type + auto & rep = impl_->base.emplace_back(); +#else impl_->base.emplace_back(); auto & rep = impl_->base.back(); +#endif rep.handle = wd; rep.keys.emplace_back(key); @@ -242,8 +246,12 @@ namespace detail return kv.second; } +#ifdef _nana_std_has_emplace_return_type + return table_.emplace_back(key).second; +#else table_.emplace_back(key); return table_.back().second; +#endif } iterator find(const Key& key) diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 5b22491e..9b278111 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -742,8 +742,14 @@ namespace nana void _m_add_agent(const detail::place_agent& ag) override { +#ifdef _nana_std_has_emplace_return_type + this->operator<<( + widgets_.emplace_back(ag.create(place_ptr_->window_handle()))->handle() + ); +#else widgets_.emplace_back(ag.create(place_ptr_->window_handle())); this->operator<<(widgets_.back()->handle()); +#endif } public: division* attached{ nullptr }; diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index ca2ea8d7..2105dd02 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -436,10 +436,13 @@ namespace nana caption_.caption(wdg->caption()); } - panels_.emplace_back(); auto wdg_ptr = wdg.get(); +#ifdef _nana_std_has_emplace_return_type + panels_.emplace_back().widget_ptr = std::move(wdg); +#else + panels_.emplace_back(); panels_.back().widget_ptr.swap(wdg); - +#endif for (auto & pn : panels_) { if (pn.widget_ptr) diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 30f33e9a..698aa543 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -835,17 +835,6 @@ namespace API size inner_size = sz; -#if 0 - if (inner_size.width < iwd->extra_width) - inner_size.width = 0; - else - inner_size.width -= iwd->extra_width; - - if (inner_size.height < iwd->extra_height) - inner_size.height = 0; - else - inner_size.height -= iwd->extra_height; -#else if (inner_size.width < static_cast(fm_extents.left + fm_extents.right)) inner_size.width = 0; else @@ -855,7 +844,6 @@ namespace API inner_size.height = 0; else inner_size.height -= static_cast(fm_extents.top + fm_extents.bottom); -#endif window_size(wd, inner_size); } diff --git a/source/gui/tooltip.cpp b/source/gui/tooltip.cpp index 862a2720..34692036 100644 --- a/source/gui/tooltip.cpp +++ b/source/gui/tooltip.cpp @@ -1,7 +1,7 @@ /* * A Tooltip 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 @@ -15,6 +15,7 @@ #include #include #include +#include namespace nana { @@ -157,7 +158,14 @@ namespace nana class controller { - typedef std::pair pair_t; + struct tip_value + { + std::string text; + event_handle evt_msenter; + event_handle evt_msleave; + event_handle evt_msdown; + event_handle evt_destroy; + }; typedef std::function deleter_type; @@ -207,7 +215,7 @@ namespace nana if (str.empty()) _m_untip(wd); else - _m_get(wd).second = str; + _m_get(wd).text = str; } void show(const std::string& text, const point* pos, std::size_t duration) @@ -236,35 +244,35 @@ namespace nana window_.reset(); //Destroy the tooltip controller when there are not tooltips. - if (cont_.empty()) + if (table_.empty()) instance(true); } private: void _m_untip(window wd) { - for (auto i = cont_.begin(); i != cont_.end(); ++i) + auto i = table_.find(wd); + if(i != table_.end()) { - if (i->first == wd) - { - cont_.erase(i); + API::umake_event(i->second.evt_msdown); + API::umake_event(i->second.evt_msenter); + API::umake_event(i->second.evt_msleave); + API::umake_event(i->second.evt_destroy); - if (cont_.empty()) - { - window_.reset(); - instance(true); - } - return; - } + table_.erase(i); + } + + if (table_.empty()) + { + window_.reset(); + instance(true); } } private: - pair_t& _m_get(window wd) + tip_value& _m_get(window wd) { - for (auto & pr : cont_) - { - if (pr.first == wd) - return pr; - } + auto i = table_.find(wd); + if (i != table_.end()) + return i->second; auto & events = API::events(wd); @@ -272,28 +280,29 @@ namespace nana { if (event_code::mouse_enter == arg.evt_code) { - auto & pr = _m_get(arg.window_handle); - if (pr.second.size()) - this->show(pr.second, nullptr, 0); + auto & value = _m_get(arg.window_handle); + if (value.text.size()) + this->show(value.text, nullptr, 0); } else this->close(); }; - events.mouse_enter.connect(mouse_fn); - events.mouse_leave.connect(mouse_fn); - events.mouse_down.connect(mouse_fn); + auto & value = table_[wd]; - events.destroy.connect([this](const arg_destroy& arg){ + value.evt_msenter = events.mouse_enter.connect(mouse_fn); + value.evt_msleave = events.mouse_leave.connect(mouse_fn); + value.evt_msdown = events.mouse_down.connect(mouse_fn); + + value.evt_destroy = events.destroy.connect([this](const arg_destroy& arg){ _m_untip(arg.window_handle); }); - cont_.emplace_back(wd, std::string()); - return cont_.back(); + return value; } private: std::unique_ptr window_; - std::vector cont_; + std::map table_; }; }//namespace tooltip }//namespace drawerbase diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 8be557ef..6d697a62 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -136,8 +136,12 @@ namespace nana{ { _THROW_IF_EMPTY() +#ifdef _nana_std_has_emplace_return_type + auto & opt = impl_->options.emplace_back(new checkbox{ handle() }); +#else impl_->options.emplace_back(new checkbox(handle())); auto & opt = impl_->options.back(); +#endif opt->transparent(true); opt->caption(std::move(text)); impl_->place_content[field_options] << *opt; diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 790ae3b8..013b5605 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -28,14 +28,28 @@ namespace nana { class renderer { - typedef widgets::skeletons::dstream::linecontainer::iterator iterator; + //Iterator of content element in a line. + using content_element_iterator = widgets::skeletons::dstream::linecontainer::const_iterator; //subsitute for member type iterator - struct pixel_tag + struct visual_line //subsitute of pixel_tag { - int x_base; //The x position where this line starts. - std::size_t pixels; - std::size_t baseline; //The baseline for drawing text. - std::vector values; //line values + 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. }; //this is a helper variable, it just keeps the status while drawing. @@ -46,8 +60,8 @@ namespace nana align_v text_align_v; nana::point pos; - std::vector pixels; - std::size_t index; + std::vector vslines; //The lines description of a line of text. substitute of member pixels. + std::size_t index; //indicates the current rendering visual line. }; struct traceable @@ -102,18 +116,19 @@ namespace nana rs.text_align = th; rs.text_align_v = tv; - std::deque > pixel_lines; + //All visual lines data of whole text. + std::deque> content_lines; std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted. for (auto & line : dstream_) { - _m_line_pixels(line, def_line_pixels, rs); + _m_prepare_visual_lines(graph, line, def_line_pixels, rs); - for (auto & m : rs.pixels) - extent_v_pixels += m.pixels; + for (auto & vsline : rs.vslines) + extent_v_pixels += vsline.extent_height_px; - pixel_lines.emplace_back(std::move(rs.pixels)); + content_lines.emplace_back(std::move(rs.vslines)); if(extent_v_pixels >= graph.height()) break; @@ -129,25 +144,21 @@ namespace nana else rs.pos.y = 0; - auto pixels_iterator = pixel_lines.begin(); - + auto vsline_iterator = content_lines.begin(); for (auto & line : dstream_) { if (rs.pos.y >= static_cast(graph.height())) break; rs.index = 0; - rs.pixels.clear(); + rs.vslines.clear(); + rs.vslines.swap(*vsline_iterator++); + rs.pos.x = rs.vslines.front().x_base; - rs.pixels.swap(*pixels_iterator++); - - rs.pos.x = rs.pixels.front().x_base; - - //Stop drawing when it goes out of range. - if(false == _m_each_line(graph, line, rs)) + if (!_m_foreach_visual_line(graph, rs)) break; - rs.pos.y += static_cast(rs.pixels.back().pixels); + rs.pos.y += static_cast(rs.vslines.back().extent_height_px); } graph.typeface(pre_font); @@ -194,8 +205,8 @@ namespace nana for(auto & line: dstream_) { - rs.pixels.clear(); - unsigned w = _m_line_pixels(line, def_line_pixels, rs); + rs.vslines.clear(); + auto w = _m_prepare_visual_lines(graph, line, def_line_pixels, rs); if(limited && (w > limited)) w = limited; @@ -203,8 +214,8 @@ namespace nana if(retsize.width < w) retsize.width = w; - for (auto & px : rs.pixels) - retsize.height += static_cast(px.pixels); + for (auto& vsline : rs.vslines) + retsize.height += static_cast(vsline.extent_height_px); } return retsize; @@ -215,8 +226,12 @@ namespace nana { if(fbp->target.size() || fbp->url.size()) { +#ifdef _nana_std_has_emplace_return_type + auto & tr = traceable_.emplace_back(); +#else traceable_.emplace_back(); auto & tr = traceable_.back(); +#endif tr.r.x = x; tr.r.y = y; tr.r.dimension(sz); @@ -315,291 +330,287 @@ namespace nana } } - void _m_align_x_base(const render_status& rs, pixel_tag & px, unsigned w) noexcept + void _m_prepare_x(const render_status& rs, visual_line & vsline, unsigned w) noexcept { - switch(rs.text_align) + switch (rs.text_align) { case align::left: - px.x_base = 0; + vsline.x_base = 0; break; case align::center: - px.x_base = (static_cast(rs.allowed_width - w) >> 1); + vsline.x_base = (static_cast(rs.allowed_width - w) >> 1); break; case align::right: - px.x_base = static_cast(rs.allowed_width - w); + vsline.x_base = static_cast(rs.allowed_width - w); break; } } - unsigned _m_line_pixels(dstream::linecontainer& line, unsigned def_line_pixels, render_status & rs) + /** + * 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) { - if (line.empty()) - { - pixel_tag px; - px.baseline = 0; - px.pixels = def_line_pixels; - px.x_base = 0; - - rs.pixels.emplace_back(px); - - return 0; - } - - unsigned total_w = 0; - unsigned w = 0; + unsigned abs_text_px = 0; unsigned max_ascent = 0; unsigned max_descent = 0; - unsigned max_px = 0; + unsigned max_content_height = 0; - //Bidi reorder is requried here + int text_pos = 0; - std::vector line_values; + std::vector vsline_elements; - for(auto i = line.begin(); i != line.end(); ++i) + for (auto i = line.cbegin(); i != line.cend(); ++i) { - data * data_ptr = i->data_ptr; - nana::size sz = data_ptr->size(); - total_w += sz.width; + auto const data = i->data_ptr; + auto fblock = i->fblock_ptr; - unsigned as = 0; //ascent - unsigned ds = 0; //descent + abs_text_px += data->size().width; - if(fblock::aligns::baseline == i->fblock_ptr->text_align) + 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)) { - as = static_cast(data_ptr->ascent()); - ds = static_cast(sz.height - as); + text_pos += static_cast(extent_size.width); - if(max_descent < ds) - max_descent = ds; - - if((false == data_ptr->is_text()) && (sz.height < max_ascent + max_descent)) - sz.height = max_ascent + max_descent; - } - - //Check if the content is displayed in a new line. - if((0 == rs.allowed_width) || (w + sz.width <= rs.allowed_width)) - { - w += sz.width; - - if(max_ascent < as) max_ascent = as; - if(max_descent < ds) max_descent = ds; - if(max_px < sz.height) max_px = sz.height; - line_values.emplace_back(i); - } - else - { - pixel_tag px; - _m_align_x_base(rs, px, (w ? w : sz.width)); - - if(w) + //Adjust height of extent_size for special text alignement. + if (fblock::aligns::baseline == fblock->text_align) { - if(max_ascent + max_descent > max_px) - max_px = max_descent + max_ascent; - else - max_ascent = max_px - max_descent; + ascent = static_cast(data->ascent()); + descent = static_cast(extent_size.height - ascent); - px.pixels = max_px; - px.baseline = max_ascent; - px.values.swap(line_values); + if (max_descent < descent) + max_descent = descent; - w = sz.width; - max_px = sz.height; - max_ascent = as; - max_descent = ds; - line_values.emplace_back(i); + 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_emplace_return_type + 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()) { - px.pixels = sz.height; - px.baseline = as; + unsigned sub_text_px = 0; + auto sub_text_len = _m_fit_text(graph, data->text().substr(text_begin), rs.allowed_width, sub_text_px); - px.values.emplace_back(i); + if (text_begin + sub_text_len < data->text().size()) + { + //make a new visual line +#ifdef _nana_std_has_emplace_return_type + 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); - max_px = 0; - max_ascent = max_descent = 0; + 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); - rs.pixels.emplace_back(px); + text_pos = static_cast(i->data_ptr->size().width); } } - if (max_px) + if (!vsline_elements.empty()) { - pixel_tag px; +#ifdef _nana_std_has_emplace_return_type + 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)); - _m_align_x_base(rs, px, w); - - if (max_ascent + max_descent > max_px) - max_px = max_descent + max_ascent; + if (max_ascent + max_descent > max_content_height) + max_content_height = max_descent + max_ascent; else - max_ascent = max_px - max_descent; + max_ascent = max_content_height - max_descent; - px.pixels = max_px; - px.baseline = max_ascent; - px.values.swap(line_values); - rs.pixels.emplace_back(px); + vsline.extent_height_px = max_content_height; + vsline.baseline = max_ascent; + vsline.elements.swap(vsline_elements); } - return total_w; + + return abs_text_px; } - bool _m_each_line(graph_reference graph, dstream::linecontainer&, render_status& rs) + //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 (unsigned i = 0; i < text.size(); ++i) + { + if (text_px + pxbuf[i] > limited_width_px) + return i; + + text_px += pxbuf[i]; + } + return static_cast(text.size()); + } + + bool _m_foreach_visual_line(graph_reference graph, render_status& rs) { std::wstring text; - iterator block_start; + + content_element_iterator block_start; - const int lastpos = static_cast(graph.height()) - 1; + auto const bottom = static_cast(graph.height()) - 1; - for(auto & px : rs.pixels) + for (auto & vsline : rs.vslines) { - for(auto & render_iterator: px.values) + rs.pos.x = vsline.x_base; + for (auto& content_elm : vsline.elements) { - auto & value = *render_iterator; - if (value.data_ptr->is_text()) - { - //hold the block while the text is empty, - //it stands for the first block - if (text.empty()) - block_start = render_iterator; - - text += value.data_ptr->text(); - continue; - } - - if(text.size()) - { - _m_draw_block(graph, text, block_start, rs); - if(lastpos <= rs.pos.y) - return false; - text.clear(); - } - nana::size sz = value.data_ptr->size(); - - pixel_tag px = rs.pixels[rs.index]; - if ((rs.allowed_width < rs.pos.x + sz.width) && (rs.pos.x != px.x_base)) - { - //Change a line. - rs.pos.y += static_cast(px.pixels); - px = rs.pixels[++rs.index]; - rs.pos.x = px.x_base; - } - - int y = rs.pos.y + _m_text_top(px, value.fblock_ptr, value.data_ptr); - - value.data_ptr->nontext_render(graph, rs.pos.x, y); - _m_insert_if_traceable(rs.pos.x, y, sz, value.fblock_ptr); - rs.pos.x += static_cast(sz.width); - - if(lastpos < y) - return false; + _m_draw_vsline_element(graph, content_elm, rs); } - if(text.size()) - { - _m_draw_block(graph, text, block_start, rs); - text.clear(); - } + ++rs.index; //next line index + rs.pos.y += static_cast(vsline.extent_height_px); + + if (rs.pos.y > bottom) + return false; } - return (rs.pos.y <= lastpos); + + return (rs.pos.y <= bottom); } - static bool _m_overline(const render_status& rs, int right, bool equal_required) noexcept + static int _m_vsline_element_top(const visual_line& vsline, fblock* fblock_ptr, const data* data_ptr) noexcept { - if(align::left == rs.text_align) - return (equal_required ? right >= static_cast(rs.allowed_width) : right > static_cast(rs.allowed_width)); - - return (equal_required ? rs.pixels[rs.index].x_base <= 0 : rs.pixels[rs.index].x_base < 0); - } - - static int _m_text_top(const pixel_tag& px, fblock* fblock_ptr, const data* data_ptr) - { - switch(fblock_ptr->text_align) + switch (fblock_ptr->text_align) { case fblock::aligns::center: - return static_cast(px.pixels - data_ptr->size().height) / 2; + return static_cast(vsline.extent_height_px - data_ptr->size().height) / 2; case fblock::aligns::bottom: - return static_cast(px.pixels - data_ptr->size().height); + return static_cast(vsline.extent_height_px - data_ptr->size().height); case fblock::aligns::baseline: - return static_cast(px.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height)); + return static_cast(vsline.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height)); default: break; } return 0; } - void _m_draw_block(graph_reference graph, const std::wstring& s, dstream::linecontainer::iterator block_start, render_status& rs) + void _m_draw_vsline_element(graph_reference graph, const visual_line::element& vsline_elm, render_status& rs) { - auto const reordered = unicode_reorder(s.data(), s.length()); + auto data = vsline_elm.content_element->data_ptr; + auto fblock = vsline_elm.content_element->fblock_ptr; - pixel_tag px = rs.pixels[rs.index]; - - for(auto & bidi : reordered) + if (data->is_text()) { - std::size_t pos = bidi.begin - s.data(); - std::size_t len = bidi.end - bidi.begin; + auto const text = data->text().c_str() + vsline_elm.range.first; + auto const reordered = unicode_reorder(text, vsline_elm.range.second); - while (true) + _m_change_font(graph, fblock); + for (auto & bidi : reordered) { - auto i = block_start; - - //Text range indicates the position of text where begin to output - //The output length is the min between len and the second of text range. - auto text_range = _m_locate(i, pos); - - if (text_range.second > len) - text_range.second = len; - - fblock * fblock_ptr = i->fblock_ptr; - data * data_ptr = i->data_ptr; - - 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)) - { - //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); - - _m_change_font(graph, fblock_ptr); - + auto extent_size = data->size(); #ifdef _nana_std_has_string_view - std::wstring_view text_sv{ data_ptr->text() }; - 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); - } + 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); - graph.string({ rs.pos.x, y }, text_sv, _m_fgcolor(fblock_ptr)); + 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 - if (text_range.second == data_ptr->text().length()) - { - graph.string({ rs.pos.x, y }, data_ptr->text(), _m_fgcolor(fblock_ptr)); - } - else - { - auto str = data_ptr->text().substr(text_range.first, text_range.second); - sz = graph.text_extent_size(str); + std::wstring text{ bidi.begin, static_cast(bidi.end - bidi.begin) }; + if (data->text().size() != text.size()) + extent_size = graph.text_extent_size(text); - graph.string({ rs.pos.x, y }, str, _m_fgcolor(fblock_ptr)); - } + 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, sz, fblock_ptr); - rs.pos.x += static_cast(sz.width); - - if(text_range.second < len) - { - len -= text_range.second; - pos += text_range.second; - } - else - break; + _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); + } } 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..489f6611 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -312,8 +312,12 @@ namespace nana size_type create(essence* ess, native_string_type&& text, unsigned pixels) { +#ifdef _nana_std_has_emplace_return_type + return cont_.emplace_back(ess, std::move(text), pixels, static_cast(cont_.size())).index; +#else cont_.emplace_back(ess, std::move(text), pixels, static_cast(cont_.size())); return cont_.back().index; +#endif } void clear() @@ -1023,9 +1027,15 @@ namespace nana } } +#ifdef _nana_std_has_emplace_return_type + auto & last_cat = categories_.emplace_back(); + last_cat.key_ptr = ptr; + return &last_cat; +#else categories_.emplace_back(); categories_.back().key_ptr = ptr; return &(categories_.back()); +#endif } /// Inserts a new category at position specified by pos @@ -1033,8 +1043,12 @@ namespace nana { if (::nana::npos == pos) { +#ifdef _nana_std_has_emplace_return_type + return &categories_.emplace_back(std::move(text)); +#else categories_.emplace_back(std::move(text)); return &categories_.back(); +#endif } return &(*categories_.emplace(this->get(pos), std::move(text))); @@ -2622,8 +2636,12 @@ namespace nana oresolver& oresolver::operator<<(std::nullptr_t) { +#ifdef _nana_std_has_emplace_return_type + cells_.emplace_back().text.assign(1, wchar_t{}); +#else cells_.emplace_back(); cells_.back().text.assign(1, wchar_t(0)); //means invalid cell +#endif return *this; } diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 3c211ba6..94d7c6c2 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -89,11 +89,18 @@ namespace nana if (shortkey && shortkey < 0x61) shortkey += (0x61 - 0x41); + +#ifdef _nana_std_has_emplace_return_type + auto & last = items.emplace_back(new item_type{std::move(transformed_text), shortkey, shortkey_pos}); + API::refresh_window(*widget_ptr); + return last->menu_obj; +#else items.emplace_back(new item_type{ std::move(transformed_text), shortkey, shortkey_pos }); API::refresh_window(*widget_ptr); return this->items.back()->menu_obj; +#endif } bool cancel() diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 16963195..ca070ace 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -199,7 +199,7 @@ namespace nana{ namespace widgets { editor_.select_.a = sel_a_; editor_.select_.b = sel_b_; - editor_._m_erase_select(); + editor_._m_erase_select(false); editor_.select_.a = editor_.select_.b; editor_.points_.caret = sel_a_; } @@ -208,7 +208,7 @@ namespace nana{ namespace widgets if (is_enter) { editor_.points_.caret = nana::upoint(0, pos_.y + 1); - editor_.backspace(false); + editor_.backspace(false, false); } else editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size()); @@ -218,11 +218,11 @@ namespace nana{ namespace widgets { if (is_enter) { - editor_.enter(false); + editor_.enter(false, false); } else { - editor_._m_put(selected_text_); + editor_._m_put(selected_text_, false); if (sel_a_ != sel_b_) { editor_.select_.a = sel_a_; @@ -234,6 +234,8 @@ namespace nana{ namespace widgets } } + editor_.textbase().text_changed(); + editor_.reset_caret(); } }; @@ -258,7 +260,7 @@ namespace nana{ namespace widgets { if (is_enter) { - editor_.enter(false); + editor_.enter(false, false); } else { @@ -266,9 +268,9 @@ namespace nana{ namespace widgets { editor_.select_.a = sel_a_; editor_.select_.b = sel_b_; - editor_._m_erase_select(); + editor_._m_erase_select(false); } - editor_.points_.caret = editor_._m_put(text_); //redo + editor_.points_.caret = editor_._m_put(text_, false); //redo } } else @@ -277,7 +279,7 @@ namespace nana{ namespace widgets { editor_.points_.caret.x = 0; ++editor_.points_.caret.y; - editor_.backspace(false); + editor_.backspace(false, false); } else { @@ -286,7 +288,7 @@ namespace nana{ namespace widgets { editor_.select_.a = pos_; editor_.select_.b = upoint(static_cast(lines.back().second - lines.back().first), static_cast(pos_.y + lines.size() - 1)); - editor_.backspace(false); + editor_.backspace(false, false); editor_.select_.a = editor_.select_.b; } else @@ -296,12 +298,14 @@ namespace nana{ namespace widgets if (!selected_text_.empty()) { editor_.points_.caret = (std::min)(sel_a_, sel_b_); - editor_._m_put(selected_text_); + editor_._m_put(selected_text_, false); editor_.points_.caret = sel_b_; editor_.select_.a = sel_a_; //Reset the selected text editor_.select_.b = sel_b_; } } + + editor_.textbase().text_changed(); editor_.reset_caret(); } private: @@ -333,8 +337,8 @@ namespace nana{ namespace widgets const auto text = editor_._m_make_select_string(); - editor_._m_erase_select(); - editor_._m_put(text); + editor_._m_erase_select(false); + editor_._m_put(text, false); editor_.select_.a = sel_a_; editor_.select_.b = sel_b_; @@ -342,6 +346,7 @@ namespace nana{ namespace widgets editor_.points_.caret = sel_b_; editor_.reset_caret(); } + editor_.textbase().text_changed(); } void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) @@ -1004,8 +1009,12 @@ namespace nana{ namespace widgets auto ki = keywords.schemes.find(ds.scheme); if ((ki != keywords.schemes.end()) && ki->second) { +#ifdef _nana_std_has_emplace_return_type + auto & last = entities.emplace_back(); +#else entities.emplace_back(); auto & last = entities.back(); +#endif last.begin = c_str + pos; last.end = last.begin + ds.text.size(); last.scheme = ki->second.get(); @@ -1192,9 +1201,9 @@ namespace nana{ namespace widgets switch (key) { case '\b': - backspace(); break; + backspace(true, true); break; case '\n': case '\r': - enter(); break; + enter(true, true); break; case keyboard::sync_idel: paste(); break; case keyboard::tab: @@ -1681,7 +1690,7 @@ namespace nana{ namespace widgets auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, str) }; undo_ptr->set_caret_pos(); - _m_put(std::move(str)); + _m_put(std::move(str), false); impl_->undo.push(std::move(undo_ptr)); @@ -1698,7 +1707,9 @@ namespace nana{ namespace widgets } } else - put(std::move(str)); + put(std::move(str), false); + + textbase().text_changed(); } std::wstring text_editor::text() const @@ -1904,6 +1915,7 @@ namespace nana{ namespace widgets if (_m_move_select(true)) { + textbase().text_changed(); this->_m_adjust_view(); impl_->try_refresh = sync_graph::refresh; return true; @@ -2021,7 +2033,7 @@ namespace nana{ namespace widgets impl_->try_refresh = sync_graph::none; } //public: - void text_editor::put(std::wstring text) + void text_editor::put(std::wstring text, bool perform_event) { if (text.empty()) return; @@ -2032,14 +2044,16 @@ namespace nana{ namespace widgets //Do not forget to assign the _m_erase_select() to caret //because _m_put() will insert the text at the position where the caret is. - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); - points_.caret = _m_put(std::move(text)); + points_.caret = _m_put(std::move(text), false); impl_->undo.push(std::move(undo_ptr)); _m_reset_content_size(true); + if (perform_event) + textbase().text_changed(); if(graph_) { @@ -2060,7 +2074,7 @@ namespace nana{ namespace widgets undo_ptr->set_selected_text(); if(refresh) - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); @@ -2070,6 +2084,8 @@ namespace nana{ namespace widgets textbase().insert(points_.caret, std::move(ch_str)); _m_pre_calc_lines(points_.caret.y, 1); + textbase().text_changed(); + points_.caret.x ++; _m_reset_content_size(); @@ -2086,6 +2102,10 @@ namespace nana{ namespace widgets void text_editor::copy() const { + //Disallows copying text if the text_editor is masked. + if (mask_char_) + return; + auto text = _m_make_select_string(); if (!text.empty()) nana::system::dataexch().set(text, API::root(this->window_)); @@ -2103,7 +2123,7 @@ namespace nana{ namespace widgets if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive) { - put(move(text)); + put(move(text), true); return; } @@ -2121,13 +2141,13 @@ namespace nana{ namespace widgets if (accepts::no_restrict != impl_->capacities.acceptive) { text.erase(i, text.end()); - put(move(text)); + put(move(text), true); } break; } } - void text_editor::enter(bool record_undo) + void text_editor::enter(bool record_undo, bool perform_event) { if(false == attributes_.multi_lines) return; @@ -2135,7 +2155,7 @@ namespace nana{ namespace widgets auto undo_ptr = std::unique_ptr(new undo_input_text(*this, std::wstring(1, '\n'))); undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); @@ -2173,21 +2193,24 @@ namespace nana{ namespace widgets { if (impl_->indent.generator) { - put(to_wstring(impl_->indent.generator())); + put(nana::to_wstring(impl_->indent.generator()), false); } else { auto & text = textbase.getline(points_.caret.y - 1); auto indent_pos = text.find_first_not_of(L"\t "); if (indent_pos != std::wstring::npos) - put(text.substr(0, indent_pos)); + put(text.substr(0, indent_pos), false); else - put(text); + put(text, false); } } else _m_reset_content_size(); + if (perform_event) + textbase.text_changed(); + auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); if (this->_m_adjust_view() || origin_moved) @@ -2211,10 +2234,10 @@ namespace nana{ namespace widgets return; //No characters behind the caret } - backspace(); + backspace(true, true); } - void text_editor::backspace(bool record_undo) + void text_editor::backspace(bool record_undo, bool perform_event) { auto undo_ptr = std::unique_ptr(new undo_backspace(*this)); bool has_to_redraw = true; @@ -2254,7 +2277,7 @@ namespace nana{ namespace widgets else { undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(); + points_.caret = _m_erase_select(false); undo_ptr->set_caret_pos(); } @@ -2263,6 +2286,11 @@ namespace nana{ namespace widgets _m_reset_content_size(false); + if (perform_event) + textbase().text_changed(); + + textbase().text_changed(); + if(has_to_redraw) { this->_m_adjust_view(); @@ -2956,7 +2984,7 @@ namespace nana{ namespace widgets select_.a = select_.b; } - nana::upoint text_editor::_m_put(std::wstring text) + nana::upoint text_editor::_m_put(std::wstring text, bool perform_event) { auto & textbase = this->textbase(); auto crtpos = points_.caret; @@ -2998,10 +3026,13 @@ namespace nana{ namespace widgets _m_pre_calc_lines(crtpos.y, 1); } + if (perform_event) + textbase.text_changed(); + return crtpos; } - nana::upoint text_editor::_m_erase_select() + nana::upoint text_editor::_m_erase_select(bool perform_event) { nana::upoint a, b; if (get_selected_points(a, b)) @@ -3023,6 +3054,9 @@ namespace nana{ namespace widgets _m_pre_calc_lines(a.y, 1); } + if (perform_event) + textbase.text_changed(); + select_.a = select_.b; return a; } @@ -3235,8 +3269,8 @@ namespace nana{ namespace widgets {//forward undo_ptr->set_caret_pos(); - _m_erase_select(); - _m_put(text); + _m_erase_select(false); + _m_put(text, false); select_.a = caret; select_.b.y = b.y + (caret.y - a.y); @@ -3245,8 +3279,8 @@ namespace nana{ namespace widgets { undo_ptr->set_caret_pos(); - _m_put(text); - _m_erase_select(); + _m_put(text, false); + _m_erase_select(false); select_.b.y = caret.y; select_.a.y = caret.y - (b.y - a.y); diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index f0a972c9..8970089b 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -750,8 +750,8 @@ namespace nana { if((pos == npos) || (pos >= list_.size())) { + pos = list_.size(); this->list_.emplace_back(); - pos = list_.size() - 1; } else list_.emplace(iterator_at(pos)); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index db57ae82..6697e567 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -428,7 +428,7 @@ namespace drawerbase { if(at_caret == false) editor->move_caret_end(false); - editor->put(to_wstring(text)); + editor->put(to_wstring(text), true); editor->try_refresh(); API::update_window(this->handle()); @@ -445,7 +445,7 @@ namespace drawerbase { if(at_caret == false) editor->move_caret_end(false); - editor->put(text); + editor->put(text, true); editor->try_refresh(); API::update_window(this->handle()); diff --git a/source/threads/pool.cpp b/source/threads/pool.cpp index e83498b0..d4b3af2f 100644 --- a/source/threads/pool.cpp +++ b/source/threads/pool.cpp @@ -351,10 +351,17 @@ namespace threads }container_; };//end class impl - pool::pool() - : impl_(new impl(4)) +#ifndef STD_THREAD_NOT_SUPPORTED + pool::pool(unsigned thread_number) + : impl_(new impl(thread_number ? thread_number : std::thread::hardware_concurrency())) { } +#else + pool::pool(unsigned thread_number) + : impl_(new impl(0)) + { + } +#endif pool::pool(pool&& other) : pool() @@ -362,11 +369,6 @@ namespace threads std::swap(impl_, other.impl_); } - pool::pool(std::size_t thread_number) - : impl_(new impl(thread_number)) - { - } - pool& pool::operator=(pool&& other) { if(this != &other) diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index 15b77d12..32ca0541 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -1,4 +1,5 @@ #include +#include namespace nana { @@ -611,8 +612,12 @@ namespace nana void unicode_bidi::_m_push_entity(const char_type * begin, const char_type *end, unsigned level, bidi_char bidi_char_type) { +#ifdef _nana_std_has_emplace_return_type + auto & e = levels_.emplace_back(); +#else levels_.emplace_back(); auto & e = levels_.back(); +#endif e.begin = begin; e.end = end; e.level = level; @@ -946,4 +951,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