diff --git a/CMakeLists.txt b/CMakeLists.txt index dba9e2c9..242a1ea2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}/include/nana/) if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(NANA_SOURCE_DIR ${CMAKE_SOURCE_DIR}/source) @@ -161,3 +161,5 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) + +set_property( TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14 ) diff --git a/README.md b/README.md index 0b034cf6..dcd10fe6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Run it! All dependencies will be resovled automatically by biicode! Amazing, isn The best way to get help with Nana library is by visiting http://nanapro.org/help.htm +## Sending a Pull Request ? + +This project is encourage you to contribute it through sending a pull request! There is a simple rule, please **don't** directly commit your contributions to the **master** branch. According to your commits, please choose the **hotfixes** branch or the **develop** branch. Thank you! + ## Introduction to the Repository There are two main branches with an infinite lifetime: diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj index 7e216758..9cdc46b0 100644 --- a/build/vc2013/nana.vcxproj +++ b/build/vc2013/nana.vcxproj @@ -67,24 +67,32 @@ - ../bin/vc2013/ + ../bin/ ..\..\include;$(IncludePath) ..\..\source;$(VC_SourcePath); + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ ..\..\include;$(IncludePath) ..\..\source;$(VC_SourcePath); - ../bin/vc2013/ + ../bin/ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ - ../bin/vc2013/ + ../bin/ ..\..\include;$(IncludePath) ..\..\source;$(VC_SourcePath); + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ ..\..\include;$(IncludePath) ..\..\source;$(VC_SourcePath); - ../bin/vc2013/ + ../bin/ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ @@ -102,7 +110,7 @@ true - $(OutDir)\nana_$(ConfigurationName)_$(PlatformShortName).lib + $(TargetPath) @@ -119,7 +127,7 @@ true - $(OutDir)\nana_$(ConfigurationName)_$(PlatformShortName).lib + $(TargetPath) @@ -141,7 +149,7 @@ true - $(OutDir)\nana_$(ConfigurationName)_$(PlatformShortName).lib + $(TargetPath) @@ -162,7 +170,7 @@ true - $(OutDir)\nana_$(ConfigurationName)_$(PlatformShortName).lib + $(TargetPath) @@ -177,6 +185,7 @@ + @@ -247,6 +256,9 @@ + + + diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters index f0f82981..ac073316 100644 --- a/build/vc2013/nana.vcxproj.filters +++ b/build/vc2013/nana.vcxproj.filters @@ -300,5 +300,18 @@ Source Files\nana\gui\widgets + + Source Files\nana\filesystem + + + + + Header Files + + + + + Header Files + \ No newline at end of file diff --git a/build/vc2015/nana.sln b/build/vc2015/nana.sln new file mode 100644 index 00000000..eab31ab5 --- /dev/null +++ b/build/vc2015/nana.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.22823.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nana", "nana.vcxproj", "{25B21068-491B-4A9F-B99F-6C27BF31BAAD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Debug|x64.ActiveCfg = Debug|x64 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Debug|x64.Build.0 = Debug|x64 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Debug|x86.ActiveCfg = Debug|Win32 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Debug|x86.Build.0 = Debug|Win32 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Release|x64.ActiveCfg = Release|x64 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Release|x64.Build.0 = Release|x64 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Release|x86.ActiveCfg = Release|Win32 + {25B21068-491B-4A9F-B99F-6C27BF31BAAD}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/build/vc2015/nana.vcxproj b/build/vc2015/nana.vcxproj new file mode 100644 index 00000000..94b72562 --- /dev/null +++ b/build/vc2015/nana.vcxproj @@ -0,0 +1,266 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {25B21068-491B-4A9F-B99F-6C27BF31BAAD} + Win32Proj + nana + 8.1 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + ../bin/ + ..\..\include;$(IncludePath) + ..\..\source;$(VC_SourcePath); + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + + + ..\..\include;$(IncludePath) + ..\..\source;$(VC_SourcePath); + ../bin/ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + + + ../bin/ + ..\..\include;$(IncludePath) + ..\..\source;$(VC_SourcePath); + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + + + ..\..\include;$(IncludePath) + ..\..\source;$(VC_SourcePath); + ../bin/ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + true + false + + + Windows + true + + + $(TargetPath) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Windows + true + + + $(TargetPath) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + true + + + Windows + true + true + true + + + $(TargetPath) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + true + true + true + + + $(TargetPath) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/vc2015/nana.vcxproj.filters b/build/vc2015/nana.vcxproj.filters new file mode 100644 index 00000000..cb51dab8 --- /dev/null +++ b/build/vc2015/nana.vcxproj.filters @@ -0,0 +1,312 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {b0bd11b1-bcbb-4e05-885e-44295bc1a7bb} + + + {aab16aa3-c8d4-4495-8606-1b21ae739ee5} + + + {c395f107-7102-415b-a019-54e7cf3575af} + + + {e2569be2-9e68-477d-8b59-e248595de6c7} + + + {52ed7f8e-fa48-495e-af1f-4df013205a35} + + + {87d14798-9015-4162-b9ab-72c741cff063} + + + {4f8e7d23-9fe1-4409-bb03-2bd0809e606b} + + + {85c9c1bb-d87b-4481-bf3c-7425f680a12d} + + + {8058b530-86ec-4d72-890d-345aa30db056} + + + {87b124cb-408d-460b-a81b-8a788bbae0d9} + + + {b10db2f1-0542-421a-9e1d-4357e3be5f68} + + + {59f186c8-f5f8-4499-8e19-f278d4754220} + + + {5acf1733-47b2-4872-a105-66c7ad15cd39} + + + {a81fa10e-1274-44e0-92a0-434fa28f89ae} + + + {e95b4a72-643f-4416-af95-b0bbaf7f0c57} + + + + + Source Files\nana\audio\detail + + + Source Files\nana\audio\detail + + + Source Files\nana\audio\detail + + + Source Files\nana\audio + + + Source Files\nana\detail\win32 + + + Source Files\nana\filesystem + + + Source Files\nana\filesystem + + + Source Files\nana\gui\detail\win32 + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\widgets\skeletons + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui\widgets + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana\paint\detail + + + Source Files\nana\paint\detail + + + Source Files\nana\paint + + + Source Files\nana\paint + + + Source Files\nana\paint + + + Source Files\nana\paint + + + Source Files\nana\paint + + + Source Files\nana\paint + + + Source Files\nana\system + + + Source Files\nana\system + + + Source Files\nana\system + + + Source Files\nana\system + + + Source Files\nana\threads + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui\detail + + + Source Files\nana\gui + + + Source Files\nana\gui + + + Source Files\nana + + + Source Files\nana\gui\detail + + + Source Files\nana\gui + + + Source Files\nana\gui\widgets + + + Source Files\nana\filesystem + + + + + Header Files + + + \ No newline at end of file diff --git a/include/nana/audio/player.hpp b/include/nana/audio/player.hpp index 278e0ecc..24378214 100644 --- a/include/nana/audio/player.hpp +++ b/include/nana/audio/player.hpp @@ -4,7 +4,10 @@ #include namespace nana{ namespace audio -{ /// play an audio file in Windows WAV format +{ /// class player + /// \brief play an audio file in PCM Windows WAV format + /// + /// \include audio_player.cpp class player : private nana::noncopyable { diff --git a/include/nana/config.hpp b/include/nana/config.hpp index 77b9005c..6d2cf836 100644 --- a/include/nana/config.hpp +++ b/include/nana/config.hpp @@ -13,6 +13,26 @@ #ifndef NANA_CONFIG_HPP #define NANA_CONFIG_HPP + +#if defined(_MSC_VER) + #define _SCL_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_DEPRECATE + #pragma warning(disable : 4996) + + #if (_MSC_VER < 1900) + // is this a good idea? + #define NOT_IMPLEMENTED_KEYWORD_noexcept + #endif // _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 +#endif // _MSVC + //Select platform automatically #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) //Windows: @@ -35,7 +55,7 @@ #define PLATFORM_SPEC_HPP #define STD_CODECVT_NOT_SUPPORTED #else -# static_assert(false, "Only Windows and Unix are support now"); +# static_assert(false, "Only Windows and Unix are supported now"); #endif #if defined(NANA_MINGW) || defined(NANA_LINUX) diff --git a/include/nana/detail/win32/platform_spec.hpp b/include/nana/detail/win32/platform_spec.hpp index 9e735f99..39f21366 100644 --- a/include/nana/detail/win32/platform_spec.hpp +++ b/include/nana/detail/win32/platform_spec.hpp @@ -55,6 +55,13 @@ namespace detail unsigned ignore; //determinate that pos or size would be ignored. }; + struct map_thread + { + rectangle update_area; + bool ignore_update_area; + bool forced; + }; + enum { tray = 0x501, diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp new file mode 100644 index 00000000..b00760d2 --- /dev/null +++ b/include/nana/filesystem/filesystem.hpp @@ -0,0 +1,469 @@ +/* + * A filesystem Implementation + * Copyright(C) 2003 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: stdex/filesystem/filesystem.hpp + * @description: + * file_iterator is a toolkit for applying each file and directory in a + * specified path. + * Modiffied by Ariel Vina-Rodriguez: + * Now mimic std::experimental::filesystem::v1 (boost v3) + * and need VC2015 or a C++11 compiler. With a few correction will be compiler by VC2013 + */ + +// http://en.cppreference.com/w/cpp/experimental/fs +// http://cpprocks.com/introduction-to-tr2-filesystem-library-in-vs2012/ --- TR2 filesystem in VS2012 +// https://msdn.microsoft.com/en-us/library/hh874694%28v=vs.140%29.aspx --- C++ 14, the header VS2015 +// https://msdn.microsoft.com/en-us/library/hh874694%28v=vs.120%29.aspx --- header VS2013 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf --- last pdf of std draft N4100 2014-07-04 +// http://cplusplus.github.io/filesystem-ts/working-draft.html --- in html format +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4099.html --- in html format +// http://article.gmane.org/gmane.comp.lib.boost.devel/256220 --- The filesystem TS unanimously approved by ISO. +// http://theboostcpplibraries.com/boost.filesystem --- Boost docs +// http://www.boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm --- +// http://www.boost.org/doc/libs/1_34_0/libs/filesystem/doc/index.htm +// http://www.boost.org/doc/libs/1_58_0/boost/filesystem.hpp +// https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x --- Table 1.4. g++ C++ Technical Specifications Implementation Status + +#ifndef NANA_FILESYSTEM_HPP +#define NANA_FILESYSTEM_HPP +#include +#include +#include + +#include + +#ifdef NANA_WINDOWS + #include + typedef HANDLE find_handle_t; +#elif defined(NANA_LINUX) + #include + #include + #include + typedef DIR* find_handle_t; +#endif + + // namespace std { namespace experimental { namespace filesystem { inline namespace v1 { + +namespace nana { namespace experimental +{ +namespace filesystem +{ + enum class file_type + { + none = 0, ///< has not been determined or an error occurred while trying to determine + not_found = -1, ///< Pseudo-type: file was not found. Is not considered an error + regular = 1, + directory = 2 , + symlink =3, ///< Symbolic link file + block =4, ///< Block special file + character= 5 , ///< Character special file + fifo = 6 , ///< FIFO or pipe file + socket =7, + unknown= 8 ///< The file does exist, but is of an operating system dependent type not covered by any of the other + }; + + enum class perms + { + none =0, ///< There are no permissions set for the file. + unknown = 0xFFFF ///< not known, such as when a file_status object is created without specifying the permissions + }; + //enum class copy_options; + //enum class directory_options; + + // class filesystem_error; + enum class error { none = 0 }; // deprecate ?? + + struct attribute // deprecate ?? + { + uintmax_t size {}; + bool directory{}; + tm modified {}; + + attribute() {} ; + attribute( uintmax_t size, bool is_directory) :size{size}, directory{is_directory} {} + }; + + struct space_info + { + uintmax_t capacity; + uintmax_t free; + uintmax_t available; + }; + using file_time_type = std::chrono::time_point< std::chrono::system_clock>;// trivial-clock> ; + + class file_status + { + file_type m_ft = file_type::none; + perms m_prms = perms::unknown; + + public: + explicit file_status(file_type ft = file_type::none, perms prms = perms::unknown) + :m_ft{ft}, m_prms{prms} + {} + + file_status(const file_status& fs) : m_ft{fs.m_ft}, m_prms{fs.m_prms}{} // = default; + file_status(file_status&& fs) : m_ft{fs.m_ft}, m_prms{fs.m_prms}{} // = default; + + ~file_status(){}; + file_status& operator=(const file_status&) = default; + file_status& operator=(file_status&&fs) // = default; + { + m_ft=fs.m_ft; m_prms = fs.m_prms; + return *this; + } + // observers + file_type type() const { return m_ft;} + perms permissions() const { return m_prms;} + // modifiers + void type (file_type ft) { m_ft=ft ;} + void permissions(perms prms) { m_prms = prms; } + }; + + /// concerned only with lexical and syntactic aspects and does not necessarily exist in + /// external storage, and the pathname is not necessarily valid for the current operating system + /// or for a particular file system + /// A sequence of elements that identify the location of a file within a filesystem. + /// The elements are the: + /// rootname (opt), root-directory (opt), and an optional sequence of filenames. + /// The maximum number of elements in the sequence is operating system dependent. + class path + { + public: + path(); + path(const nana::string&); + + bool empty() const; + path root() const; + file_type what() const; + + nana::string filename() const; +#if defined(NANA_WINDOWS) + public: + nana::string to_string() const { return text_; } + operator nana::string() const { return text_; } + private: + nana::string text_; +#else + public: + std::string to_string() const { return text_; } + operator std::string() const { return text_; } + private: + std::string text_; +#endif + }; + + struct directory_entry + { + path m_path; + + attribute attr{}; + //file_status m_status; + + directory_entry(){} + directory_entry(const nana::string& filename_, bool is_directory, uintmax_t size) + :m_path{filename_}, attr{size, is_directory} + {} + + void assign (const path& p){ m_path=p;} + void replace_filename(const path& p){ m_path=p;} + + //file_status status() const; + + operator const path&() const {return m_path;}; + const path& path() const {return m_path;} + + }; + + /// an iterator for a sequence of directory_entry elements representing the files in a directory, not an recursive_directory_iterator + //template + class directory_iterator :public std::iterator + { + public: + using value_type = directory_entry ; + typedef ptrdiff_t difference_type; + typedef const directory_entry* pointer; + typedef const directory_entry& reference; + typedef std::input_iterator_tag iterator_category; + + directory_iterator():end_(true), handle_(nullptr){} + + directory_iterator(const nana::string& file_path) { _m_prepare(file_path); } + //directory_iterator(const path& file_path) { _m_prepare(file_path.filename()); } + + const value_type& + operator*() const { return value_; } + + const value_type* + operator->() const { return &(operator*()); } + + directory_iterator& operator++() + { _m_read(); return *this; } + + directory_iterator operator++(int) + { + directory_iterator tmp = *this; + _m_read(); + return tmp; + } + + bool equal(const directory_iterator& x) const + { + if(end_ && (end_ == x.end_)) return true; + return (value_.path().filename() == x.value_.path().filename()); + } + + + // enable directory_iterator range-based for statements + directory_iterator begin( ) { return *this; } + directory_iterator end( ) { return {}; } + + private: + template + static bool _m_ignore(const Char * p) + { + while(*p == '.') + ++p; + return (*p == 0); + } + + void _m_prepare(const nana::string& file_path) + { + #if defined(NANA_WINDOWS) + path_ = file_path; + auto pat = file_path; + DWORD attr = ::GetFileAttributes(pat.data()); + if((attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY)) + pat += STR("\\*"); + + ::HANDLE handle = ::FindFirstFile(pat.data(), &wfd_); + + if(handle == INVALID_HANDLE_VALUE) + { + end_ = true; + return; + } + + while(_m_ignore(wfd_.cFileName)) + { + if(::FindNextFile(handle, &wfd_) == 0) + { + end_ = true; + ::FindClose(handle); + return; + } + } + + value_ = value_type(wfd_.cFileName, + (FILE_ATTRIBUTE_DIRECTORY & wfd_.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY, + wfd_.nFileSizeLow); + + #elif defined(NANA_LINUX) + path_ = nana::charset(file_path); + if(path_.size() && (path_[path_.size() - 1] != '/')) + path_ += '/'; + find_handle_t handle = opendir(path_.c_str()); + end_ = true; + if(handle) + { + struct dirent * dnt = readdir(handle); + if(dnt) + { + while(_m_ignore(dnt->d_name)) + { + dnt = readdir(handle); + if(dnt == 0) + { + closedir(handle); + return; + } + } + + struct stat fst; + if(stat((path_ + dnt->d_name).c_str(), &fst) == 0) + { + value_ = value_type(nana::charset(dnt->d_name), 0 != S_ISDIR(fst.st_mode), fst.st_size); + } + else + { + value_.m_path = nana::charset(dnt->d_name); + value_.size = 0; + value_.directory = false; + } + end_ = false; + } + } + #endif + if(false == end_) + { + find_ptr_ = std::shared_ptr(new find_handle_t(handle), inner_handle_deleter()); + handle_ = handle; + } + } + + void _m_read() + { + if(handle_) + { + #if defined(NANA_WINDOWS) + if(::FindNextFile(handle_, &wfd_) != 0) + { + while(_m_ignore(wfd_.cFileName)) + { + if(::FindNextFile(handle_, &wfd_) == 0) + { + end_ = true; + return; + } + } + value_ = value_type(wfd_.cFileName, + (FILE_ATTRIBUTE_DIRECTORY & wfd_.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY, + wfd_.nFileSizeLow); + } + else + end_ = true; + #elif defined(NANA_LINUX) + struct dirent * dnt = readdir(handle_); + if(dnt) + { + while(_m_ignore(dnt->d_name)) + { + dnt = readdir(handle_); + if(dnt == 0) + { + end_ = true; + return; + } + } + struct stat fst; + if(stat((path_ + "/" + dnt->d_name).c_str(), &fst) == 0) + value_ = value_type(wfd_.cFileName, + (FILE_ATTRIBUTE_DIRECTORY & wfd_.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY, + wfd_.nFileSizeLow); + else + value_.m_path = nana::charset(dnt->d_name); + } + else + end_ = true; + #endif + } + } + private: + struct inner_handle_deleter + { + void operator()(find_handle_t * handle) + { + if(handle && *handle) + { + #if defined(NANA_WINDOWS) + ::FindClose(*handle); + #elif defined(NANA_LINUX) + ::closedir(*handle); + #endif + } + delete handle; + } + }; + private: + bool end_{false}; + +#if defined(NANA_WINDOWS) + WIN32_FIND_DATA wfd_; + nana::string path_; +#elif defined(NANA_LINUX) + std::string path_; +#endif + std::shared_ptr find_ptr_; + + find_handle_t handle_{nullptr}; + value_type value_; + }; + + + //class recursive_directory_iterator; + //// enable recursive_directory_iterator range-based for statements + //recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; + //recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; + + //template + inline bool operator==(const directory_iterator/**/ & x, const directory_iterator/**/ & y) + { + return x.equal(y); + } + + //template + inline bool operator!=(const directory_iterator/**/ & x, const directory_iterator/**/ & y) + { + return !x.equal(y); + } + + + // file_status status(const path& p); + bool file_attrib(const nana::string& file, attribute&); + + inline bool is_directory(file_status s) { return s.type() == file_type::directory ;} + inline bool is_directory(const path& p) { return directory_iterator{ p }->attr.directory; }//works?? + inline bool is_directory(const directory_entry& d) { return d.attr.directory; } + //bool is_directory(const path& p, error_code& ec) noexcept; + + //bool is_regular_file(file_status s) noexcept; + + inline bool is_empty(const path& p) + { + directory_iterator d(p) ; + return d->attr.directory ? d == directory_iterator() + : d->attr.size == 0; + } + //bool is_empty(const path& p, error_code& ec) noexcept; + + uintmax_t file_size(const nana::string& file); // deprecate? + inline uintmax_t file_size(const path& p){return file_size(p.filename());} + //uintmax_t file_size(const path& p, error_code& ec) noexcept; + //long long filesize(const nana::string& file); + + + bool create_directories(const path& p); + //bool create_directories(const path& p, error_code& ec) noexcept; + bool create_directory(const path& p); + //bool create_directory(const path& p, error_code& ec) noexcept; + bool create_directory(const path& p, const path& attributes); + //bool create_directory(const path& p, const path& attributes, error_code& ec) noexcept; + bool create_directory(const nana::string& dir, bool & if_exist); + inline bool create_directory(const path& p, bool & if_exist) + { + return create_directory(p.filename(), if_exist); + }; + + + bool modified_file_time(const nana::string& file, struct tm&); + + + nana::string path_user(); + + + path current_path(); + //path current_path(error_code& ec); + void current_path(const path& p); + //void current_path(const path& p, error_code& ec) noexcept; + //nana::string path_current(); + + + //bool remove(const path& p); + //bool remove(const path& p, error_code& ec) noexcept; + bool rmfile(const nana::char_t* file); + + //uintmax_t remove_all(const path& p); + //uintmax_t remove_all(const path& p, error_code& ec) noexcept; + bool rmdir(const nana::char_t* dir, bool fails_if_not_empty); + nana::string root(const nana::string& path); + + +}//end namespace filesystem +} //end namespace experimental +}//end namespace nana + +#endif diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index c0ef0aa5..789ab5c3 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -84,6 +84,7 @@ namespace nana end_of_medium = 0x19, //Ctrl+Y substitute = 0x1A, //Ctrl+Z escape = 0x1B, + space = 0x20, //Space //The following names are intuitive name of ASCII control codes select_all = start_of_headline, diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 9be7aa26..599ee379 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -25,6 +25,11 @@ namespace detail { struct basic_window; + enum class visible_state + { + invisible, visible, displayed + }; + class caret_descriptor { public: @@ -43,15 +48,16 @@ namespace detail void size(const ::nana::size&); void update(); - private: - void _m_visible(bool isshow); + //private: + //void _m_visible(bool isshow); //deprecated private: core_window_t* wd_; ::nana::point point_; ::nana::size size_; ::nana::size paint_size_; - bool visible_; - bool real_visible_state_; + visible_state visible_state_; + //bool visible_; + //bool real_visible_state_; //deprecated bool out_of_range_; ::nana::rectangle effective_range_; };//end class caret_descriptor @@ -112,7 +118,9 @@ namespace detail bool is_ancestor_of(const basic_window* wd) const; bool visible_parents() const; + bool displayed() const; bool belong_to_lazy() const; + const basic_window * child_caret() const; //Returns a child which owns a caret bool is_draw_through() const; ///< Determines whether it is a draw-through window. diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index ebf53dbd..91bc71ed 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -44,7 +44,7 @@ namespace detail ~bedrock(); void pump_event(window, bool is_modal); - void map_thread_root_buffer(core_window_t*, bool forced); + void map_thread_root_buffer(core_window_t*, bool forced, const rectangle* update_area = nullptr); static int inc_window(unsigned tid = 0); thread_context* open_thread_context(unsigned tid = 0); thread_context* get_thread_context(unsigned tid = 0); diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index f6948a45..fa62974b 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -23,12 +23,18 @@ namespace nana { class widget; + namespace detail + { + class drawer; + } + class drawer_trigger : ::nana::noncopyable, ::nana::nonmovable { + friend class detail::drawer; public: - typedef widget& widget_reference; - typedef paint::graphics& graph_reference; + using widget_reference = widget&; + using graph_reference = paint::graphics&; virtual ~drawer_trigger(); virtual void attached(widget_reference, graph_reference); //none-const @@ -56,10 +62,11 @@ namespace nana virtual void key_release(graph_reference, const arg_keyboard&); virtual void shortkey(graph_reference, const arg_keyboard&); - void _m_reset_overrided(); - bool _m_overrided() const; private: - bool overrided_{false}; + void _m_reset_overrided(); + bool _m_overrided(event_code) const; + private: + unsigned overrided_{ 0xFFFFFFFF }; }; namespace detail @@ -83,7 +90,7 @@ namespace nana enum class method_state { - unknown, + pending, overrided, not_overrided }; @@ -110,7 +117,7 @@ namespace nana void key_char(const arg_keyboard&); void key_release(const arg_keyboard&); void shortkey(const arg_keyboard&); - void map(window, bool forced); //Copy the root buffer to screen + void map(window, bool forced, const rectangle* update_area = nullptr); //Copy the root buffer to screen void refresh(); drawer_trigger* realizer() const; void attached(widget&, drawer_trigger&); @@ -128,31 +135,27 @@ namespace nana template void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr) { - if (realizer_) + const int pos = static_cast(evt_code); + if (realizer_ && (method_state::not_overrided != mth_state_[pos])) { - const int pos = static_cast(evt_code); - if (method_state::not_overrided != mth_state_[pos]) + _m_bground_pre(); + + if (method_state::pending == mth_state_[pos]) { - _m_bground_pre(); + (realizer_->*mfptr)(graphics, arg); + + //Check realizer, when the window is closed in that event handler, the drawer will be + //detached and realizer will be a nullptr + if(realizer_) + mth_state_[pos] = (realizer_->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided); + } + else + (realizer_->*mfptr)(graphics, arg); - if (method_state::unknown == mth_state_[pos]) - { - realizer_->_m_reset_overrided(); - (realizer_->*mfptr)(graphics, arg); - - //Check realizer, when the window is closed in that event handler, the drawer will be - //detached and realizer will be a nullptr - if(realizer_) - mth_state_[pos] = (realizer_->_m_overrided() ? method_state::overrided : method_state::not_overrided); - } - else - (realizer_->*mfptr)(graphics, arg); - - if (_m_lazy_decleared()) - { - _m_draw_dynamic_drawing_object(); - _m_bground_end(); - } + if (_m_lazy_decleared()) + { + _m_draw_dynamic_drawing_object(); + _m_bground_end(); } } } diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index f44ceb93..2937a1d5 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -13,9 +13,9 @@ namespace nana{ { edge_nimbus_renderer() = default; public: - typedef CoreWindow core_window_t; - typedef window_layout window_layer; - typedef nana::paint::graphics & graph_reference; + using core_window_t = CoreWindow; + using window_layer = window_layout; + using graph_reference = ::nana::paint::graphics&; static edge_nimbus_renderer& instance() { @@ -23,37 +23,69 @@ namespace nana{ return object; } - std::size_t weight() const + unsigned weight() const { return 2; } - bool render(core_window_t * wd, bool forced) + void erase(core_window_t* wd) { - bool rendered = false; + if (effects::edge_nimbus::none == wd->effect.edge_nimbus) + return; + core_window_t * root_wd = wd->root_widget; auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus; - if(nimbus.size()) + for (auto i = nimbus.cbegin(); i != nimbus.cend(); ++i) { - core_window_t * focused = root_wd->other.attribute.root->focus; - native_window_type native = root_wd->root; - std::size_t pixels = weight(); + if (i->window == wd) + { + auto pixels = weight(); + rectangle r{wd->pos_root, wd->dimension}; + r.x -= static_cast(pixels); + r.y -= static_cast(pixels); + r.width += static_cast(pixels << 1); + r.height += static_cast(pixels << 1); + + root_wd->root_graph->paste(root_wd->root, r, r.x, r.y); + + nimbus.erase(i); + break; + } + } + } + + void render(core_window_t * wd, bool forced, const rectangle* update_area = nullptr) + { + bool copy_separately = true; + std::vector> rd_set; + + if (wd->root_widget->other.attribute.root->effects_edge_nimbus.size()) + { + auto root_wd = wd->root_widget; + + auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus; + + auto focused = root_wd->other.attribute.root->focus; + + const unsigned pixels = weight(); auto graph = root_wd->root_graph; - std::vector erase; - std::vector> rd_set; nana::rectangle r; for(auto & action : nimbus) { if(_m_edge_nimbus(focused, action.window) && window_layer::read_visual_rectangle(action.window, r)) { - if(action.window == wd) - rendered = true; + if (action.window == wd) + { + if (update_area) + ::nana::overlap(*update_area, rectangle(r), r); + copy_separately = false; + } //Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered. - if ((forced && (action.window == wd)) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) + if ((forced && (action.window == wd)) || (focused == action.window) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) { rd_set.emplace_back(r, action.window); action.rendered = true; @@ -62,29 +94,36 @@ namespace nana{ else if(action.rendered) { action.rendered = false; - erase.push_back(action.window); + + if (action.window == wd) + copy_separately = false; + + ::nana::rectangle erase_r( + action.window->pos_root.x - static_cast(pixels), + action.window->pos_root.y - static_cast(pixels), + static_cast(action.window->dimension.width + (pixels << 1)), + static_cast(action.window->dimension.height + (pixels << 1)) + ); + + graph->paste(root_wd->root, erase_r, erase_r.x, erase_r.y); } } - - //Erase - for(auto el : erase) - { - if(el == wd) - rendered = true; - - r.x = el->pos_root.x - static_cast(pixels); - r.y = el->pos_root.y - static_cast(pixels); - r.width = static_cast(el->dimension.width + (pixels << 1)); - r.height = static_cast(el->dimension.height + (pixels << 1)); - - graph->paste(native, r, r.x, r.y); - } - - //Render - for (auto & rd : rd_set) - _m_render_edge_nimbus(rd.second, rd.first); } - return rendered; + + if (copy_separately) + { + rectangle vr; + if (window_layer::read_visual_rectangle(wd, vr)) + { + if (update_area) + ::nana::overlap(*update_area, rectangle(vr), vr); + wd->root_graph->paste(wd->root, vr, vr.x, vr.y); + } + } + + //Render + for (auto & rd : rd_set) + _m_render_edge_nimbus(rd.second, rd.first); } private: static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd) @@ -103,8 +142,8 @@ namespace nana{ rectangle good_r; if (overlap(r, rectangle{ wd->root_graph->size() }, good_r)) { - if( (good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) || - (good_r.x + good_r.width > visual.x + visual.width) || (good_r.y + good_r.height > visual.y + visual.height)) + if ((good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) || + (good_r.right() > visual.right()) || (good_r.bottom() > visual.bottom())) { auto graph = wd->root_graph; nana::paint::pixel_buffer pixbuf(graph->handle(), r); diff --git a/include/nana/gui/detail/inner_fwd_implement.hpp b/include/nana/gui/detail/inner_fwd_implement.hpp index 30de2c71..e420c554 100644 --- a/include/nana/gui/detail/inner_fwd_implement.hpp +++ b/include/nana/gui/detail/inner_fwd_implement.hpp @@ -121,7 +121,6 @@ namespace nana{ { core_window_t* pressed{nullptr}; //The handle to a window which is being pressed core_window_t* hovered{nullptr}; //the latest window that mouse moved - bool tabstop_focus_changed{false}; //KeyDown may set it true, if it is true KeyChar will ignore the message }condition; root_misc(core_window_t * wd, unsigned width, unsigned height) diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index c86dd821..c4dfb59e 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -70,13 +70,11 @@ namespace detail std::vector stack_; }; public: - typedef native_window_type native_window; - typedef revertible_mutex mutex_type; + using native_window = native_window_type; + using mutex_type = revertible_mutex; - typedef basic_window core_window_t; - typedef std::vector cont_type; - - typedef window_layout wndlayout_type; + using core_window_t = basic_window; + using window_layer = window_layout; window_manager(); ~window_manager(); @@ -126,9 +124,9 @@ namespace detail core_window_t* root(native_window_type) const; //Copy the root buffer that wnd specified into DeviceContext - void map(core_window_t*, bool forced); + void map(core_window_t*, bool forced, const rectangle* update_area = nullptr); - bool update(core_window_t*, bool redraw, bool force); + bool update(core_window_t*, bool redraw, bool force, const rectangle* update_area = nullptr); void refresh_tree(core_window_t*); bool do_lazy_refresh(core_window_t*, bool force_copy_to_screen); diff --git a/include/nana/gui/screen.hpp b/include/nana/gui/screen.hpp index ef9126e8..06c344ee 100644 --- a/include/nana/gui/screen.hpp +++ b/include/nana/gui/screen.hpp @@ -34,27 +34,38 @@ namespace nana virtual const ::nana::rectangle& workarea() const = 0; }; + /// Provides some functions to get the metrics of the monitors \include screen.cpp class screen { struct implement; public: - static ::nana::size desktop_size(); - static ::nana::size primary_monitor_size(); + /// gets the size in pixel of the whole virtual desktop + static ::nana::size desktop_size(); + + /// gets the resolution in pixel of the primary monitor, + /// if there is only one monitor installed in the system, + /// the return value of primary_monitor_size is equal to desktop_size's. + static ::nana::size primary_monitor_size(); + screen(); /// Reload has no preconditions, it's safe to call on moved-from void reload(); - /// Returns the number of display monitors + /// Returns the number of display monitors installed in the system std::size_t count() const; + /// gets the display monitor that contains the specified point display& from_point(const point&); + + /// gets the display monitor that contains the specified window display& from_window(window); display& get_display(std::size_t index) const; display& get_primary() const; + /// applies a given function to all display monitors void for_each(std::function) const; private: std::shared_ptr impl_; diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index eac5ec29..3661b8a3 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -166,9 +166,13 @@ namespace nana std::size_t pos_{0}; }; - //struct essence_t - //@brief: this struct gives many data for listbox, - // the state of the struct does not effect on member funcions, therefore all data members are public. + + + typedef std::vector selection; + + /// struct essence_t + ///@brief: this struct gives many data for listbox, + /// the state of the struct does not effect on member funcions, therefore all data members are public. struct essence_t; struct category_t; @@ -371,13 +375,17 @@ namespace nana return iter; } - void append(std::initializer_list); + /// Appends one item at the end of this category with the specifies text in the column fields + void append(std::initializer_list); size_type columns() const; cat_proxy& text(nana::string); nana::string text() const; + cat_proxy & select(bool); + bool selected() const; + /// Behavior of a container void push_back(nana::string); @@ -480,16 +488,54 @@ namespace nana color_proxy header_grabbed{ static_cast(0x8BD6F6)}; color_proxy header_floated{ static_cast(0xBABBBC)}; color_proxy item_selected{ static_cast(0xD5EFFC) }; + + unsigned max_header_width{3000}, /// \todo how to implement some geometrical parameters ?? + ext_w = 5; }; } }//end namespace drawerbase -/*! \brief A rectangle containing a list of strings from which the user can select. This widget contain a list of \a categories, with in turn contain a list of \a items. +/*! \class listbox +\brief A rectangle containing a list of strings from which the user can select. This widget contain a list of \a categories, with in turn contain a list of \a items. A category is a text with can be \a selected, \a checked and \a expanded to show the items. An item is formed by \a column-fields, each corresponding to one of the \a headers. An item can be \a selected and \a checked. -The user can \a drag the header to \a reisize it or to \a reorganize it. -By \a clicking on a header the list get \a reordered, first up, and then down alternatively. +The user can \a drag the header to \a resize it or to \a reorganize it. +By \a clicking on one header the list get \a reordered, first up, and then down alternatively. + +1. The resolver is used to resolute an object of the specified type for a listbox item. +3. nana::listbox creates the category 0 by default. The member functions without the categ parameter operate the items that belong to category 0. +4. A sort compare is used for sorting the items. It is a strict weak ordering comparer that must meet the requirement: + Irreflexivity (comp(x, x) returns false) + and + antisymmetry(comp(a, b) != comp(b, a) returns true) + A simple example. + bool sort_compare( const nana::string& s1, nana::any*, + const nana::string& s2, nana::any*, bool reverse) + { + return (reverse ? s1 > s2 : s1 < s2); + } + listbox.set_sort_compare(0, sort_compare); + The listbox supports attaching a customer's object for each item, therefore the items can be + sorted by comparing these customer's object. + bool sort_compare( const nana::string&, nana::any* o1, + const nana::string&, nana::any* o2, bool reverse) + { + if(o1 && o2) //some items may not attach a customer object. + { + int * i1 = o1->get(); + int * i2 = o2->get(); + return (i1 && i2 && (reverse ? *i1 > *i2 : *i1 < *i2)); + ;//some types may not be int. + } + return false; + } + listbox.anyobj(0, 0, 10); //the type of customer's object is int. + listbox.anyobj(0, 0, 20); +\todo doc: actualize this example listbox.at(0)... +\see nana::drawerbase::listbox::cat_proxy +\see nana::drawerbase::listbox::item_proxy +\example listbox_Resolver.cpp */ class listbox : public widget_object, @@ -512,14 +558,17 @@ By \a clicking on a header the list get \a reordered, first up, and then down al listbox(window, bool visible); listbox(window, const rectangle& = {}, bool visible = true); - void auto_draw(bool); ///); ///); ///< Appends categories at the end cat_proxy insert(cat_proxy, nana::string); cat_proxy at(size_type pos) const; diff --git a/include/nana/gui/widgets/menu.hpp b/include/nana/gui/widgets/menu.hpp index 883760cf..512d30d7 100644 --- a/include/nana/gui/widgets/menu.hpp +++ b/include/nana/gui/widgets/menu.hpp @@ -108,7 +108,7 @@ namespace nana virtual void background(graph_reference, window) = 0; virtual void item(graph_reference, const nana::rectangle&, const attr&) = 0; - virtual void item_image(graph_reference, const nana::point&, const paint::image&) = 0; + virtual void item_image(graph_reference, const nana::point&, unsigned image_px, const paint::image&) = 0; virtual void item_text(graph_reference, const nana::point&, const nana::string&, unsigned text_pixels, const attr&) = 0; virtual void sub_arrow(graph_reference, const nana::point&, unsigned item_pixels, const attr&) = 0; }; diff --git a/include/nana/gui/widgets/progress.hpp b/include/nana/gui/widgets/progress.hpp index 91e58ab7..f7697d7e 100644 --- a/include/nana/gui/widgets/progress.hpp +++ b/include/nana/gui/widgets/progress.hpp @@ -30,6 +30,8 @@ namespace nana unsigned Max(unsigned); void unknown(bool); bool unknown() const; + bool stop(bool s = true); + bool stoped() const; private: void attached(widget_reference, graph_reference) override; void refresh(graph_reference) override; @@ -45,6 +47,7 @@ namespace nana nana::paint::graphics* graph_{nullptr}; unsigned draw_width_{static_cast(-1)}; bool unknown_{false}; + bool stop_{false}; unsigned max_{100}; unsigned value_{0}; }; //end class drawer @@ -67,6 +70,8 @@ namespace nana unsigned amount(unsigned value); void unknown(bool); bool unknown() const; + bool stop(bool s=true); ///< request stop or cancel and return previus stop status + bool stoped() const; }; }//end namespace nana #endif diff --git a/include/nana/gui/widgets/toolbar.hpp b/include/nana/gui/widgets/toolbar.hpp index efb8af74..0b097676 100644 --- a/include/nana/gui/widgets/toolbar.hpp +++ b/include/nana/gui/widgets/toolbar.hpp @@ -14,7 +14,6 @@ #define NANA_GUI_WIDGET_TOOLBAR_HPP #include "widget.hpp" -#include namespace nana { @@ -39,10 +38,10 @@ namespace nana basic_event selected; ///< A mouse click on a control button. basic_event enter; ///< The mouse enters a control button. basic_event leave; ///< The mouse leaves a control button. - }; struct item_type; + class item_container; class drawer : public drawer_trigger @@ -50,15 +49,12 @@ namespace nana struct drawer_impl_type; public: - typedef std::size_t size_type; + using size_type = std::size_t; drawer(); ~drawer(); - void append(const nana::string&, const nana::paint::image&); - void append(); - bool enable(size_type) const; - bool enable(size_type, bool); + item_container& items() const; void scale(unsigned); private: void refresh(graph_reference) override; @@ -69,32 +65,28 @@ namespace nana void mouse_down(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse&) override; private: - size_type _m_which(int x, int y, bool want_if_disabled) const; - void _m_draw_background(const ::nana::color&); - void _m_draw(); - void _m_owner_sized(const arg_resized&); - private: - void _m_fill_pixels(item_type*, bool force); + size_type _m_which(point, bool want_if_disabled) const; + void _m_calc_pixels(item_type*, bool force); private: ::nana::toolbar* widget_; - ::nana::paint::graphics* graph_; drawer_impl_type* impl_; }; }//end namespace toolbar }//end namespace drawerbase + /// Control bar that contains buttons for controlling class toolbar : public widget_object { public: - typedef std::size_t size_type; ///< A type to count the number of elements. + using size_type = std::size_t; ///< A type to count the number of elements. toolbar() = default; toolbar(window, bool visible); toolbar(window, const rectangle& = rectangle(), bool visible = true); - void append(); ///< Adds a separator. + void separate(); ///< Adds a separator. void append(const nana::string& text, const nana::paint::image& img); ///< Adds a control button. void append(const nana::string& text); ///< Adds a control button. bool enable(size_type index) const; diff --git a/source/audio/detail/audio_stream.cpp b/source/audio/detail/audio_stream.cpp index 96def3d9..998b85bf 100644 --- a/source/audio/detail/audio_stream.cpp +++ b/source/audio/detail/audio_stream.cpp @@ -8,7 +8,8 @@ namespace nana{ namespace audio //class audio_stream bool audio_stream::open(const nana::string& file) { - fs_.open(static_cast(nana::charset(file)), std::ios::binary); + std::string fname{nana::charset(file)};//static_cast() + fs_.open(fname, std::ios::binary); if(fs_) { wave_spec::master_riff_chunk riff; diff --git a/source/charset.cpp b/source/charset.cpp index c4a0f256..7e952503 100644 --- a/source/charset.cpp +++ b/source/charset.cpp @@ -799,7 +799,7 @@ namespace nana switch(utf_x_) { case unicode::utf8: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) strbuf = detail::utf8_to_utf16(data_, true); detail::put_utf16char(strbuf, 0, true); #else @@ -808,7 +808,7 @@ namespace nana #endif break; case unicode::utf16: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) strbuf = data_; detail::put_utf16char(strbuf, 0, true); #else @@ -817,7 +817,7 @@ namespace nana #endif break; case unicode::utf32: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) strbuf = detail::utf32_to_utf16(data_); detail::put_utf16char(strbuf, 0, true); #else @@ -907,21 +907,21 @@ namespace nana switch(utf_x_) { case unicode::utf8: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) bytes = detail::utf8_to_utf16(data_, true); #else bytes = detail::utf8_to_utf32(data_, true); #endif break; case unicode::utf16: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) bytes = data_; #else bytes = detail::utf16_to_utf32(data_); #endif break; case unicode::utf32: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) bytes = detail::utf32_to_utf16(data_); #else bytes = data_; @@ -984,19 +984,19 @@ namespace nana switch(encoding) { case unicode::utf8: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) return detail::utf16_to_utf8(std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t))); #else return detail::utf32_to_utf8(std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t))); #endif case unicode::utf16: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) return std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t)); #else return detail::utf32_to_utf16(std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t))); #endif case unicode::utf32: -#if defined(NANA_MINGW) +#if defined(NANA_WINDOWS) return detail::utf16_to_utf32(std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t))); #else return std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t)); diff --git a/source/detail/linux_X11/platform_spec.cpp b/source/detail/linux_X11/platform_spec.cpp index f53c046b..c8ccf682 100644 --- a/source/detail/linux_X11/platform_spec.cpp +++ b/source/detail/linux_X11/platform_spec.cpp @@ -44,7 +44,7 @@ namespace detail bool conf::open(const char* file) { ifs_.open(file); - return static_cast(ifs_ != 0); + return static_cast(ifs_); } std::string conf::value(const char* key) diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp new file mode 100644 index 00000000..4c6e8d22 --- /dev/null +++ b/source/filesystem/filesystem.cpp @@ -0,0 +1,444 @@ +/* + * A FileSystem Utility Implementation + * Copyright(C) 2003-2013 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/filesystem/filesystem.cpp + * @description: + * provide some interface for file managment + */ + +#include +#include +#if defined(NANA_WINDOWS) + #include + + #if defined(NANA_MINGW) + #ifndef _WIN32_IE + #define _WIN32_IE 0x0500 + #endif + #endif + + #include + #include +#elif defined(NANA_LINUX) + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace nana { + namespace experimental + { + namespace filesystem + { + //Because of No wide character version of POSIX +#if defined(NANA_LINUX) + typedef std::string string_t; + const char* splstr = "/\\"; +#else + typedef nana::string string_t; + const nana::char_t* splstr = STR("/\\"); +#endif + //class path + path::path() {} + + path::path(const nana::string& text) +#if defined(NANA_WINDOWS) + : text_(text) + { +#else + :text_(nana::charset(text)) + { +#endif + auto pos = text_.find_last_of(splstr); + for (; (pos != string_t::npos) && (pos + 1 == text_.size()); pos = text_.find_last_of(splstr)) + text_.erase(pos); + } + + bool path::empty() const + { +#if defined(NANA_WINDOWS) + return (::GetFileAttributes(text_.c_str()) == INVALID_FILE_ATTRIBUTES); +#elif defined(NANA_LINUX) + struct stat sta; + return (::stat(text_.c_str(), &sta) == -1); +#endif + } + + path path::root() const + { +#if defined(NANA_WINDOWS) + return path(filesystem::root(text_)); +#elif defined(NANA_LINUX) + return path(filesystem::root(nana::charset(text_))); +#endif + } + + file_type path::what() const + { +#if defined(NANA_WINDOWS) + unsigned long attr = ::GetFileAttributes(text_.c_str()); + if (INVALID_FILE_ATTRIBUTES == attr) + return file_type::not_found; //?? + + if (FILE_ATTRIBUTE_DIRECTORY & attr) + return file_type::directory; + + return file_type::regular; +#elif defined(NANA_LINUX) + struct stat sta; + if (-1 == ::stat(text_.c_str(), &sta)) + return file_type::not_found; //?? + + if ((S_IFDIR & sta.st_mode) == S_IFDIR) + return file_type::directory; + + if ((S_IFREG & sta.st_mode) == S_IFREG) + return file_type::regular; + + return file_type::none; +#endif + } + + nana::string path::filename() const + { + string_t::size_type pos = text_.find_last_of(splstr); +#if defined(NANA_WINDOWS) + return text_.substr(pos + 1); +#else + return nana::charset(text_.substr(pos + 1)); +#endif + } + //end class path + + namespace detail + { + //rm_dir_recursive + //@brief: remove a directory, if it is not empty, recursively remove it's subfiles and sub directories + bool rm_dir_recursive(nana::string&& dir) + { + std::vector files; + nana::string path = dir; + path += '\\'; + + std::copy(directory_iterator(dir), directory_iterator(), std::back_inserter(files)); + + for (auto & f : files) + { + if (f.attr.directory) + rm_dir_recursive(path + f.path().filename()); + else + rmfile((path + f.path().filename()).c_str()); + } + + return rmdir(dir.c_str(), true); + } + + bool mkdir_helper(const nana::string& dir, bool & if_exist) + { +#if defined(NANA_WINDOWS) + if (::CreateDirectory(dir.c_str(), 0)) + { + if_exist = false; + return true; + } + + if_exist = (::GetLastError() == ERROR_ALREADY_EXISTS); +#elif defined(NANA_LINUX) + if (0 == ::mkdir(static_cast(nana::charset(dir)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) + { + if_exist = false; + return true; + } + + if_exist = (errno == EEXIST); +#endif + return false; + } + +#if defined(NANA_WINDOWS) + void filetime_to_c_tm(FILETIME& ft, struct tm& t) + { + FILETIME local_file_time; + if (::FileTimeToLocalFileTime(&ft, &local_file_time)) + { + SYSTEMTIME st; + ::FileTimeToSystemTime(&local_file_time, &st); + t.tm_year = st.wYear - 1900; + t.tm_mon = st.wMonth - 1; + t.tm_mday = st.wDay; + t.tm_wday = st.wDayOfWeek - 1; + t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay); + + t.tm_hour = st.wHour; + t.tm_min = st.wMinute; + t.tm_sec = st.wSecond; + } + } +#endif + }//end namespace detail + + bool file_attrib(const nana::string& file, attribute& attr) + { +#if defined(NANA_WINDOWS) + WIN32_FILE_ATTRIBUTE_DATA fad; + if (::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &fad)) + { + LARGE_INTEGER li; + li.u.LowPart = fad.nFileSizeLow; + li.u.HighPart = fad.nFileSizeHigh; + attr.size = li.QuadPart; + attr.directory = (0 != (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + detail::filetime_to_c_tm(fad.ftLastWriteTime, attr.modified); + return true; + } +#elif defined(NANA_LINUX) + struct stat fst; + if (0 == ::stat(static_cast(nana::charset(file)).c_str(), &fst)) + { + attr.bytes = fst.st_size; + attr.is_directory = (0 != (040000 & fst.st_mode)); + attr.modified = *(::localtime(&fst.st_ctime)); + return true; + } +#endif + return false; + } + + uintmax_t file_size(const nana::string& file) + { +#if defined(NANA_WINDOWS) + //Some compilation environment may fail to link to GetFileSizeEx + typedef BOOL(__stdcall *GetFileSizeEx_fptr_t)(HANDLE, PLARGE_INTEGER); + GetFileSizeEx_fptr_t get_file_size_ex = reinterpret_cast(::GetProcAddress(::GetModuleHandleA("Kernel32.DLL"), "GetFileSizeEx")); + if (get_file_size_ex) + { + HANDLE handle = ::CreateFile(file.c_str(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (INVALID_HANDLE_VALUE != handle) + { + LARGE_INTEGER li; + if (!get_file_size_ex(handle, &li)) + li.QuadPart = 0; + + ::CloseHandle(handle); + return li.QuadPart; + } + } + return 0; +#elif defined(NANA_LINUX) + FILE * stream = ::fopen(static_cast(nana::charset(file)).c_str(), "rb"); + long long size = 0; + if (stream) + { + fseeko64(stream, 0, SEEK_END); + size = ftello64(stream); + fclose(stream); + } + return size; +#endif + } + + bool modified_file_time(const nana::string& file, struct tm& t) + { +#if defined(NANA_WINDOWS) + WIN32_FILE_ATTRIBUTE_DATA attr; + if (::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &attr)) + { + FILETIME local_file_time; + if (::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &local_file_time)) + { + SYSTEMTIME st; + ::FileTimeToSystemTime(&local_file_time, &st); + t.tm_year = st.wYear - 1900; + t.tm_mon = st.wMonth - 1; + t.tm_mday = st.wDay; + t.tm_wday = st.wDayOfWeek - 1; + t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay); + + t.tm_hour = st.wHour; + t.tm_min = st.wMinute; + t.tm_sec = st.wSecond; + return true; + } + } +#elif defined(NANA_LINUX) + struct stat attr; + if (0 == ::stat(static_cast(nana::charset(file)).c_str(), &attr)) + { + t = *(::localtime(&attr.st_ctime)); + return true; + } +#endif + return false; + } + + bool create_directory(const nana::string& path, bool & if_exist) + { + if_exist = false; + if (path.size() == 0) return false; + + nana::string root; +#if defined(NANA_WINDOWS) + if (path.size() > 3 && path[1] == STR(':')) + root = path.substr(0, 3); +#elif defined(NANA_LINUX) + if (path[0] == STR('/')) + root = '/'; +#endif + bool mkstat = false; + std::size_t beg = root.size(); + + while (true) + { + beg = path.find_first_not_of(STR("/\\"), beg); + if (beg == path.npos) + break; + + std::size_t pos = path.find_first_of(STR("/\\"), beg + 1); + if (pos != path.npos) + { + root += path.substr(beg, pos - beg); + + mkstat = detail::mkdir_helper(root, if_exist); + if (mkstat == false && if_exist == false) + return false; + +#if defined(NANA_WINDOWS) + root += STR('\\'); +#elif defined(NANA_LINUX) + root += STR('/'); +#endif + } + else + { + if (beg + 1 < path.size()) + { + root += path.substr(beg); + mkstat = detail::mkdir_helper(root, if_exist); + } + break; + } + beg = pos + 1; + } + return mkstat; + } + + bool rmfile(const nana::char_t* file) + { +#if defined(NANA_WINDOWS) + bool ret = false; + if (file) + { + ret = (::DeleteFile(file) == TRUE); + if (!ret) + ret = (ERROR_FILE_NOT_FOUND == ::GetLastError()); + } + + return ret; +#elif defined(NANA_LINUX) + if (std::remove(static_cast(nana::charset(file)).c_str())) + return (errno == ENOENT); + return true; +#endif + } + + bool rmdir(const nana::char_t* dir, bool fails_if_not_empty) + { + bool ret = false; + if (dir) + { +#if defined(NANA_WINDOWS) + ret = (::RemoveDirectory(dir) == TRUE); + if (!fails_if_not_empty && (::GetLastError() == ERROR_DIR_NOT_EMPTY)) + ret = detail::rm_dir_recursive(dir); +#elif defined(NANA_LINUX) + std::string mbstr = nana::charset(dir); + if (::rmdir(mbstr.c_str())) + { + if (!fails_if_not_empty && (errno == EEXIST || errno == ENOTEMPTY)) + ret = detail::rm_dir_recursive(dir); + } + else + ret = true; +#endif + } + return ret; + } + + nana::string root(const nana::string& path) + { + std::size_t index = path.size(); + + if (index) + { + const nana::char_t * str = path.c_str(); + + for (--index; index > 0; --index) + { + nana::char_t c = str[index]; + if (c != '\\' && c != '/') + break; + } + + for (--index; index > 0; --index) + { + nana::char_t c = str[index]; + if (c == '\\' || c == '/') + break; + } + } + + return index ? path.substr(0, index + 1) : nana::string(); + } + + nana::string path_user() + { +#if defined(NANA_WINDOWS) + nana::char_t path[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, path))) + return path; +#elif defined(NANA_LINUX) + const char * s = ::getenv("HOME"); + if (s) + return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8); +#endif + return nana::string(); + } + + path current_path() + { +#if defined(NANA_WINDOWS) + nana::char_t buf[MAX_PATH]; + DWORD len = ::GetCurrentDirectory(MAX_PATH, buf); + if (len) + { + if (len > MAX_PATH) + { + nana::char_t * p = new nana::char_t[len + 1]; + ::GetCurrentDirectory(len + 1, p); + nana::string s = p; + delete[] p; + return s; + } + return buf; + } +#elif defined(NANA_LINUX) + const char * s = ::getenv("PWD"); + if (s) + return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8); +#endif + return nana::string(); + } + }//end namespace filesystem + } //end namespace experimental +}//end namespace nana diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 7948564b..4b6e1ecd 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -7,7 +7,7 @@ namespace nana { //class caret_descriptor caret_descriptor::caret_descriptor(core_window_t* wd, unsigned width, unsigned height) - :wd_(wd), size_(width, height), visible_(false), real_visible_state_(false), out_of_range_(false) + :wd_(wd), size_(width, height), visible_state_(visible_state::invisible), out_of_range_(false) {} caret_descriptor::~caret_descriptor() @@ -22,8 +22,9 @@ namespace nana if(active) { native_interface::caret_create(wd_->root, size_); - real_visible_state_ = false; - visible_ = false; + //real_visible_state_ = false; + //visible_ = false; + visible_state_ = visible_state::invisible; this->position(point_.x, point_.y); } else @@ -76,19 +77,36 @@ namespace nana return point_; } - void caret_descriptor::visible(bool isshow) + void caret_descriptor::visible(bool is_show) { - if(visible_ != isshow) + /* + if(visible_ != isshow) //deprecated { visible_ = isshow; if(visible_ == false || false == out_of_range_) _m_visible(isshow); } + */ + + auto pre_displayed = (visible_state::displayed == visible_state_); + + if (is_show) + { + visible_state_ = visible_state::visible; + if (wd_->displayed() && (! out_of_range_)) + visible_state_ = visible_state::displayed; + } + else + visible_state_ = visible_state::invisible; + + if (pre_displayed != (visible_state::displayed == visible_state_)) + native_interface::caret_visible(wd_->root, !pre_displayed); } bool caret_descriptor::visible() const { - return visible_; + //return visible_; //deprecated + return (visible_state::invisible != visible_state_); } nana::size caret_descriptor::size() const @@ -101,17 +119,21 @@ namespace nana size_ = s; update(); - if(visible_) this->visible(true); + //if(visible_) this->visible(true); //deprecated + if (visible_state::invisible != visible_state_) + visible(true); } + /* void caret_descriptor::_m_visible(bool isshow) { - if(real_visible_state_ != isshow) + if(real_visible_state_ != isshow) //deprecated { real_visible_state_ = isshow; - native_interface::caret_visible(wd_->root, isshow); + native_interface::caret_visible(wd_->root, isshow && wd_->displayed()); } } + */ void caret_descriptor::update() { @@ -138,8 +160,10 @@ namespace nana { out_of_range_ = true; - if(visible_) - _m_visible(false); + //if(visible_) + // _m_visible(false); //deprecated + if (visible_state::invisible != visible_state_) + visible(false); } } else @@ -164,19 +188,27 @@ namespace nana if(out_of_range_) { - if(paint_size_ == size) - _m_visible(true); + //if(paint_size_ == size) //deprecated + // _m_visible(true); + if (paint_size_ == size) + visible(true); out_of_range_ = false; } if(paint_size_ != size) { + bool vs = (visible_state::invisible != visible_state_); native_interface::caret_destroy(wd_->root); native_interface::caret_create(wd_->root, size); - real_visible_state_ = false; - if(visible_) - _m_visible(true); + //real_visible_state_ = false; //deprecated + //if(visible_) + // _m_visible(true); + + visible_state_ = visible_state::invisible; + if (vs) + visible(true); + paint_size_ = size; } @@ -281,6 +313,11 @@ namespace nana return true; } + bool basic_window::displayed() const + { + return (visible && visible_parents()); + } + bool basic_window::belong_to_lazy() const { for (auto wd = this; wd; wd = wd->parent) @@ -291,6 +328,26 @@ namespace nana return false; } + const basic_window* get_child_caret(const basic_window* wd, bool this_is_a_child) + { + if (this_is_a_child && wd->together.caret) + return wd; + + for (auto child : wd->children) + { + auto caret_wd = get_child_caret(child, true); + if (caret_wd) + return caret_wd; + } + + return nullptr; + } + + const basic_window * basic_window::child_caret() const + { + return get_child_caret(this, false); + } + bool basic_window::is_draw_through() const { if (::nana::category::flags::root == this->other.category) diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index c102c20a..64d1d354 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -93,6 +93,18 @@ namespace nana arg.window_handle = reinterpret_cast(wd); if (emit(event_code::expose, wd, arg, false, get_thread_context())) { + const core_window_t * caret_wd = (wd->together.caret ? wd : wd->child_caret()); + if (caret_wd) + { + if (exposed) + { + if (wd->root_widget->other.attribute.root->focus == caret_wd) + caret_wd->together.caret->visible(true); + } + else + caret_wd->together.caret->visible(false); + } + if (!exposed) { if (category::flags::root != wd->other.category) @@ -118,7 +130,7 @@ namespace nana arg.x = x; arg.y = y; if (emit(event_code::move, wd, arg, false, get_thread_context())) - wd_manager.update(wd, true, true); + wd_manager.update(wd, false, true); } } diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index fe5695b7..084219d0 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -34,99 +34,99 @@ namespace nana void drawer_trigger::resizing(graph_reference, const arg_resizing&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::resizing)); } void drawer_trigger::resized(graph_reference graph, const arg_resized&) { - overrided_ = true; + overrided_ |= (1 << static_cast(event_code::resized)); this->refresh(graph); detail::bedrock::instance().thread_context_lazy_refresh(); } void drawer_trigger::move(graph_reference, const arg_move&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::move)); } void drawer_trigger::click(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::click)); } void drawer_trigger::dbl_click(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::dbl_click)); } void drawer_trigger::mouse_enter(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_enter)); } void drawer_trigger::mouse_move(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_move)); } void drawer_trigger::mouse_leave(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_leave)); } void drawer_trigger::mouse_down(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_down)); } void drawer_trigger::mouse_up(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_up)); } void drawer_trigger::mouse_wheel(graph_reference, const arg_wheel&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_wheel)); } void drawer_trigger::mouse_dropfiles(graph_reference, const arg_dropfiles&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_drop)); } void drawer_trigger::focus(graph_reference, const arg_focus&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::focus)); } void drawer_trigger::key_press(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_press)); } void drawer_trigger::key_char(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_char)); } void drawer_trigger::key_release(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_release)); } void drawer_trigger::shortkey(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::shortkey)); } void drawer_trigger::_m_reset_overrided() { - overrided_ = true; + overrided_ = 0xFFFFFFFF; } - bool drawer_trigger::_m_overrided() const + bool drawer_trigger::_m_overrided(event_code evt_code) const { - return overrided_; + return 0 != (overrided_ & (1 << static_cast(evt_code))); } //end class drawer_trigger @@ -240,12 +240,12 @@ namespace nana _m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey); } - void drawer::map(window wd, bool forced) //Copy the root buffer to screen + void drawer::map(window wd, bool forced, const rectangle* update_area) //Copy the root buffer to screen { if(wd) { - bedrock_type::core_window_t* iwd = reinterpret_cast(wd); - bedrock_type::core_window_t * caret_wd = iwd->root_widget->other.attribute.root->focus; + auto iwd = reinterpret_cast(wd); + auto caret_wd = iwd->root_widget->other.attribute.root->focus; bool owns_caret = (caret_wd && (caret_wd->together.caret) && (caret_wd->together.caret->visible())); @@ -262,12 +262,7 @@ namespace nana #endif } - if (false == edge_nimbus_renderer_t::instance().render(iwd, forced)) - { - nana::rectangle vr; - if(bedrock_type::window_manager_t::wndlayout_type::read_visual_rectangle(iwd, vr)) - iwd->root_graph->paste(iwd->root, vr, vr.x, vr.y); - } + edge_nimbus_renderer_t::instance().render(iwd, forced, update_area); if(owns_caret) { @@ -302,9 +297,10 @@ namespace nana void drawer::attached(widget& wd, drawer_trigger& realizer) { for (auto i = std::begin(mth_state_), end = std::end(mth_state_); i != end; ++i) - *i = method_state::unknown; + *i = method_state::pending; realizer_ = &realizer; + realizer._m_reset_overrided(); realizer.attached(wd, graphics); } diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index a8df88ac..cedd1a41 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -152,7 +152,7 @@ namespace detail delete impl_; } - void bedrock::map_thread_root_buffer(core_window_t*, bool forced) + void bedrock::map_thread_root_buffer(core_window_t*, bool forced, const rectangle*) { //GUI in X11 is thread-independent, so no implementation. } @@ -921,11 +921,10 @@ namespace detail if(msgwnd->visible && (msgwnd->root_graph->empty() == false)) { nana::detail::platform_scope_guard psg; - nana::detail::drawable_impl_type* drawer_impl = msgwnd->root_graph->handle(); - ::XCopyArea(display, drawer_impl->pixmap, reinterpret_cast(native_window), drawer_impl->context, - xevent.xexpose.x, xevent.xexpose.y, - xevent.xexpose.width, xevent.xexpose.height, - xevent.xexpose.x, xevent.xexpose.y); + //Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed. + ::nana::rectangle update_area(xevent.xexpose.x, xevent.xexpose.y, xevent.xexpose.width, xevent.xexpose.height); + if (!update_area.empty()) + msgwnd->drawer.map(reinterpret_cast(msgwnd), true, &update_area); } break; case KeyPress: @@ -1011,13 +1010,11 @@ namespace detail { arg_keyboard argkey; brock.get_key_state(argkey); - auto tstop_wd = brock.wd_manager.tabstop(msgwnd, argkey.shift); + auto tstop_wd = brock.wd_manager.tabstop(msgwnd, !argkey.shift); if (tstop_wd) { brock.wd_manager.set_focus(tstop_wd, false); - brock.wd_manager.do_lazy_refresh(msgwnd, false); brock.wd_manager.do_lazy_refresh(tstop_wd, true); - root_runtime->condition.tabstop_focus_changed = true; } } else if(keyboard::alt == keychar) @@ -1066,6 +1063,7 @@ namespace detail break; } case XLookupChars: + if (msgwnd->flags.enabled) { const ::nana::char_t* charbuf; #if defined(NANA_UNICODE) @@ -1082,6 +1080,10 @@ namespace detail arg.ignore = false; arg.key = charbuf[i]; + // When tab is pressed, only tab-eating mode is allowed + if ((keyboard::tab == arg.key) && !(msgwnd->flags.tab & tab_type::eating)) + continue; + if(context.is_alt_pressed) { arg.ctrl = arg.shift = false; @@ -1130,7 +1132,9 @@ namespace detail brock.get_key_state(arg); brock.emit(event_code::key_release, msgwnd, arg, true, &context); } - brock.delay_restore(2); //Restores while key release + + if (context.platform.keychar < keyboard::os_arrow_left || keyboard::os_arrow_down < wParam) + brock.delay_restore(2); //Restores while key release } else { diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index 994bd7f4..78609ac0 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -328,9 +328,14 @@ namespace detail return bedrock_object; } - void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced) + void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced, const rectangle* update_area) { - ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), static_cast(forced ? TRUE : FALSE)); + auto stru = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(detail::messages::map_thread))); + if (stru) + { + if (FALSE == ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), reinterpret_cast(stru))) + ::HeapFree(::GetProcessHeap(), 0, stru); + } } void interior_helper_for_menu(MSG& msg, native_window_type menu_window) @@ -348,7 +353,7 @@ namespace detail void bedrock::pump_event(window modal_window, bool is_modal) { const unsigned tid = ::GetCurrentThreadId(); - thread_context * context = this->open_thread_context(tid); + auto context = this->open_thread_context(tid); if(0 == context->window_count) { //test if there is not a window @@ -581,8 +586,12 @@ namespace detail } return true; case nana::detail::messages::map_thread_root_buffer: - bedrock.wd_manager.map(reinterpret_cast(wParam), (TRUE == lParam)); - ::UpdateWindow(wd); + { + auto stru = reinterpret_cast(lParam); + bedrock.wd_manager.map(reinterpret_cast(wParam), stru->forced, (stru->ignore_update_area ? nullptr : &stru->update_area)); + ::UpdateWindow(wd); + ::HeapFree(::GetProcessHeap(), 0, stru); + } return true; case nana::detail::messages::remote_thread_move_window: { @@ -1282,13 +1291,11 @@ namespace detail ::PAINTSTRUCT ps; ::HDC dc = ::BeginPaint(root_window, &ps); - if((ps.rcPaint.left != ps.rcPaint.right) && (ps.rcPaint.bottom != ps.rcPaint.top)) - { - ::BitBlt(dc, - ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, - reinterpret_cast(msgwnd->root_graph->handle()->context), - ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); - } + //Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed. + ::nana::rectangle update_area(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top); + if (!update_area.empty()) + msgwnd->drawer.map(reinterpret_cast(msgwnd), true, &update_area); + ::EndPaint(root_window, &ps); } break; @@ -1372,7 +1379,7 @@ namespace detail if(msgwnd) { - if((wParam == 9) && (false == (msgwnd->flags.tab & tab_type::eating))) //Tab + if((wParam == 9) && (!msgwnd->visible || (false == (msgwnd->flags.tab & tab_type::eating)))) //Tab { bool is_forward = (::GetKeyState(VK_SHIFT) >= 0); @@ -1382,7 +1389,6 @@ namespace detail brock.wd_manager.set_focus(tstop_wd, false); brock.wd_manager.do_lazy_refresh(msgwnd, false); brock.wd_manager.do_lazy_refresh(tstop_wd, true); - root_runtime->condition.tabstop_focus_changed = true; } } else @@ -1411,9 +1417,10 @@ namespace detail break; case WM_CHAR: msgwnd = brock.focus(); - if(false == root_runtime->condition.tabstop_focus_changed) + if (msgwnd && msgwnd->flags.enabled) { - if(msgwnd && msgwnd->flags.enabled) + // When tab is pressed, only tab-eating mode is allowed + if ((9 != wParam) || (msgwnd->flags.tab & tab_type::eating)) { arg_keyboard arg; arg.evt_code = event_code::key_char; @@ -1429,8 +1436,6 @@ namespace detail brock.wd_manager.do_lazy_refresh(msgwnd, false); } } - else - root_runtime->condition.tabstop_focus_changed = false; return 0; case WM_KEYUP: if(wParam != 18) //MUST NOT BE AN ALT @@ -1450,7 +1455,10 @@ namespace detail else brock.set_keyboard_shortkey(false); - brock.delay_restore(2); //Restores while key release + //Do delay restore if key is not arrow_left/right/up/down, otherwise + //A menubar will be restored if the item is empty(not have a menu item) + if (wParam < 37 || 40 < wParam) + brock.delay_restore(2); //Restores while key release break; case WM_CLOSE: { diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index e8cd1f60..c2edb173 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -24,7 +24,7 @@ namespace nana //class window_layout void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed) { - if (wd->flags.refreshing) + if (wd->flags.refreshing || wd->drawer.graphics.empty()) return; if (nullptr == wd->effect.bground) @@ -61,7 +61,6 @@ namespace nana if (wd->parent) { std::vector blocks; - blocks.reserve(10); if (read_overlaps(wd, vr, blocks)) { nana::point p_src; @@ -101,7 +100,7 @@ namespace nana // The result is a rectangle that is a visible area for its ancesters. bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual) { - if (false == wd->visible) return false; + if (! wd->displayed()) return false; visual = rectangle{ wd->pos_root, wd->dimension }; @@ -356,7 +355,7 @@ namespace nana nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension); for (auto wd : data_sect.effects_bground_windows) { - if (wd == sigwd || !wd->visible || !wd->visible_parents() || + if (wd == sigwd || !wd->displayed() || (false == overlap(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd))) continue; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index f6053bce..d87168ef 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -357,21 +357,26 @@ namespace detail //@brief: Delete the window handle void window_manager::destroy(core_window_t* wd) { - core_window_t* parent = nullptr; + //Thread-Safe Required! + std::lock_guard lock(mutex_); + if (impl_->wd_register.available(wd) == false) return; + + rectangle update_area(wd->pos_owner, wd->dimension); + + auto parent = wd->parent; + if (parent) + utl::erase(parent->children, wd); + + _m_destroy(wd); + + while (parent && (parent->other.category == ::nana::category::flags::lite_widget)) { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - if (impl_->wd_register.available(wd) == false) return; - - if (wd->parent) - { - parent = wd->parent; - utl::erase(wd->parent->children, wd); - } - - _m_destroy(wd); + update_area.x += parent->pos_owner.x; + update_area.y += parent->pos_owner.y; + parent = parent->parent; } - update(parent, false, false); + + update(parent, false, false, &update_area); } //destroy_handle @@ -408,35 +413,34 @@ namespace detail { //Thread-Safe Required! std::lock_guard lock(mutex_); - if (impl_->wd_register.available(wd)) + if (!impl_->wd_register.available(wd)) + return false; + + if(visible != wd->visible) { - if(visible != wd->visible) + native_window_type nv = nullptr; + switch(wd->other.category) { - native_window_type nv = nullptr; - switch(wd->other.category) - { - case category::root_tag::value: - nv = wd->root; break; - case category::frame_tag::value: - nv = wd->other.attribute.frame->container; break; - default: //category::widget_tag, category::lite_widget_tag - break; - } - - if(visible && wd->effect.bground) - wndlayout_type::make_bground(wd); - - //Don't set the visible attr of a window if it is a root. - //The visible attr of a root will be set in the expose event. - if(category::root_tag::value != wd->other.category) - bedrock::instance().event_expose(wd, visible); - - if(nv) - native_interface::show_window(nv, visible, wd->flags.take_active); + case category::root_tag::value: + nv = wd->root; break; + case category::frame_tag::value: + nv = wd->other.attribute.frame->container; break; + default: //category::widget_tag, category::lite_widget_tag + break; } - return true; + + if(visible && wd->effect.bground) + window_layer::make_bground(wd); + + //Don't set the visible attr of a window if it is a root. + //The visible attr of a root will be set in the expose event. + if(category::flags::root != wd->other.category) + bedrock::instance().event_expose(wd, visible); + + if(nv) + native_interface::show_window(nv, visible, wd->flags.take_active); } - return false; + return true; } window_manager::core_window_t* window_manager::find_window(native_window_type root, int x, int y) @@ -474,9 +478,6 @@ namespace detail wd->pos_owner.y = y; _m_move_core(wd, delta); - if(wd->together.caret && wd->together.caret->visible()) - wd->together.caret->update(); - auto &brock = bedrock::instance(); arg_move arg; arg.window_handle = reinterpret_cast(wd); @@ -503,7 +504,7 @@ namespace detail auto & brock = bedrock::instance(); bool moved = false; const bool size_changed = (r.width != wd->dimension.width || r.height != wd->dimension.height); - if(wd->other.category != category::root_tag::value) + if(category::flags::root != wd->other.category) { //Move child widgets if(r.x != wd->pos_owner.x || r.y != wd->pos_owner.y) @@ -514,9 +515,6 @@ namespace detail _m_move_core(wd, delta); moved = true; - if(wd->together.caret && wd->together.caret->visible()) - wd->together.caret->update(); - arg_move arg; arg.window_handle = reinterpret_cast(wd); arg.x = r.x; @@ -625,7 +623,7 @@ namespace detail if(wd->effect.bground && wd->parent) { wd->other.glass_buffer.make(sz); - wndlayout_type::make_bground(wd); + window_layer::make_bground(wd); } } } @@ -657,7 +655,7 @@ namespace detail } //Copy the root buffer that wnd specified into DeviceContext - void window_manager::map(core_window_t* wd, bool forced) + void window_manager::map(core_window_t* wd, bool forced, const rectangle* update_area) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -665,12 +663,12 @@ namespace detail { //Copy the root buffer that wd specified into DeviceContext #if defined(NANA_LINUX) - wd->drawer.map(reinterpret_cast(wd), forced); + wd->drawer.map(reinterpret_cast(wd), forced, update_area); #elif defined(NANA_WINDOWS) if(nana::system::this_thread_id() == wd->thread_id) - wd->drawer.map(reinterpret_cast(wd), forced); + wd->drawer.map(reinterpret_cast(wd), forced, update_area); else - bedrock::instance().map_thread_root_buffer(wd, forced); + bedrock::instance().map_thread_root_buffer(wd, forced, update_area); #endif } } @@ -679,25 +677,25 @@ namespace detail //@brief: update is used for displaying the screen-off buffer. // Because of a good efficiency, if it is called in an event procedure and the event procedure window is the // same as update's, update would not map the screen-off buffer and just set the window for lazy refresh - bool window_manager::update(core_window_t* wd, bool redraw, bool forced) + bool window_manager::update(core_window_t* wd, bool redraw, bool forced, const rectangle* update_area) { //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) == false) return false; - if (wd->visible && wd->visible_parents()) + if (wd->displayed()) { if(forced || (false == wd->belong_to_lazy())) { if (!wd->flags.refreshing) { - wndlayout_type::paint(wd, redraw, false); - this->map(wd, forced); + window_layer::paint(wd, redraw, false); + this->map(wd, forced, update_area); return true; } } - else if(redraw) - wndlayout_type::paint(wd, true, false); + else if (redraw) + window_layer::paint(wd, true, false); if (wd->other.upd_state == core_window_t::update_state::lazy) wd->other.upd_state = core_window_t::update_state::refresh; @@ -711,8 +709,8 @@ namespace detail std::lock_guard lock(mutex_); //It's not worthy to redraw if visible is false - if (impl_->wd_register.available(wd) && wd->visible && wd->visible_parents()) - wndlayout_type::paint(wd, true, true); + if (impl_->wd_register.available(wd) && wd->displayed()) + window_layer::paint(wd, true, true); } //do_lazy_refresh @@ -733,18 +731,19 @@ namespace detail { if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen) { - wndlayout_type::paint(wd, false, false); + window_layer::paint(wd, false, false); this->map(wd, force_copy_to_screen); } else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) { //Update the nimbus effect - using nimbus_renderer = detail::edge_nimbus_renderer; - nimbus_renderer::instance().render(wd, force_copy_to_screen); + //using nimbus_renderer = detail::edge_nimbus_renderer; //deprecated + //nimbus_renderer::instance().render(wd, true); + this->map(wd, true); } } else - wndlayout_type::paint(wd, true, false); //only refreshing if it has an invisible parent + window_layer::paint(wd, true, false); //only refreshing if it has an invisible parent } wd->other.upd_state = core_window_t::update_state::none; return true; @@ -764,7 +763,7 @@ namespace detail result.make(wd->drawer.graphics.size()); result.bitblt(0, 0, wd->drawer.graphics); - wndlayout_type::paste_children_to_graphics(wd, result); + window_layer::paste_children_to_graphics(wd, result); return true; } @@ -773,7 +772,7 @@ namespace detail //Thread-Safe Required! std::lock_guard lock(mutex_); return (impl_->wd_register.available(wd) ? - wndlayout_type::read_visual_rectangle(wd, r) : + window_layer::read_visual_rectangle(wd, r) : false); } @@ -1002,6 +1001,39 @@ namespace detail } } + + // preconditions of get_tabstop: tabstop is not empty and at least one window is visible + window_manager::core_window_t* get_tabstop(window_manager::core_window_t* wd, bool forward) + { + auto & tabs = wd->root_widget->other.attribute.root->tabstop; + + if (forward) + { + if (detail::tab_type::none == wd->flags.tab) + return (tabs.front()); + else if (detail::tab_type::tabstop & wd->flags.tab) + { + auto end = tabs.cend(); + auto i = std::find(tabs.cbegin(), end, wd); + if (i != end) + { + ++i; + window_manager::core_window_t* ts = (i != end ? (*i) : tabs.front()); + return (ts != wd ? ts : 0); + } + else + return tabs.front(); + } + } + else if (tabs.size() > 1) //at least 2 elments in tabs are required when moving backward. + { + auto i = std::find(tabs.cbegin(), tabs.cend(), wd); + if (i != tabs.cend()) + return (tabs.cbegin() == i ? tabs.back() : *(i - 1)); + } + return nullptr; + } + auto window_manager::tabstop(core_window_t* wd, bool forward) const -> core_window_t* { //Thread-Safe Required! @@ -1013,21 +1045,30 @@ namespace detail if (tabs.empty()) return nullptr; - if ((detail::tab_type::none == wd->flags.tab) || !(detail::tab_type::tabstop & wd->flags.tab)) - return (forward ? tabs.front() : tabs.back()); - - auto i = std::find(tabs.cbegin(), tabs.cend(), wd); - if (tabs.cend() == i) - return (forward ? tabs.front() : tabs.back()); - - if (forward) + bool precondition = false; + for (auto & tab_wd : tabs) { - ++i; - core_window_t* ts = (i != tabs.cend() ? (*i) : tabs.front()); - return (ts != wd ? ts : nullptr); + if (tab_wd->displayed()) + { + precondition = true; + break; + } } - - return (tabs.cbegin() == i ? tabs.back() : *(i - 1)); + + if (precondition) + { + auto new_stop = get_tabstop(wd, forward); + + while (new_stop && (wd != new_stop)) + { + if (new_stop->flags.enabled && new_stop->displayed()) + return new_stop; + + new_stop = get_tabstop(new_stop, forward); + } + } + + return nullptr; } void window_manager::remove_trash_handle(unsigned tid) @@ -1040,7 +1081,7 @@ namespace detail //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd)) - return wndlayout_type::enable_effects_bground(wd, enabled); + return window_layer::enable_effects_bground(wd, enabled); return false; } @@ -1224,18 +1265,9 @@ namespace detail if (!established) { - if (effects::edge_nimbus::none != wd->effect.edge_nimbus) - { - auto & cont = root_attr->effects_edge_nimbus; - for (auto i = cont.begin(); i != cont.end(); ++i) - { - if (i->window == wd) - { - cont.erase(i); - break; - } - } - } + //remove the window from edge nimbus effect when it is destroying + using edge_nimbus = detail::edge_nimbus_renderer; + edge_nimbus::instance().erase(wd); } else if (pa_root_attr != root_attr) { @@ -1347,7 +1379,7 @@ namespace detail _m_disengage(wd, nullptr); - wndlayout_type::enable_effects_bground(wd, false); + window_layer::enable_effects_bground(wd, false); wd->drawer.detached(); wd->widget_notifier->destroy(); @@ -1371,7 +1403,12 @@ namespace detail if(wd->other.category != category::root_tag::value) //A root widget always starts at (0, 0) and its childs are not to be changed { wd->pos_root += delta; - if(wd->other.category == category::frame_tag::value) + if (category::flags::frame != wd->other.category) + { + if (wd->together.caret && wd->together.caret->visible()) + wd->together.caret->update(); + } + else native_interface::move_window(wd->other.attribute.frame->container, wd->pos_root.x, wd->pos_root.y); for (auto child : wd->children) diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 1ed89214..327ffff9 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -722,7 +722,7 @@ namespace API if(restrict::window_manager.available(iwd) && (iwd->flags.enabled != enabled)) { iwd->flags.enabled = enabled; - restrict::window_manager.update(iwd, true, false); + restrict::window_manager.update(iwd, true, true); if(category::flags::root == iwd->other.category) restrict::interface_type::enable_window(iwd->root, enabled); } diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index f1c24564..a43c47b4 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -151,7 +151,7 @@ namespace nana{ namespace drawerbase void trigger::key_char(graph_reference, const arg_keyboard& arg) { - if(arg.key == static_cast(keyboard::enter)) + if (static_cast(keyboard::enter) == arg.key || static_cast(keyboard::space) == arg.key) emit_click(); } diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 5f8498f7..9dd59ad1 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -18,6 +18,8 @@ #include #include +#include + namespace nana { arg_combox::arg_combox(combox& wdg): widget(wdg) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index a83485bf..2507c2e7 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -402,18 +402,20 @@ namespace nana return{}; } - void create(nana::string&& text, unsigned pixels) + size_type create(nana::string&& text, unsigned pixels) { cont_.emplace_back(std::move(text), pixels, static_cast(cont_.size())); + return cont_.back().index; } void item_width(size_type pos, unsigned width) { for(auto & m : cont_) - { if(m.index == pos) + { m.pixels = width; - } + return; + } } unsigned item_width(size_type pos) const @@ -1606,31 +1608,7 @@ namespace nana } /// set all items in cat to selection sel, emiting events, actualizing last_selected_abs, but not check for single_selection_ - bool categ_selected(size_type cat, bool sel) - { - bool changed = false; - auto & items = _m_at(cat)->items; - - index_pair pos(cat, 0); - for(auto & m : items) - { - if(m.flags.selected != sel) - { - m.flags.selected = sel; - - arg_listbox arg{ item_proxy(ess_, pos), sel }; - wd_ptr()->events().selected.emit(arg); - changed = true; - - if (sel) // not check for single_selection_ - last_selected_abs = pos; - else if (last_selected_abs == pos) - last_selected_abs.set_both(npos); - } - ++pos.item; - } - return changed; - } + void categ_selected(size_type cat, bool sel); void reverse_categ_selected(size_type categ) { @@ -1658,9 +1636,22 @@ namespace nana /// absolute position of the last displayed item index_pair last_displ() const { - return absolute ( last_displ() ); + return absolute ( last() ); } + /// can be used as the absolute position of the first absolute item, or as the display pos of the first displayed item + index_pair first() const + { + index_pair fst{0,npos}; + good_item(fst,fst); + return fst; + } + /// absolute position of the first displayed item + index_pair first_displ() const + { + return absolute ( first() ); + } + bool good(size_type cat) const { return (cat < list_.size()); @@ -1670,32 +1661,32 @@ namespace nana { return ((pos.cat < list_.size()) && (pos.item < size_item(pos.cat))); } - + /// if good return the same item (in arg item), or just the next cat and true, but If fail return false bool good_item(index_pair pos, index_pair& item) const { if (!good(pos.cat)) - return false; + return false; // cat out of range if (pos.is_category()) { - item = pos; - if (0 == pos.cat) - item.item = 0; - - return true; + item = pos; // return the cat self + if (0 == pos.cat) // but for cat 0 return first item + item.item = 0; // let check this is good + else + return true; } - auto i = _m_at(pos.cat); + auto i = _m_at(pos.cat); // pos is not a cat and i point to it cat if (pos.item < i->items.size()) { - item = pos; + item = pos; // good item, return it return true; } - if (++i == list_.end()) + if (++i == list_.end()) // item out of range and no more cat return false; - item.cat = pos.cat + 1; + item.cat = pos.cat + 1; // select the next cat item.item = npos; return true; } @@ -1703,7 +1694,7 @@ namespace nana ///Translate relative position (position in display) into absolute position (original data order) size_type absolute(const index_pair& display_pos) const { - if(sorted_index_ == npos) + if(sorted_index_ == npos || display_pos.item == npos) return display_pos.item ; auto & catobj = *_m_at(display_pos.cat); @@ -2202,7 +2193,7 @@ namespace nana } else { - new_where.second = (y - header_visible_px() + 1) / item_size; + new_where.second = ((y + 1) - header_visible_px()) / item_size; // y>1 ! new_where.first = parts::lister; if(checkable) { @@ -2324,6 +2315,27 @@ namespace nana } } + unsigned auto_width(size_type pos, unsigned max = 3000) /// \todo introduce parametr max_header_width + { + unsigned max_w{ 0 }; + for (const auto &cat : lister.cat_container()) + for (const auto &it : cat.items) + { + if (pos >= it.cells.size()) continue; + // precalcule text geometry + unsigned ts = static_cast (graph->text_extent_size(it.cells[pos].text).width); + if (max_w < ts) + max_w = ts; + } + if (!max_w) return 0; + + unsigned ext_w = scheme_ptr->ext_w; + if (pos == 0 && checkable) // only before the first column (display_order=0 ?) + ext_w += 18; + header.item_width(pos, max_w + ext_w + 1 < max ? max_w + ext_w + 1 : max); + return max_w; + } + inline_pane * open_inline(pat::abstract_factory* factory, inline_indicator* indicator) { std::unique_ptr pane_ptr; @@ -2494,7 +2506,7 @@ namespace nana } } - nana::string es_lister::to_string(const export_options& exp_opt) const + nana::string es_lister::to_string(const export_options& exp_opt) const { nana::string list_str; bool first{true}; @@ -2515,6 +2527,17 @@ namespace nana return list_str ; } + void es_lister::categ_selected(size_type cat, bool sel) + { + cat_proxy cpx{ess_,cat}; + for (item_proxy &it : cpx ) + { + if (it.selected() != sel) + it.select(sel); + } + + last_selected_abs = last_selected_dpl = index_pair {cat, npos}; + } class drawer_header_impl { @@ -3450,6 +3473,14 @@ namespace nana void trigger::dbl_click(graph_reference graph, const arg_mouse&) { + if (essence_->pointer_where.first == essence_t::parts::header) + if (cursor::size_we == essence_->lister.wd_ptr()->cursor()) + { + if (essence(). auto_width(drawer_header_->item_spliter() )) // ? in order + essence().update(); + return; + } + if (essence_->pointer_where.first != essence_t::parts::lister) return; @@ -3518,21 +3549,35 @@ namespace nana if (! scrl.make_page_scroll(!up)) return; essence_->lister.select_for_all(false); - if (up) - item_proxy {essence_, essence_->scroll_y_abs()}.select(true); - else - { - index_pair idx{essence_->scroll_y_dpl()}; + + index_pair idx{essence_->scroll_y_dpl()}; + if (!up) essence_->lister.forward(idx, scrl.range()-1, idx); - item_proxy::from_display(essence_,idx).select(true); - } + + if (idx.is_item()) + item_proxy::from_display(essence_, idx).select(true); + else + if(!essence_->lister.single_selection()) + essence_->lister.categ_selected(idx.cat, true); + + essence_->trace_last_selected_item (); + break; } case keyboard::os_home: + { essence_->lister.select_for_all(false); - item_proxy::from_display(essence_, {0,0}).select(true); + + index_pair frst{essence_->lister.first()}; + if (frst.is_item()) + item_proxy::from_display(essence_, frst).select(true); + else + if(!essence_->lister.single_selection()) + essence_->lister.categ_selected(frst.cat, true); + essence_->trace_last_selected_item (); break; + } case keyboard::os_end: essence_->lister.select_for_all(false); item_proxy::from_display(essence_, essence_->lister.last()).select(true); @@ -3584,7 +3629,7 @@ namespace nana { auto i = ess_->lister.cat_container().begin(); std::advance(i, pos.cat); - cat_ = &(*i); + cat_ = &(*i); // what is pos is a cat? } } @@ -3617,6 +3662,7 @@ namespace nana m.flags.checked = ck; arg_listbox arg{*this, ck}; ess_->lister.wd_ptr()->events().checked.emit(arg); + ess_->update(); } return *this; } @@ -3629,7 +3675,7 @@ namespace nana /// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected item_proxy & item_proxy::select(bool s) { - auto & m = cat_->items.at(pos_.item); // a ref to the real item + auto & m = cat_->items.at(pos_.item); // a ref to the real item // what is pos is a cat? if(m.flags.selected == s) return *this; // ignore if no change m.flags.selected = s; // actually change selection @@ -3646,6 +3692,7 @@ namespace nana ess_->update(); + ess_->update(); return *this; } @@ -3865,6 +3912,24 @@ namespace nana } } + cat_proxy & cat_proxy::select(bool sel) + { + for (item_proxy &it : *this ) + it.select(sel); + + ess_->lister.last_selected_abs = + ess_->lister.last_selected_dpl = index_pair {this->pos_, npos}; + + return *this; + } + bool cat_proxy::selected() const + { + for (item_proxy &it : *this ) + if (!it.selected()) + return false; + return true; + } + auto cat_proxy::columns() const -> size_type { return ess_->header.cont().size(); @@ -3907,6 +3972,11 @@ namespace nana //Behavior of a container item_proxy cat_proxy::begin() const { + auto i = ess_->lister.cat_container().begin(); + std::advance(i, pos_); + if (i->items.empty()) + return end(); + return item_proxy(ess_, index_pair(pos_, 0)); } @@ -4121,11 +4191,12 @@ namespace nana _m_ess().set_auto_draw(ad); } - void listbox::append_header(nana::string text, unsigned width) + listbox::size_type listbox::append_header(nana::string text, unsigned width) { auto & ess = _m_ess(); - ess.header.create(std::move(text), width); + listbox::size_type index = ess.header.create(std::move(text), width); ess.update(); + return index; } listbox& listbox::header_width(size_type pos, unsigned pixels) @@ -4135,6 +4206,13 @@ namespace nana ess.update(); return *this; } + unsigned listbox::auto_width(size_type pos, unsigned max) + { + auto & ess = _m_ess(); + unsigned max_w = ess.auto_width(pos, max); + ess.update(); + return max_w; + } unsigned listbox::header_width(size_type pos) const { diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 9f6ec24c..8691c67f 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -114,7 +114,7 @@ namespace nana { if(at.item_state == state::active) { - graph.rectangle(r, false, {0xa8, 0xd8, 0xeb}); + graph.rectangle(r, false, static_cast(0xa8d8eb)); nana::point points[4] = { nana::point(r.x, r.y), nana::point(r.x + r.width - 1, r.y), @@ -144,9 +144,9 @@ namespace nana } } - void item_image(graph_reference graph, const nana::point& pos, const paint::image& img) + void item_image(graph_reference graph, const nana::point& pos, unsigned image_px, const paint::image& img) { - img.paste(graph, pos.x, pos.y); + img.stretch(rectangle{ img.size() }, graph, rectangle{ pos, ::nana::size(image_px, image_px) }); } void item_text(graph_reference graph, const nana::point& pos, const nana::string& text, unsigned text_pixels, const attr& at) @@ -200,35 +200,35 @@ namespace nana void checked(std::size_t index, bool check) { - if(root_.items.size() > index) + if (root_.items.size() <= index) + return; + + item_type & m = root_.items[index]; + if(check && (checks::option == m.style)) { - item_type & m = root_.items[index]; - if(check && (checks::option == m.style)) + if(index) { - if(index) + std::size_t i = index; + do { - std::size_t i = index; - do - { - item_type& el = root_.items[--i]; - if(el.flags.splitter) break; - - if(checks::option == el.style) - el.flags.checked = false; - }while(i); - } - - for(std::size_t i = index + 1; i < root_.items.size(); ++i) - { - item_type & el = root_.items[i]; + item_type& el = root_.items[--i]; if(el.flags.splitter) break; if(checks::option == el.style) el.flags.checked = false; - } + }while(i); + } + + for(std::size_t i = index + 1; i < root_.items.size(); ++i) + { + item_type & el = root_.items[i]; + if(el.flags.splitter) break; + + if(checks::option == el.style) + el.flags.checked = false; } - m.flags.checked = check; } + m.flags.checked = check; } menu_type& data() @@ -304,7 +304,7 @@ namespace nana : public drawer_trigger { public: - typedef menu_item_type::item_proxy item_proxy; + using item_proxy = menu_item_type::item_proxy; renderer_interface * renderer; @@ -330,12 +330,12 @@ namespace nana detail_.monitor_pos = API::cursor_position(); } - void mouse_move(graph_reference, const arg_mouse& arg) + void mouse_move(graph_reference graph, const arg_mouse& arg) { state_.nullify_mouse = false; - if(track_mouse(arg.pos.x, arg.pos.y)) + if(track_mouse(arg.pos)) { - draw(); + refresh(graph); API::lazy_refresh(); } } @@ -350,9 +350,70 @@ namespace nana state_.nullify_mouse = false; } - void refresh(graph_reference) + void refresh(graph_reference graph) { - draw(); + if (nullptr == menu_) return; + + _m_adjust_window_size(); + + renderer->background(graph, *widget_); + + const unsigned item_h_px = _m_item_height(); + const unsigned image_px = item_h_px - 2; + nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px); + + unsigned strpixels = item_r.width - 60; + + int text_top_off = (item_h_px - graph.text_extent_size(STR("jh({[")).height) / 2; + + std::size_t pos = 0; + for (auto & m : menu_->items) + { + if (m.flags.splitter) + { + graph_->set_color(colors::gray_border); + graph_->line({ item_r.x + 40, item_r.y }, { static_cast(graph.width()) - 1, item_r.y }); + item_r.y += 2; + ++pos; + continue; + } + + renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m); + //Draw item background + renderer->item(*graph_, item_r, attr); + + //Draw text, the text is transformed from orignal for hotkey character + nana::char_t hotkey; + nana::string::size_type hotkey_pos; + nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); + + if (m.image.empty() == false) + renderer->item_image(graph, nana::point(item_r.x + 5, item_r.y + static_cast(item_h_px - image_px) / 2 - 1), image_px, m.image); + + renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); + + if (hotkey) + { + m.hotkey = hotkey; + if (m.flags.enabled) + { + unsigned off_w = (hotkey_pos ? graph.text_extent_size(text, static_cast(hotkey_pos)).width : 0); + nana::size hotkey_size = graph.text_extent_size(text.c_str() + hotkey_pos, 1); + int x = item_r.x + 40 + off_w; + int y = item_r.y + text_top_off + hotkey_size.height; + + graph_->set_color(colors::black); + graph_->line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }); + } + } + + if (m.sub_menu) + renderer->sub_arrow(graph, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr); + + item_r.y += item_r.height + 1; + + ++pos; + } } std::size_t active() const @@ -411,21 +472,21 @@ namespace nana state_.active = pos; state_.sub_window = false; - draw(); + refresh(*graph_); return true; } return false; } - bool track_mouse(int x, int y) + bool track_mouse(const ::nana::point& pos) { - if(state_.nullify_mouse == false) + if (!state_.nullify_mouse) { - std::size_t index = _m_get_index_by_pos(x, y); - if(index != state_.active) + std::size_t index = _m_get_index_by_pos(pos.x, pos.y); + if (index != state_.active) { - if((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window) + if ((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window) return false; state_.active = (index != npos && menu_->items.at(index).flags.splitter) ? npos : index; @@ -433,6 +494,7 @@ namespace nana return true; } } + return false; } @@ -451,29 +513,35 @@ namespace nana state_.sub_window = subw; } - menu_type* retrive_sub_menu(nana::point& pos, std::size_t interval) const + menu_type* get_sub(nana::point& pos, unsigned long& tmstamp) const { - if(state_.active != npos && (nana::system::timestamp() - state_.active_timestamp >= interval)) + if (npos == state_.active) + return nullptr; + + auto sub = menu_->items.at(state_.active).sub_menu; + if (sub) { - pos.x = graph_->width() - 2; + pos.x = static_cast(graph_->width()) - 2; pos.y = 2; - std::size_t index = 0; - for(auto & m : menu_->items) + auto index = state_.active; + for (auto & m : menu_->items) { - if(false == m.flags.splitter) + if (m.flags.splitter) { - if(index == state_.active) - break; - - pos.y += _m_item_height() + 1; - } - else pos.y += 2; + continue; + } - ++index; + if (0 == index) + break; + + pos.y += _m_item_height() + 1; + --index; } - return (menu_->items.at(state_.active).sub_menu); + + tmstamp = state_.active_timestamp; + return sub; } return nullptr; } @@ -498,8 +566,7 @@ namespace nana state_.active = index; state_.active_timestamp = nana::system::timestamp(); - draw(); - API::update_window(*widget_); + API::refresh_window(*widget_); return 2; } else if(m.flags.enabled) @@ -514,71 +581,6 @@ namespace nana } return 0; } - - void draw() const - { - if(nullptr == menu_) return; - - _m_adjust_window_size(); - - renderer->background(*graph_, *widget_); - - const unsigned item_h_px = _m_item_height(); - nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px); - - unsigned strpixels = item_r.width - 60; - - int text_top_off = (item_h_px - graph_->text_extent_size(STR("jh({[")).height) / 2; - - std::size_t pos = 0; - for(auto & m : menu_->items) - { - if(m.flags.splitter) - { - graph_->set_color(colors::gray_border); - graph_->line({ item_r.x + 40, item_r.y }, { static_cast(graph_->width()) - 1, item_r.y }); - item_r.y += 2; - ++pos; - continue; - } - - renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m); - //Draw item background - renderer->item(*graph_, item_r, attr); - - //Draw text, the text is transformed from orignal for hotkey character - nana::char_t hotkey; - nana::string::size_type hotkey_pos; - nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); - - if(m.image.empty() == false) - renderer->item_image(*graph_, nana::point(item_r.x + 5, item_r.y + (item_h_px - m.image.size().height) / 2), m.image); - - renderer->item_text(*graph_, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); - - if(hotkey) - { - m.hotkey = hotkey; - if(m.flags.enabled) - { - unsigned off_w = (hotkey_pos ? graph_->text_extent_size(text, static_cast(hotkey_pos)).width : 0); - nana::size hotkey_size = graph_->text_extent_size(text.c_str() + hotkey_pos, 1); - int x = item_r.x + 40 + off_w; - int y = item_r.y + text_top_off + hotkey_size.height; - - graph_->set_color(colors::black); - graph_->line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }); - } - } - - if(m.sub_menu) - renderer->sub_arrow(*graph_, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr); - - item_r.y += item_r.height + 1; - - ++pos; - } - } private: static renderer_interface::attr _m_make_renderer_attr(bool active, const menu_item_type & m) { @@ -683,10 +685,10 @@ namespace nana struct state { - std::size_t active; - unsigned long active_timestamp; - unsigned long sub_window: 1; - unsigned long nullify_mouse: 1; + std::size_t active; + unsigned active_timestamp; + bool sub_window: 1; + bool nullify_mouse: 1; }state_; struct widget_detail @@ -699,15 +701,18 @@ namespace nana class menu_window : public widget_object { - typedef menu_drawer drawer_type; - typedef widget_object base_type; + using drawer_type = menu_drawer; + using base_type = widget_object; public: - typedef menu_builder::item_type item_type; + using item_type = menu_builder::item_type; - menu_window(window wd, const point& pos, renderer_interface * rdptr) + + menu_window(window wd, bool is_wd_parent_menu, const point& pos, renderer_interface * rdptr) + //add a is_wd_parent_menu to determine whether the menu wants the focus. + //if a submenu gets the focus, the program may cause a crash error when the submenu is being destroyed : base_type(wd, false, rectangle(pos, nana::size(2, 2)), appear::bald()), - want_focus_(nullptr == wd || (API::focus_window() != wd)), - event_focus_(nullptr) + want_focus_{ (!wd) || ((!is_wd_parent_menu) && (API::focus_window() != wd)) }, + event_focus_{ nullptr } { caption(STR("nana menu window")); get_drawer_trigger().close_menu_tree([this]{ this->_m_close_all(); }); @@ -718,7 +723,19 @@ namespace nana submenu_.child = submenu_.parent = nullptr; submenu_.object = nullptr; - _m_make_mouse_event(); + state_.mouse_pos = API::cursor_position(); + events().mouse_move.connect_unignorable([this]{ + nana::point pos = API::cursor_position(); + if (pos != state_.mouse_pos) + { + menu_window * root = this; + while (root->submenu_.parent) + root = root->submenu_.parent; + root->state_.auto_popup_submenu = true; + + state_.mouse_pos = pos; + } + }); } void popup(menu_type& menu, bool owner_menubar) @@ -745,13 +762,19 @@ namespace nana _m_key_down(arg); }); - events().mouse_up.connect_unignorable([this]{ - pick(); + events().mouse_down.connect_unignorable([this](const arg_mouse& arg) + { + this->_m_open_sub(0); //Try to open submenu immediately + }); + + events().mouse_up.connect_unignorable([this](const arg_mouse& arg){ + if (arg.left_button) + pick(); }); timer_.interval(100); timer_.elapse([this]{ - this->_m_check_repeatly(); + this->_m_open_sub(500); //Try to open submenu }); timer_.start(); @@ -797,29 +820,27 @@ namespace nana bool submenu(bool enter) { - menu_window * object = this; - while (object->submenu_.child) - object = object->submenu_.child; + menu_window * menu_wd = this; + while (menu_wd->submenu_.child) + menu_wd = menu_wd->submenu_.child; state_.auto_popup_submenu = false; - if (enter) + if (!enter) { - if (object->submenu_.parent) + if (menu_wd->submenu_.parent) { - auto & sub = object->submenu_.parent->submenu_; + auto & sub = menu_wd->submenu_.parent->submenu_; sub.child = nullptr; sub.object = nullptr; - object->close(); + menu_wd->close(); return true; } return false; } - nana::point pos; - menu_type * sbm = object->get_drawer_trigger().retrive_sub_menu(pos, 0); - return object->_m_show_submenu(sbm, pos, true); + return menu_wd->_m_manipulate_sub(0, true); } int send_shortkey(nana::char_t key) @@ -965,63 +986,51 @@ namespace nana } } - void _m_make_mouse_event() + bool _m_manipulate_sub(unsigned long delay_ms, bool forced) { - state_.mouse_pos = API::cursor_position(); - events().mouse_move.connect_unignorable([this]{ - _m_mouse_event(); - }); - } + auto & drawer = get_drawer_trigger(); + ::nana::point pos; + unsigned long tmstamp; - void _m_mouse_event() - { - nana::point pos = API::cursor_position(); - if(pos != state_.mouse_pos) + auto menu_ptr = drawer.get_sub(pos, tmstamp); + + if (menu_ptr == submenu_.object) + return false; + + if (menu_ptr && (::nana::system::timestamp() - tmstamp < delay_ms)) + return false; + + if (submenu_.object && (menu_ptr != submenu_.object)) { - menu_window * root = this; - while(root->submenu_.parent) - root = root->submenu_.parent; - root->state_.auto_popup_submenu = true; - - state_.mouse_pos = pos; - } - } - - bool _m_show_submenu(menu_type* sbm, nana::point pos, bool forced) - { - auto & mdtrigger = get_drawer_trigger(); - if(submenu_.object && (sbm != submenu_.object)) - { - mdtrigger.set_sub_window(false); + drawer.set_sub_window(false); submenu_.child->close(); submenu_.child = nullptr; submenu_.object = nullptr; } - if(sbm) + if (menu_ptr) { menu_window * root = this; - while(root->submenu_.parent) + while (root->submenu_.parent) root = root->submenu_.parent; - if((submenu_.object == nullptr) && sbm && (forced || root->state_.auto_popup_submenu)) + if ((submenu_.object == nullptr) && menu_ptr && (forced || root->state_.auto_popup_submenu)) { - sbm->item_pixels = mdtrigger.data()->item_pixels; - sbm->gaps = mdtrigger.data()->gaps; - pos.x += sbm->gaps.x; - pos.y += sbm->gaps.y; + menu_ptr->item_pixels = drawer.data()->item_pixels; + menu_ptr->gaps = drawer.data()->gaps; + pos += menu_ptr->gaps; - menu_window & mwnd = form_loader()(handle(), pos, mdtrigger.renderer); + menu_window & mwnd = form_loader()(handle(), true, pos, drawer.renderer); mwnd.state_.self_submenu = true; - submenu_.child = & mwnd; + submenu_.child = &mwnd; submenu_.child->submenu_.parent = this; - submenu_.object = sbm; + submenu_.object = menu_ptr; API::set_window_z_order(handle(), mwnd.handle(), z_order_action::none); - mwnd.popup(*sbm, state_.owner_menubar); - mdtrigger.set_sub_window(true); - if(forced) + mwnd.popup(*menu_ptr, state_.owner_menubar); + drawer.set_sub_window(true); + if (forced) mwnd.goto_next(true); return true; @@ -1030,17 +1039,16 @@ namespace nana return false; } - void _m_check_repeatly() + void _m_open_sub(unsigned delay_ms) //check_repeatly { if(state_.auto_popup_submenu) { - nana::point pos = API::cursor_position(); + auto pos = API::cursor_position(); - drawer_type& drawer = get_drawer_trigger(); API::calc_window_point(handle(), pos); - drawer.track_mouse(pos.x, pos.y); - menu_type* sbm = drawer.retrive_sub_menu(pos, 500); - _m_show_submenu(sbm, pos, false); + get_drawer_trigger().track_mouse(pos); + + _m_manipulate_sub(delay_ms, false); } } private: @@ -1293,7 +1301,7 @@ namespace nana { close(); - impl_->uiobj = &(form_loader()(wd, point(x, y), &(*impl_->mbuilder.renderer()))); + impl_->uiobj = &(form_loader()(wd, false, point(x, y), &(*impl_->mbuilder.renderer()))); impl_->uiobj->events().destroy.connect_unignorable([this]{ impl_->uiobj = nullptr; if (impl_->destroy_answer) diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 3baaf0d0..7ad4584a 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -89,6 +89,15 @@ namespace nana { return unknown_; } + bool trigger::stoped() const + { + return stop_; + } + bool trigger::stop(bool s) + { + std::swap(s,stop_); + return s; + } void trigger::refresh(graph_reference) { @@ -197,5 +206,13 @@ namespace nana { return get_drawer_trigger().unknown(); } + bool progress::stop(bool s) + { + return get_drawer_trigger().stop(s); + } + bool progress::stoped() const + { + return get_drawer_trigger().stoped(); + } //end class progress }//end namespace nana diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index a7f05a56..6cc33b51 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1482,6 +1482,8 @@ namespace nana{ namespace widgets behavior_->pre_calc_lines(width_pixels()); _m_scrollbar(); + + move_caret(points_.caret); return true; } diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 81b75c7f..980a80ac 100644 --- a/source/gui/widgets/toolbar.cpp +++ b/source/gui/widgets/toolbar.cpp @@ -11,10 +11,10 @@ */ #include -#include -#include #include +#include + namespace nana { arg_toolbar::arg_toolbar(toolbar& tbar, std::size_t btn) @@ -25,13 +25,6 @@ namespace nana { namespace toolbar { - struct listitem - { - nana::string text; - nana::paint::image image; - bool enable; - }; - struct item_type { enum kind{ button, container}; @@ -43,28 +36,23 @@ namespace nana unsigned pixels{0}; nana::size textsize; bool enable{true}; - window other{nullptr}; kind type; - std::function answer; - std::vector children; item_type(const nana::string& text, const nana::paint::image& img, kind type) :text(text), image(img), type(type) {} }; - class container - { - container(const container&) = delete; - container& operator=(const container&) = delete; - public: - typedef std::vector::size_type size_type; - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; - container() = default; - ~container() + + class item_container + { + public: + using container_type = std::vector; + using size_type = container_type::size_type; + + ~item_container() { for(auto ptr : cont_) delete ptr; @@ -98,7 +86,7 @@ namespace nana cont_.push_back(nullptr); } - void push_back() + void separate() { cont_.push_back(nullptr); } @@ -108,35 +96,17 @@ namespace nana return cont_.size(); } - item_type* at(size_type n) + container_type& container() { - if(n < cont_.size()) - return cont_[n]; - - throw std::out_of_range("toolbar: bad index!"); + return cont_; } - iterator begin() + item_type * at(size_type pos) { - return cont_.begin(); - } - - iterator end() - { - return cont_.end(); - } - - const_iterator begin() const - { - return cont_.cbegin(); - } - - const_iterator end() const - { - return cont_.cend(); + return cont_.at(pos); } private: - std::vector cont_; + container_type cont_; }; class item_renderer @@ -152,38 +122,36 @@ namespace nana void operator()(int x, int y, unsigned width, unsigned height, item_type& item, state_t state) { //draw background - if(state != state_t::normal) - graph.rectangle({ x, y, width, height }, false, { 0x33, 0x99, 0xFF }); - switch(state) + if (state != state_t::normal) { - case state_t::highlighted: - graph.gradual_rectangle({ x + 1, y + 1, width - 2, height - 2 }, bgcolor, { 0xC0, 0xDD, 0xFC }, true); - break; - case state_t::selected: - graph.gradual_rectangle({ x + 1, y + 1, width - 2, height - 2 }, bgcolor, { 0x99, 0xCC, 0xFF }, true); - default: break; + nana::rectangle background_r(x, y, width, height); + graph.rectangle(background_r, false, static_cast(0x3399FF)); + + if (state_t::highlighted == state || state_t::selected == state) + graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(state_t::selected == state ? 0x99CCFF : 0xC0DDFC), true); } - if(item.image.empty() == false) + if(!item.image.empty()) { - nana::size size = item.image.size(); - if(size.width > scale) size.width = scale; - if(size.height > scale) size.height = scale; + auto imgsize = item.image.size(); + + if (imgsize.width > scale) imgsize.width = scale; + if (imgsize.height > scale) imgsize.height = scale; nana::point pos(x, y); - pos.x += static_cast(scale + extra_size - size.width) / 2; - pos.y += static_cast(height - size.height) / 2; + pos.x += static_cast(scale + extra_size - imgsize.width) / 2; + pos.y += static_cast(height - imgsize.height) / 2; - item.image.paste(::nana::rectangle{ size }, graph, pos); + item.image.paste(::nana::rectangle{ imgsize }, graph, pos); if(item.enable == false) { - nana::paint::graphics gh(size); - gh.bitblt(::nana::rectangle{ size }, graph, pos); + nana::paint::graphics gh(imgsize); + gh.bitblt(::nana::rectangle{ imgsize }, graph, pos); gh.rgb_to_wb(); gh.paste(graph, pos.x, pos.y); } else if(state == state_t::normal) - graph.blend(nana::rectangle(pos, size), ::nana::color(0xc0, 0xdd, 0xfc).blend(bgcolor, 0.5), 0.25); + graph.blend(nana::rectangle(pos, imgsize), ::nana::color(0xc0, 0xdd, 0xfc).blend(bgcolor, 0.5), 0.25); x += scale; width -= scale; @@ -204,13 +172,15 @@ namespace nana struct drawer::drawer_impl_type { - event_handle event_size{nullptr}; + event_handle event_size{ nullptr }; + paint::graphics* graph_ptr{ nullptr }; + unsigned scale{16}; bool textout{false}; size_type which{npos}; item_renderer::state_t state{item_renderer::state_t::normal}; - container cont; + item_container items; ::nana::tooltip tooltip; }; @@ -225,116 +195,118 @@ namespace nana delete impl_; } - void drawer::append(const nana::string& text, const nana::paint::image& img) + item_container& drawer::items() const { - impl_->cont.push_back(text, img); - } - - void drawer::append() - { - impl_->cont.push_back(); - } - - bool drawer::enable(drawer::size_type n) const - { - if(impl_->cont.size() > n) - { - auto item = impl_->cont.at(n); - return (item && item->enable); - } - return false; - } - - bool drawer::enable(size_type n, bool eb) - { - if(impl_->cont.size() > n) - { - item_type * item = impl_->cont.at(n); - if(item && (item->enable != eb)) - { - item->enable = eb; - return true; - } - } - return false; + return impl_->items; } void drawer::scale(unsigned s) { impl_->scale = s; - for(auto m : impl_->cont) - _m_fill_pixels(m, true); + for(auto m : impl_->items.container()) + _m_calc_pixels(m, true); } - void drawer::refresh(graph_reference) + void drawer::refresh(graph_reference graph) { - _m_draw(); + int x = 2, y = 2; + + auto bgcolor = API::bgcolor(widget_->handle()); + graph.set_text_color(bgcolor); + graph.gradual_rectangle(rectangle{ graph.size() }, bgcolor.blend(colors::white, 0.9), bgcolor.blend(colors::black, 0.95), true); + + item_renderer ir(graph, impl_->textout, impl_->scale, bgcolor); + size_type index = 0; + + for (auto item : impl_->items.container()) + { + if (item) + { + _m_calc_pixels(item, false); + ir(x, y, item->pixels, impl_->scale + ir.extra_size, *item, (index == impl_->which ? impl_->state : item_renderer::state_t::normal)); + x += item->pixels; + } + else + { + x += 2; + graph.line({ x, y + 2 }, { x, y + static_cast(impl_->scale + ir.extra_size) - 4 }, static_cast(0x808080)); + x += 4; + } + ++index; + } } void drawer::attached(widget_reference widget, graph_reference graph) { - graph_ = &graph; + impl_->graph_ptr = &graph; widget_ = static_cast< ::nana::toolbar*>(&widget); - widget.caption(STR("Nana Toolbar")); - impl_->event_size = widget.events().resized.connect_unignorable(std::bind(&drawer::_m_owner_sized, this, std::placeholders::_1)); + widget.caption(L"Nana Toolbar"); + impl_->event_size = API::events(widget.parent()).resized.connect_unignorable([this](const arg_resized& arg) + { + auto wd = widget_->handle(); + API::window_size(wd, nana::size(arg.width, widget_->size().height)); + API::update_window(wd); + }); } void drawer::detached() { API::umake_event(impl_->event_size); impl_->event_size = nullptr; + impl_->graph_ptr = nullptr; } void drawer::mouse_move(graph_reference graph, const arg_mouse& arg) { - if(arg.left_button == false) + if (arg.left_button) + return; + + size_type which = _m_which(arg.pos, true); + if(impl_->which != which) { - size_type which = _m_which(arg.pos.x, arg.pos.y, true); - if(impl_->which != which) + auto & container = impl_->items.container(); + if (impl_->which != npos && container.at(impl_->which)->enable) { - if (impl_->which != npos && impl_->cont.at(impl_->which)->enable) - { - ::nana::arg_toolbar arg{ *widget_, impl_->which }; - widget_->events().leave.emit(arg); - } - - impl_->which = which; - if(which == npos || impl_->cont.at(which)->enable) - { - impl_->state = (arg.left_button ? item_renderer::state_t::selected : item_renderer::state_t::highlighted); - - _m_draw(); - API::lazy_refresh(); - - if (impl_->state == item_renderer::state_t::highlighted) - { - ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().enter.emit(arg); - } - } - - if(which != npos) - impl_->tooltip.show(widget_->handle(), nana::point(arg.pos.x, arg.pos.y + 20), (*(impl_->cont.begin() + which))->text, 0); - else - impl_->tooltip.close(); + ::nana::arg_toolbar arg{ *widget_, impl_->which }; + widget_->events().leave.emit(arg); } + + impl_->which = which; + if (which == npos || container.at(which)->enable) + { + impl_->state = (arg.left_button ? item_renderer::state_t::selected : item_renderer::state_t::highlighted); + + refresh(graph); + API::lazy_refresh(); + + if (impl_->state == item_renderer::state_t::highlighted) + { + ::nana::arg_toolbar arg{ *widget_, which }; + widget_->events().enter.emit(arg); + } + } + + if(which != npos) + impl_->tooltip.show(widget_->handle(), nana::point(arg.pos.x, arg.pos.y + 20), (*(container.begin() + which))->text, 0); + else + impl_->tooltip.close(); } } - void drawer::mouse_leave(graph_reference, const arg_mouse&) + void drawer::mouse_leave(graph_reference graph, const arg_mouse&) { if(impl_->which != npos) { size_type which = impl_->which; impl_->which = npos; - _m_draw(); + refresh(graph); API::lazy_refresh(); - if (which != npos && impl_->cont.at(which)->enable) + if (which != npos && impl_->items.at(which)->enable) { ::nana::arg_toolbar arg{ *widget_, which }; widget_->events().leave.emit(arg); @@ -343,22 +315,22 @@ namespace nana impl_->tooltip.close(); } - void drawer::mouse_down(graph_reference, const arg_mouse&) + void drawer::mouse_down(graph_reference graph, const arg_mouse&) { impl_->tooltip.close(); - if(impl_->which != npos && (impl_->cont.at(impl_->which)->enable)) + if(impl_->which != npos && (impl_->items.at(impl_->which)->enable)) { impl_->state = item_renderer::state_t::selected; - _m_draw(); + refresh(graph); API::lazy_refresh(); } } - void drawer::mouse_up(graph_reference, const arg_mouse& arg) + void drawer::mouse_up(graph_reference graph, const arg_mouse& arg) { if(impl_->which != npos) { - size_type which = _m_which(arg.pos.x, arg.pos.y, false); + size_type which = _m_which(arg.pos, false); if(impl_->which == which) { ::nana::arg_toolbar arg{ *widget_, which }; @@ -372,89 +344,47 @@ namespace nana impl_->state = (which == npos ? item_renderer::state_t::normal : item_renderer::state_t::highlighted); } - _m_draw(); + refresh(graph); API::lazy_refresh(); } } - drawer::size_type drawer::_m_which(int x, int y, bool want_if_disabled) const + drawer::size_type drawer::_m_which(point pos, bool want_if_disabled) const { - if(x < 2 || y < 2 || y >= static_cast(impl_->scale + item_renderer::extra_size + 2)) return npos; + if (pos.x < 2 || pos.y < 2 || pos.y >= static_cast(impl_->scale + item_renderer::extra_size + 2)) return npos; - x -= 2; + pos.x -= 2; - size_type pos = 0; - for(auto m: impl_->cont) + std::size_t index = 0; + for(auto m: impl_->items.container()) { - bool compart = (nullptr == m); + auto px = static_cast(m ? m->pixels : 3); - if(x < static_cast(compart ? 3 : m->pixels)) - return ((compart || (m->enable == false && want_if_disabled == false)) ? npos : pos); + if(pos.x < px) + return (((!m) || (!m->enable && !want_if_disabled)) ? npos : index); - x -= (compart ? 3 : m->pixels); + pos.x -= px; - ++pos; + ++index; } return npos; } - void drawer::_m_draw_background(const ::nana::color& clr) + void drawer::_m_calc_pixels(item_type* item, bool force) { - graph_->gradual_rectangle(::nana::rectangle{ graph_->size() }, clr.blend(colors::white, 0.9), clr.blend(colors::black, 0.95), true); - } - - void drawer::_m_draw() - { - int x = 2, y = 2; - - auto bgcolor = API::bgcolor(widget_->handle()); - graph_->set_text_color(bgcolor); - _m_draw_background(bgcolor); - - item_renderer ir(*graph_, impl_->textout, impl_->scale, bgcolor); - size_type index = 0; - - for(auto item : impl_->cont) + if (item && (force || (0 == item->pixels))) { - if(item) - { - _m_fill_pixels(item, false); - ir(x, y, item->pixels, impl_->scale + ir.extra_size, *item, (index == impl_->which ? impl_->state : item_renderer::state_t::normal)); - x += item->pixels; - } - else - { - graph_->line({ x + 2, y + 2 }, { x + 2, y + static_cast(impl_->scale + ir.extra_size) - 4 }, { 0x80, 0x80, 0x80 }); - x += 6; - } - ++index; - } - } + if (item->text.size()) + item->textsize = impl_->graph_ptr->text_extent_size(item->text); - void drawer::_m_owner_sized(const arg_resized& arg) - { - auto wd = widget_->handle(); - API::window_size(wd, nana::size(arg.width, widget_->size().height)); - _m_draw(); - API::update_window(wd); - } - - void drawer::_m_fill_pixels(item_type* item, bool force) - { - if(item && (force || (0 == item->pixels))) - { - if(item->text.size()) - item->textsize = graph_->text_extent_size(item->text); - - if(item->image.empty() == false) + if (item->image.empty() == false) item->pixels = impl_->scale + item_renderer::extra_size; - if(item->textsize.width && impl_->textout) + if (item->textsize.width && impl_->textout) item->pixels += item->textsize.width + 8; } } - //};//class drawer - + //class drawer }//end namespace toolbar }//end namespace drawerbase @@ -469,33 +399,48 @@ namespace nana create(wd, r, visible); } - void toolbar::append() + void toolbar::separate() { - get_drawer_trigger().append(); + get_drawer_trigger().items().separate(); API::refresh_window(handle()); } void toolbar::append(const nana::string& text, const nana::paint::image& img) { - get_drawer_trigger().append(text, img); + get_drawer_trigger().items().push_back(text, img); API::refresh_window(handle()); } void toolbar::append(const nana::string& text) { - get_drawer_trigger().append(text, nana::paint::image()); + get_drawer_trigger().items().push_back(text, {}); API::refresh_window(this->handle()); } - bool toolbar::enable(size_type n) const + bool toolbar::enable(size_type pos) const { - return get_drawer_trigger().enable(n); + auto & items = get_drawer_trigger().items(); + + if (items.size() <= pos) + return false; + + auto m = items.at(pos); + return (m && m->enable); } - void toolbar::enable(size_type n, bool eb) + void toolbar::enable(size_type pos, bool eb) { - if(get_drawer_trigger().enable(n, eb)) - API::refresh_window(this->handle()); + auto & items = get_drawer_trigger().items(); + + if (items.size() > pos) + { + auto m = items.at(pos); + if (m && (m->enable != eb)) + { + m->enable = eb; + API::refresh_window(this->handle()); + } + } } void toolbar::scale(unsigned s) @@ -503,5 +448,5 @@ namespace nana get_drawer_trigger().scale(s); API::refresh_window(handle()); } - //}; class toolbar + //end class toolbar }//end namespace nana diff --git a/source/paint/detail/native_paint_interface.cpp b/source/paint/detail/native_paint_interface.cpp index 3aea449d..05b66977 100644 --- a/source/paint/detail/native_paint_interface.cpp +++ b/source/paint/detail/native_paint_interface.cpp @@ -156,6 +156,9 @@ namespace detail nana::size text_extent_size(drawable_type dw, const nana::char_t * text, std::size_t len) { + if (nullptr == dw || nullptr == text || 0 == len) + return{}; + nana::size extents = raw_text_extent_size(dw, text, len); const nana::char_t* const end = text + len;