diff --git a/include/nana/audio/detail/buffer_preparation.hpp b/include/nana/audio/detail/buffer_preparation.hpp index 57e6f32e..7272d9b1 100644 --- a/include/nana/audio/detail/buffer_preparation.hpp +++ b/include/nana/audio/detail/buffer_preparation.hpp @@ -23,6 +23,8 @@ #include #endif +#include + namespace nana{ namespace audio { namespace detail @@ -52,8 +54,8 @@ namespace nana{ namespace audio private: void _m_prepare_routine(); private: - volatile bool running_; - volatile bool wait_for_buffer_; + std::atomic running_; + std::atomic wait_for_buffer_; std::thread thr_; mutable std::mutex token_buffer_, token_prepared_; mutable std::condition_variable cond_buffer_, cond_prepared_; 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/gui/basis.hpp b/include/nana/gui/basis.hpp index 4153d439..a582d7b9 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -51,6 +51,13 @@ namespace nana top_left, top_right, bottom_left, bottom_right }; + enum class window_relationship + { + owner, ///< Owner window. + parent, ///< Parent window. + either_po ///< One between owner and parent. + }; + enum class bground_mode { none, diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 0a4881b4..9a9496e2 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -172,7 +172,7 @@ namespace nana } else if constexpr(std::is_invocable_v) { - return _m_emplace(new docker{ this, [fn](arg_reference){ + return _m_emplace(new docker{ this, [fn](arg_reference) mutable{ fn(); }, false }, false); } @@ -200,7 +200,7 @@ namespace nana } else if constexpr(std::is_invocable_v) { - return _m_emplace(new docker{ this, [fn](arg_reference) { + return _m_emplace(new docker{ this, [fn](arg_reference) mutable{ fn(); }, true }, in_front); } diff --git a/include/nana/gui/detail/native_window_interface.hpp b/include/nana/gui/detail/native_window_interface.hpp index 25b2fc7f..dac3fa97 100644 --- a/include/nana/gui/detail/native_window_interface.hpp +++ b/include/nana/gui/detail/native_window_interface.hpp @@ -1,7 +1,7 @@ /* * Platform 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 @@ -36,6 +36,14 @@ namespace detail unsigned extra_height; //extra border size, it is useful in Windows, ignore in X11 always 0 }; + struct frame_extents + { + int left; + int right; + int top; + int bottom; + }; + using native_string_type = ::nana::detail::native_string_type; //Execute a function in a thread which is associated with the specified native window. @@ -70,14 +78,15 @@ namespace detail static void bring_top(native_window_type, bool activated); static void set_window_z_order(native_window_type, native_window_type wd_after, z_order_action action_if_no_wd_after); + static frame_extents window_frame_extents(native_window_type); static bool window_size(native_window_type, const size&); static void get_window_rect(native_window_type, rectangle&); static void window_caption(native_window_type, const native_string_type&); static native_string_type window_caption(native_window_type); static void capture_window(native_window_type, bool); static nana::point cursor_position(); - static native_window_type get_owner_window(native_window_type); - static native_window_type parent_window(native_window_type); + + static native_window_type get_window(native_window_type wd, window_relationship); static native_window_type parent_window(native_window_type child, native_window_type new_parent, bool returns_previous); //For Caret static void caret_create(native_window_type, const ::nana::size&); diff --git a/include/nana/gui/msgbox.hpp b/include/nana/gui/msgbox.hpp index 02b4fe35..a004fd21 100644 --- a/include/nana/gui/msgbox.hpp +++ b/include/nana/gui/msgbox.hpp @@ -1,7 +1,7 @@ /* * A Message Box Class * 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 @@ -255,7 +255,11 @@ namespace nana bool show_modal(Args&& ... args) { std::vector contents; +#ifdef __cpp_fold_expressions + (contents.emplace_back(&args), ...); +#else _m_fetch_args(contents, std::forward(args)...); +#endif if (contents.empty()) return false; diff --git a/include/nana/gui/widgets/detail/widget_iterator.hpp b/include/nana/gui/widgets/detail/widget_iterator.hpp index c78e5d4d..6f8945ef 100644 --- a/include/nana/gui/widgets/detail/widget_iterator.hpp +++ b/include/nana/gui/widgets/detail/widget_iterator.hpp @@ -1,6 +1,6 @@ /* * A Widget Iterator Template - * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2017-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -23,6 +23,7 @@ namespace nana { template class widget_iterator { + public: using iterator_category = Category; using value_type = T; using difference_type = std::ptrdiff_t; diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 9a70cc6d..6e518fe9 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1033,6 +1033,7 @@ namespace nana template void append_model(const T& t) { + nana::internal_scope_guard lock; _m_try_append_model(const_virtual_pointer{ &t }); _m_update(); } 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/widget.hpp b/include/nana/gui/widgets/widget.hpp index d7be3acf..7448eea9 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -170,7 +170,8 @@ namespace nana } /// Base class of all the classes defined as a widget window. Defaultly a widget_tag - template + template::value>::type> //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -182,7 +183,9 @@ namespace nana widget_object() : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } - {} + { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); + } ~widget_object() { @@ -291,7 +294,7 @@ namespace nana };//end class widget_object /// Base class of all the classes defined as a non-graphics-buffer widget window. The second template parameter DrawerTrigger is always ignored.\see nana::panel - template + template //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -302,7 +305,9 @@ namespace nana widget_object() : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } - {} + { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); + } ~widget_object() { @@ -355,7 +360,7 @@ namespace nana /// Base class of all the classes defined as a root window. \see nana::form - template + template //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -367,10 +372,12 @@ namespace nana widget_object() : widget_object(nullptr, false, API::make_center(300, 150), appearance(), this) { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); } widget_object(window owner, bool nested, const rectangle& r = {}, const appearance& apr = {}) { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); handle_ = API::dev::create_window(owner, nested, r, apr, this); _m_bind_and_attach(); } diff --git a/include/nana/internationalization.hpp b/include/nana/internationalization.hpp index cab02568..cba03bf5 100644 --- a/include/nana/internationalization.hpp +++ b/include/nana/internationalization.hpp @@ -1,7 +1,7 @@ /* * An Implementation of i18n * 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 diff --git a/include/nana/pat/cloneable.hpp b/include/nana/pat/cloneable.hpp index 350dbe1a..60d691f6 100644 --- a/include/nana/pat/cloneable.hpp +++ b/include/nana/pat/cloneable.hpp @@ -193,7 +193,7 @@ namespace nana{ namespace pat{ cwrapper_.reset(); } - operator operator_bool_t() const volatile noexcept + operator operator_bool_t() const noexcept { return (fast_ptr_ ? &inner_bool::true_stand : nullptr); } diff --git a/include/nana/pop_ignore_diagnostic b/include/nana/pop_ignore_diagnostic index 23c39a69..ff563550 100644 --- a/include/nana/pop_ignore_diagnostic +++ b/include/nana/pop_ignore_diagnostic @@ -1,5 +1,5 @@ -#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6) +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) # pragma GCC diagnostic pop #endif diff --git a/include/nana/push_ignore_diagnostic b/include/nana/push_ignore_diagnostic index 9ba463ad..4931e9db 100644 --- a/include/nana/push_ignore_diagnostic +++ b/include/nana/push_ignore_diagnostic @@ -1,4 +1,4 @@ -#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6) +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Weffc++" #endif diff --git a/include/nana/system/timepiece.hpp b/include/nana/system/timepiece.hpp index fe188f45..443a4a0e 100644 --- a/include/nana/system/timepiece.hpp +++ b/include/nana/system/timepiece.hpp @@ -1,6 +1,6 @@ /* * Timepiece Implementation - * Copyright(C) 2003-2013 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,6 +13,8 @@ #ifndef NANA_SYSTEM_TIMEPIECE_HPP #define NANA_SYSTEM_TIMEPIECE_HPP +#include "../c++defines.hpp" + namespace nana { namespace system @@ -21,11 +23,11 @@ namespace system { public: timepiece(); - timepiece(const volatile timepiece&); + timepiece(const timepiece&); ~timepiece(); - timepiece & operator=(const volatile timepiece &); - void start() volatile; ///< Set the begin time. - double calc() const volatile; ///< Get the intervals from the begin time. + timepiece & operator=(const timepiece &); + void start() noexcept; ///< Set the begin time. + double calc() const noexcept; ///< Get the intervals from the begin time. private: struct impl_t; impl_t * impl_; 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/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index a1845683..3505f6e3 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -1,7 +1,7 @@ /* * Platform Specification 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 diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 8baf1d8f..1a58ec6c 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -486,6 +486,7 @@ namespace detail atombase_.wm_protocols = ::XInternAtom(display_, "WM_PROTOCOLS", False); atombase_.wm_change_state = ::XInternAtom(display_, "WM_CHANGE_STATE", False); atombase_.wm_delete_window = ::XInternAtom(display_, "WM_DELETE_WINDOW", False); + atombase_.net_frame_extents = ::XInternAtom(display_, "_NET_FRAME_EXTENTS", False); atombase_.net_wm_state = ::XInternAtom(display_, "_NET_WM_STATE", False); atombase_.net_wm_state_skip_taskbar = ::XInternAtom(display_, "_NET_WM_STATE_SKIP_TASKBAR", False); atombase_.net_wm_state_fullscreen = ::XInternAtom(display_, "_NET_WM_STATE_FULLSCREEN", False); @@ -588,16 +589,59 @@ namespace detail } //There are three members make_owner(), get_owner() and remove(), - //they are maintain a table to discribe the owner of windows because the feature in X11, the + //they are maintain a table to discribe the owner of windows because of the feature in X11, the //owner of top level window must be RootWindow. void platform_spec::make_owner(native_window_type owner, native_window_type wd) { - platform_scope_guard psg; + platform_scope_guard lock; wincontext_[wd].owner = owner; - window_context_t & context = wincontext_[owner]; - if(context.owned == 0) - context.owned = new std::vector; - context.owned->push_back(wd); + + auto& owner_ctx = wincontext_[owner]; + if(!owner_ctx.owned) + owner_ctx.owned = new std::vector; + owner_ctx.owned->push_back(wd); + } + + bool platform_spec::umake_owner(native_window_type child) + { + platform_scope_guard lock; + + auto i = wincontext_.find(child); + if(i == wincontext_.end()) + return false; + + if(i->second.owner) + { + auto u = wincontext_.find(i->second.owner); + if(u != wincontext_.end()) + { + auto * owned = u->second.owned; + if(owned) + { + auto j = std::find(owned->begin(), owned->end(), child); + if(j != owned->end()) + owned->erase(j); + + if(owned->empty()) + { + delete owned; + u->second.owned = nullptr; + //The owner owns no child. If it is not a child of other owners, + //remove it. + if(nullptr == u->second.owner) + wincontext_.erase(u); + } + } + } + + i->second.owner = nullptr; + } + + //Don't remove the ownerships which the child is a owner window. + if(nullptr == i->second.owned) + wincontext_.erase(i); + + return true; } native_window_type platform_spec::get_owner(native_window_type wd) const @@ -610,7 +654,9 @@ namespace detail void platform_spec::remove(native_window_type wd) { msg_dispatcher_->erase(reinterpret_cast(wd)); - platform_scope_guard psg; + + platform_scope_guard lock; +#if 0 auto i = wincontext_.find(wd); if(i == wincontext_.end()) return; @@ -641,6 +687,28 @@ namespace detail } delete vec; wincontext_.erase(i); +#else + if(umake_owner(wd)) + { + auto i = wincontext_.find(wd); + if(i != wincontext_.end()) + { + if(i->second.owned) + { + set_error_handler(); + auto & wd_manager = detail::bedrock::instance().wd_manager(); + for(auto u = i->second.owned->rbegin(); u != i->second.owned->rend(); ++u) + wd_manager.close(wd_manager.root(*u)); + + rev_error_handler(); + + delete i->second.owned; + } + + wincontext_.erase(i); + } + } +#endif iconbase_.erase(wd); } diff --git a/source/detail/platform_spec_windows.cpp b/source/detail/platform_spec_windows.cpp index 179b87cf..812c2ed0 100644 --- a/source/detail/platform_spec_windows.cpp +++ b/source/detail/platform_spec_windows.cpp @@ -1,7 +1,7 @@ /** * Platform Specification 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 diff --git a/source/detail/posix/msg_dispatcher.hpp b/source/detail/posix/msg_dispatcher.hpp index 8aed6ce5..9ba12ad1 100644 --- a/source/detail/posix/msg_dispatcher.hpp +++ b/source/detail/posix/msg_dispatcher.hpp @@ -1,6 +1,6 @@ /* * Message Dispatcher Implementation - * 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 @@ -26,6 +26,7 @@ #include #include #include +#include namespace nana { @@ -221,6 +222,8 @@ namespace detail case 0: msg_pack.kind = msg_pack.kind_xevent; msg_pack.u.xevent = event; + _m_msg_dispatch(msg_pack); + break; case 1: _m_msg_dispatch(msg_pack); } @@ -338,7 +341,7 @@ namespace detail private: Display * display_; - volatile bool is_work_{ false }; + std::atomic is_work_{ false }; std::unique_ptr thrd_; struct table_tag diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index e8b2be24..3191ddb1 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -1,7 +1,7 @@ /* * Platform Specification 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 @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -132,6 +133,7 @@ namespace detail Atom wm_change_state; Atom wm_delete_window; //ext + Atom net_frame_extents; Atom net_wm_state; Atom net_wm_state_skip_taskbar; Atom net_wm_state_fullscreen; @@ -213,6 +215,9 @@ namespace detail const atombase_tag & atombase() const; void make_owner(native_window_type owner, native_window_type wd); + + // Cancel the ownership + bool umake_owner(native_window_type child); native_window_type get_owner(native_window_type) const; void remove(native_window_type); @@ -264,7 +269,7 @@ namespace detail std::recursive_mutex xlib_locker_; struct caret_holder_tag { - volatile bool exit_thread; + std::atomic exit_thread; std::unique_ptr thr; std::map carets; }caret_holder_; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index a69d5807..221d04c8 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -308,11 +308,11 @@ namespace nana { if (pi_data_->menu.window && (pi_data_->menu.window != wd)) { - wd = native_interface::get_owner_window(wd); + wd = native_interface::get_window(wd, window_relationship::owner); while (wd) { if (wd != pi_data_->menu.window) - wd = native_interface::get_owner_window(wd); + wd = native_interface::get_window(wd, window_relationship::owner); else return false; } @@ -329,7 +329,7 @@ namespace nana erase_menu(true); pi_data_->menu.window = menu_wd; - pi_data_->menu.owner = native_interface::get_owner_window(menu_wd); + pi_data_->menu.owner = native_interface::get_window(menu_wd, window_relationship::owner); pi_data_->menu.has_keyboard = has_keyboard; } } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index abb38f11..debbbfee 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -26,6 +26,9 @@ namespace nana { namespace detail { + //Declarations of helper functions defined in native_window_interface.cpp + void x11_apply_exposed_position(native_window_type wd); + #pragma pack(1) union event_mask { @@ -572,6 +575,62 @@ namespace detail return true; } + void x_lookup_chars(const root_misc* rruntime, basic_window * msgwd, char* keybuf, std::size_t keybuf_len, const arg_keyboard& modifiers_status) + { + if (!msgwd->flags.enabled) + return; + + static auto& brock = detail::bedrock::instance(); + auto & wd_manager = brock.wd_manager(); + + auto& context = *brock.get_thread_context(msgwd->thread_id); + + auto const native_window = rruntime->window->root; + + + nana::detail::charset_conv charset(NANA_UNICODE, "UTF-8"); + const std::string& str = charset.charset(std::string(keybuf, keybuf + keybuf_len)); + auto const charbuf = reinterpret_cast(str.c_str()); + auto const len = str.size() / sizeof(wchar_t); + + for(std::size_t 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) && rruntime->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(msgwd); + msgwd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwd)); + if(arg.ignore == false && wd_manager.available(msgwd)) + draw_invoker(&drawer::key_char, msgwd, arg, &context); + } + + if(brock.shortkey_occurred(false)) + context.is_alt_pressed = false; + } + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -905,6 +964,9 @@ namespace detail break; case MapNotify: case UnmapNotify: + if(xevent.type == MapNotify) + x11_apply_exposed_position(native_window); + brock.event_expose(msgwnd, (xevent.type == MapNotify)); context.platform.motion_window = nullptr; break; @@ -1071,6 +1133,8 @@ namespace detail wd_manager.do_lazy_refresh(msgwnd, false); break; } +#if 0 + //Fall through case XLookupChars: if (msgwnd->flags.enabled) { @@ -1118,6 +1182,13 @@ namespace detail 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); @@ -1281,7 +1352,7 @@ namespace detail if(condition_wd && is_modal) { native_window_type modal = reinterpret_cast(condition_wd)->root; - owner_native = native_interface::get_owner_window(modal); + owner_native = native_interface::get_window(modal, window_relationship::owner); if(owner_native) { native_interface::enable_window(owner_native, false); diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index aee3ea8e..d6f4b99b 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -1,7 +1,7 @@ /* * Platform 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 @@ -129,6 +129,96 @@ namespace nana{ { nana::detail::platform_spec & spec = nana::detail::platform_spec::instance(); } + + + //The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is + //unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that + //set to a unmapped windows should be kept and use the position when the window is mapped. + std::map exposed_positions; //locked by platform_scope_guard + + //Returns the parent window. + //It may return a decoration frame window if the requested window is a top level and WM is a + //reparenting window manager. + native_window_type x11_parent_window(native_window_type wd) + { + Window root; + Window parent; + Window * children; + unsigned size; + + platform_scope_guard lock; + + if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast(wd), + &root, &parent, &children, &size)) + { + ::XFree(children); + return reinterpret_cast(parent); + } + return nullptr; + } + + native_window_type x11_decoration_frame(native_window_type wd) + { + auto const owner = restrict::spec.get_owner(wd); + auto const root_wd = restrict::spec.root_window(); + + if(owner) + { + auto test_wd = wd; + while(true) + { + auto upper = x11_parent_window(test_wd); + if((reinterpret_cast(upper) != root_wd) && (upper != owner)) + { + test_wd = upper; + } + else if(wd != test_wd) + return test_wd; + else + return nullptr; + } + } + + return nullptr; + } + + + void x11_apply_exposed_position(native_window_type wd) + { + nana::detail::platform_scope_guard lock; + + auto i = exposed_positions.find(reinterpret_cast(wd)); + if(i == exposed_positions.cend()) + return; + + native_interface::move_window(wd, i->second.x, i->second.y); + + exposed_positions.erase(i); + } + + namespace x11_wait + { + static Bool configure(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast(arg)); + } + + static Bool map(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == MapNotify) && (evt->xmap.window == *reinterpret_cast(arg)); + } + + static Bool unmap(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == MapNotify) && (evt->xunmap.window == *reinterpret_cast(arg)); + } + } + + static void x11_wait_for(Window wd, Bool(*pred_fn)(Display*, XEvent*, char*)) + { + XEvent dummy; + ::XPeekIfEvent(restrict::spec.open_display(), &dummy, pred_fn, reinterpret_cast(&wd)); + } #endif //struct native_interface @@ -196,13 +286,6 @@ namespace nana{ return rectangle{ primary_monitor_size() }; } -#ifdef NANA_X11 - //The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is - //unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that - //set to a unmapped windows should be kept and use the position when the window is mapped. - std::map exposed_positions; //locked by platform_scope_guard -#endif - //platform-dependent native_interface::window_result native_interface::create_window(native_window_type owner, bool nested, const rectangle& r, const appearance& app) { @@ -267,7 +350,7 @@ namespace nana{ XSetWindowAttributes win_attr; unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel | - CWWinGravity | CWBitGravity | CWColormap | CWEventMask; + CWColormap | CWEventMask; Display * disp = restrict::spec.open_display(); win_attr.colormap = restrict::spec.colormap(); @@ -276,8 +359,6 @@ namespace nana{ win_attr.background_pixel = 0xFFFFFF; win_attr.border_pixmap = None; win_attr.border_pixel = 0x0; - win_attr.bit_gravity = 0; - win_attr.win_gravity = NorthWestGravity; win_attr.backing_store = 0; win_attr.backing_planes = 0; win_attr.backing_pixel = 0; @@ -296,6 +377,8 @@ namespace nana{ win_attr.save_under = True; attr_mask |= CWSaveUnder; + ///The parameter of XCreateWindow to create a top level window must be root. + ///But after creation, the real parent is the reparenting frame window. parent = restrict::spec.root_window(); calc_screen_point(owner, pos); } @@ -309,9 +392,10 @@ namespace nana{ if(handle) { //make owner if it is a popup window - if((!nested) && owner) + if(!nested) { - restrict::spec.make_owner(owner, reinterpret_cast(handle)); + auto origin_owner = (owner ? owner : reinterpret_cast(restrict::spec.root_window())); + restrict::spec.make_owner(origin_owner, reinterpret_cast(handle)); exposed_positions[handle] = pos; } @@ -421,7 +505,7 @@ namespace nana{ XSetWindowAttributes win_attr; unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel | - CWWinGravity | CWBitGravity | CWColormap | CWEventMask; + CWColormap | CWEventMask | CWOverrideRedirect; Display * disp = restrict::spec.open_display(); win_attr.colormap = restrict::spec.colormap(); @@ -430,15 +514,12 @@ namespace nana{ win_attr.background_pixel = 0xFFFFFF; win_attr.border_pixmap = None; win_attr.border_pixel = 0x0; - win_attr.bit_gravity = 0; - win_attr.win_gravity = NorthWestGravity; win_attr.backing_store = 0; win_attr.backing_planes = 0; win_attr.backing_pixel = 0; win_attr.colormap = restrict::spec.colormap(); win_attr.override_redirect = True; - attr_mask |= CWOverrideRedirect; nana::point pos(r.x, r.y); win_attr.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; @@ -668,21 +749,21 @@ namespace nana{ { ::XMapWindow(disp, reinterpret_cast(wd)); - auto i = exposed_positions.find(reinterpret_cast(wd)); - if(i != exposed_positions.end()) - { - ::XMoveWindow(disp, reinterpret_cast(wd), i->second.x, i->second.y); - exposed_positions.erase(i); - } + //Wait for the mapping notify to update the local attribute of visibility so that + //the followed window_visible() call can return the updated visibility value. + x11_wait_for(reinterpret_cast(wd), x11_wait::map); Window grab = restrict::spec.grab(0); if(grab == reinterpret_cast(wd)) capture_window(wd, true); } else + { ::XUnmapWindow(disp, reinterpret_cast(wd)); - - ::XFlush(disp); + //Wait for the mapping notify to update the local attribute of visibility so that + //the followed window_visible() call can return the updated visibility value. + x11_wait_for(reinterpret_cast(wd), x11_wait::unmap); + } } static_cast(active); //eliminate unused parameter compiler warning. #endif @@ -844,19 +925,33 @@ namespace nana{ } return nana::point(r.left, r.top); #elif defined(NANA_X11) - int x, y; - nana::detail::platform_scope_guard psg; - Window coord_wd = reinterpret_cast(restrict::spec.get_owner(wd)); - if(!coord_wd) + point scr_pos; + nana::detail::platform_scope_guard lock; + + + point origin{}; + + auto coord_wd = restrict::spec.get_owner(wd); + if(coord_wd) { - coord_wd = reinterpret_cast(parent_window(wd)); - if(!coord_wd) - coord_wd = restrict::spec.root_window(); + auto fm_extents = window_frame_extents(wd); + origin.x = -fm_extents.left; + origin.y = -fm_extents.top; + + if(reinterpret_cast(coord_wd) != restrict::spec.root_window()) + { + fm_extents = window_frame_extents(coord_wd); + origin.x += fm_extents.left; + origin.y += fm_extents.top; + } } + else + coord_wd = get_window(wd, window_relationship::parent); + Window child; - if(True == ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast(wd), coord_wd, 0, 0, &x, &y, &child)) - return nana::point(x, y); - return nana::point(0, 0); + ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast(wd), reinterpret_cast(coord_wd), origin.x, origin.y, &scr_pos.x, &scr_pos.y, &child); + + return scr_pos; #endif } @@ -887,13 +982,15 @@ namespace nana{ #elif defined(NANA_X11) Display * disp = restrict::spec.open_display(); - nana::detail::platform_scope_guard psg; - Window owner = reinterpret_cast(restrict::spec.get_owner(wd)); - if(owner) + nana::detail::platform_scope_guard lock; + + if(point{x, y} == window_position(wd)) { - Window child; - ::XTranslateCoordinates(disp, owner, restrict::spec.root_window(), - x, y, &x, &y, &child); + //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; @@ -901,7 +998,19 @@ namespace nana{ if(attr.map_state == IsUnmapped) exposed_positions[reinterpret_cast(wd)] = ::nana::point{x, y}; + auto const owner = restrict::spec.get_owner(wd); + if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) + { + auto origin = window_position(owner); + x += origin.x; + y += origin.y; + } + ::XMoveWindow(disp, reinterpret_cast(wd), x, y); + + //Wait for the configuration notify to update the local attribute of position so that + //the followed window_position() call can return the updated position value. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); #endif } @@ -951,16 +1060,6 @@ namespace nana{ else hints.flags = 0; - Window owner = reinterpret_cast(restrict::spec.get_owner(wd)); - int x = r.x; - int y = r.y; - if(owner) - { - Window child; - ::XTranslateCoordinates(disp, owner, restrict::spec.root_window(), - r.x, r.y, &x, &y, &child); - } - XWindowAttributes attr; ::XGetWindowAttributes(disp, reinterpret_cast(wd), &attr); if(attr.map_state == IsUnmapped) @@ -969,13 +1068,32 @@ namespace nana{ hints.width = r.width; hints.height = r.height; - exposed_positions[reinterpret_cast(wd)] = point{x, y}; + exposed_positions[reinterpret_cast(wd)] = r.position(); } if(hints.flags) ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); + int x = r.x; + int y = r.y; + + auto const owner = restrict::spec.get_owner(wd); + if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) + { + auto origin = window_position(owner); + x += origin.x; + y += origin.y; + } + ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); + + //Wait for the configuration notify to update the local attribute of position so that + //the followed window_position() call can return the updated position value. + + //It seems that XMoveResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called + //to make sure the local attribute is updated. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); + return true; #endif } @@ -1055,6 +1173,46 @@ namespace nana{ #endif } + native_interface::frame_extents native_interface::window_frame_extents(native_window_type wd) + { + frame_extents fm_extents{0, 0, 0, 0}; + + #if defined(NANA_WINDOWS) + ::RECT client; + ::GetClientRect(reinterpret_cast(wd), &client); //The right and bottom of client by GetClientRect indicate the width and height of the area + ::RECT wd_area; + ::GetWindowRect(reinterpret_cast(wd), &wd_area); + + fm_extents.left = client.left - wd_area.left; + fm_extents.right = wd_area.right - client.right; + fm_extents.top = client.top - wd_area.top; + fm_extents.bottom = wd_area.bottom - client.bottom; + #elif defined(NANA_X11) + Atom type; + int format; + unsigned long len, bytes_left = 0; + unsigned char *data; + + nana::detail::platform_scope_guard lock; + if(Success == ::XGetWindowProperty(restrict::spec.open_display(), reinterpret_cast(wd), + restrict::spec.atombase().net_frame_extents, 0, 16, 0, + XA_CARDINAL, &type, &format, + &len, &bytes_left, &data)) + { + if(type != None && len == 4) + { + fm_extents.left = ((long*)data)[0]; + fm_extents.right = ((long*)data)[1]; + fm_extents.top = ((long*)data)[2]; + fm_extents.bottom = ((long*)data)[3]; + } + ::XFree(data); + } + #endif + + return fm_extents; + } + bool native_interface::window_size(native_window_type wd, const size& sz) { #if defined(NANA_WINDOWS) @@ -1093,6 +1251,10 @@ namespace nana{ ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); } ::XResizeWindow(disp, reinterpret_cast(wd), sz.width, sz.height); + + //It seems that XResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called + //to make sure the local attribute is updated. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); return true; #endif } @@ -1222,41 +1384,37 @@ namespace nana{ Window drop_wd; int x, y; unsigned mask; - nana::detail::platform_scope_guard psg; + nana::detail::platform_scope_guard lock; ::XQueryPointer(restrict::spec.open_display(), restrict::spec.root_window(), &drop_wd, &drop_wd, &pos.x, &pos.y, &x, &y, &mask); return pos; #endif } - native_window_type native_interface::get_owner_window(native_window_type wd) - { -#if defined(NANA_WINDOWS) - return reinterpret_cast(::GetWindow(reinterpret_cast(wd), GW_OWNER)); -#elif defined(NANA_X11) - return restrict::spec.get_owner(wd); -#endif - } - - native_window_type native_interface::parent_window(native_window_type wd) + native_window_type native_interface::get_window(native_window_type wd, window_relationship rsp) { #ifdef NANA_WINDOWS - return reinterpret_cast(::GetParent(reinterpret_cast(wd))); + if(window_relationship::either_po == rsp) + return reinterpret_cast(::GetParent(reinterpret_cast(wd))); + else if(window_relationship::parent == rsp) + return reinterpret_cast(::GetAncestor(reinterpret_cast(wd), GA_PARENT)); + else if(window_relationship::owner == rsp) + return reinterpret_cast(::GetWindow(reinterpret_cast(wd), GW_OWNER)); #elif defined(NANA_X11) - Window root; - Window parent; - Window * children; - unsigned size; - platform_scope_guard lock; - if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast(wd), - &root, &parent, &children, &size)) + auto owner = restrict::spec.get_owner(wd); + + if(window_relationship::either_po == rsp) { - ::XFree(children); - return reinterpret_cast(parent); + if(owner) + return owner; } - return nullptr; + else if(window_relationship::owner == rsp) + return owner; + else if(window_relationship::parent == rsp) + return x11_parent_window(wd); #endif + return nullptr; } native_window_type native_interface::parent_window(native_window_type child, native_window_type new_parent, bool returns_previous) @@ -1276,11 +1434,21 @@ namespace nana{ platform_scope_guard lock; if(returns_previous) - prev = parent_window(child); + prev = get_window(child, window_relationship::either_po); + + if(native_window_type{} == new_parent) + new_parent = reinterpret_cast(restrict::spec.root_window()); ::XReparentWindow(restrict::spec.open_display(), reinterpret_cast(child), reinterpret_cast(new_parent), 0, 0); + + + // If umake_owner returns true, it indicates the child windows is a popup window. + // So make the ownership of new_parent and child. + if(restrict::spec.umake_owner(child)) + restrict::spec.make_owner(new_parent, child); + return prev; #endif } diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 45764b81..19ec8133 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -166,10 +166,18 @@ namespace nana if (!cover->visible) continue; - if (is_wd_root ? - (category::flags::root == cover->other.category) - : - ((category::flags::root != cover->other.category) && (nullptr == cover->effect.bground))) + if (is_wd_root) + { + if(category::flags::root == cover->other.category) + { + if (overlap(vis_rect, rectangle{ native_interface::window_position(cover->root), cover->dimension }, block.r)) + { + block.window = cover; + blocks.push_back(block); + } + } + } + else if((category::flags::root != cover->other.category) && (nullptr == cover->effect.bground)) { if (overlap(vis_rect, rectangle{ cover->pos_root, cover->dimension }, block.r)) { diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 2a98269e..785f45be 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -950,6 +950,19 @@ namespace detail if (wd->dimension == sz) return false; + std::vector presence; + + if (wd->dimension.width < sz.width || wd->dimension.height < sz.height) + { + auto wd_r = rectangle{ wd->dimension }; + for (auto child : wd->children) + { + auto child_r = rectangle{ child->pos_owner, child->dimension }; + if (!overlapped(wd_r, child_r)) + presence.push_back(child); + } + } + //Before resiz the window, creates the new graphics paint::graphics graph; paint::graphics root_graph; @@ -1010,6 +1023,11 @@ namespace detail } } + for (auto child : presence) + { + refresh_tree(child); + } + arg_resized arg; arg.window_handle = reinterpret_cast(wd); arg.width = sz.width; diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index 36a7c202..caf6380b 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -1,7 +1,7 @@ /* * A Message Box Class * 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 @@ -1294,7 +1294,7 @@ namespace nana min_width_entry_field_pixels_ = pixels; } -#ifndef _nana_cxx_folding_expression +#ifndef __cpp_fold_expressions void inputbox::_m_fetch_args(std::vector&) {} #endif diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 96d7ac6d..5b22491e 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -798,17 +798,20 @@ namespace nana case number_t::kind::real: return static_cast(number.real()); case number_t::kind::percent: - adjustable_px = area_px * number.real(); case number_t::kind::none: - { - auto fpx = adjustable_px + precise_px; - auto px = static_cast(fpx); - precise_px = fpx - px; - return px; - } break; + default: + return 0; //Useless } - return 0; //Useless + + if(number_t::kind::percent == number.kind_of()) + adjustable_px = area_px * number.real() + precise_px; + else + adjustable_px += precise_px; + + auto const px = static_cast(adjustable_px); + precise_px = adjustable_px - px; + return px; } std::pair calc_weight_floor() @@ -2879,25 +2882,26 @@ namespace nana } field_gather * attached_field = nullptr; - if (name.size()) + + //find the field with specified name. + //the field may not be created. + auto i = fields.find(name); + if (fields.end() != i) { - //find the field with specified name. - //the field may not be created. - auto i = fields.find(name); - if (fields.end() != i) - { - attached_field = i->second; - //the field is attached to a division, it means there is another division with same name. - if (attached_field->attached) - throw std::runtime_error("place, the name '" + name + "' is redefined."); - } + attached_field = i->second; + //the field is attached to a division, it means there is another division with same name. + if (attached_field->attached) + throw std::runtime_error("place, the name '" + name + "' is redefined."); } token unmatch = token::width; switch (div_type) { - case token::eof: unmatch = token::height; // "horitontal" div - case token::vert: // "vertical" div + case token::eof: // "horitontal" div + case token::vert: // "vertical" div + if(token::eof == div_type) + unmatch = token::height; + for (auto& ch : children) if (ch->weigth_type == unmatch) throw std::invalid_argument("nana.place: unmatch vertical-heigth/horizontal-width betwen division '" diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index b17be9cd..ca2ea8d7 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -1,7 +1,7 @@ /* * Parts of Class Place * 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 @@ -607,12 +607,16 @@ namespace nana it = ib = 0; il = ir = 1; break; - default: - il = 3; //left case 3: //top, right, bottom it = 0; ir = 1; ib = 2; + break; + default: //left, top, right, bottom, left + it = 0; + ir = 1; + ib = 2; + il = 3; } int pos = 0; @@ -661,12 +665,16 @@ namespace nana it = ib = 0; il = ir = 1; break; - default: - il = 3; //left case 3: //top, right, bottom it = 0; ir = 1; ib = 2; + break; + default: //left, top, right, bottom, left + it = 0; + ir = 1; + ib = 2; + il = 3; } using px_type = decltype(r.height); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 374b4250..30f33e9a 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -669,7 +669,7 @@ namespace API internal_scope_guard lock; if(restrict::wd_manager().available(iwd) && (iwd->other.category == category::flags::root)) { - auto owner = interface_type::get_owner_window(iwd->root); + auto owner = interface_type::get_window(iwd->root, window_relationship::owner); if(owner) return reinterpret_cast(restrict::wd_manager().root(owner)); } @@ -812,8 +812,14 @@ namespace API return{}; auto sz = window_size(wd); - sz.width += iwd->extra_width; - sz.height += iwd->extra_height; + + if(category::flags::root == iwd->other.category) + { + auto fm_extents = interface_type::window_frame_extents(iwd->root); + sz.width += fm_extents.left + fm_extents.right; + sz.height += fm_extents.top + fm_extents.bottom; + } + return sz; } @@ -825,7 +831,11 @@ namespace API { if (category::flags::root == iwd->other.category) { + auto fm_extents = interface_type::window_frame_extents(iwd->root); + size inner_size = sz; + +#if 0 if (inner_size.width < iwd->extra_width) inner_size.width = 0; else @@ -835,6 +845,17 @@ namespace API 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 + inner_size.width -= static_cast(fm_extents.left + fm_extents.right); + + if (inner_size.height < static_cast(fm_extents.top + fm_extents.bottom)) + 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/widgets/button.cpp b/source/gui/widgets/button.cpp index c9ba1221..d407f0eb 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -150,11 +150,12 @@ namespace nana{ namespace drawerbase if (false == cite_.draw(graph, attr_.bgcolor, attr_.fgcolor, ::nana::rectangle{ graph.size() }, e_state)) { - if (!API::is_transparent_background(*wdg_)) - { + if (API::is_transparent_background(*wdg_)) + API::dev::copy_transparent_background(*wdg_, graph); + else _m_draw_background(graph); - _m_draw_border(graph); - } + + _m_draw_border(graph); } _m_draw_title(graph, eb); } diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index a8401d5f..619446a2 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -732,7 +732,6 @@ namespace nana bool call_other_keys = false; if(drawer_->editable()) { - bool is_move_up = false; switch(arg.key) { case keyboard::os_arrow_left: @@ -741,9 +740,8 @@ namespace nana drawer_->editor()->reset_caret(); break; case keyboard::os_arrow_up: - is_move_up = true; case keyboard::os_arrow_down: - drawer_->move_items(is_move_up, true); + drawer_->move_items((keyboard::os_arrow_up == arg.key), true); break; default: call_other_keys = true; @@ -751,15 +749,15 @@ namespace nana } else { - bool is_move_up = false; switch(arg.key) { case keyboard::os_arrow_left: case keyboard::os_arrow_up: - is_move_up = true; + drawer_->move_items(true, true); + break; case keyboard::os_arrow_right: case keyboard::os_arrow_down: - drawer_->move_items(is_move_up, true); + drawer_->move_items(false, true); break; default: call_other_keys = true; diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index e9ece21a..8be557ef 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -264,15 +264,22 @@ namespace nana{ drawing dw(*this); + //When the group is resized, the drawing is called before moving the caption, but + //the drawing of group requires the lastest position of caption for gradual rectangle. + //For the requirement, a move event handler is required for listning the change of caption's position. + impl_->caption.events().move([this](const arg_move&){ + if (align::left != impl_->caption_align) + API::refresh_window(*this); + }); + // This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !! dw.draw([this](paint::graphics& graph) { auto gap_px = impl_->gap - 1; - graph.rectangle(true, API::bgcolor(this->parent())); - auto const top_round_line = static_cast(impl_->caption_dimension.height) / 2; + graph.rectangle(true, API::bgcolor(this->parent())); graph.round_rectangle(rectangle(point(gap_px, top_round_line), nana::size(graph.width() - 2 * gap_px, graph.height() - top_round_line - gap_px) ), @@ -281,11 +288,10 @@ namespace nana{ auto opt_r = API::window_rectangle(impl_->caption); if (opt_r) { - rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width, static_cast(top_round_line - opt_r->y) } }; + rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, static_cast(top_round_line - opt_r->y) } }; grad_r.y += top_round_line*2 / 3; grad_r.x -= 2; - grad_r.width += 4; graph.gradual_rectangle(grad_r, API::bgcolor(this->parent()), this->bgcolor(), true 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 230891c2..7f16fd3d 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -2934,9 +2934,9 @@ namespace nana cat_proxy(ess_, pos.cat).at(pos.item).select(true); } - void hovered(index_type /*pos*/) override + void hovered(index_type pos) override { - auto offset = ess_->content_view->origin().y / ess_->item_height(); + auto offset = ess_->lister.distance(ess_->first_display(), ess_->lister.index_cast(pos, false)); if (ess_->pointer_where.first != parts::list || ess_->pointer_where.second != offset) { @@ -2996,11 +2996,11 @@ namespace nana } else { - auto last_off = this->distance(this->first(), this->last()) * ess_->item_height(); - if (last_off - off >= screen_px) + auto const content_px = ess_->content_view->content_size().height; + if (content_px - off >= screen_px) origin.y = static_cast(off); - else if (last_off >= screen_px) - origin.y = static_cast(last_off - screen_px); + else if (content_px >= screen_px) + origin.y = static_cast(content_px - screen_px); } if (ess_->content_view->move_origin(origin - ess_->content_view->origin())) @@ -4378,9 +4378,11 @@ namespace nana essence_->ptr_state = item_state::highlighted; bool need_refresh = false; - //Do sort - if (essence_->header.sortable() && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed) + + //Don't sort the column when the mouse is due to released for stopping resizing column. + if ((drawer_header_->splitter() == npos) && essence_->header.sortable() && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed) { + //Try to sort the column if(essence_->pointer_where.second < essence_->header.cont().size()) need_refresh = essence_->lister.sort_column(essence_->pointer_where.second, nullptr); } @@ -4475,14 +4477,11 @@ namespace nana if (list.first().empty()) return; - bool upward = false; - switch(arg.key) { case keyboard::os_arrow_up: - upward = true; case keyboard::os_arrow_down: - list.move_select(upward, !arg.shift, true); + list.move_select((keyboard::os_arrow_up == arg.key), !arg.shift, true); break; case L' ': { @@ -4494,9 +4493,9 @@ namespace nana } break; case keyboard::os_pageup : - upward = true; case keyboard::os_pagedown: { + auto const upward = (keyboard::os_pageup == arg.key); auto const item_px = essence_->item_height(); auto picked_items = list.pick_items(true, true); index_pair init_idx = (picked_items.empty() ? list.first() : picked_items[0]); @@ -5390,6 +5389,7 @@ namespace nana void listbox::auto_draw(bool enabled) noexcept { + internal_scope_guard lock; auto & ess = _m_ess(); if (ess.auto_draw != enabled) { @@ -5400,6 +5400,7 @@ namespace nana void listbox::scroll(bool to_bottom, size_type cat_pos) { + internal_scope_guard lock; auto & ess = _m_ess(); auto cats = this->size_categ(); @@ -5498,6 +5499,7 @@ namespace nana rectangle listbox::content_area() const { + internal_scope_guard lock; auto & ess = _m_ess(); auto carea = ess.content_area(); carea.x += ess.header.margin(); @@ -5544,29 +5546,34 @@ namespace nana listbox::cat_proxy listbox::at(size_type pos) { + internal_scope_guard lock; check_range(pos, size_categ()); return{ &_m_ess(), pos }; } const listbox::cat_proxy listbox::at(size_type pos) const { + internal_scope_guard lock; check_range(pos, size_categ()); return{ &_m_ess(), pos }; } listbox::item_proxy listbox::at(const index_pair& abs_pos) { + internal_scope_guard lock; return at(abs_pos.cat).at(abs_pos.item); } const listbox::item_proxy listbox::at(const index_pair& pos_abs) const { + internal_scope_guard lock; return at(pos_abs.cat).at(pos_abs.item); } // Contributed by leobackes(pr#97) listbox::index_pair listbox::cast( const point& pos ) const { + internal_scope_guard lock; auto & ess=_m_ess(); auto _where = ess.where(pos); @@ -5578,27 +5585,32 @@ namespace nana auto listbox::column_at(size_type pos, bool disp_order) -> column_interface& { + internal_scope_guard lock; return _m_ess().header.at(pos, disp_order); } auto listbox::column_at(size_type pos, bool disp_order) const -> const column_interface& { + internal_scope_guard lock; return _m_ess().header.at(pos, disp_order); } auto listbox::column_size() const ->size_type { + internal_scope_guard lock; return _m_ess().header.cont().size(); } //Contributed by leobackes(pr#97) listbox::size_type listbox::column_from_pos ( const point& pos ) const { + internal_scope_guard lock; return _m_ess().column_from_pos(pos.x); } void listbox::checkable(bool chkable) { + internal_scope_guard lock; auto & ess = _m_ess(); if(ess.checkable != chkable) { @@ -5609,11 +5621,13 @@ namespace nana auto listbox::checked() const -> index_pairs { + internal_scope_guard lock; return _m_ess().lister.pick_items(false); } void listbox::clear(size_type cat) { + internal_scope_guard lock; auto & ess = _m_ess(); auto origin = ess.content_view->origin(); @@ -5637,6 +5651,7 @@ namespace nana void listbox::clear() { + internal_scope_guard lock; auto & ess = _m_ess(); ess.lister.clear(); @@ -5651,6 +5666,7 @@ namespace nana void listbox::erase(size_type cat) { + internal_scope_guard lock; auto & ess = _m_ess(); auto origin = ess.content_view->origin(); @@ -5675,6 +5691,7 @@ namespace nana void listbox::erase() { + internal_scope_guard lock; auto & ess = _m_ess(); ess.lister.erase(); ess.calc_content_size(); @@ -5683,6 +5700,8 @@ namespace nana void listbox::erase(index_pairs indexes) { + internal_scope_guard lock; + std::sort(indexes.begin(), indexes.end(), [](const index_pair& pos1, const index_pair& pos2) { return (pos1 > pos2); @@ -5732,6 +5751,7 @@ namespace nana if(ip.empty()) return ip; + internal_scope_guard lock; auto * ess = ip._m_ess(); auto _where = ip.pos(); @@ -5754,48 +5774,57 @@ namespace nana bool listbox::sortable() const { + internal_scope_guard lock; return _m_ess().header.sortable(); } void listbox::sortable(bool enable) { + internal_scope_guard lock; _m_ess().header.sortable(enable); } void listbox::set_sort_compare(size_type col, std::function strick_ordering) { + internal_scope_guard lock; _m_ess().header.at(col).weak_ordering = std::move(strick_ordering); } /// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items void listbox::sort_col(size_type col, bool reverse) { + internal_scope_guard lock; _m_ess().lister.sort_column(col, &reverse); } auto listbox::sort_col() const -> size_type { + internal_scope_guard lock; return _m_ess().lister.sort_attrs().column; } /// potencially ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items void listbox::unsort() { + internal_scope_guard lock; this->sort_col(npos, false); } bool listbox::freeze_sort(bool freeze) { + internal_scope_guard lock; return !_m_ess().lister.active_sort(!freeze); } auto listbox::selected() const -> index_pairs { + internal_scope_guard lock; return _m_ess().lister.pick_items(true); // absolute positions, no relative to display } void listbox::show_header(bool sh) { + internal_scope_guard lock; _m_ess().header.visible(sh); _m_ess().update(); } @@ -5807,6 +5836,7 @@ namespace nana void listbox::move_select(bool upwards) /// icon_renderer) { + internal_scope_guard lock; _m_ess().ctg_icon_renderer.swap(icon_renderer); _m_ess().update(); return *this; @@ -5846,6 +5877,7 @@ namespace nana listbox& listbox::category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed) { + internal_scope_guard lock; _m_ess().ctg_icon_renderer = [img_expanded, img_collapsed](paint::graphics& graph, const rectangle& rt_icon, bool expanded) { if (expanded) diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 8a679c4c..49fbda99 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -49,7 +49,7 @@ namespace nana if (value_ptr) { if (unknown_) - value_ += (*value_ptr ? 5 : 0); + value_ += 5; else value_ = (std::min)(max_, *value_ptr); @@ -99,7 +99,7 @@ namespace nana auto value_px = (widget_->size().width - border_px * 2); //avoid overflow - if (value_ < max_) + if (unknown_ || (value_ < max_)) value_px = static_cast(value_px * (double(value_) / double(max_))); if (value_px != value_px_) diff --git a/source/gui/widgets/scroll.cpp b/source/gui/widgets/scroll.cpp index acc2c8b6..10b2c6da 100644 --- a/source/gui/widgets/scroll.cpp +++ b/source/gui/widgets/scroll.cpp @@ -99,12 +99,14 @@ namespace nana //Check scroll_area to avoiding division by zero. if (scroll_area) - metrics_.value = pos * value_max / scroll_area; + metrics_.value = static_cast(pos * (static_cast(value_max) / scroll_area)); //converting to double to avoid overflow. - if(metrics_.value < value_max) + if (metrics_.value < value_max) { - int selfpos = static_cast(metrics_.value * scroll_area / value_max); - int nextpos = static_cast((metrics_.value + 1) * scroll_area / value_max); + //converting to double to avoid overflow. + auto const px_per_value = static_cast(scroll_area) / value_max; + int selfpos = static_cast(metrics_.value * px_per_value); + int nextpos = static_cast((metrics_.value + 1) * px_per_value); if(selfpos != nextpos && (pos - selfpos > nextpos - pos)) ++metrics_.value; diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index c68abb4c..3cbf61b7 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1853,7 +1853,6 @@ namespace nana item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y); impl_->attr.tree_cont.for_each(shape.first, nl); - auto & node_state = impl_->node_state; node_state.pressed_node = nl.node(); @@ -1869,7 +1868,6 @@ namespace nana else return; - impl_->draw(true); API::dev::lazy_refresh(); } diff --git a/source/internationalization.cpp b/source/internationalization.cpp index 6e4a99d5..d41b775c 100644 --- a/source/internationalization.cpp +++ b/source/internationalization.cpp @@ -417,7 +417,7 @@ namespace nana } } -#ifndef _nana_cxx_folding_expression +#ifndef __cpp_fold_expressions void internationalization::_m_fetch_args(std::vector&) {} #endif diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index fbf07358..004a34a5 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -498,41 +498,42 @@ namespace paint { if (nullptr == impl_->handle || nullptr == impl_->handle->context) return {}; - if (text.empty()) return std::unique_ptr{new unsigned[1]}; + auto pxbuf = std::unique_ptr{ new unsigned[text.size() ? text.size() : 1] }; - unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels; -#if defined(NANA_WINDOWS) - int * dx = new int[text.size()]; - SIZE extents; - ::GetTextExtentExPoint(impl_->handle->context, text.data(), static_cast(text.size()), 0, 0, dx, &extents); - - auto pxbuf = std::unique_ptr{ new unsigned[text.size()] }; - - pxbuf[0] = (text[0] == '\t' ? tab_pixels : dx[0]); - - for (std::size_t i = 1; i < text.size(); ++i) + if (!text.empty()) { - pxbuf[i] = (text[i] == '\t' ? tab_pixels : dx[i] - dx[i - 1]); - } - delete[] dx; + unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels; +#if defined(NANA_WINDOWS) + int * dx = new int[text.size()]; + SIZE extents; + ::GetTextExtentExPoint(impl_->handle->context, text.data(), static_cast(text.size()), 0, 0, dx, &extents); + + pxbuf[0] = (text[0] == '\t' ? tab_pixels : dx[0]); + + for (std::size_t i = 1; i < text.size(); ++i) + { + pxbuf[i] = (text[i] == '\t' ? tab_pixels : dx[i] - dx[i - 1]); + } + delete[] dx; #elif defined(NANA_X11) && defined(NANA_USE_XFT) - auto disp = nana::detail::platform_spec::instance().open_display(); - auto xft = reinterpret_cast(impl_->handle->font->native_handle()); + auto disp = nana::detail::platform_spec::instance().open_display(); + auto xft = reinterpret_cast(impl_->handle->font->native_handle()); - XGlyphInfo extents; - for (std::size_t i = 0; i < len; ++i) - { - if (text[i] != '\t') + XGlyphInfo extents; + for (std::size_t i = 0; i < text.size(); ++i) { - FT_UInt glyphs = ::XftCharIndex(disp, xft, text[i]); - ::XftGlyphExtents(disp, xft, &glyphs, 1, &extents); - pxbuf[i] = extents.xOff; + if (text[i] != '\t') + { + FT_UInt glyphs = ::XftCharIndex(disp, xft, text[i]); + ::XftGlyphExtents(disp, xft, &glyphs, 1, &extents); + pxbuf[i] = extents.xOff; + } + else + pxbuf[i] = tab_pixels; } - else - pxbuf[i] = tab_pixels; - } #endif + } return pxbuf; } diff --git a/source/system/timepiece.cpp b/source/system/timepiece.cpp index fb2f71b9..e8f98d24 100644 --- a/source/system/timepiece.cpp +++ b/source/system/timepiece.cpp @@ -24,7 +24,7 @@ namespace system : impl_(new impl_t) {} - timepiece::timepiece(const volatile timepiece& rhs) + timepiece::timepiece(const timepiece& rhs) : impl_(new impl_t(*rhs.impl_)) {} @@ -33,7 +33,7 @@ namespace system delete impl_; } - timepiece & timepiece::operator=(const volatile timepiece & rhs) + timepiece & timepiece::operator=(const timepiece & rhs) { if(this != &rhs) *impl_ = *rhs.impl_; @@ -41,7 +41,7 @@ namespace system return *this; } - void timepiece::start() volatile + void timepiece::start() noexcept { #if defined(NANA_WINDOWS) ::QueryPerformanceCounter(&impl_->beg_timestamp); @@ -51,7 +51,7 @@ namespace system #endif } - double timepiece::calc() const volatile + double timepiece::calc() const noexcept { #if defined(NANA_WINDOWS) LARGE_INTEGER li; 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