diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index ecc5079c..dbfb8218 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -1,13 +1,13 @@ /** * Predefined Symbols for C++ * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2016-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file nana/config.hpp + * @file nana/c++defines.hpp * * @brief Provide switches to adapt to the target OS, use of external libraries or workarounds compiler errors or lack of std C++ support. * @@ -31,13 +31,14 @@ * - _SCL_SECURE_NO_WARNNGS, _CRT_SECURE_NO_DEPRECATE (VC) * - STD_CODECVT_NOT_SUPPORTED (VC RC, is a known issue on libstdc++, it works on libc++) * - STD_THREAD_NOT_SUPPORTED (GCC < 4.8.1) - * - STD_put_time_NOT_SUPPORTED (GCC < 5) * - STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED (MinGW with GCC < 4.8.1) * - STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED (MinGW with GCC < 4.8.1) * - STD_TO_STRING_NOT_SUPPORTED (MinGW with GCC < 4.8) * - STD_FILESYSTEM_NOT_SUPPORTED (GCC < 5.3) .... * - CXX_NO_INLINE_NAMESPACE (Visual C++ < 2015) - * - STD_MAKE_UNIQUE_NOT_SUPPORTED (GCC < 4.9) + * - _enable_std_make_unique (GCC < 4.9) + * - _enable_std_put_time (GCC < 5) + * - _enable_std_clamp (Visual C++ < 2017) */ #ifndef NANA_CXX_DEFINES_INCLUDED @@ -57,7 +58,7 @@ # define CXX_NO_INLINE_NAMESPACE //no support of C++11 inline namespace until Visual C++ 2015 # define noexcept //no support of noexcept until Visual C++ 2015 -# define constexpr const //no support of constexpr until Visual C++ 2015 ? const ?? +# define constexpr //no support of constexpr until Visual C++ 2015 ? const ?? # else # undef STD_FILESYSTEM_NOT_SUPPORTED # endif @@ -102,14 +103,18 @@ #define _CRT_SECURE_NO_DEPRECATE #pragma warning(disable : 4996) - #if (_MSC_VER >= 1900) +# if (_MSC_VER >= 1900) // google: break any code that tries to use codecvt or codecvt. // google: It appears the C++ libs haven't been compiled with native char16_t/char32_t support. // google: Those definitions are for codecvt::id, codecvt::id and codecvt::id respectively. // However, the codecvt::id and codecvt::id definitions aren't there, and indeed, if you look at locale0.cpp in the CRT source code you'll see they're not defined at all. // google: That's a known issue, tracked by an active bug (DevDiv#1060849). We were able to update the STL's headers in response to char16_t/char32_t, but we still need to update the separately compiled sources. - #define STD_CODECVT_NOT_SUPPORTED - #endif // _MSC_VER == 1900 +# define STD_CODECVT_NOT_SUPPORTED +# endif // _MSC_VER == 1900 + +# if (_MSC_VER < 1910) //VS2017 RTM +# define _enable_std_clamp +# endif #elif defined(__clang__) //Clang @@ -119,13 +124,15 @@ #define STD_CODECVT_NOT_SUPPORTED #if !defined(__cpp_lib_make_unique) || (__cpp_lib_make_unique != 201304) - #ifndef STD_MAKE_UNIQUE_NOT_SUPPORTED - #define STD_MAKE_UNIQUE_NOT_SUPPORTED + #ifndef _enable_std_make_unique + #define _enable_std_make_unique #endif #endif #endif +# define _enable_std_clamp + #elif defined(__GNUC__) //GCC #include //Introduces some implement-specific flags of ISO C++ Library @@ -145,9 +152,9 @@ #endif - #if ((__GNUC__ < 5) ) - # define STD_put_time_NOT_SUPPORTED - #endif +# if ((__GNUC__ < 5) ) +# define _enable_std_put_time +# endif #if ((__GNUC__ > 5) || ((__GNUC__ == 5) && (__GNUC_MINOR__ >= 3 ) ) ) # undef STD_FILESYSTEM_NOT_SUPPORTED @@ -160,7 +167,7 @@ #endif #if (__GNUC_MINOR__ < 9) - #define STD_MAKE_UNIQUE_NOT_SUPPORTED + #define _enable_std_make_unique #endif #if defined(NANA_MINGW) @@ -181,6 +188,8 @@ #endif #endif #endif + +# define _enable_std_clamp #endif diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index 82025904..b5f92f56 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -17,99 +17,10 @@ #include #include +#include - -#include #include -//Implement workarounds for GCC/MinGW which version is below 4.8.2 -#if defined(STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED) -namespace std -{ - //Workaround for no implemenation of std::stoi in MinGW. - int stoi(const std::string&, std::size_t * pos = nullptr, int base = 10); - int stoi(const std::wstring&, std::size_t* pos = nullptr, int base = 10); - - //Workaround for no implemenation of std::stof in MinGW. - float stof(const std::string&, std::size_t * pos = nullptr); - float stof(const std::wstring&, std::size_t* pos = nullptr); - - //Workaround for no implemenation of std::stod in MinGW. - double stod(const std::string&, std::size_t * pos = nullptr); - double stod(const std::wstring&, std::size_t* pos = nullptr); - - //Workaround for no implemenation of std::stold in MinGW. - long double stold(const std::string&, std::size_t * pos = nullptr); - long double stold(const std::wstring&, std::size_t* pos = nullptr); - - //Workaround for no implemenation of std::stol in MinGW. - long stol(const std::string&, std::size_t* pos = nullptr, int base = 10); - long stol(const std::wstring&, std::size_t* pos = nullptr, int base = 10); - - //Workaround for no implemenation of std::stoll in MinGW. - long long stoll(const std::string&, std::size_t* pos = nullptr, int base = 10); - long long stoll(const std::wstring&, std::size_t* pos = nullptr, int base = 10); - - //Workaround for no implemenation of std::stoul in MinGW. - unsigned long stoul(const std::string&, std::size_t* pos = nullptr, int base = 10); - unsigned long stoul(const std::wstring&, std::size_t* pos = nullptr, int base = 10); - - //Workaround for no implemenation of std::stoull in MinGW. - unsigned long long stoull(const std::string&, std::size_t* pos = nullptr, int base = 10); - unsigned long long stoull(const std::wstring&, std::size_t* pos = nullptr, int base = 10); -} -#endif //STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED - -#ifdef STD_TO_STRING_NOT_SUPPORTED -namespace std -{ - //Workaround for no implemenation of std::to_string/std::to_wstring in MinGW. - std::string to_string(long double); - std::string to_string(double); - std::string to_string(unsigned); - std::string to_string(int); - std::string to_string(long); - std::string to_string(unsigned long); - std::string to_string(long long); - std::string to_string(unsigned long long); - std::string to_string(float); -} -#endif - -#ifdef STD_TO_WSTRING_NOT_SUPPORTED -namespace std -{ - std::wstring to_wstring(long double); - std::wstring to_wstring(double); - std::wstring to_wstring(unsigned); - std::wstring to_wstring(int); - std::wstring to_wstring(long); - std::wstring to_wstring(unsigned long); - std::wstring to_wstring(long long); - std::wstring to_wstring(unsigned long long); - std::wstring to_wstring(float); -} -#endif - -#ifdef STD_put_time_NOT_SUPPORTED -#include -namespace std -{ - //Workaround for no implemenation of std::put_time in gcc < 5. - /* std unspecified return type */ - //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t - //RTSTR put_time(const std::tm* tmb, const CharT* fmt); - - //template< > - std::string put_time/**/(const std::tm* tmb, const char* fmt); - - //Defined in header - // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); - //template<> - //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt); -} -#endif // STD_put_time_NOT_SUPPORTED - namespace nana { /// move to *.h ?? @@ -192,45 +103,5 @@ namespace nana #define NANA_RGB(a) (((DWORD)(a) & 0xFF)<<16) | ((DWORD)(a) & 0xFF00) | (((DWORD)(a) & 0xFF0000) >> 16 ) - -#ifdef STD_MAKE_UNIQUE_NOT_SUPPORTED -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm - -#include -#include -#include -#include - -namespace std { - template struct _Unique_if { - typedef unique_ptr _Single_object; - }; - - template struct _Unique_if { - typedef unique_ptr _Unknown_bound; - }; - - template struct _Unique_if { - typedef void _Known_bound; - }; - - template - typename _Unique_if::_Single_object - make_unique(Args&&... args) { - return unique_ptr(new T(std::forward(args)...)); - } - - template - typename _Unique_if::_Unknown_bound - make_unique(size_t n) { - typedef typename remove_extent::type U; - return unique_ptr(new U[n]()); - } - - template - typename _Unique_if::_Known_bound - make_unique(Args&&...) = delete; -} -#endif //STD_make_unique_NOT_SUPPORTED #include #endif //NANA_DEPLOY_HPP diff --git a/include/nana/filesystem/filesystem_ext.hpp b/include/nana/filesystem/filesystem_ext.hpp index 39fc4a07..2c28acad 100644 --- a/include/nana/filesystem/filesystem_ext.hpp +++ b/include/nana/filesystem/filesystem_ext.hpp @@ -1,6 +1,6 @@ /** * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -23,13 +23,13 @@ namespace filesystem_ext { #if defined(NANA_WINDOWS) - constexpr auto def_root = "C:"; - constexpr auto def_rootstr = "C:\\"; - constexpr auto def_rootname = "Local Drive(C:)"; + constexpr auto const def_root = "C:"; + constexpr auto const def_rootstr = "C:\\"; + constexpr auto const def_rootname = "Local Drive(C:)"; #elif defined(NANA_LINUX) - constexpr auto def_root = "/"; - constexpr auto def_rootstr = "/"; - constexpr auto def_rootname = "Root/"; + constexpr auto const def_root = "/"; + constexpr auto const def_rootstr = "/"; + constexpr auto const def_rootname = "Root/"; #endif std::experimental::filesystem::path path_user(); ///< extention ? diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index a21ef4ad..1140b5e7 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -17,6 +17,7 @@ #include "drawer.hpp" #include "events_holder.hpp" #include "widget_geometrics.hpp" +#include "widget_content_measurer_interface.hpp" #include "widget_notifier_interface.hpp" #include #include @@ -191,6 +192,7 @@ namespace detail //The following pointers refer to the widget's object. std::shared_ptr events_ptr; widget_geometrics* scheme{ nullptr }; + ::nana::dev::widget_content_measurer_interface* content_measurer{ nullptr }; }annex; struct diff --git a/include/nana/gui/detail/widget_content_measurer_interface.hpp b/include/nana/gui/detail/widget_content_measurer_interface.hpp new file mode 100644 index 00000000..4f175493 --- /dev/null +++ b/include/nana/gui/detail/widget_content_measurer_interface.hpp @@ -0,0 +1,48 @@ +/* +* Widget Content Measurer Interface +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) +* +* Distributed under the Boost Software License, Version 1.0. +* (See accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +* +* @file: nana/gui/detail/widget_content_measurer_interface.hpp +*/ + +#ifndef NANA_WIDGET_CONTENT_MEASURER_INTERFACE_HEADER_INCLUDED +#define NANA_WIDGET_CONTENT_MEASURER_INTERFACE_HEADER_INCLUDED + +#include +#include +#include + +namespace nana +{ + namespace dev + { + /// An interface for measuring content of the widget + class widget_content_measurer_interface + { + public: + using graph_reference = paint::graphics&; + virtual ~widget_content_measurer_interface() = default; + + /// Measures content + /** + * @param graph The graphics for the operation. + * @param limit_pixels The number of pixels of the limited edge. If this parameter is zero, it is ignored. + * @param limit_width True if limits the width, false if limits the height. + * @return the size of content. + */ + virtual optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const = 0; + + /// Returns the extension to the size of widget from content extent + /** + * @return the width and height of extension to the widget size. + */ + virtual size extension() const = 0; + }; + } +} +#endif diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 06ff5716..fe571076 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -16,6 +16,7 @@ #include "effects.hpp" #include "detail/general_events.hpp" #include "detail/color_schemes.hpp" +#include "detail/widget_content_measurer_interface.hpp" #include #include @@ -73,6 +74,9 @@ namespace API void set_scheme(window, widget_geometrics*); widget_geometrics* get_scheme(window); + /// Sets a content measurer + void set_measurer(window, ::nana::dev::widget_content_measurer_interface*); + void attach_drawer(widget&, drawer_trigger&); ::nana::detail::native_string_type window_caption(window) throw(); void window_caption(window, ::nana::detail::native_string_type); @@ -399,6 +403,17 @@ namespace API bool ignore_mouse_focus(window); ///< Determines whether the mouse focus is enabled void at_safe_place(window, std::function); + + /// Returns a widget content extent size + /** + * @param wd A handle to a window that returns its content extent size. + * @param limited_px Specifies the max pixels of width or height. If this parameter is zero, this parameter will be ignored. + * @param limit_width Indicates whether the it limits the width or height. If this parameter is *true*, the width is limited. + * If the parameter is *false*, the height is limited. This parameter is ignored if limited_px = 0. + * @return if optional has a value, the first size indicates the content extent, the second size indicates the size of + * widget by the content extent. + */ + optional> content_extent(window wd, unsigned limited_px, bool limit_width); }//end namespace API }//end namespace nana diff --git a/include/nana/gui/widgets/button.hpp b/include/nana/gui/widgets/button.hpp index e1813917..d68c0d7c 100644 --- a/include/nana/gui/widgets/button.hpp +++ b/include/nana/gui/widgets/button.hpp @@ -25,6 +25,7 @@ namespace nana{ /// Draw the button class trigger: public drawer_trigger { + class measurer; public: trigger(); ~trigger(); @@ -58,6 +59,8 @@ namespace nana{ element::cite_bground cite_{"button"}; + std::unique_ptr measurer_; + struct attr_tag { element_state e_state; diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index 7e478557..fc8e4522 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -1,7 +1,7 @@ /** * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -68,7 +68,7 @@ namespace nana void key_press(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override; private: - drawer_impl * drawer_; + drawer_impl * const drawer_; }; class item_proxy diff --git a/include/nana/gui/widgets/label.hpp b/include/nana/gui/widgets/label.hpp index f8c97eb9..9573439a 100644 --- a/include/nana/gui/widgets/label.hpp +++ b/include/nana/gui/widgets/label.hpp @@ -31,11 +31,11 @@ namespace nana class trigger: public drawer_trigger { public: - struct impl_t; + struct implement; trigger(); ~trigger(); - impl_t * impl() const; + implement * impl() const; private: void attached(widget_reference, graph_reference) override; void refresh(graph_reference) override; @@ -43,7 +43,7 @@ namespace nana void mouse_leave(graph_reference, const arg_mouse&) override; void click(graph_reference, const arg_click&) override; private: - impl_t * impl_; + implement * impl_; }; }//end namespace label diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 74bf335a..b2a8bcf1 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -647,7 +647,7 @@ namespace nana /// usefull for both absolute and display (sorted) positions struct index_pair { - constexpr static size_type npos = ::nana::npos; + constexpr static const size_type npos = ::nana::npos; size_type cat; //The pos of category size_type item; //the pos of item in a category. diff --git a/include/nana/gui/widgets/picture.hpp b/include/nana/gui/widgets/picture.hpp index fc8e9c63..9050f8be 100644 --- a/include/nana/gui/widgets/picture.hpp +++ b/include/nana/gui/widgets/picture.hpp @@ -1,7 +1,7 @@ /** * A Picture Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 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/optional.hpp b/include/nana/optional.hpp new file mode 100644 index 00000000..4fe5bf47 --- /dev/null +++ b/include/nana/optional.hpp @@ -0,0 +1,364 @@ +/** + * Optional + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file nana/optional.hpp + * + * @brief An implementation of experimental library optional of C++ standard(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html) + */ + +#ifndef NANA_STD_OPTIONAL_HEADER_INCLUDED +#define NANA_STD_OPTIONAL_HEADER_INCLUDED + +#include +#include +namespace nana +{ + namespace detail + { + template + class storage + { + public: + using value_type = T; + + storage() = default; + + template + storage(U&& value) + : initialized_{ true } + { + ::new (data_) value_type(std::forward(value)); + } + + template + storage(const U& value) + : initialized_{ true } + { + ::new (data_) value_type(value); + } + + storage(const storage& other) + : initialized_{ other.initialized_ } + { + if (other.initialized_) + ::new (data_) value_type(*other.ptr()); + } + + storage(storage&& other) + : initialized_{ other.initialized_ } + { + if (other.initialized_) + ::new (data_) value_type(std::move(*other.ptr())); + } + + template + storage(const storage& other) + : initialized_{ other.initialized_ } + { + if (other.initialized_) + ::new (data_) value_type(*other.ptr()); + } + + ~storage() + { + destroy(); + } + + bool initialized() const noexcept + { + return initialized_; + } + + void set_initialized() + { + initialized_ = true; + } + + void destroy() + { + if (initialized_) + { + ptr()->~T(); + initialized_ = false; + } + } + + template + void assign(U&& value) + { + if (initialized_) + *ptr() = std::forward(value); + else + { + ::new (data) value_type(std::forward(value)); + initialized_ = true; + } + } + + void assign(const storage& other) + { + if (!other.initialized_) + { + destroy(); + return; + } + + if (initialized_) + *ptr = *other.ptr(); + else + { + ::new (data) value_type(*other.ptr()); + initialized_ = true; + } + } + + void assign(storage&& other) + { + if (!other.initialized_) + { + destroy(); + return; + } + + if (initialized_) + *ptr() = std::move(*other.ptr()); + else + { + ::new (data) value_type(std::move(*other.ptr())); + initialized_ = true; + } + } + + const T* ptr() const + { + return reinterpret_cast(data_); + } + + T* ptr() + { + return reinterpret_cast(data_); + } + private: + bool initialized_{ false }; + char data_[sizeof(value_type)]; + }; + }//end namespace detail + + class bad_optional_access + : public std::logic_error + { + public: + bad_optional_access() + : std::logic_error("Attempted to access the value of an uninitialized optional object.") + {} + }; + + template + class optional + { + public: + using value_type = T; + + constexpr optional() = default; + constexpr optional(std::nullptr_t) {} + + optional(const optional& other) + : storage_(other.storage_) + {} + + optional(optional&& other) + : storage_(std::move(other.storage_)) + {} + + constexpr optional(const value_type& value) + : storage_(value) + {} + + constexpr optional(value_type&& value) + : storage_(std::move(value)) + {} + + optional& operator=(std::nullptr_t) + { + storage_.destroy(); + return *this; + } + + optional& operator=(const optional& other) + { + if (this != &other) + { + storage_.assign(other.storage_); + } + return *this; + } + + optional& operator=(optional&& other) + { + if (this != &other) + { + storage_.assign(std::move(other.storage_)) + } + return *this;l + } + + template + optional& operator=(U&& value) + { + storage_.assign(std::forward(value)); + + return *this; + } + + //Value access + //constexpr + value_type* operator->() + { + return storage_.ptr(); + } + constexpr const value_type* operator->() const + { + return storage_.ptr(); + } + + //constexpr + value_type& operator*() + { + return *storage_.ptr(); + } + + constexpr const value_type& operator*() const + { + return *storage_.ptr(); + } + + /* + //constexpr + value_type&& operator*() && + { + return std::move(*storage_.ptr()); + } + + //constexpr + const value_type&& operator*() const && + { + return std::move(*storage_.ptr()); + } + */ + + //Condition + constexpr explicit operator bool() const + { + return storage_.initialized(); + } + + constexpr bool has_value() const + { + return storage_.initialized(); + } + + //constexpr + value_type& value() + { + if (!storage_.initialized()) + throw bad_optional_access{}; + + return *storage_.ptr(); + } + + constexpr const value_type& value() const + { + if (!storage_.initialized()) + throw bad_optional_access{}; + + return *storage_.ptr(); + } + /* + //constexpr + value_type&& value() + { + if (!storage_.initialized()) + throw bad_optional_access{}; + + return std::move(*storage_.ptr()); + } + + constexpr const value_type&& value() const + { + if (!storage_.initialized()) + throw bad_optional_access{}; + + return std::move(*storage_.ptr()); + } + */ + + template + constexpr T value_or(U&& default_value) const + { + return (has_value() ? **this : static_cast(std::forward(default_value))); + } + + template + //constexpr + T value_or(U&& default_value) + { + return (has_value() ? std::move(**this) : static_cast(std::forward(default_value))); + } + + //Modifiers + void swap(optional& other) + { + if (has_value() && other.has_value()) + { + std::swap(**this, *other); + return; + } + else if (has_value()) + { + other.emplace(std::move(***this)); + storage_.destroy(); + } + else if (other.has_value()) + { + this->emplace(std::move(*other)); + other.storage_.destroy(); + } + } + + void reset() + { + storage_.destroy(); + } + + template + void emplace(Args&&... args) + { + storage_.destroy(); + ::new (storage_.ptr()) T(std::forward(args)...); + + storage_.set_initialized(); + } + + template + void emplace(std::initializer_list il, Args&& ... args) + { + storage_.destroy(); + ::new (storage_.ptr()) T(il, std::forward(args)...); + + storage_.set_initialized(); + } + + + private: + detail::storage storage_; + + }; +} + +#endif diff --git a/include/nana/pat/abstract_factory.hpp b/include/nana/pat/abstract_factory.hpp index 27966fe2..9f23642e 100644 --- a/include/nana/pat/abstract_factory.hpp +++ b/include/nana/pat/abstract_factory.hpp @@ -84,7 +84,7 @@ namespace nana { std::unique_ptr create() override { - constexpr auto Size = std::tuple_size::value; + constexpr auto const Size = std::tuple_size::value; return std::unique_ptr{ _m_new(make_pack{}) }; } diff --git a/include/nana/stdc++.hpp b/include/nana/stdc++.hpp new file mode 100644 index 00000000..7784d593 --- /dev/null +++ b/include/nana/stdc++.hpp @@ -0,0 +1,155 @@ +/** + * Standard Library for C++11/14/17 + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file nana/stdc++.hpp + * + * @brief Implement the lack support of standard library. + */ + +#include "c++defines.hpp" + +//Implement workarounds for GCC/MinGW which version is below 4.8.2 +#if defined(STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED) +namespace std +{ + //Workaround for no implemenation of std::stoi in MinGW. + int stoi(const std::string&, std::size_t * pos = nullptr, int base = 10); + int stoi(const std::wstring&, std::size_t* pos = nullptr, int base = 10); + + //Workaround for no implemenation of std::stof in MinGW. + float stof(const std::string&, std::size_t * pos = nullptr); + float stof(const std::wstring&, std::size_t* pos = nullptr); + + //Workaround for no implemenation of std::stod in MinGW. + double stod(const std::string&, std::size_t * pos = nullptr); + double stod(const std::wstring&, std::size_t* pos = nullptr); + + //Workaround for no implemenation of std::stold in MinGW. + long double stold(const std::string&, std::size_t * pos = nullptr); + long double stold(const std::wstring&, std::size_t* pos = nullptr); + + //Workaround for no implemenation of std::stol in MinGW. + long stol(const std::string&, std::size_t* pos = nullptr, int base = 10); + long stol(const std::wstring&, std::size_t* pos = nullptr, int base = 10); + + //Workaround for no implemenation of std::stoll in MinGW. + long long stoll(const std::string&, std::size_t* pos = nullptr, int base = 10); + long long stoll(const std::wstring&, std::size_t* pos = nullptr, int base = 10); + + //Workaround for no implemenation of std::stoul in MinGW. + unsigned long stoul(const std::string&, std::size_t* pos = nullptr, int base = 10); + unsigned long stoul(const std::wstring&, std::size_t* pos = nullptr, int base = 10); + + //Workaround for no implemenation of std::stoull in MinGW. + unsigned long long stoull(const std::string&, std::size_t* pos = nullptr, int base = 10); + unsigned long long stoull(const std::wstring&, std::size_t* pos = nullptr, int base = 10); +} +#endif //STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED + +#ifdef STD_TO_STRING_NOT_SUPPORTED +namespace std +{ + //Workaround for no implemenation of std::to_string/std::to_wstring in MinGW. + std::string to_string(long double); + std::string to_string(double); + std::string to_string(unsigned); + std::string to_string(int); + std::string to_string(long); + std::string to_string(unsigned long); + std::string to_string(long long); + std::string to_string(unsigned long long); + std::string to_string(float); +} +#endif + +#ifdef STD_TO_WSTRING_NOT_SUPPORTED +namespace std +{ + std::wstring to_wstring(long double); + std::wstring to_wstring(double); + std::wstring to_wstring(unsigned); + std::wstring to_wstring(int); + std::wstring to_wstring(long); + std::wstring to_wstring(unsigned long); + std::wstring to_wstring(long long); + std::wstring to_wstring(unsigned long long); + std::wstring to_wstring(float); +} +#endif + +#ifdef _enable_std_make_unique + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm + +#include +#include +#include +#include + +namespace std { + template struct _Unique_if { + typedef unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename remove_extent::type U; + return unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} +#endif //_enable_std_make_unique + +#ifdef _enable_std_put_time +#include +namespace std +{ + //Workaround for no implemenation of std::put_time in gcc < 5. + /* std unspecified return type */ + //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t + //RTSTR put_time(const std::tm* tmb, const CharT* fmt); + + //template< > + std::string put_time/**/(const std::tm* tmb, const char* fmt); + + //Defined in header + // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); + //template<> + //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt); +} +#endif // _enable_std_put_time + +#if defined(_enable_std_clamp) +namespace std +{ + // since C++17 + template + constexpr const T& clamp(const T& v, const T& lo, const T& hi) + { + return (v < lo ? lo : (hi < v ? hi : v)); + } +} +#endif \ No newline at end of file diff --git a/include/nana/verbose_preprocessor.hpp b/include/nana/verbose_preprocessor.hpp index 07b306f8..a8bf2d7b 100644 --- a/include/nana/verbose_preprocessor.hpp +++ b/include/nana/verbose_preprocessor.hpp @@ -80,7 +80,7 @@ #pragma message ( SHOW_VALUE(USE_github_com_meganz_mingw_std_threads) ) #pragma message ( SHOW_VALUE(NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) ) #pragma message ( SHOW_VALUE(STD_THREAD_NOT_SUPPORTED) ) - #pragma message ( SHOW_VALUE(STD_put_time_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(_enable_std_put_time) ) #pragma message ( SHOW_VALUE(STD_MAKE_UNIQUE_NOT_SUPPORTED) ) #pragma message ( SHOW_VALUE(STD_FILESYSTEM_NOT_SUPPORTED) ) diff --git a/source/deploy.cpp b/source/deploy.cpp index 7f715ef9..41146058 100644 --- a/source/deploy.cpp +++ b/source/deploy.cpp @@ -1,7 +1,7 @@ /* * The Deploy Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -24,460 +24,6 @@ #include #endif -//Implement workarounds for GCC/MinGW which version is below 4.8.2 -#if defined(STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED) -#include -namespace std -{ - int stoi(const std::string& str, std::size_t * pos, int base) - { - auto sptr = str.c_str(); - char *end; - errno = 0; - auto result = std::strtol(sptr, &end, base); - - if (sptr == end) - throw std::invalid_argument("invalid stoi argument"); - if (errno == ERANGE) - throw std::out_of_range("stoi argument out of range"); - - if (pos) - *pos = (std::size_t)(end - sptr); - return ((int)result); - } - - int stoi(const std::wstring& str, std::size_t* pos, int base) - { - auto sptr = str.data(); - wchar_t *end; - errno = 0; - auto result = std::wcstol(sptr, &end, base); - - if (sptr == end) - throw std::invalid_argument("invalid stoi argument"); - if (errno == ERANGE) - throw std::out_of_range("stoi argument out of range"); - - if (pos) - *pos = (std::size_t)(end - sptr); - return ((int)result); - } - using ::strtof; - using ::strtold; - using ::wcstold; - using ::strtoll; - using ::wcstoll; - using ::strtoull; - using ::wcstoull; - - float stof(const std::string& str, std::size_t * pos) - { - auto *ptr = str.data(); - errno = 0; - char *end; - auto result = std::strtof(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stof argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - float stof(const std::wstring& str, std::size_t* pos) - { - auto *ptr = str.data(); - errno = 0; - wchar_t *end; - auto result = std::wcstof(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stof argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - double stod(const std::string& str, std::size_t * pos) - { - auto *ptr = str.data(); - errno = 0; - char *end; - auto result = std::strtod(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stod argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - double stod(const std::wstring& str, std::size_t* pos) - { - auto *ptr = str.data(); - errno = 0; - wchar_t *end; - auto result = std::wcstod(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stod argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - long double stold(const std::string& str, std::size_t * pos) - { - auto *ptr = str.data(); - errno = 0; - char *end; - auto result = std::strtold(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stold argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - long double stold(const std::wstring& str, std::size_t* pos) - { - auto *ptr = str.data(); - errno = 0; - wchar_t *end; - auto result = std::wcstold(ptr, &end); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stold argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - long stol(const std::string& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - char *end; - auto result = std::strtol(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stol argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - long stol(const std::wstring& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - wchar_t *end; - auto result = std::wcstol(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stol argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - //Workaround for no implemenation of std::stoll in MinGW. - long long stoll(const std::string& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - char* end; - auto result = std::strtoll(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoll argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - long long stoll(const std::wstring& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - wchar_t* end; - auto result = std::wcstoll(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoll argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - unsigned long long stoull(const std::string& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - char* end; - auto result = std::strtoull(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoull argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - unsigned long long stoull(const std::wstring& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - wchar_t* end; - auto result = std::wcstoull(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoull argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - //Workaround for no implemenation of std::stoul in MinGW. - unsigned long stoul(const std::string& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - char* end; - auto result = std::strtoul(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoul argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } - - unsigned long stoul(const std::wstring& str, std::size_t* pos, int base) - { - auto *ptr = str.data(); - errno = 0; - wchar_t* end; - auto result = std::wcstoul(ptr, &end, base); - - if (ptr == end) - throw std::invalid_argument("invalid stod argument"); - if (errno == ERANGE) - throw std::out_of_range("stoul argument out of range"); - if (pos) - *pos = (std::size_t)(end - ptr); - return result; - } -}//end namespace std -#endif //STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED - -#ifdef STD_TO_STRING_NOT_SUPPORTED -#include -namespace std -{ - std::string to_string(double v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(long double v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(unsigned v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(int v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(long v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(unsigned long v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(long long v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(unsigned long long v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } - - std::string to_string(float v) - { - std::stringstream ss; - ss << v; - return ss.str(); - } -} -#endif // STD_TO_STRING_NOT_SUPPORTED - -#ifdef STD_TO_WSTRING_NOT_SUPPORTED -#include -namespace std -{ - std::wstring to_wstring(double v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(long double v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(unsigned v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(int v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(long v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(unsigned long v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(long long v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(unsigned long long v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } - - std::wstring to_wstring(float v) - { - std::wstringstream ss; - ss << v; - return ss.str(); - } -} -#endif - -//#ifdef STD_put_time_NOT_SUPPORTED -#include -#include -namespace std -{ - //Workaround for no implemenation of std::put_time in gcc < 5. - /* std unspecified return type */ - //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t - //RTSTR put_time(const std::tm* tmb, const CharT* fmt); - - //template< > - std::string put_time/**/(const std::tm* tmb, const char* fmt) - { - std::size_t sz = 200; - std::string str(sz, '\0'); - sz = std::strftime(&str[0], str.size() - 1, fmt, tmb); - str.resize(sz); - return str; - } - //Defined in header - // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); - //template<> - //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt) - //{ - // unsigned sz = 200; - // std::wstring str(sz, L'\0'); - // sz = std::wcsftime(&str[0], str.size() - 1, fmt, tmb); - // str.resize(sz); - // return str; - //} - // http://en.cppreference.com/w/cpp/chrono/c/wcsftime - // Defined in header - // std::size_t wcsftime(wchar_t* str, std::size_t count, const wchar_t* format, const std::tm* time); - // Converts the date and time information from a given calendar time time to a null - terminated - // wide character string str according to format string format.Up to count bytes are written. - // Parameters - // str - pointer to the first element of the wchar_t array for output - // count - maximum number of wide characters to write - // format - pointer to a null - terminated wide character string specifying the format of conversion. - - } -//#endif // STD_put_time_NOT_SUPPORTED #include @@ -523,12 +69,12 @@ namespace nana # endif #endif - void utf8_Error::emit() - { - if (use_throw) - throw utf8_Error(*this); - std::cerr << what(); - } + void utf8_Error::emit() + { + if (use_throw) + throw utf8_Error(*this); + std::cerr << what(); + } //bool utf8_Error::use_throw{true}; bool utf8_Error::use_throw{ false }; @@ -536,25 +82,18 @@ namespace nana void throw_not_utf8(const std::string& text) { - if (!is_utf8(text.c_str(), text.length())) - return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + text).emit(); + throw_not_utf8(text.c_str(), text.size()); + } + + void throw_not_utf8(const char* text) + { + throw_not_utf8(text, std::strlen(text)); } void throw_not_utf8(const char* text, std::size_t len) { if (!is_utf8(text, len)) return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + std::string(text, len) ).emit(); - - //throw std::invalid_argument("The text is not encoded in UTF8"); - } - - void throw_not_utf8(const char* text) - { - if (!is_utf8(text, std::strlen(text))) - return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + text).emit(); - - //throw std::invalid_argument("The text is not encoded in UTF8"); - } std::string recode_to_utf8(std::string no_utf8) @@ -587,8 +126,6 @@ namespace nana return false; } - - const std::string& to_utf8(const std::string& str) { return str; diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 73f487de..e3ffd163 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,7 +1,7 @@ /* * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -30,6 +29,7 @@ #include //numeric_limits #include //std::abs #include //std::memset +#include //std::isalpha/std::isalnum #include "place_parts.hpp" @@ -56,52 +56,47 @@ namespace nana enum class token { div_start, div_end, splitter, - identifier, dock, vert, grid, number, array, reparray, + identifier, dock, fit, hfit, vfit, vert, grid, number, array, reparray, weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible, collapse, parameters, equal, eof, error }; - tokenizer(const char* p) + tokenizer(const char* p) noexcept : divstr_(p), sp_(p) {} - const std::string& idstr() const + const std::string& idstr() const noexcept { return idstr_; } - number_t number() const + const number_t& number() const { return number_; } - std::vector& array() + std::vector& array() noexcept { return array_; } - repeated_array&& reparray() + repeated_array&& reparray() noexcept { return std::move(reparray_); } - std::vector& parameters() + std::vector& parameters() noexcept { return parameters_; } - std::size_t pos() const + std::size_t pos() const noexcept { return (sp_ - divstr_); } - std::string pos_str() const - { - return std::to_string(pos()); - } - token read() { sp_ = _m_eat_whitespace(sp_); @@ -230,11 +225,11 @@ namespace nana break; } - if ('_' == *sp_ || isalpha(*sp_)) + if ('_' == *sp_ || std::isalpha(*sp_)) { const char * idstart = sp_++; - while ('_' == *sp_ || isalpha(*sp_) || isalnum(*sp_)) + while ('_' == *sp_ || std::isalpha(*sp_) || std::isalnum(*sp_)) ++sp_; idstr_.assign(idstart, sp_); @@ -252,15 +247,24 @@ namespace nana } else if ("dock" == idstr_) return token::dock; + else if ("fit" == idstr_) + return token::fit; else if ("vertical" == idstr_ || "vert" == idstr_) return token::vert; else if ("variable" == idstr_ || "repeated" == idstr_) return ('v' == idstr_[0] ? token::variable : token::repeated); - else if ("arrange" == idstr_ || "gap" == idstr_) + else if ("arrange" == idstr_ || "hfit" == idstr_ || "vfit" == idstr_ || "gap" == idstr_) { auto ch = idstr_[0]; _m_attr_reparray(); - return ('a' == ch ? token::arrange : token::gap); + switch (ch) + { + case 'a': return token::arrange; + case 'h': return token::hfit; + case 'v': return token::vfit; + case 'g': return token::gap; + default: break; + } } else if ("grid" == idstr_ || "margin" == idstr_) { @@ -295,19 +299,10 @@ namespace nana std::string err = "an invalid character '"; err += *sp_; err += "'"; - _m_throw_error(err); return token::error; //Useless, just for syntax correction. } private: - void _m_throw_error(char err_char) - { - std::string str = "place: invalid character '"; - str += err_char; - str += '\''; - _m_throw_error(str); - } - void _m_throw_error(const std::string& err) { throw std::runtime_error("nana::place: " + err + " at " + std::to_string(static_cast(sp_ - divstr_))); @@ -357,14 +352,14 @@ namespace nana } } - static const char* _m_eat_whitespace(const char* sp) + static const char* _m_eat_whitespace(const char* sp) noexcept { - while (*sp && !isgraph(*sp)) + while (*sp && !std::isgraph(*sp)) ++sp; return sp; } - std::size_t _m_number(const char* sp, bool negative) + std::size_t _m_number(const char* sp, bool negative) noexcept { const char* allstart = sp; sp = _m_eat_whitespace(sp); @@ -409,23 +404,22 @@ namespace nana if (gotcha) { sp = _m_eat_whitespace(sp); - if ('%' == *sp) + if ('%' != *sp) + return sp - allstart; + + switch (number_.kind_of()) { - switch (number_.kind_of()) - { - case number_t::kind::integer: - number_.assign_percent(number_.integer()); - break; - case number_t::kind::real: - number_.assign_percent(number_.real()); - break; - default: - break; - } - - return sp - allstart + 1; + case number_t::kind::integer: + number_.assign_percent(number_.integer()); + break; + case number_t::kind::real: + number_.assign_percent(number_.real()); + break; + default: + break; } - return sp - allstart; + + return sp - allstart + 1; } number_.reset(); return 0; @@ -441,13 +435,23 @@ namespace nana }; //end class tokenizer } - - inline bool is_idchar(int ch) + static bool is_vert_dir(::nana::direction dir) { - return ('_' == ch || isalnum(ch)); + return (dir == ::nana::direction::north || dir == ::nana::direction::south); } - std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0) + static int horz_point(bool vert, const point& pos) + { + return (vert ? pos.y : pos.x); + } + + + static bool is_idchar(int ch) noexcept + { + return ('_' == ch || std::isalnum(ch)); + } + + static std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0) { const auto len = std::strlen(idstr); @@ -465,7 +469,65 @@ namespace nana return text.npos; } - std::pair get_field_bound(const std::string& div, const char* idstr, int depth) + //Find the text bound of a field. The parameter start_pos is one of bound characters of the field whose bound will be returned + static std::pair get_field_bound(const std::string& div, std::size_t start_pos) + { + int depth = 0; + if ('<' == div[start_pos]) + { + auto off = start_pos + 1; + while (off < div.length()) + { + auto pos = div.find_first_of("<>", off); + if (div.npos == pos) + break; + + if ('<' == div[pos]) + { + ++depth; + off = pos + 1; + continue; + } + + if (0 == depth) + return{ start_pos, pos + 1 }; + + --depth; + off = pos + 1; + } + } + else if (('>' == div[start_pos]) && (start_pos > 0)) + { + auto off = start_pos - 1; + while (true) + { + auto pos = div.find_last_of("<>", off); + if (div.npos == pos) + break; + + if ('>' == div[pos]) + { + ++depth; + if (0 == pos) + break; + + off = pos - 1; + } + + if (0 == depth) + return{ pos, start_pos + 1 }; + + if (0 == pos) + break; + + off = pos - 1; + } + } + + return{}; + } + + static std::pair get_field_bound(const std::string& div, const char* idstr, int depth) { auto start_pos = find_idstr(div, idstr); @@ -495,31 +557,9 @@ namespace nana start_pos = pos - 1; } - auto off = start_pos + 1; - - while (true) - { - auto pos = div.find_first_of("<>", off); - - if (div.npos == pos) - return{}; - - if ('<' == div[pos]) - { - ++depth; - off = pos + 1; - continue; - } - - if (0 == depth) - return{ start_pos, pos + 1 }; - - --depth; - off = pos + 1; - } + return get_field_bound(div, start_pos + 1); } - //struct implement struct place::implement { @@ -552,13 +592,13 @@ namespace nana void collocate(); - static division * search_div_name(division* start, const std::string&); + static division * search_div_name(division* start, const std::string&) noexcept; std::unique_ptr scan_div(place_parts::tokenizer&); void check_unique(const division*) const; //connect the field/dock with div object void connect(division* start); - void disconnect(); + void disconnect() noexcept; }; //end struct implement class place::implement::field_gather @@ -570,16 +610,16 @@ namespace nana window handle; event_handle evt_destroy; - element_t(window h, event_handle event_destroy) + element_t(window h, event_handle event_destroy) noexcept :handle(h), evt_destroy(event_destroy) {} }; - field_gather(place * p) + field_gather(place * p) noexcept : place_ptr_(p) {} - ~field_gather() + ~field_gather() noexcept { for (auto & e : elements) API::umake_event(e.evt_destroy); @@ -597,7 +637,7 @@ namespace nana API::show_window(e.handle, vsb); } - static event_handle erase_element(std::vector& elements, window handle) + static event_handle erase_element(std::vector& elements, window handle) noexcept { for (auto i = elements.begin(); i != elements.end(); ++i) { @@ -611,18 +651,31 @@ namespace nana return nullptr; } private: - //Listen to destroy of a window - //It will delete the element and recollocate when the window destroyed. - event_handle _m_make_destroy(window wd) + void _m_insert_widget(window wd, bool to_fasten) { - return API::events(wd).destroy.connect([this, wd](const arg_destroy&) + if (API::empty_window(wd)) + throw std::invalid_argument("Place: An invalid window handle."); + + if (API::get_parent_window(wd) != place_ptr_->window_handle()) + throw std::invalid_argument("Place: the window is not a child of place bind window"); + + //Listen to destroy of a window + //It will delete the element and recollocate when the window destroyed. + auto evt = API::events(wd).destroy.connect([this, to_fasten](const arg_destroy& arg) { - if (erase_element(elements, wd)) + if (!to_fasten) { - if (!API::is_destroying(API::get_parent_window(wd))) - place_ptr_->collocate(); + if (erase_element(elements, arg.window_handle)) + { + if (!API::is_destroying(API::get_parent_window(arg.window_handle))) + place_ptr_->collocate(); + } } + else + erase_element(fastened, arg.window_handle); }); + + (to_fasten ? &fastened : &elements)->emplace_back(wd, evt); } field_interface& operator<<(const char* label_text) override @@ -637,33 +690,13 @@ namespace nana field_interface& operator<<(window wd) override { - if (API::empty_window(wd)) - throw std::invalid_argument("Place: An invalid window handle."); - - if (API::get_parent_window(wd) != place_ptr_->window_handle()) - throw std::invalid_argument("Place: the window is not a child of place bind window"); - - auto evt = _m_make_destroy(wd); - elements.emplace_back(wd, evt); + _m_insert_widget(wd, false); return *this; } field_interface& fasten(window wd) override { - if (API::empty_window(wd)) - throw std::invalid_argument("Place: An invalid window handle."); - - if (API::get_parent_window(wd) != place_ptr_->window_handle()) - throw std::invalid_argument("Place: the window is not a child of place bind window"); - - //Listen to destroy of a window. The deleting a fastened window - //does not change the layout. - auto evt = API::events(wd).destroy.connect([this](const arg_destroy& arg) - { - erase_element(fastened, arg.window_handle); - }); - - fastened.emplace_back(wd, evt); + _m_insert_widget(wd, true); return *this; } @@ -683,7 +716,6 @@ namespace nana class place::implement::field_dock { - public: div_dockpane * attached{ nullptr }; //attached div object std::unique_ptr dockarea; //the dockable widget @@ -691,13 +723,20 @@ namespace nana };//end class field_dock + enum class fit_policy + { + none, //Doesn't fit the content + both, //Fits both width and height of content + horz, //Fits width of content with a specified height + vert //Fits height of content with a specified width + }; class place::implement::division { public: enum class kind{ arrange, vertical_arrange, grid, splitter, dock, dockpane}; - division(kind k, std::string&& n) + division(kind k, std::string&& n) noexcept : kind_of_division(k), name(std::move(n)) {} @@ -709,6 +748,140 @@ namespace nana field->attached = nullptr; } + static unsigned calc_number(const place_parts::number_t& number, unsigned area_px, double adjustable_px, double& precise_px) + { + switch (number.kind_of()) + { + case number_t::kind::integer: + return static_cast(number.integer()); + 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; + } + return 0; //Useless + } + + std::pair calc_weight_floor() + { + std::pair floor; + run_.fit_extents.clear(); + + run_.weight_floor = floor; + + if (this->display) + { + double ratio = 0; + + for (auto & child : children) + { + auto child_floor = child->calc_weight_floor(); + + if(child->is_percent()) + { + ratio += child->weight.real(); + } + else + { + floor.first += child_floor.first; + floor.second += child_floor.second; + } + } + + auto const vert_fields = (kind::vertical_arrange == this->kind_of_division); + auto const vert_div = (this->div_owner && (kind::vertical_arrange == this->div_owner->kind_of_division)); + double& fv = (vert_div ? floor.second : floor.first); + + if((ratio > 0.001) && (fv > 0)) + fv /= ratio; + + if (!this->weight.empty()) + { + if(!this->is_percent()) + fv = this->weight.real(); + } + else + { + if (fit_policy::none != this->fit) + { + std::size_t fit_count = 0; + + unsigned max_value = 0; + auto const fit_horz = (fit_policy::vert == this->fit); + + std::size_t pos = 0; + for (auto & elm : this->field->elements) + { + ++pos; + + unsigned edge_px = 0; + if (fit_policy::both != this->fit) + { + auto fit_val = this->fit_parameters.at(pos - 1); + if (fit_val.empty()) + continue; + + edge_px = fit_val.integer(); + } + + auto extent = API::content_extent(elm.handle, edge_px, fit_horz); + if (extent) + { + run_.fit_extents[elm.handle] = extent->second; + ++fit_count; + if (vert_fields) + floor.second += extent->second.height; + else + floor.first += extent->second.width; + + max_value = (std::max)(max_value, (vert_fields ? extent->second.width : extent->second.height)); + } + } + + if (max_value) + { + if (vert_fields) + floor.first = max_value; + else + floor.second = max_value; + } + + + if (fit_count > 1) + { + double percent = 0; + for (std::size_t i = 0; i < fit_count - 1; ++i) + { + auto gap_value = gap.at(i); + if (gap_value.kind_of() == number_t::kind::percent) + { + percent += gap_value.real(); + } + else + { + double precise_px = 0; + fv += calc_number(gap_value, 100, 0, precise_px); + } + } + + fv *= (1 + percent); + } + } + run_.weight_floor = floor; + } + } + + return floor; + } + void set_visible(bool vsb) { if (field) @@ -769,26 +942,39 @@ namespace nana } } - bool is_back(const division* div) const + bool is_back(const division* div) const noexcept { - for (auto i = children.crbegin(); i != children.crend(); ++i) + const division * last = nullptr; + for (auto & p : children) { - if (!(i->get()->display)) + if (!(p->display)) continue; - return (div == i->get()); + last = p.get(); } - return false; + + return (div == last); } - static double limit_px(const division* div, double px, unsigned area_px) + static double limit_px(const division* div, double px, unsigned area_px) noexcept { + auto const vert = (div->div_owner && (div->div_owner->kind_of_division == kind::vertical_arrange)); + + auto weight_floor = (vert? div->run_.weight_floor.second : div->run_.weight_floor.first); + if (!div->min_px.empty()) { auto v = div->min_px.get_value(static_cast(area_px)); + + if ((weight_floor > 0) && (v < weight_floor)) + v = weight_floor; + if (px < v) return v; } + else if ((weight_floor > 0) && (px < weight_floor)) + return weight_floor; + if (!div->max_px.empty()) { auto v = div->max_px.get_value(static_cast(area_px)); @@ -813,7 +999,7 @@ namespace nana return margin.area(field_area); } - division * previous() const + division * previous() const noexcept { if (div_owner) { @@ -822,10 +1008,9 @@ namespace nana return child.get(); } return nullptr; - } public: - void _m_visible_for_child(division * div, bool vsb) + static void _m_visible_for_child(division * div, bool vsb) noexcept { for (auto & child : div->children) { @@ -842,6 +1027,8 @@ namespace nana kind kind_of_division; bool display{ true }; bool visible{ true }; + fit_policy fit{ fit_policy::none }; + repeated_array fit_parameters; //it is ignored when fit is not fit_policy::horz or fit_policy::vert ::nana::direction dir{::nana::direction::west}; std::string name; std::vector> children; @@ -855,13 +1042,19 @@ namespace nana field_gather * field{ nullptr }; division * div_next{ nullptr }; division * div_owner{ nullptr }; + + struct run_data + { + std::pair weight_floor; + std::map fit_extents; + }run_; };//end class division class place::implement::div_arrange : public division { public: - div_arrange(bool vert, std::string&& name, place_parts::repeated_array&& arr) + div_arrange(bool vert, std::string&& name, place_parts::repeated_array&& arr) noexcept : division((vert ? kind::vertical_arrange : kind::arrange), std::move(name)), arrange_(std::move(arr)) {} @@ -901,6 +1094,7 @@ namespace nana child_px = adjustable_px; child_px = limit_px(child, child_px, area_px); + auto npx = static_cast(child_px); precise_px = child_px - npx; child_px = npx; @@ -936,13 +1130,54 @@ namespace nana for (auto & el : field->elements) { element_r.x_ref() = static_cast(position); - auto px = _m_calc_number(arrange_.at(index), area_px, adjustable_px, precise_px); - element_r.w_ref() = px; - API::move_window(el.handle, element_r.result()); + bool moved = false; + unsigned px = 0; + + auto move_r = element_r.result(); + if (fit_policy::none != this->fit) + { + auto i = run_.fit_extents.find(el.handle); + if (run_.fit_extents.end() != i) + { + move_r.dimension(i->second); + + if (vert) + move_r.x += place_parts::differ(area_margined.width, move_r.width) / 2; + else + move_r.y += place_parts::differ(area_margined.height, move_r.height) / 2; + + px = (vert ? move_r.height : move_r.width); + moved = true; + } + /* + auto extent = API::content_extent(el.handle, 0, false); //deprecated + if (extent) + { + move_r.dimension(extent->second); + + if (vert) + move_r.x += place_parts::differ(area_margined.width, move_r.width) / 2; + else + move_r.y += place_parts::differ(area_margined.height, move_r.height) / 2; + + px = (vert ? move_r.height : move_r.width); + moved = true; + } + */ + } + + if (!moved) + { + px = calc_number(arrange_.at(index), area_px, adjustable_px, precise_px); + element_r.w_ref() = px; + move_r = element_r.result(); + } + + API::move_window(el.handle, move_r); if (index + 1 < field->elements.size()) - position += (px + _m_calc_number(gap.at(index), area_px, 0, precise_px)); + position += (px + calc_number(gap.at(index), area_px, 0, precise_px)); ++index; } @@ -952,28 +1187,6 @@ namespace nana } } private: - static unsigned _m_calc_number(const place_parts::number_t& number, unsigned area_px, double adjustable_px, double& precise_px) - { - switch (number.kind_of()) - { - case number_t::kind::integer: - return static_cast(number.integer()); - 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; - } - return 0; //Useless - } - static std::pair _m_calc_fa(const place_parts::number_t& number, unsigned area_px, double& precise_px) { std::pair result; @@ -1001,17 +1214,31 @@ namespace nana } //Returns the fixed pixels and the number of adjustable items. - std::pair _m_fixed_and_adjustable(kind match_kind, unsigned area_px) const + std::pair _m_fixed_and_adjustable(kind match_kind, unsigned area_px) const noexcept { std::pair result; if (field && (kind_of_division == match_kind)) { + auto const vert = (kind_of_division == kind::vertical_arrange); + //Calculate fixed and adjustable of elements double precise_px = 0; auto count = field->elements.size(); for (decltype(count) i = 0; i < count; ++i) { auto fa = _m_calc_fa(arrange_.at(i), area_px, precise_px); + + //The fit-content element is like a fixed element + if (fit_policy::none != this->fit) + { + auto fi = this->run_.fit_extents.find(field->elements[i].handle); + if (this->run_.fit_extents.cend() != fi) + { + fa.first = (vert ? fi->second.height : fi->second.width); + fa.second = 0; //This isn't an adjustable element + } + } + result.first += fa.first; result.second += fa.second; @@ -1046,7 +1273,7 @@ namespace nana double max_px; }; - static double _m_find_lowest_revised_division(const std::vector& revises, double level_px) + static double _m_find_lowest_revised_division(const std::vector& revises, double level_px) noexcept { double v = (std::numeric_limits::max)(); @@ -1060,7 +1287,7 @@ namespace nana return v; } - static std::size_t _m_remove_revised(std::vector& revises, double value, std::size_t& full_count) + static std::size_t _m_remove_revised(std::vector& revises, double value, std::size_t& full_count) noexcept { full_count = 0; std::size_t reached_mins = 0; @@ -1089,32 +1316,28 @@ namespace nana double var_px = area_px - fa.first; - /* - struct revise_t //deprecated - { - division * div; - double min_px; - double max_px; - }; - */ - std::size_t min_count = 0; double sum_min_px = 0; - //std::vector revises; //deprecated std::vector revises; for (auto& child : children) { - if ((!child->weight.empty()) || !child->display) + if ((!child->weight.empty()) || (!child->display)) continue; double min_px = std::numeric_limits::lowest(), max_px = std::numeric_limits::lowest(); if (!child->min_px.empty()) - { min_px = child->min_px.get_value(static_cast(area_px)); + + auto weight_floor = (this->kind_of_division == kind::arrange ? child->run_.weight_floor.first : child->run_.weight_floor.second); + if ((weight_floor > 0) && (min_px < weight_floor)) + min_px = weight_floor; + + if(!child->min_px.empty() || (weight_floor > 0)) + { sum_min_px += min_px; - ++min_count; + ++min_count; } if (!child->max_px.empty()) @@ -1122,7 +1345,9 @@ namespace nana if (min_px >= 0 && max_px >= 0 && min_px > max_px) { - if (child->min_px.kind_of() == number_t::kind::percent) + if(weight_floor > 0) + max_px = min_px; + else if (child->min_px.kind_of() == number_t::kind::percent) min_px = std::numeric_limits::lowest(); else if (child->max_px.kind_of() == number_t::kind::percent) max_px = std::numeric_limits::lowest(); @@ -1135,47 +1360,6 @@ namespace nana if (revises.empty()) return var_px / fa.second; - /* - auto find_lowest = [&revises](double level_px) //deprecated - { - double v = (std::numeric_limits::max)(); - - for (auto i = revises.begin(); i != revises.end(); ++i) - { - auto & rev = *i; - if (rev.min_px >= 0 && rev.min_px < v && rev.min_px > level_px) - v = rev.min_px; - else if (rev.max_px >= 0 && rev.max_px < v) - v = rev.max_px; - } - return v; - }; - */ - - /* - auto remove_full = [&revises](double value, std::size_t& full_count) - { - full_count = 0; - std::size_t reached_mins = 0; - auto i = revises.begin(); - while(i != revises.end()) - { - if (i->max_px == value) - { - ++full_count; - i = revises.erase(i); - } - else - { - if (i->min_px == value) - ++reached_mins; - ++i; - } - } - return reached_mins; - }; - */ - double block_px = 0; double level_px = 0; auto rest_px = var_px - sum_min_px; @@ -1183,8 +1367,8 @@ namespace nana while ((rest_px > 0) && blocks) { - //auto lowest = find_lowest(level_px); //deprecated auto lowest = _m_find_lowest_revised_division(revises, level_px); + double fill_px = 0; //blocks may be equal to min_count. E.g, all child divisions have min/max attribute. if (blocks > min_count) @@ -1202,7 +1386,6 @@ namespace nana rest_px -= (lowest-level_px) * (blocks - min_count); std::size_t full_count; - //min_count -= remove_full(lowest, full_count); //deprecated min_count -= _m_remove_revised(revises, lowest, full_count); blocks -= full_count; level_px = lowest; @@ -1218,7 +1401,7 @@ namespace nana : public division { public: - div_grid(std::string&& name, place_parts::repeated_array&& arrange, std::vector&& collapses) + div_grid(std::string&& name, place_parts::repeated_array&& arrange, std::vector&& collapses) noexcept : division(kind::grid, std::move(name)), arrange_(std::move(arrange)), collapses_(std::move(collapses)) @@ -1226,7 +1409,7 @@ namespace nana dimension.first = dimension.second = 0; } - void revise_collapses() + void revise_collapses() noexcept { if (collapses_.empty()) return; @@ -1268,163 +1451,134 @@ namespace nana if (!field || !(visible && display)) return; - auto area = margin_area(); + auto const area = margin_area(); + auto const gap_size = static_cast(gap.at(0).get_value(area.width)); //gap_size is 0 if gap isn't specified - unsigned gap_size = 0; - auto gap_number = gap.at(0); - if (!gap_number.empty()) - gap_size = static_cast(gap_number.get_value(area.width)); + auto i = field->elements.cbegin(); + auto const end = field->elements.cend(); - //When the amount pixels of gaps is out of the area bound. - if ((gap_size * dimension.first >= area.width) || (gap_size * dimension.second >= area.height)) + //The total gaps must not beyond the bound of the area. + if ((gap_size * dimension.first < area.width) && (gap_size * dimension.second < area.height)) { - for (auto & el : field->elements) - API::window_size(el.handle, size{ 0, 0 }); - return; - } - - if (dimension.first <= 1 && dimension.second <= 1) - { - auto n_of_wd = field->elements.size(); - std::size_t edge; - switch (n_of_wd) + if (dimension.first <= 1 && dimension.second <= 1) { - case 0: - case 1: - edge = 1; break; - case 2: case 3: case 4: - edge = 2; break; - default: - edge = static_cast(std::sqrt(n_of_wd)); - if ((edge * edge) < n_of_wd) ++edge; - } - - bool exit_for = false; - double y = area.y; - double block_w = area.width / double(edge); - double block_h = area.height / double((n_of_wd / edge) + (n_of_wd % edge ? 1 : 0)); - unsigned uns_block_w = static_cast(block_w); - unsigned uns_block_h = static_cast(block_h); - unsigned height = (uns_block_h > gap_size ? uns_block_h - gap_size : uns_block_h); - - auto i = field->elements.cbegin(), end = field->elements.cend(); - std::size_t arr_pos = 0; - for (std::size_t u = 0; u < edge; ++u) - { - double x = area.x; - for (std::size_t v = 0; v < edge; ++v) + auto n_of_wd = field->elements.size(); + std::size_t edge; + switch (n_of_wd) { - if (i == end) - { - exit_for = true; - break; - } - - unsigned value = 0; - auto arr = arrange_.at(arr_pos++); - - if (arr.empty()) - value = static_cast(block_w); - else - value = static_cast(arr.get_value(static_cast(area.width))); - - unsigned width = (value > uns_block_w ? uns_block_w : value); - if (width > gap_size) width -= gap_size; - API::move_window(i->handle, rectangle{ static_cast(x), static_cast(y), width, height }); - x += block_w; - ++i; - } - if (exit_for) break; - y += block_h; - } - } - else - { - double block_w = int(area.width - gap_size * (dimension.first - 1)) / double(dimension.first); - double block_h = int(area.height - gap_size * (dimension.second - 1)) / double(dimension.second); - - std::unique_ptr table_ptr(new char[dimension.first * dimension.second]); - - char *table = table_ptr.get(); - std::memset(table, 0, dimension.first * dimension.second); - - std::size_t lbp = 0; - bool exit_for = false; - - auto i = field->elements.cbegin(), end = field->elements.cend(); - - double precise_h = 0; - for (std::size_t c = 0; c < dimension.second; ++c) - { - unsigned block_height_px = static_cast(block_h + precise_h); - precise_h = (block_h + precise_h) - block_height_px; - - double precise_w = 0; - for (std::size_t l = 0; l < dimension.first; ++l) - { - if (table[l + lbp]) - { - precise_w += block_w; - auto px = static_cast(precise_w); - precise_w -= px; - continue; - } - - if (i == end) - { - exit_for = true; - break; - } - - std::pair room{ 1, 1 }; - - _m_find_collapse(static_cast(l), static_cast(c), room); - - int pos_x = area.x + static_cast(l * (block_w + gap_size)); - int pos_y = area.y + static_cast(c * (block_h + gap_size)); - - unsigned result_h; - if (room.first <= 1 && room.second <= 1) - { - precise_w += block_w; - result_h = block_height_px; - table[l + lbp] = 1; - } - else - { - precise_w += block_w * room.first + (room.first - 1) * gap_size; - result_h = static_cast(block_h * room.second + precise_h + (room.second - 1) * gap_size); - - for (unsigned y = 0; y < room.second; ++y) - for (unsigned x = 0; x < room.first; ++x) - table[l + x + lbp + y * dimension.first] = 1; - } - - unsigned result_w = static_cast(precise_w); - precise_w -= result_w; - - API::move_window(i->handle, rectangle{ pos_x, pos_y, result_w, result_h }); - ++i; + case 0: + case 1: + edge = 1; break; + case 2: case 3: case 4: + edge = 2; break; + default: + edge = static_cast(std::sqrt(n_of_wd)); + if ((edge * edge) < n_of_wd) ++edge; } - if (exit_for) + double y = area.y; + const double block_w = area.width / double(edge); + const double block_h = area.height / double((n_of_wd / edge) + (n_of_wd % edge ? 1 : 0)); + const unsigned uns_block_w = static_cast(block_w); + const unsigned uns_block_h = static_cast(block_h); + const unsigned height = (uns_block_h > gap_size ? uns_block_h - gap_size : uns_block_h); + + std::size_t arr_pos = 0; + for (std::size_t u = 0; (u < edge && i != end); ++u) { - size empty_sz; - for (; i != end; ++i) - API::window_size(i->handle, empty_sz); - break; + double x = area.x; + for (std::size_t v = 0; (v < edge && i != end); ++v, ++i) + { + unsigned value = 0; + auto arr = arrange_.at(arr_pos++); + + if (arr.empty()) + value = static_cast(block_w); + else + value = static_cast(arr.get_value(static_cast(area.width))); + + unsigned width = (value > uns_block_w ? uns_block_w : value); + if (width > gap_size) width -= gap_size; + API::move_window(i->handle, rectangle{ static_cast(x), static_cast(y), width, height }); + x += block_w; + } + y += block_h; + } + } + else + { + const double block_w = int(area.width - gap_size * (dimension.first - 1)) / double(dimension.first); + const double block_h = int(area.height - gap_size * (dimension.second - 1)) / double(dimension.second); + + std::unique_ptr table_ptr{ new char[dimension.first * dimension.second] }; + char *table = table_ptr.get(); + std::memset(table, 0, dimension.first * dimension.second); + + std::size_t lbp = 0; + + double precise_h = 0; + for (std::size_t u = 0; (u < dimension.second && i != end); ++u) + { + auto const block_height_px = static_cast(block_h + precise_h); + precise_h = (block_h + precise_h) - block_height_px; + + double precise_w = 0; + for (std::size_t v = 0; (v < dimension.first && i != end); ++v) + { + auto const epos = v + lbp; + if (table[epos]) + { + precise_w += block_w; + precise_w -= static_cast(precise_w); + continue; + } + + std::pair room{ 1, 1 }; + _m_find_collapse(static_cast(v), static_cast(u), room); + + const int pos_x = area.x + static_cast(v * (block_w + gap_size)); + const int pos_y = area.y + static_cast(u * (block_h + gap_size)); + + unsigned result_h; + if (room.first <= 1 && room.second <= 1) + { + precise_w += block_w; + result_h = block_height_px; + table[epos] = 1; + } + else + { + precise_w += block_w * room.first + (room.first - 1) * gap_size; + result_h = static_cast(block_h * room.second + precise_h + (room.second - 1) * gap_size); + + for (unsigned y = 0; y < room.second; ++y) + for (unsigned x = 0; x < room.first; ++x) + table[epos + x + y * dimension.first] = 1; + } + + unsigned result_w = static_cast(precise_w); + precise_w -= result_w; + + API::move_window(i->handle, rectangle{ pos_x, pos_y, result_w, result_h }); + ++i; + } + + lbp += dimension.first; } - lbp += dimension.first; } } + // Empty the size of windows that are out range of grid + for (; i != end; ++i) + API::window_size(i->handle, size{}); + for (auto & fsn : field->fastened) API::move_window(fsn.handle, area); } public: std::pair dimension; private: - void _m_find_collapse(int x, int y, std::pair& collapse) const + void _m_find_collapse(int x, int y, std::pair& collapse) const noexcept { for (auto & col : collapses_) { @@ -1450,14 +1604,14 @@ namespace nana int pixels; double scale; - div_block(division* d, int px) + div_block(division* d, int px) noexcept : div(d), pixels(px) {} }; enum{splitter_px = 4}; public: - div_splitter(place_parts::number_t init_weight, implement* impl): + div_splitter(const place_parts::number_t & init_weight, implement* impl) noexcept : division(kind::splitter, std::string()), impl_(impl), init_weight_(init_weight) @@ -1465,7 +1619,7 @@ namespace nana this->weight.assign(splitter_px); } - void direction(bool horizontal) + void direction(bool horizontal) noexcept { splitter_cursor_ = (horizontal ? cursor::size_we : cursor::size_ns); } @@ -1487,6 +1641,9 @@ namespace nana if ((false == arg.left_button) && (mouse::left_button != arg.button)) return; + auto const leaf_left = _m_leaf(true); + auto const leaf_right = _m_leaf(false); + if (event_code::mouse_down == arg.evt_code) { begin_point_ = splitter_.pos(); @@ -1494,8 +1651,8 @@ namespace nana auto px_ptr = &nana::rectangle::width; //Use field_area of leaf, not margin_area. Otherwise splitter would be at wrong position - auto area_left = _m_leaf_left()->field_area; - auto area_right = _m_leaf_right()->field_area; + auto const area_left = leaf_left->field_area; + auto const area_right = leaf_right->field_area; if (nana::cursor::size_we != splitter_cursor_) { @@ -1524,37 +1681,29 @@ namespace nana if(!grabbed_) return; - const bool vert = (::nana::cursor::size_we != splitter_cursor_); + auto const vert = (::nana::cursor::size_we != splitter_cursor_); + auto const delta = horz_point(vert, splitter_.pos() - begin_point_); + + const auto total_pixels = static_cast(left_pixels_ + right_pixels_); + + auto left_px = std::clamp(static_cast(left_pixels_) + delta, 0, total_pixels); + auto area_px = rectangle_rotator(vert, div_owner->margin_area()).w(); - int delta = (vert ? splitter_.pos().y - begin_point_.y : splitter_.pos().x - begin_point_.x); - - int total_pixels = static_cast(left_pixels_ + right_pixels_); - - auto left_px = static_cast(left_pixels_) + delta; - if (left_px > total_pixels) - left_px = total_pixels; - else if (left_px < 0) - left_px = 0; - double imd_rate = 100.0 / area_px; - left_px = static_cast(limit_px(_m_leaf_left(), left_px, area_px)); - _m_leaf_left()->weight.assign_percent(imd_rate * left_px); + left_px = static_cast(limit_px(leaf_left, left_px, area_px)); + leaf_left->weight.assign_percent(imd_rate * left_px); - auto right_px = static_cast(right_pixels_) - delta; - if (right_px > total_pixels) - right_px = total_pixels; - else if (right_px < 0) - right_px = 0; + auto right_px = std::clamp(static_cast(right_pixels_) - delta, 0, total_pixels); - right_px = static_cast(limit_px(_m_leaf_right(), right_px, area_px)); - _m_leaf_right()->weight.assign_percent(imd_rate * right_px); + right_px = static_cast(limit_px(leaf_right, right_px, area_px)); + leaf_right->weight.assign_percent(imd_rate * right_px); pause_move_collocate_ = true; div_owner->collocate(splitter_.parent()); //After the collocating, the splitter keeps the calculated weight of left division, //and clear the weight of right division. - _m_leaf_right()->weight.reset(); + leaf_right->weight.reset(); pause_move_collocate_ = false; } @@ -1572,18 +1721,15 @@ namespace nana { const bool vert = (::nana::cursor::size_we != splitter_cursor_); - auto leaf_left = _m_leaf_left(); - auto leaf_right = _m_leaf_right(); + auto leaf_left = _m_leaf(true); + auto leaf_right = _m_leaf(false); rectangle_rotator left(vert, leaf_left->field_area); rectangle_rotator right(vert, leaf_right->field_area); auto area_px = right.right() - left.x(); auto right_px = static_cast(limit_px(leaf_right, init_weight_.get_value(area_px), static_cast(area_px))); - auto pos = area_px - right_px - splitter_px; //New position of splitter - if (pos < limited_range.x()) - pos = limited_range.x(); - else if (pos > limited_range.right()) - pos = limited_range.right(); + //New position of splitter + const auto pos = std::clamp(static_cast(area_px - right_px - splitter_px), limited_range.x(), limited_range.right()); rectangle_rotator sp_r(vert, field_area); sp_r.x_ref() = pos; @@ -1616,7 +1762,7 @@ namespace nana splitter_.move(this->field_area); } private: - static int _m_search_name(const division* div, std::string& name) + static int _m_search_name(const division* div, std::string& name) noexcept { if (div->name.size()) { @@ -1640,64 +1786,6 @@ namespace nana return -1; } - static std::pair _m_field_bound(const std::string& div, std::size_t start_pos) - { - int depth = 0; - - if ('<' == div[start_pos]) - { - auto off = start_pos + 1; - while (off < div.length()) - { - auto pos = div.find_first_of("<>", off); - if (div.npos == pos) - break; - - if ('<' == div[pos]) - { - ++depth; - off = pos + 1; - continue; - } - - if (0 == depth) - return{ start_pos, pos + 1}; - - --depth; - off = pos + 1; - } - } - else if (('>' == div[start_pos]) && (start_pos > 0)) - { - auto off = start_pos - 1; - while (true) - { - auto pos = div.find_last_of("<>", off); - if (div.npos == pos) - break; - - if ('>' == div[pos]) - { - ++depth; - if (0 == pos) - break; - - off = pos - 1; - } - - if (0 == depth) - return{ pos, start_pos + 1}; - - if (0 == pos) - break; - - off = pos - 1; - } - } - - return{}; - } - static void _m_remove_attr(std::string& div, const char* attr) { auto attr_pos = div.find(attr); @@ -1764,16 +1852,19 @@ namespace nana void _m_update_div(std::string& div) { + auto const leaf_left = _m_leaf(true); + auto const leaf_right = _m_leaf(false); + std::string name; bool left = true; //Search a name recursively from a specified leaf field. //It returns the depth from the leaf div to the div which has a name. - auto depth = _m_search_name(_m_leaf_left(), name); + auto depth = _m_search_name(leaf_left, name); if (-1 == depth) { left = false; - depth = _m_search_name(_m_leaf_right(), name); + depth = _m_search_name(leaf_right, name); if (-1 == depth) return; } @@ -1786,33 +1877,19 @@ namespace nana auto fieldstr = div.substr(bound.first, bound.second - bound.first); _m_remove_attr(fieldstr, "weight"); - decltype(bound) other_bound; - if (left) - { - //Get the bound of leaf right - auto pos = div.find('<', bound.second + 1); - if (div.npos == pos) - throw std::runtime_error("please report an issue if it throws"); + std::string::size_type tag_pos{ left ? div.find('<', bound.second + 1) : div.rfind('>', bound.first - 1) }; + if (div.npos == tag_pos) + throw std::runtime_error("place report an issue if it throws"); - other_bound = _m_field_bound(div, pos); - } - else - { - //Get the bound of leaf left - auto pos = div.rfind('>', bound.first - 1); - if (div.npos == pos) - throw std::runtime_error("place report an issue if it throws"); - - other_bound = _m_field_bound(div, pos); - } + auto other_bound = get_field_bound(div, tag_pos); auto other_fieldstr = div.substr(other_bound.first, other_bound.second - other_bound.first); _m_remove_attr(other_fieldstr, "weight"); const bool vert = (::nana::cursor::size_we != splitter_cursor_); - rectangle_rotator r_left(vert, _m_leaf_left()->field_area); - rectangle_rotator r_right(vert, _m_leaf_right()->field_area); + rectangle_rotator r_left(vert, leaf_left->field_area); + rectangle_rotator r_right(vert, leaf_right->field_area); rectangle_rotator r_owner(vert, this->div_owner->field_area); double percent = double((left ? r_left : r_right).w()) / double(r_owner.w()); @@ -1838,14 +1915,9 @@ namespace nana } } - division * _m_leaf_left() const + division * _m_leaf(bool left) const noexcept { - return previous(); - } - - division * _m_leaf_right() const - { - return div_next; + return (left ? previous() : div_next); } rectangle_rotator _m_update_splitter_range() @@ -1854,8 +1926,8 @@ namespace nana rectangle_rotator area(vert, div_owner->margin_area()); - auto leaf_left = _m_leaf_left(); - auto leaf_right = _m_leaf_right(); + auto leaf_left = _m_leaf(true); + auto leaf_right = _m_leaf(false); rectangle_rotator left(vert, leaf_left->field_area); rectangle_rotator right(vert, leaf_right->field_area); @@ -1865,30 +1937,16 @@ namespace nana int endpos = right_base; if (!leaf_left->min_px.empty()) - { - auto v = leaf_left->min_px.get_value(area.w()); - pos += static_cast(v); - } + pos += static_cast(leaf_left->min_px.get_value(area.w())); + if (!leaf_left->max_px.empty()) - { - auto v = leaf_left->max_px.get_value(area.w()); - endpos = left_base + static_cast(v); - } + endpos = left_base + static_cast(leaf_left->max_px.get_value(area.w())); if (!leaf_right->min_px.empty()) - { - auto v = leaf_right->min_px.get_value(area.w()); - auto x = right_base - static_cast(v); - if (x < endpos) - endpos = x; - } + endpos = (std::min)(right_base - static_cast(leaf_right->min_px.get_value(area.w())), endpos); + if (!leaf_right->max_px.empty()) - { - auto v = leaf_right->max_px.get_value(area.w()); - auto x = right_base - static_cast(v); - if (x > pos) - pos = x; - } + pos = (std::max)(right_base - static_cast(leaf_right->max_px.get_value(area.w())), pos); area.x_ref() = pos; area.w_ref() = unsigned(endpos - pos + splitter_px); @@ -1914,7 +1972,7 @@ namespace nana : public division, public place_parts::dock_notifier_interface { public: - div_dockpane(std::string && name, implement* impl, direction pane_dir) + div_dockpane(std::string && name, implement* impl, direction pane_dir) noexcept : division(kind::dockpane, std::move(name)), impl_ptr_{impl} { @@ -1922,7 +1980,7 @@ namespace nana this->display = false; } - ~div_dockpane() + ~div_dockpane() noexcept { if (dockable_field) { @@ -1968,7 +2026,7 @@ namespace nana void notify_move() override { - if (!_m_indicator()) + if (!_m_hit_test(false)) //hit test on indicator { indicator_.docker.reset(); return; @@ -2005,7 +2063,7 @@ namespace nana }); } - if (_m_dockable()) + if (_m_hit_test(true)) //hit test on docker { if (!indicator_.dock_area) { @@ -2060,12 +2118,12 @@ namespace nana indicator_.graph.release(); } } - } void notify_move_stopped() override { - if (_m_dockable() && dockable_field && dockable_field->dockarea) + //hit test on docker + if (_m_hit_test(true) && dockable_field && dockable_field->dockarea) dockable_field->dockarea->dock(); indicator_.docker.reset(); @@ -2088,25 +2146,22 @@ namespace nana API::close_window(window_handle); } private: - bool _m_indicator() const + bool _m_hit_test(bool try_docker) const { - ::nana::point pos; - API::calc_screen_point(impl_ptr_->window_handle, pos); + window handle = nullptr; + if (try_docker) + { + if (!indicator_.docker) + return false; - rectangle r{ pos, API::window_size(impl_ptr_->window_handle) }; - return r.is_hit(API::cursor_position()); - } + handle = indicator_.docker->handle(); //hit test for docker + } + else + handle = impl_ptr_->window_handle; //hit test for indicator - bool _m_dockable() const - { - if (!indicator_.docker) - return false; - - ::nana::point pos; - API::calc_screen_point(indicator_.docker->handle(), pos); - - rectangle r{ pos, API::window_size(indicator_.docker->handle()) }; - return r.is_hit(API::cursor_position()); + point pos; + API::calc_screen_point(handle, pos); + return rectangle{ pos, API::window_size(handle) }.is_hit(API::cursor_position()); } public: field_dock * dockable_field{ nullptr }; @@ -2136,25 +2191,21 @@ namespace nana : panel(wd, true), dir_(dir), dock_dv_(dock_dv), pane_dv_(pane_dv) { this->bgcolor(colors::alice_blue); - this->cursor(_m_is_vert(dir_) ? ::nana::cursor::size_ns : ::nana::cursor::size_we); - + this->cursor(is_vert_dir(dir_) ? ::nana::cursor::size_ns : ::nana::cursor::size_we); auto grab_fn = [this, wd](const arg_mouse& arg) { + auto const is_vert = is_vert_dir(dir_); + if (event_code::mouse_down == arg.evt_code) //press mouse button { if (arg.button != ::nana::mouse::left_button) return; - bool is_vert = _m_is_vert(dir_); - this->set_capture(true); - auto basepos = API::cursor_position(); - base_pos_.x = (is_vert ? basepos.y : basepos.x); - - basepos = this->pos(); - base_pos_.y = (is_vert ? basepos.y : basepos.x); + base_pos_.x = horz_point(is_vert, API::cursor_position()); + base_pos_.y = horz_point(is_vert, this->pos()); base_px_ = (is_vert ? pane_dv_->field_area.height : pane_dv_->field_area.width); } @@ -2163,8 +2214,7 @@ namespace nana if (!arg.is_left_button()) return; - auto now_pos = API::cursor_position(); - int delta = (_m_is_vert(dir_) ? now_pos.y : now_pos.x) - base_pos_.x; + auto delta = horz_point(is_vert, API::cursor_position()) - base_pos_.x; int new_pos = base_pos_.y + delta; if (new_pos < range_.x) { @@ -2177,8 +2227,8 @@ namespace nana delta = new_pos - base_pos_.y; } - now_pos = this->pos(); - if (_m_is_vert(dir_)) + auto now_pos = this->pos(); + if (is_vert) now_pos.y = new_pos; else now_pos.x = new_pos; @@ -2205,7 +2255,7 @@ namespace nana break; } - auto dock_px = (_m_is_vert(dir_) ? dock_dv_->field_area.height : dock_dv_->field_area.width); + auto dock_px = (is_vert ? dock_dv_->field_area.height : dock_dv_->field_area.width); pane_dv_->weight.assign_percent(double(px) / double(dock_px) * 100); @@ -2221,7 +2271,7 @@ namespace nana evt.mouse_move.connect(grab_fn); } - void range(int begin, int end) + void range(int begin, int end) noexcept { range_.x = begin; range_.y = end; @@ -2236,11 +2286,11 @@ namespace nana }; public: - div_dock(std::string && name, implement* impl) + div_dock(std::string && name, implement* impl) noexcept : division(kind::dock, std::move(name)), impl_(impl) {} - division* front() const + division* front() const noexcept { for (auto & child : children) { @@ -2265,7 +2315,7 @@ namespace nana if (!child->display) continue; - const auto is_vert = _m_is_vert(child->dir); + const auto is_vert = is_vert_dir(child->dir); if (is_first) { is_first = false; @@ -2305,7 +2355,7 @@ namespace nana } auto child_dv = dynamic_cast(child.get()); - const bool is_vert = _m_is_vert(child->dir); + const bool is_vert = is_vert_dir(child->dir); auto room_px = (is_vert ? room.height : room.width); @@ -2416,11 +2466,6 @@ namespace nana } } private: - static bool _m_is_vert(::nana::direction dir) - { - return (dir == ::nana::direction::north || dir == ::nana::direction::south); - } - static div_dockpane* _m_right(division* dv) { dv = dv->div_next; @@ -2458,6 +2503,8 @@ namespace nana if (root_division->field_area.empty()) return; + root_division->calc_weight_floor(); + root_division->collocate(window_handle); for (auto & field : fields) @@ -2486,7 +2533,7 @@ namespace nana //search_div_name //search a division with the specified name. - place::implement::division * place::implement::search_div_name(division* start, const std::string& name) + place::implement::division * place::implement::search_div_name(division* start, const std::string& name) noexcept { if (start) { @@ -2503,12 +2550,27 @@ namespace nana return nullptr; } + static int get_parameter(place_parts::tokenizer& tknizer, std::size_t pos) + { + auto & arg = tknizer.parameters()[pos]; + + if (arg.kind_of() == number_t::kind::integer) + return arg.integer(); + else if (arg.kind_of() == number_t::kind::real) + return static_cast(arg.real()); + + const char* pos_strs[] = { "1st", "2nd", "3rd", "4th" }; + throw std::invalid_argument("nana.place: the type of the " + std::string{pos_strs[pos]} +"th parameter for collapse should be integer."); + } + auto place::implement::scan_div(place_parts::tokenizer& tknizer) -> std::unique_ptr { typedef place_parts::tokenizer::token token; std::unique_ptr div; token div_type = token::eof; + auto fit = fit_policy::none; + place_parts::repeated_array fit_parameters; //These variables stand for the new division's attributes std::string name; @@ -2523,17 +2585,24 @@ namespace nana bool undisplayed = false; bool invisible = false; - for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read()) + for (token tk = tknizer.read(); (tk != token::eof && tk != token::div_end); tk = tknizer.read()) { - bool exit_for = false; switch (tk) { case token::dock: if (token::eof != div_type && token::dock != div_type) - throw std::invalid_argument("nana.place: conflict of div type at " + tknizer.pos_str()); + throw std::invalid_argument("nana.place: conflict of div type at " + std::to_string(tknizer.pos())); div_type = token::dock; break; + case token::fit: + fit = fit_policy::both; + break; + case token::hfit: + case token::vfit: + fit = (token::hfit == tk ? fit_policy::horz : fit_policy::vert); + fit_parameters = tknizer.reparray(); + break; case token::splitter: //Ignore the splitter when there is not a division. if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division)) @@ -2579,29 +2648,13 @@ namespace nana if (tknizer.parameters().size() != 4) throw std::invalid_argument("nana.place: collapse requires 4 parameters."); - auto get_number = [](const number_t & arg, const std::string& nth) - { - if (arg.kind_of() == number_t::kind::integer) - return arg.integer(); - else if (arg.kind_of() == number_t::kind::real) - return static_cast(arg.real()); - - throw std::invalid_argument("nana.place: the type of the "+ nth +" parameter for collapse should be integer."); + ::nana::rectangle col{ + get_parameter(tknizer, 0), + get_parameter(tknizer, 1), + static_cast(get_parameter(tknizer, 2)), + static_cast(get_parameter(tknizer, 3)) }; - ::nana::rectangle col; - auto arg = tknizer.parameters().at(0); - col.x = get_number(arg, "1st"); - - arg = tknizer.parameters().at(1); - col.y = get_number(arg, "2nd"); - - arg = tknizer.parameters().at(2); - col.width = static_cast(get_number(arg, "3rd")); - - arg = tknizer.parameters().at(3); - col.height = static_cast(get_number(arg, "4th")); - //Check the collapse area. //Ignore this collapse if its area is less than 2(col.width * col.height < 2) if (!col.empty() && (col.width > 1 || col.height > 1) && (col.x >= 0 && col.y >= 0)) @@ -2650,7 +2703,7 @@ namespace nana switch (tknizer.read()) { case token::number: - margin.set_value(tknizer.number()); + margin.push(tknizer.number(), true); break; case token::array: margin.set_array(tknizer.array()); @@ -2667,9 +2720,6 @@ namespace nana break; } break; - case token::div_end: - exit_for = true; - break; case token::identifier: name = tknizer.idstr(); break; @@ -2687,8 +2737,6 @@ namespace nana invisible = true; break; default: break; } - if (exit_for) - break; } field_gather * attached_field = nullptr; @@ -2750,19 +2798,6 @@ namespace nana max_px.reset(); } - /* - //The weight will be ignored if one of min and max is specified. //deprecated - if (min_px.empty() && max_px.empty()) - { - div->weight = weight; - } - else - { - div->min_px = min_px; - div->max_px = max_px; - } - */ - if (!min_px.empty()) div->min_px = min_px; @@ -2815,10 +2850,10 @@ namespace nana } division * next = nullptr; - for (auto i = adjusted_children.rbegin(); i != adjusted_children.rend(); ++i) + for (int i = static_cast(adjusted_children.size()) - 1; i >= 0; --i) { - i->get()->div_next = next; - next = i->get(); + adjusted_children[i]->div_next = next; + next = adjusted_children[i].get(); } children.swap(adjusted_children); @@ -2831,6 +2866,9 @@ namespace nana div->display = !undisplayed; div->visible = !(undisplayed || invisible); + div->fit = fit; + div->fit_parameters = std::move(fit_parameters); + return div; } @@ -2869,6 +2907,8 @@ namespace nana if (!start) return; + check_unique(start); //may throw if there is a redefined name of field. + this->disconnect(); std::map docks_to_be_closed; @@ -2890,20 +2930,20 @@ namespace nana if (i != docks.end()) { docks_to_be_closed.erase(div->name); - auto pane = dynamic_cast(div); - pane->dockable_field = i->second; - auto old_pane = pane->dockable_field->attached; - if (old_pane) + auto const pane = dynamic_cast(div); + auto fd_dock = pane->dockable_field; + fd_dock = i->second; + + if (fd_dock->attached) { - //old div_dockpane will be deleted - old_pane->dockable_field = nullptr; - div->display = old_pane->display; + fd_dock->attached->dockable_field = nullptr; + div->display = fd_dock->attached->display; } - pane->dockable_field->attached = pane; - if (pane->dockable_field->dockarea) - pane->dockable_field->dockarea->set_notifier(pane); + fd_dock->attached = pane; + if (fd_dock->dockarea) + fd_dock->dockarea->set_notifier(pane); } } else @@ -2934,7 +2974,7 @@ namespace nana } } - void place::implement::disconnect() + void place::implement::disconnect() noexcept { for (auto & fd : fields) { @@ -2973,6 +3013,7 @@ namespace nana if (impl_->root_division) { impl_->root_division->field_area.dimension({ arg.width, arg.height }); + impl_->root_division->calc_weight_floor(); impl_->root_division->collocate(arg.window_handle); } }); @@ -2990,8 +3031,7 @@ namespace nana auto div = impl_->scan_div(tknizer); try { - impl_->check_unique(div.get()); //may throw if there is a redefined name of field. - impl_->connect(div.get()); + impl_->connect(div.get()); //throws if there is a redefined name of field. impl_->root_division.reset(); //clear atachments div-fields impl_->root_division.swap(div); impl_->div_text.assign(s); @@ -3034,7 +3074,6 @@ namespace nana throw std::invalid_argument(what); } - std::unique_ptr* replaced = nullptr; implement::division * div_owner = div_ptr->div_owner; @@ -3064,8 +3103,7 @@ namespace nana replaced->swap(modified); - impl_->check_unique(impl_->root_division.get()); - impl_->connect(impl_->root_division.get()); + impl_->connect(impl_->root_division.get()); //throws if there is a duplicate name impl_->tmp_replaced.reset(); update_div(impl_->div_text, name, div_text, update_operation::replace); @@ -3324,7 +3362,7 @@ namespace nana dock_ptr->dockarea->move(dock_ptr->attached->field_area); } - return dock_ptr->dockarea->add_pane(i->second->factories[factory]); + return dock_ptr->dockarea->add_pane(dock_ptr->factories[factory]); } return nullptr; diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 2bd29c1d..a3f5c8ae 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-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -29,21 +29,16 @@ namespace nana virtual ~splitter_interface(){} }; - class splitter_dtrigger - : public drawer_trigger - { - }; - template class splitter - : public widget_object ::type, splitter_dtrigger>, + : public widget_object ::type, drawer_trigger>, public splitter_interface { private: void _m_complete_creation() override { this->caption("place-splitter"); - widget_object ::type, splitter_dtrigger>::_m_complete_creation(); + widget_object ::type, drawer_trigger>::_m_complete_creation(); } }; @@ -92,7 +87,7 @@ namespace nana { color xclr = colors::red; - if(x_state_ == ::nana::mouse_action::pressed) + if(x_state_ == ::nana::mouse_action::pressed) xclr = xclr.blend(colors::white, 0.8); graph.rectangle(r, true, xclr); @@ -144,11 +139,8 @@ namespace nana private: ::nana::rectangle _m_button_area() const { - ::nana::rectangle r{API::window_size(window_handle_)}; - - r.x = r.right() - 20; - r.width = 20; - return r; + auto sz = API::window_size(window_handle_); + return{static_cast(sz.width) - 20, 0, 20, sz.height}; } public: window window_handle_; @@ -161,12 +153,17 @@ namespace nana }; class dockarea_caption - : public widget_object < category::widget_tag, dockcaption_dtrigger > + : public widget_object { public: using widget_object::get_drawer_trigger; }; + static unsigned differ(unsigned x, unsigned y) noexcept + { + return (x > y ? x - y : 0); + } + class dockarea : public widget_object { @@ -183,6 +180,7 @@ namespace nana { notifier_ = notifier; } + void create(window parent) { host_window_ = parent; @@ -191,17 +189,14 @@ namespace nana caption_.create(*this, true); caption_.get_drawer_trigger().on_close([this] { - bool destroy_dockarea = true; - if (tabbar_) { tabbar_->erase(tabbar_->selected()); - - destroy_dockarea = (0 == tabbar_->length()); + if (tabbar_->length()) + return; } - if (destroy_dockarea) - notifier_->request_close(); + notifier_->request_close(); }); this->events().resized.connect([this](const arg_resized& arg) @@ -331,14 +326,11 @@ namespace nana private: widget* _m_add_pane(factory & fn) { - rectangle r{ point(), this->size() }; + rectangle r{ this->size() }; //get a rectangle excluding caption r.y = 20; - if (r.height > 20) - r.height -= 20; - else - r.height = 0; + r.height = differ(r.height, 20); if (!tabbar_) { @@ -426,7 +418,7 @@ namespace nana value_.integer = 0; } - void reset() + void reset() noexcept { kind_ = kind::none; value_.integer = 0; @@ -434,30 +426,21 @@ namespace nana bool is_negative() const { - switch (kind_) - { - case kind::integer: - return (value_.integer < 0); - case kind::real: - case kind::percent: - return (value_.real < 0); - default: - break; - } - return false; + return (((kind::integer == kind_) && (value_.integer < 0)) || + ((kind::real == kind_ || kind::percent == kind_) && (value_.real < 0))); } - bool empty() const throw() + bool empty() const noexcept { return (kind::none == kind_); } - kind kind_of() const + kind kind_of() const noexcept { return kind_; } - double get_value(int ref_percent) const + double get_value(int ref_percent) const noexcept { switch (kind_) { @@ -473,33 +456,33 @@ namespace nana return 0; } - int integer() const + int integer() const noexcept { if (kind::integer == kind_) return value_.integer; return static_cast(value_.real); } - double real() const + double real() const noexcept { if (kind::integer == kind_) return value_.integer; return value_.real; } - void assign(int i) + void assign(int i) noexcept { kind_ = kind::integer; value_.integer = i; } - void assign(double d) + void assign(double d) noexcept { kind_ = kind::real; value_.real = d; } - void assign_percent(double d) + void assign_percent(double d) noexcept { kind_ = kind::percent; value_.real = d / 100; @@ -532,15 +515,12 @@ namespace nana all_edges_ = true; margins_.clear(); } - - void push(const number_t& v) + + void push(const number_t& v, bool reset = false) { - margins_.emplace_back(v); - } + if (reset) + clear(); - void set_value(const number_t& v) - { - clear(); margins_.emplace_back(v); } @@ -559,12 +539,11 @@ namespace nana if (all_edges_) { auto px = static_cast(margins_.back().get_value(static_cast(r.width))); - const auto dbl_px = static_cast(px << 1); r.x += px; - r.width = (r.width < dbl_px ? 0 : r.width - dbl_px); + r.width = differ(r.width, (static_cast(px) << 1)); r.y += px; - r.height = (r.height < dbl_px ? 0 : r.height - dbl_px); + r.height = differ(r.height, (static_cast(px) << 1)); } else { @@ -587,49 +566,44 @@ namespace nana ib = 2; } - typedef decltype(r.height) px_type; - auto calc = [](px_type a, px_type b) - { - return (a > b ? a - b : 0); - }; + using px_type = decltype(r.height); if (0 == it) //top { auto px = static_cast(margins_[it].get_value(static_cast(field_area.height))); r.y += px; - r.height = calc(r.height, static_cast(px)); + r.height = differ(r.height, static_cast(px)); } if (-1 != ib) //bottom { auto px = static_cast(margins_[ib].get_value(static_cast(field_area.height))); - r.height = calc(r.height, static_cast(px)); + r.height = differ(r.height, static_cast(px)); } if (-1 != il) //left { auto px = static_cast(margins_[il].get_value(static_cast(field_area.width))); r.x += px; - r.width = calc(r.width, static_cast(px)); + r.width = differ(r.width, static_cast(px)); } if (-1 != ir) //right { auto px = static_cast(margins_[ir].get_value(static_cast(field_area.width))); - r.width = calc(r.width, static_cast(px)); + r.width = differ(r.width, static_cast(px)); } } return r; } private: - bool all_edges_ = true; + bool all_edges_{ true }; std::vector margins_; };//end class margin class repeated_array { public: - //A workaround for VC2013, becuase it does not generated an implicit declared move-constructor as defaulted. repeated_array() = default; @@ -678,15 +652,10 @@ namespace nana number_t at(std::size_t pos) const { - if (values_.empty()) - return{}; + if (values_.size() && (repeated_ || pos < values_.size())) + return values_[pos % values_.size()]; - if (repeated_) - pos %= values_.size(); - else if (pos >= values_.size()) - return{}; - - return values_[pos]; + return{}; } private: bool repeated_ = false; diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 486e6fae..10763d4b 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -1,7 +1,7 @@ /* * Nana GUI Programming Interface Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -210,6 +210,14 @@ namespace API return (restrict::wd_manager().available(iwd) ? iwd->annex.scheme : nullptr); } + void set_measurer(window wd, ::nana::dev::widget_content_measurer_interface* measurer) + { + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + if (restrict::wd_manager().available(iwd)) + iwd->annex.content_measurer = measurer; + } + void attach_drawer(widget& wd, drawer_trigger& dr) { const auto iwd = reinterpret_cast(wd.handle()); @@ -1009,6 +1017,8 @@ namespace API else return; + //modal has to guarantee that does not lock the mutex of window_manager before invokeing the pump_event, + //otherwise, the modal will prevent the other thread access the window. restrict::bedrock.pump_event(wd, true); } @@ -1429,5 +1439,29 @@ namespace API { restrict::wd_manager().set_safe_place(reinterpret_cast(wd), std::move(fn)); } + + optional> content_extent(window wd, unsigned limited_px, bool limit_width) + { + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + + if (restrict::wd_manager().available(iwd) && iwd->annex.content_measurer) + { + paint::graphics* graph = &iwd->drawer.graphics; + paint::graphics temp_graph; + if (graph->empty()) + { + temp_graph.make({ 1, 1 }); + temp_graph.typeface(graph->typeface()); + graph = &temp_graph; + } + + auto extent = iwd->annex.content_measurer->measure(*graph, limited_px, limit_width); + if (extent) + return std::make_pair(extent.value(), extent.value() + iwd->annex.content_measurer->extension()); + } + + return{}; + } }//end namespace API }//end namespace nana diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 241d3a63..16cf441b 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -1,7 +1,7 @@ /* * A Button Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -11,12 +11,44 @@ */ #include +#include + #include namespace nana{ namespace drawerbase { namespace button { + class trigger::measurer + : public dev::widget_content_measurer_interface + { + public: + measurer(trigger* t) + : trigger_{ t } + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Button doesn't provide a support of vfit and hfit + if (limit_pixels) + return{}; + + wchar_t shortkey; + std::string::size_type shortkey_pos; + + auto str = to_wstring(API::transform_shortkey_text(trigger_->wdg_->caption(), shortkey, &shortkey_pos)); + auto text_sz = graph.text_extent_size(str); + + return size{ text_sz.width, text_sz.height }; + } + + size extension() const override + { + return { 14, 10}; + } + private: + trigger * trigger_; + }; //trigger //@brief: draw the button @@ -26,6 +58,8 @@ namespace nana{ namespace drawerbase attr_.omitted = attr_.focused = attr_.pushed = attr_.enable_pushed = attr_.keep_pressed = false; attr_.focus_color = true; attr_.icon = nullptr; + + measurer_.reset(new measurer{this}); } trigger::~trigger() @@ -44,6 +78,7 @@ namespace nana{ namespace drawerbase API::tabstop(wd); API::effects_edge_nimbus(wd, effects::edge_nimbus::active); API::effects_edge_nimbus(wd, effects::edge_nimbus::over); + API::dev::set_measurer(widget, measurer_.get()); } bool trigger::enable_pushed(bool eb) @@ -251,17 +286,17 @@ namespace nana{ namespace drawerbase } else { - graph.palette(true, ::nana::color(colors::white)); + graph.palette(true, color{ colors::white }); if(attr_.omitted) { tr.render(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen, omitted_pixels, true); - graph.palette(true, ::nana::color(colors::gray)); + graph.palette(true, color{ colors::gray }); tr.render(pos, txtptr, txtlen, omitted_pixels, true); } else { graph.bidi_string(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen); - graph.palette(true, ::nana::color(colors::gray)); + graph.palette(true, color{ colors::gray }); graph.bidi_string(pos, txtptr, txtlen); } } diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 4ec63f52..77f199ab 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -1,7 +1,7 @@ /* * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -17,8 +17,10 @@ #include #include #include +#include #include +#include namespace nana { @@ -80,6 +82,46 @@ namespace nana class drawer_impl { + class content_measurer + : public dev::widget_content_measurer_interface + { + public: + content_measurer(drawer_impl* drwimpl) + : drw_{ drwimpl } + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Button doesn't provide a support of vfit and hfit + if (limit_pixels) + return{}; + + size content_size; + for (auto i = 0; i < drw_->the_number_of_options(); ++i) + { + auto & m = drw_->at(i); + auto sz = graph.text_extent_size(m.item_text); + + content_size.width = (std::max)(content_size.width, sz.width); + content_size.height = (std::max)(content_size.height, sz.height); + } + + return content_size; + } + + size extension() const override + { + auto text_size = drw_->editor()->text_area(false).dimension(); + auto wdg_size = drw_->widget_ptr()->size(); + + return{ + wdg_size.width > text_size.width ? wdg_size.width - text_size.width : 0, + wdg_size.height > text_size.height ? wdg_size.height - text_size.height : 0 + }; + } + private: + drawer_impl* const drw_; + }; public: using graph_reference = paint::graphics&; using widget_reference = widget&; @@ -92,6 +134,8 @@ namespace nana state_.button_state = element_state::normal; state_.pointer_where = parts::none; state_.lister = nullptr; + + measurer_.reset(new content_measurer{this}); } void renderer(drawerbase::float_listbox::item_renderer* ir) @@ -111,6 +155,8 @@ namespace nana evt_agent_.reset(new event_agent{ static_cast(wd) }); editor_->textbase().set_event_agent(evt_agent_.get()); + + API::dev::set_measurer(wd, measurer_.get()); } void detached() @@ -528,6 +574,8 @@ namespace nana unsigned image_pixels_{ 16 }; widgets::skeletons::text_editor * editor_{ nullptr }; std::unique_ptr evt_agent_; + + std::unique_ptr measurer_; struct state_type { bool focused; @@ -537,188 +585,191 @@ namespace nana nana::float_listbox * lister; std::size_t item_index_before_selection; }state_; - }; + + + }; //end class drawer_impl //class trigger - trigger::trigger() - : drawer_(new drawer_impl) - {} + trigger::trigger() : + drawer_(new drawer_impl) + { + } - trigger::~trigger() - { - delete drawer_; - } + trigger::~trigger() + { + delete drawer_; + } - drawer_impl& trigger::get_drawer_impl() - { - return *drawer_; - } + drawer_impl& trigger::get_drawer_impl() + { + return *drawer_; + } - const drawer_impl& trigger::get_drawer_impl() const - { - return *drawer_; - } + const drawer_impl& trigger::get_drawer_impl() const + { + return *drawer_; + } - void trigger::attached(widget_reference wdg, graph_reference graph) - { - wdg.bgcolor(colors::white); - drawer_->attached(wdg, graph); + void trigger::attached(widget_reference wdg, graph_reference graph) + { + wdg.bgcolor(colors::white); + drawer_->attached(wdg, graph); - API::effects_edge_nimbus(wdg, effects::edge_nimbus::active); - API::effects_edge_nimbus(wdg, effects::edge_nimbus::over); - } + API::effects_edge_nimbus(wdg, effects::edge_nimbus::active); + API::effects_edge_nimbus(wdg, effects::edge_nimbus::over); + } - void trigger::detached() - { - drawer_->detached(); - } + void trigger::detached() + { + drawer_->detached(); + } - void trigger::refresh(graph_reference) + void trigger::refresh(graph_reference) + { + drawer_->draw(); + } + + void trigger::focus(graph_reference, const arg_focus& arg) + { + drawer_->set_focused(arg.getting); + if(drawer_->widget_ptr()->enabled()) { drawer_->draw(); + drawer_->editor()->reset_caret(); + API::dev::lazy_refresh(); } + } - void trigger::focus(graph_reference, const arg_focus& arg) + void trigger::mouse_enter(graph_reference, const arg_mouse&) + { + drawer_->set_button_state(element_state::hovered, true); + if(drawer_->widget_ptr()->enabled()) { - drawer_->set_focused(arg.getting); - if(drawer_->widget_ptr()->enabled()) + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_leave(graph_reference, const arg_mouse&) + { + drawer_->set_button_state(element_state::normal, true); + drawer_->editor()->mouse_enter(false); + if(drawer_->widget_ptr()->enabled()) + { + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_down(graph_reference, const arg_mouse& arg) + { + //drawer_->set_mouse_press(true); + drawer_->set_button_state(element_state::pressed, false); + if(drawer_->widget_ptr()->enabled()) + { + auto * editor = drawer_->editor(); + editor->mouse_pressed(arg); + drawer_->open_lister_if_push_button_positioned(); + + drawer_->draw(); + if(editor->attr().editable) + editor->reset_caret(); + + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_up(graph_reference, const arg_mouse& arg) + { + if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) + { + drawer_->editor()->mouse_pressed(arg); + drawer_->set_button_state(element_state::hovered, false); + drawer_->draw(); + API::dev::lazy_refresh(); + } + } + + void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) + { + if(drawer_->widget_ptr()->enabled()) + { + bool redraw = drawer_->calc_where(graph, arg.pos.x, arg.pos.y); + redraw |= drawer_->editor()->mouse_move(arg.left_button, arg.pos); + + if(redraw) { drawer_->draw(); drawer_->editor()->reset_caret(); API::dev::lazy_refresh(); } } + } - void trigger::mouse_enter(graph_reference, const arg_mouse&) + void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) + { + if(drawer_->widget_ptr()->enabled()) { - drawer_->set_button_state(element_state::hovered, true); - if(drawer_->widget_ptr()->enabled()) - { - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_leave(graph_reference, const arg_mouse&) - { - drawer_->set_button_state(element_state::normal, true); - drawer_->editor()->mouse_enter(false); - if(drawer_->widget_ptr()->enabled()) - { - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_down(graph_reference, const arg_mouse& arg) - { - //drawer_->set_mouse_press(true); - drawer_->set_button_state(element_state::pressed, false); - if(drawer_->widget_ptr()->enabled()) - { - auto * editor = drawer_->editor(); - editor->mouse_pressed(arg); - drawer_->open_lister_if_push_button_positioned(); - - drawer_->draw(); - if(editor->attr().editable) - editor->reset_caret(); - - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_up(graph_reference, const arg_mouse& arg) - { - if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) - { - drawer_->editor()->mouse_pressed(arg); - drawer_->set_button_state(element_state::hovered, false); - drawer_->draw(); - API::dev::lazy_refresh(); - } - } - - void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) - { - if(drawer_->widget_ptr()->enabled()) - { - bool redraw = drawer_->calc_where(graph, arg.pos.x, arg.pos.y); - redraw |= drawer_->editor()->mouse_move(arg.left_button, arg.pos); - - if(redraw) - { - drawer_->draw(); - drawer_->editor()->reset_caret(); - API::dev::lazy_refresh(); - } - } - } - - void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) - { - if(drawer_->widget_ptr()->enabled()) - { - if(drawer_->has_lister()) - drawer_->scroll_items(arg.upwards); - else - drawer_->move_items(arg.upwards, false); - } - } - - void trigger::key_press(graph_reference, const arg_keyboard& arg) - { - if(!drawer_->widget_ptr()->enabled()) - return; - - bool call_other_keys = false; - if(drawer_->editable()) - { - bool is_move_up = false; - switch(arg.key) - { - case keyboard::os_arrow_left: - case keyboard::os_arrow_right: - drawer_->editor()->respond_key(arg); - 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); - break; - default: - call_other_keys = true; - } - } + if(drawer_->has_lister()) + drawer_->scroll_items(arg.upwards); else - { - bool is_move_up = false; - switch(arg.key) - { - case keyboard::os_arrow_left: - case keyboard::os_arrow_up: - is_move_up = true; - case keyboard::os_arrow_right: - case keyboard::os_arrow_down: - drawer_->move_items(is_move_up, true); - break; - default: - call_other_keys = true; - } - } - if (call_other_keys) - drawer_->editor()->respond_key(arg); - - API::dev::lazy_refresh(); + drawer_->move_items(arg.upwards, false); } + } - void trigger::key_char(graph_reference, const arg_keyboard& arg) + void trigger::key_press(graph_reference, const arg_keyboard& arg) + { + if(!drawer_->widget_ptr()->enabled()) + return; + + bool call_other_keys = false; + if(drawer_->editable()) { - if (drawer_->editor()->respond_char(arg)) - API::dev::lazy_refresh(); + bool is_move_up = false; + switch(arg.key) + { + case keyboard::os_arrow_left: + case keyboard::os_arrow_right: + drawer_->editor()->respond_key(arg); + 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); + break; + default: + call_other_keys = true; + } } + else + { + bool is_move_up = false; + switch(arg.key) + { + case keyboard::os_arrow_left: + case keyboard::os_arrow_up: + is_move_up = true; + case keyboard::os_arrow_right: + case keyboard::os_arrow_down: + drawer_->move_items(is_move_up, true); + break; + default: + call_other_keys = true; + } + } + if (call_other_keys) + drawer_->editor()->respond_key(arg); + + API::dev::lazy_refresh(); + } + + void trigger::key_char(graph_reference, const arg_keyboard& arg) + { + if (drawer_->editor()->respond_char(arg)) + API::dev::lazy_refresh(); + } //end class trigger //class item_proxy diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 93a1d9a1..c83a09f5 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -1,7 +1,7 @@ /* * A Label Control Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,8 +13,9 @@ */ #include -#include #include +#include +#include #include #include #include @@ -80,14 +81,14 @@ namespace nana { traceable_.clear(); - nana::paint::font ft = graph.typeface(); //used for restoring the font + auto pre_font = graph.typeface(); //used for restoring the font const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height; - font_ = ft; + font_ = pre_font; fblock_ = nullptr; - _m_set_default(ft, fgcolor); + _m_set_default(pre_font, fgcolor); _m_measure(graph); @@ -145,7 +146,7 @@ namespace nana rs.pos.y += static_cast(rs.pixels.back().pixels); } - graph.typeface(ft); + graph.typeface(pre_font); } bool find(int x, int y, std::wstring& target, std::wstring& url) const @@ -183,10 +184,10 @@ namespace nana rs.text_align = th; rs.text_align_v = tv; - for(auto i = dstream_.begin(), end = dstream_.end(); i != end; ++i) + for(auto & line: dstream_) { rs.pixels.clear(); - unsigned w = _m_line_pixels(*i, def_line_pixels, rs); + unsigned w = _m_line_pixels(line, def_line_pixels, rs); if(limited && (w > limited)) w = limited; @@ -365,7 +366,8 @@ namespace nana sz.height = max_ascent + max_descent; } - if(w + sz.width <= rs.allowed_width) + //Check if the content is displayed in a new line. + if((0 == rs.allowed_width) || (w + sz.width <= rs.allowed_width)) { w += sz.width; @@ -613,10 +615,13 @@ namespace nana //class trigger //@brief: Draw the label - struct trigger::impl_t + struct trigger::implement { + class measurer; + widget * wd{nullptr}; paint::graphics * graph{nullptr}; + std::unique_ptr msr_ptr{ nullptr }; align text_align{align::left}; align_v text_align_v; @@ -643,16 +648,44 @@ namespace nana std::vector> listener_; }; + class trigger::implement::measurer + : public dev::widget_content_measurer_interface + { + public: + measurer(implement* impl) + : impl_{ impl } + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Label now doesn't support to measure content with a specified height. + if (graph && ((0 == limit_pixels) || limit_width)) + { + return impl_->renderer.measure(graph, limit_pixels, impl_->text_align, impl_->text_align_v); + } + return{}; + } + + size extension() const override + { + return{ 2, 2 }; + } + private: + implement * const impl_; + }; + trigger::trigger() - :impl_(new impl_t) - {} + :impl_(new implement) + { + impl_->msr_ptr.reset(new trigger::implement::measurer{impl_}); + } trigger::~trigger() { delete impl_; } - trigger::impl_t * trigger::impl() const + trigger::implement * trigger::impl() const { return impl_; } @@ -661,6 +694,7 @@ namespace nana { impl_->graph = &graph; impl_->wd = &widget; + API::dev::set_measurer(widget, impl_->msr_ptr.get()); } void trigger::mouse_move(graph_reference, const arg_mouse& arg) diff --git a/source/gui/widgets/picture.cpp b/source/gui/widgets/picture.cpp index bf739693..650f434c 100644 --- a/source/gui/widgets/picture.cpp +++ b/source/gui/widgets/picture.cpp @@ -1,7 +1,7 @@ /* * A Picture Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -16,6 +16,7 @@ #include #include #include +#include namespace nana { @@ -23,11 +24,13 @@ namespace nana { namespace picture { + class content_measurer; + struct implement { widget* wdg_ptr{nullptr}; paint::graphics* graph_ptr{nullptr}; - + std::unique_ptr measurer; struct gradual_bground_tag { @@ -47,9 +50,37 @@ namespace nana }backimg; }; + class content_measurer + : public dev::widget_content_measurer_interface + { + public: + content_measurer(implement* impl) + : impl_{impl} + {} + + optional measure(graph_reference graph, unsigned limit_pixels, bool limit_width) const override + { + //Button doesn't provide a support of vfit and hfit + if (!limit_pixels) + { + if (impl_->backimg.valid_area.empty()) + return impl_->backimg.image.size(); + } + return{}; + } + + size extension() const override + { + return{}; + } + private: + implement* const impl_; + }; + //class drawer drawer::drawer() :impl_(new implement) { + impl_->measurer.reset(new content_measurer{impl_}); } drawer::~drawer() @@ -61,6 +92,7 @@ namespace nana { impl_->wdg_ptr = &wdg; impl_->graph_ptr = &graph; + API::dev::set_measurer(wdg, impl_->measurer.get()); } void drawer::refresh(graph_reference graph) diff --git a/source/stdc++.cpp b/source/stdc++.cpp new file mode 100644 index 00000000..87f99e52 --- /dev/null +++ b/source/stdc++.cpp @@ -0,0 +1,468 @@ +/** +* Standard Library for C++11/14/17 +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) +* +* Distributed under the Boost Software License, Version 1.0. +* (See accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +* +* @file nana/stdc++.cpp +*/ + +#include + +//Implement workarounds for GCC/MinGW which version is below 4.8.2 +#if defined(STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED) +#include +namespace std +{ + int stoi(const std::string& str, std::size_t * pos, int base) + { + auto sptr = str.c_str(); + char *end; + errno = 0; + auto result = std::strtol(sptr, &end, base); + + if (sptr == end) + throw std::invalid_argument("invalid stoi argument"); + if (errno == ERANGE) + throw std::out_of_range("stoi argument out of range"); + + if (pos) + *pos = (std::size_t)(end - sptr); + return ((int)result); + } + + int stoi(const std::wstring& str, std::size_t* pos, int base) + { + auto sptr = str.data(); + wchar_t *end; + errno = 0; + auto result = std::wcstol(sptr, &end, base); + + if (sptr == end) + throw std::invalid_argument("invalid stoi argument"); + if (errno == ERANGE) + throw std::out_of_range("stoi argument out of range"); + + if (pos) + *pos = (std::size_t)(end - sptr); + return ((int)result); + } + using ::strtof; + using ::strtold; + using ::wcstold; + using ::strtoll; + using ::wcstoll; + using ::strtoull; + using ::wcstoull; + + float stof(const std::string& str, std::size_t * pos) + { + auto *ptr = str.data(); + errno = 0; + char *end; + auto result = std::strtof(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stof argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + float stof(const std::wstring& str, std::size_t* pos) + { + auto *ptr = str.data(); + errno = 0; + wchar_t *end; + auto result = std::wcstof(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stof argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + double stod(const std::string& str, std::size_t * pos) + { + auto *ptr = str.data(); + errno = 0; + char *end; + auto result = std::strtod(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stod argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + double stod(const std::wstring& str, std::size_t* pos) + { + auto *ptr = str.data(); + errno = 0; + wchar_t *end; + auto result = std::wcstod(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stod argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + long double stold(const std::string& str, std::size_t * pos) + { + auto *ptr = str.data(); + errno = 0; + char *end; + auto result = std::strtold(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stold argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + long double stold(const std::wstring& str, std::size_t* pos) + { + auto *ptr = str.data(); + errno = 0; + wchar_t *end; + auto result = std::wcstold(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stold argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + long stol(const std::string& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + char *end; + auto result = std::strtol(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stol argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + long stol(const std::wstring& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + wchar_t *end; + auto result = std::wcstol(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stol argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + //Workaround for no implemenation of std::stoll in MinGW. + long long stoll(const std::string& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + char* end; + auto result = std::strtoll(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoll argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + long long stoll(const std::wstring& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + wchar_t* end; + auto result = std::wcstoll(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoll argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + unsigned long long stoull(const std::string& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + char* end; + auto result = std::strtoull(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoull argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + unsigned long long stoull(const std::wstring& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + wchar_t* end; + auto result = std::wcstoull(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoull argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + //Workaround for no implemenation of std::stoul in MinGW. + unsigned long stoul(const std::string& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + char* end; + auto result = std::strtoul(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoul argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } + + unsigned long stoul(const std::wstring& str, std::size_t* pos, int base) + { + auto *ptr = str.data(); + errno = 0; + wchar_t* end; + auto result = std::wcstoul(ptr, &end, base); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stoul argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; + } +}//end namespace std +#endif //STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED + +#ifdef STD_TO_STRING_NOT_SUPPORTED +#include +namespace std +{ + std::string to_string(double v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(long double v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(unsigned v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(int v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(long v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(unsigned long v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(long long v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(unsigned long long v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } + + std::string to_string(float v) + { + std::stringstream ss; + ss << v; + return ss.str(); + } +} +#endif // STD_TO_STRING_NOT_SUPPORTED + +#ifdef STD_TO_WSTRING_NOT_SUPPORTED +#include +namespace std +{ + std::wstring to_wstring(double v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(long double v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(unsigned v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(int v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(long v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(unsigned long v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(long long v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(unsigned long long v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } + + std::wstring to_wstring(float v) + { + std::wstringstream ss; + ss << v; + return ss.str(); + } +} +#endif + +#ifdef _enable_std_put_time +#include +#include +namespace std +{ + //Workaround for no implemenation of std::put_time in gcc < 5. + /* std unspecified return type */ + //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t + //RTSTR put_time(const std::tm* tmb, const CharT* fmt); + + //template< > + std::string put_time/**/(const std::tm* tmb, const char* fmt) + { + std::size_t sz = 200; + std::string str(sz, '\0'); + sz = std::strftime(&str[0], str.size() - 1, fmt, tmb); + str.resize(sz); + return str; + } + //Defined in header + // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); + //template<> + //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt) + //{ + // unsigned sz = 200; + // std::wstring str(sz, L'\0'); + // sz = std::wcsftime(&str[0], str.size() - 1, fmt, tmb); + // str.resize(sz); + // return str; + //} + // http://en.cppreference.com/w/cpp/chrono/c/wcsftime + // Defined in header + // std::size_t wcsftime(wchar_t* str, std::size_t count, const wchar_t* format, const std::tm* time); + // Converts the date and time information from a given calendar time time to a null - terminated + // wide character string str according to format string format.Up to count bytes are written. + // Parameters + // str - pointer to the first element of the wchar_t array for output + // count - maximum number of wide characters to write + // format - pointer to a null - terminated wide character string specifying the format of conversion. + +} +#endif // _enable_std_put_time \ No newline at end of file