From 2bd1cf715f4359d7b4f687062da0c66d5464aaf9 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 5 Feb 2017 08:37:16 +0800 Subject: [PATCH] add fit-content and improve place --- include/nana/gui/detail/basic_window.hpp | 2 + .../widget_content_measurer_interface.hpp | 40 ++ include/nana/gui/programming_interface.hpp | 4 + include/nana/gui/widgets/label.hpp | 6 +- include/nana/optional.hpp | 364 ++++++++++++++ include/nana/stdc++.hpp | 155 ++++++ source/gui/place.cpp | 255 ++++++---- source/gui/programming_interface.cpp | 10 + source/gui/widgets/label.cpp | 42 +- source/stdc++.cpp | 468 ++++++++++++++++++ 10 files changed, 1228 insertions(+), 118 deletions(-) create mode 100644 include/nana/gui/detail/widget_content_measurer_interface.hpp create mode 100644 include/nana/optional.hpp create mode 100644 include/nana/stdc++.hpp create mode 100644 source/stdc++.cpp 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..cbb3cb47 --- /dev/null +++ b/include/nana/gui/detail/widget_content_measurer_interface.hpp @@ -0,0 +1,40 @@ +/* +* Widget Content Measurer Interface +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2016 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 + +namespace nana +{ + namespace dev + { + /// An interface for measuring content of the widget + class widget_content_measurer_interface + { + public: + virtual ~widget_content_measurer_interface() = default; + + /// Measures content + /** + * @param limit_width true if limits the width, false if limits the height. + * @param limit_pixels the number of pixels of the limited edge. If this parameter is zero, it is ignored + * @return the size of content + */ + virtual optional measure(bool limit_width, unsigned limit_pixels) const = 0; + }; + } +} +#endif diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 06ff5716..1b3e6e9d 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); 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/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/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/source/gui/place.cpp b/source/gui/place.cpp index 73f487de..9ba982a8 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 @@ -30,6 +30,7 @@ #include //numeric_limits #include //std::abs #include //std::memset +#include //std::isalpha/std::isalnum #include "place_parts.hpp" @@ -56,18 +57,18 @@ namespace nana enum class token { div_start, div_end, splitter, - identifier, dock, vert, grid, number, array, reparray, + identifier, dock, fit, 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_; } @@ -77,22 +78,22 @@ namespace nana 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_); } @@ -230,11 +231,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,6 +253,8 @@ 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_) @@ -357,7 +360,7 @@ 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)) ++sp; @@ -442,9 +445,9 @@ namespace nana } - inline bool is_idchar(int ch) + inline bool is_idchar(int ch) noexcept { - return ('_' == ch || isalnum(ch)); + return ('_' == ch || std::isalnum(ch)); } std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0) @@ -519,7 +522,6 @@ namespace nana } } - //struct implement struct place::implement { @@ -683,7 +685,6 @@ namespace nana class place::implement::field_dock { - public: div_dockpane * attached{ nullptr }; //attached div object std::unique_ptr dockarea; //the dockable widget @@ -697,7 +698,7 @@ namespace nana 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 +710,53 @@ namespace nana field->attached = nullptr; } + std::pair calc_weight_floor() + { + std::pair floor; + + run_.weight_floor = floor; + + if (this->display) + { + double ratio = 0; + + for (auto & child : children) + { + auto child_floor = child->calc_weight_floor(); + + if(child->weight.kind_of() == number_t::kind::percent) + { + ratio += child->weight.real(); + } + else + { + floor.first += child_floor.first; + floor.second += child_floor.second; + } + } + + auto const vert = (this->div_owner && (this->div_owner->kind_of_division == kind::vertical_arrange)); + + double& fv = (vert ? floor.second : floor.first); + + if(ratio > 0.001) + { + if(fv > 0) + fv = fv / ratio; + } + + if (!this->weight.empty()) + { + if (this->weight.kind_of() != number_t::kind::percent) + fv = this->weight.real(); + } + else + run_.weight_floor = floor; + } + + return floor; + } + void set_visible(bool vsb) { if (field) @@ -769,26 +817,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) { + 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 +874,7 @@ namespace nana return margin.area(field_area); } - division * previous() const + division * previous() const noexcept { if (div_owner) { @@ -822,7 +883,6 @@ namespace nana return child.get(); } return nullptr; - } public: void _m_visible_for_child(division * div, bool vsb) @@ -842,6 +902,7 @@ namespace nana kind kind_of_division; bool display{ true }; bool visible{ true }; + bool fit_content{ false }; ::nana::direction dir{::nana::direction::west}; std::string name; std::vector> children; @@ -855,13 +916,18 @@ namespace nana field_gather * field{ nullptr }; division * div_next{ nullptr }; division * div_owner{ nullptr }; + + struct run_data + { + std::pair weight_floor; + }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)) {} @@ -1089,32 +1155,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 +1184,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 +1199,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 +1206,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 +1225,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 +1240,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 +1248,7 @@ namespace nana dimension.first = dimension.second = 0; } - void revise_collapses() + void revise_collapses() noexcept { if (collapses_.empty()) return; @@ -1424,7 +1446,7 @@ namespace nana 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 +1472,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 +1487,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); } @@ -1528,23 +1550,29 @@ namespace nana 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; + const auto total_pixels = static_cast(left_pixels_ + right_pixels_); +/* + auto left_px = static_cast(left_pixels_) + delta; //deprecated if (left_px > total_pixels) left_px = total_pixels; else if (left_px < 0) left_px = 0; +*/ + auto left_px = std::clamp(static_cast(left_pixels_) + delta, 0, total_pixels); 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); - auto right_px = static_cast(right_pixels_) - delta; + /* + auto right_px = static_cast(right_pixels_) - delta; //deprecated 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); @@ -1579,11 +1607,17 @@ namespace nana 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))); + /* //deprecated 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; @@ -1865,29 +1899,31 @@ 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 v = leaf_right->min_px.get_value(area.w()); //deprecated 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 v = leaf_right->max_px.get_value(area.w()); //deprecated 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; @@ -1914,7 +1950,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 +1958,7 @@ namespace nana this->display = false; } - ~div_dockpane() + ~div_dockpane() noexcept { if (dockable_field) { @@ -2060,7 +2096,6 @@ namespace nana indicator_.graph.release(); } } - } void notify_move_stopped() override @@ -2221,7 +2256,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,7 +2271,7 @@ 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) {} @@ -2458,6 +2493,8 @@ namespace nana if (root_division->field_area.empty()) return; + root_division->calc_weight_floor(); + root_division->collocate(window_handle); for (auto & field : fields) @@ -2509,6 +2546,7 @@ namespace nana std::unique_ptr div; token div_type = token::eof; + bool fit_content = false; //These variables stand for the new division's attributes std::string name; @@ -2534,6 +2572,9 @@ namespace nana div_type = token::dock; break; + case token::fit: + fit_content = true; + break; case token::splitter: //Ignore the splitter when there is not a division. if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division)) @@ -2831,6 +2872,7 @@ namespace nana div->display = !undisplayed; div->visible = !(undisplayed || invisible); + div->fit_content = fit_content; return div; } @@ -2973,6 +3015,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); } }); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 486e6fae..f2761988 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -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); } diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 93a1d9a1..115710e4 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 @@ -613,10 +614,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}; + class measurer * measurer{ nullptr }; align text_align{align::left}; align_v text_align_v; @@ -643,8 +647,28 @@ namespace nana std::vector> listener_; }; + class trigger::implement::measurer + : public dev::widget_content_measurer_interface + { + public: + measurer(implement* impl) + : impl_{ impl } + {} + + optional measure(bool limit_width, unsigned limit_pixels) const override + { + if (impl_->graph) + { + + } + return{}; + } + private: + implement * const impl_; + }; + trigger::trigger() - :impl_(new impl_t) + :impl_(new implement) {} trigger::~trigger() @@ -652,7 +676,7 @@ namespace nana delete impl_; } - trigger::impl_t * trigger::impl() const + trigger::implement * trigger::impl() const { return impl_; } 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